Ledger integration (#931)
Merges the keybase and Ledger code from go-crypto (which is no more) into the SDK Adds support for Ledger into gaiacli Cherry-picks updated error handling from #1158
This commit is contained in:
+55
-27
@@ -12,7 +12,9 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/tendermint/go-crypto/keys"
|
||||
ccrypto "github.com/cosmos/cosmos-sdk/crypto"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
)
|
||||
|
||||
@@ -21,6 +23,8 @@ const (
|
||||
flagRecover = "recover"
|
||||
flagNoBackup = "no-backup"
|
||||
flagDryRun = "dry-run"
|
||||
flagAccount = "account"
|
||||
flagIndex = "index"
|
||||
)
|
||||
|
||||
func addKeyCommand() *cobra.Command {
|
||||
@@ -32,10 +36,13 @@ If you select --seed/-s you can recover a key from the seed
|
||||
phrase, otherwise, a new key will be generated.`,
|
||||
RunE: runAddCmd,
|
||||
}
|
||||
cmd.Flags().StringP(flagType, "t", "ed25519", "Type of private key (ed25519|secp256k1|ledger)")
|
||||
cmd.Flags().StringP(flagType, "t", "secp256k1", "Type of private key (secp256k1|ed25519)")
|
||||
cmd.Flags().Bool(client.FlagUseLedger, false, "Store a local reference to a private key on a Ledger device")
|
||||
cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating")
|
||||
cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)")
|
||||
cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore")
|
||||
cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation")
|
||||
cmd.Flags().Uint32(flagIndex, 0, "Index number for HD derivation")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -70,21 +77,34 @@ func runAddCmd(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
pass, err = client.GetCheckPassword(
|
||||
"Enter a passphrase for your key:",
|
||||
"Repeat the passphrase:", buf)
|
||||
if err != nil {
|
||||
return err
|
||||
// ask for a password when generating a local key
|
||||
if !viper.GetBool(client.FlagUseLedger) {
|
||||
pass, err = client.GetCheckPassword(
|
||||
"Enter a passphrase for your key:",
|
||||
"Repeat the passphrase:", buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if viper.GetBool(flagRecover) {
|
||||
if viper.GetBool(client.FlagUseLedger) {
|
||||
account := uint32(viper.GetInt(flagAccount))
|
||||
index := uint32(viper.GetInt(flagIndex))
|
||||
path := ccrypto.DerivationPath{44, 118, account, 0, index}
|
||||
algo := keys.SigningAlgo(viper.GetString(flagType))
|
||||
info, err := kb.CreateLedger(name, path, algo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printCreate(info, "")
|
||||
} else if viper.GetBool(flagRecover) {
|
||||
seed, err := client.GetSeed(
|
||||
"Enter your recovery seed phrase:", buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := kb.Recover(name, pass, seed)
|
||||
info, err := kb.CreateFundraiserKey(name, seed, pass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,8 +112,8 @@ func runAddCmd(cmd *cobra.Command, args []string) error {
|
||||
viper.Set(flagNoBackup, true)
|
||||
printCreate(info, "")
|
||||
} else {
|
||||
algo := keys.CryptoAlgo(viper.GetString(flagType))
|
||||
info, seed, err := kb.Create(name, pass, algo)
|
||||
algo := keys.SigningAlgo(viper.GetString(flagType))
|
||||
info, seed, err := kb.CreateMnemonic(name, keys.English, pass, algo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -108,7 +128,7 @@ func printCreate(info keys.Info, seed string) {
|
||||
case "text":
|
||||
printInfo(info)
|
||||
// print seed unless requested not to.
|
||||
if !viper.GetBool(flagNoBackup) {
|
||||
if !viper.GetBool(client.FlagUseLedger) && !viper.GetBool(flagNoBackup) {
|
||||
fmt.Println("**Important** write this seed phrase in a safe place.")
|
||||
fmt.Println("It is the only way to recover your account if you ever forget your password.")
|
||||
fmt.Println()
|
||||
@@ -139,7 +159,12 @@ func printCreate(info keys.Info, seed string) {
|
||||
type NewKeyBody struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
Seed string `json:"seed"`
|
||||
}
|
||||
|
||||
// new key response REST body
|
||||
type NewKeyResponse struct {
|
||||
Address string `json:"address"`
|
||||
Mnemonic string `json:"mnemonic"`
|
||||
}
|
||||
|
||||
// add new key REST handler
|
||||
@@ -172,16 +197,11 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("You have to specify a password for the locally stored account."))
|
||||
return
|
||||
}
|
||||
if m.Seed == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("You have to specify a seed for the locally stored account."))
|
||||
return
|
||||
}
|
||||
|
||||
// check if already exists
|
||||
infos, err := kb.List()
|
||||
for _, i := range infos {
|
||||
if i.Name == m.Name {
|
||||
if i.GetName() == m.Name {
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
w.Write([]byte(fmt.Sprintf("Account with name %s already exists.", m.Name)))
|
||||
return
|
||||
@@ -189,22 +209,30 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// create account
|
||||
info, err := kb.Recover(m.Name, m.Password, m.Seed)
|
||||
info, mnemonic, err := kb.CreateMnemonic(m.Name, keys.English, m.Password, keys.Secp256k1)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
w.Write([]byte(info.PubKey.Address().String()))
|
||||
bz, err := json.Marshal(NewKeyResponse{
|
||||
Address: info.GetPubKey().Address().String(),
|
||||
Mnemonic: mnemonic,
|
||||
})
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
w.Write(bz)
|
||||
}
|
||||
|
||||
// function to just a new seed to display in the UI before actually persisting it in the keybase
|
||||
func getSeed(algo keys.CryptoAlgo) string {
|
||||
func getSeed(algo keys.SigningAlgo) string {
|
||||
kb := client.MockKeyBase()
|
||||
pass := "throwing-this-key-away"
|
||||
name := "inmemorykey"
|
||||
|
||||
_, seed, _ := kb.Create(name, pass, algo)
|
||||
_, seed, _ := kb.CreateMnemonic(name, keys.English, pass, algo)
|
||||
return seed
|
||||
}
|
||||
|
||||
@@ -212,11 +240,11 @@ func getSeed(algo keys.CryptoAlgo) string {
|
||||
func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
algoType := vars["type"]
|
||||
// algo type defaults to ed25519
|
||||
// algo type defaults to secp256k1
|
||||
if algoType == "" {
|
||||
algoType = "ed25519"
|
||||
algoType = "secp256k1"
|
||||
}
|
||||
algo := keys.CryptoAlgo(algoType)
|
||||
algo := keys.SigningAlgo(algoType)
|
||||
|
||||
seed := getSeed(algo)
|
||||
w.Write([]byte(seed))
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/gorilla/mux"
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/gorilla/mux"
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -28,7 +28,7 @@ var showKeysCmd = &cobra.Command{
|
||||
func getKey(name string) (keys.Info, error) {
|
||||
kb, err := GetKeyBase()
|
||||
if err != nil {
|
||||
return keys.Info{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kb.Get(name)
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/gorilla/mux"
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
|
||||
@@ -49,6 +49,7 @@ func SetKeyBase(kb keys.Keybase) {
|
||||
// used for outputting keys.Info over REST
|
||||
type KeyOutput struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Address string `json:"address"`
|
||||
PubKey string `json:"pub_key"`
|
||||
Seed string `json:"seed,omitempty"`
|
||||
@@ -69,16 +70,17 @@ func Bech32KeysOutput(infos []keys.Info) ([]KeyOutput, error) {
|
||||
|
||||
// create a KeyOutput in bech32 format
|
||||
func Bech32KeyOutput(info keys.Info) (KeyOutput, error) {
|
||||
bechAccount, err := sdk.Bech32ifyAcc(sdk.Address(info.PubKey.Address().Bytes()))
|
||||
bechAccount, err := sdk.Bech32ifyAcc(sdk.Address(info.GetPubKey().Address().Bytes()))
|
||||
if err != nil {
|
||||
return KeyOutput{}, err
|
||||
}
|
||||
bechPubKey, err := sdk.Bech32ifyAccPub(info.PubKey)
|
||||
bechPubKey, err := sdk.Bech32ifyAccPub(info.GetPubKey())
|
||||
if err != nil {
|
||||
return KeyOutput{}, err
|
||||
}
|
||||
return KeyOutput{
|
||||
Name: info.Name,
|
||||
Name: info.GetName(),
|
||||
Type: info.GetType(),
|
||||
Address: bechAccount,
|
||||
PubKey: bechPubKey,
|
||||
}, nil
|
||||
@@ -91,7 +93,7 @@ func printInfo(info keys.Info) {
|
||||
}
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
|
||||
fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
|
||||
printKeyOutput(ko)
|
||||
case "json":
|
||||
out, err := MarshalJSON(ko)
|
||||
@@ -109,7 +111,7 @@ func printInfos(infos []keys.Info) {
|
||||
}
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
|
||||
fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
|
||||
for _, ko := range kos {
|
||||
printKeyOutput(ko)
|
||||
}
|
||||
@@ -123,5 +125,5 @@ func printInfos(infos []keys.Info) {
|
||||
}
|
||||
|
||||
func printKeyOutput(ko KeyOutput) {
|
||||
fmt.Printf("%s\t%s\t%s\n", ko.Name, ko.Address, ko.PubKey)
|
||||
fmt.Printf("%s\t%s\t%s\t%s\n", ko.Name, ko.Type, ko.Address, ko.PubKey)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user