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:
Christopher Goes
2018-06-29 02:54:47 +02:00
committed by GitHub
parent ac3adff1e8
commit 59aadf42aa
143 changed files with 2699 additions and 424 deletions
+55 -27
View File
@@ -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))
+1 -1
View File
@@ -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
View File
@@ -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)
+1 -1
View File
@@ -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"
)
+9 -7
View File
@@ -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)
}