Merge PR #3670: CLI support for showing bech32 addresses in Ledger devices

This commit is contained in:
Juan Leni 2019-02-25 12:34:39 +01:00 committed by Christopher Goes
parent c96d8f3e81
commit 3eb0acda88
10 changed files with 117 additions and 24 deletions

6
Gopkg.lock generated
View File

@ -513,12 +513,12 @@
version = "v0.9.0"
[[projects]]
digest = "1:fca24169988a61ea725d1326de30910d8049fe68bcbc194d28803f9a76dda380"
digest = "1:a2b34d1436ac24a0db176f84c015d80438602eeec12f2f8358bb72749711ab52"
name = "github.com/zondax/ledger-cosmos-go"
packages = ["."]
pruneopts = "UT"
revision = "69fdb8ce5e5b9d9c3b22b9248e117b231d4f06dd"
version = "v0.9.7"
revision = "e2f595b3b7b222e1cbe9daf89e73e4dcab02d54c"
version = "v0.9.8"
[[projects]]
digest = "1:f8e4c0b959174a1fa5946b12f1f2ac7ea5651bef20a9e4a8dac55dbffcaa6cd6"

View File

@ -44,7 +44,7 @@
[[constraint]]
name = "github.com/zondax/ledger-cosmos-go"
version = "=v0.9.7"
version = "=v0.9.8"
## deps without releases:

View File

@ -44,6 +44,7 @@ decoded automatically.
### Gaia CLI
* [\#3670] CLI support for showing bech32 addresses in Ledger devices
* [\#3711] Update `tx sign` to use `--from` instead of the deprecated `--name`
CLI flag.

View File

@ -1,20 +1,20 @@
package keys
import (
"errors"
"fmt"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"errors"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
"github.com/spf13/viper"
tmcrypto "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/multisig"
"github.com/tendermint/tendermint/libs/cli"
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
@ -24,6 +24,8 @@ const (
FlagPublicKey = "pubkey"
// FlagBechPrefix defines a desired Bech32 prefix encoding for a key.
FlagBechPrefix = "bech"
// FlagBechPrefix defines a desired Bech32 prefix encoding for a key.
FlagDevice = "device"
flagMultiSigThreshold = "multisig-threshold"
defaultMultiSigKeyName = "multi"
@ -33,13 +35,16 @@ var _ keys.Info = (*multiSigKey)(nil)
type multiSigKey struct {
name string
key crypto.PubKey
key tmcrypto.PubKey
}
func (m multiSigKey) GetName() string { return m.name }
func (m multiSigKey) GetType() keys.KeyType { return keys.TypeLocal }
func (m multiSigKey) GetPubKey() crypto.PubKey { return m.key }
func (m multiSigKey) GetPubKey() tmcrypto.PubKey { return m.key }
func (m multiSigKey) GetAddress() sdk.AccAddress { return sdk.AccAddress(m.key.Address()) }
func (m multiSigKey) GetPath() (*hd.BIP44Params, error) {
return nil, fmt.Errorf("BIP44 Paths are not available for this type")
}
func showKeysCmd() *cobra.Command {
cmd := &cobra.Command{
@ -53,6 +58,7 @@ func showKeysCmd() *cobra.Command {
cmd.Flags().String(FlagBechPrefix, sdk.PrefixAccount, "The Bech32 prefix encoding for a key (acc|val|cons)")
cmd.Flags().BoolP(FlagAddress, "a", false, "output the address only (overrides --output)")
cmd.Flags().BoolP(FlagPublicKey, "p", false, "output the public key only (overrides --output)")
cmd.Flags().BoolP(FlagDevice, "d", false, "output the address in the device")
cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures")
return cmd
@ -67,7 +73,7 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
return err
}
} else {
pks := make([]crypto.PubKey, len(args))
pks := make([]tmcrypto.PubKey, len(args))
for i, keyName := range args {
info, err := GetKeyInfo(keyName)
if err != nil {
@ -90,6 +96,7 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
isShowAddr := viper.GetBool(FlagAddress)
isShowPubKey := viper.GetBool(FlagPublicKey)
isShowDevice := viper.GetBool(FlagDevice)
isOutputSet := false
tmp := cmd.Flag(cli.OutputFlag)
@ -119,6 +126,26 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
printKeyInfo(info, bechKeyOut)
}
if isShowDevice {
if isShowPubKey {
return fmt.Errorf("the device flag (-d) can only be used for addresses not pubkeys")
}
if viper.GetString(FlagBechPrefix) != "acc" {
return fmt.Errorf("the device flag (-d) can only be used for accounts")
}
// Override and show in the device
if info.GetType() != keys.TypeLedger {
return fmt.Errorf("the device flag (-d) can only be used for accounts stored in devices")
}
hdpath, err := info.GetPath()
if err != nil {
return nil
}
return crypto.LedgerShowAddress(*hdpath, info.GetPubKey())
}
return nil
}

View File

@ -79,6 +79,21 @@ func Test_runShowCmd(t *testing.T) {
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
assert.NoError(t, err)
// Now try multisig key - set bech to acc + threshold=2
viper.Set(FlagBechPrefix, "acc")
viper.Set(FlagDevice, true)
viper.Set(flagMultiSigThreshold, 2)
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
assert.EqualError(t, err, "the device flag (-d) can only be used for accounts stored in devices")
viper.Set(FlagBechPrefix, "val")
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
assert.EqualError(t, err, "the device flag (-d) can only be used for accounts")
viper.Set(FlagPublicKey, true)
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
assert.EqualError(t, err, "the device flag (-d) can only be used for addresses not pubkeys")
// TODO: Capture stdout and compare
}

View File

@ -74,9 +74,9 @@ func TestCreateLedger(t *testing.T) {
pk, err = sdk.Bech32ifyAccPub(pubKey)
assert.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk)
linfo := restoredKey.(ledgerInfo)
assert.Equal(t, "44'/118'/3'/0/1", linfo.GetPath().String())
path, err := restoredKey.GetPath()
assert.NoError(t, err)
assert.Equal(t, "44'/118'/3'/0/1", path.String())
}
// TestKeyManagement makes sure we can manipulate these keys well

View File

@ -1,10 +1,12 @@
package keys
import (
"github.com/tendermint/tendermint/crypto"
"fmt"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/tendermint/crypto"
)
// Keybase exposes operations on a generic keystore
@ -82,6 +84,8 @@ type Info interface {
GetPubKey() crypto.PubKey
// Address
GetAddress() types.AccAddress
// Bip44 Path
GetPath() (*hd.BIP44Params, error)
}
var _ Info = &localInfo{}
@ -119,6 +123,10 @@ func (i localInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes()
}
func (i localInfo) GetPath() (*hd.BIP44Params, error) {
return nil, fmt.Errorf("BIP44 Paths are not available for this type")
}
// ledgerInfo is the public information about a Ledger key
type ledgerInfo struct {
Name string `json:"name"`
@ -150,8 +158,9 @@ func (i ledgerInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes()
}
func (i ledgerInfo) GetPath() hd.BIP44Params {
return i.Path
func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) {
tmp := i.Path
return &tmp, nil
}
// offlineInfo is the public information about an offline key
@ -183,6 +192,10 @@ func (i offlineInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes()
}
func (i offlineInfo) GetPath() (*hd.BIP44Params, error) {
return nil, fmt.Errorf("BIP44 Paths are not available for this type")
}
// encoding info
func writeInfo(i Info) []byte {
return cdc.MustMarshalBinaryLengthPrefixed(i)

View File

@ -20,7 +20,10 @@ func Test_writeReadLedgerInfo(t *testing.T) {
tmpKey,
*hd.NewFundraiserParams(5, 1)}
assert.Equal(t, TypeLedger, lInfo.GetType())
assert.Equal(t, "44'/118'/5'/0/1", lInfo.GetPath().String())
path, err := lInfo.GetPath()
assert.NoError(t, err)
assert.Equal(t, "44'/118'/5'/0/1", path.String())
assert.Equal(t,
"cosmospub1addwnpepqddddqg2glc8x4fl7vxjlnr7p5a3czm5kcdp4239sg6yqdc4rc2r5wmxv8p",
types.MustBech32ifyAccPub(lInfo.GetPubKey()))
@ -36,5 +39,8 @@ func Test_writeReadLedgerInfo(t *testing.T) {
assert.Equal(t, lInfo.GetType(), restoredInfo.GetType())
assert.Equal(t, lInfo.GetPubKey(), restoredInfo.GetPubKey())
assert.Equal(t, lInfo.GetPath(), restoredInfo.(ledgerInfo).GetPath())
restoredPath, err := restoredInfo.GetPath()
assert.NoError(t, err)
assert.Equal(t, path, restoredPath)
}

View File

@ -3,10 +3,12 @@
package crypto
import (
"fmt"
"github.com/btcsuite/btcd/btcec"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/tests"
"github.com/cosmos/go-bip39"
bip39 "github.com/cosmos/go-bip39"
"github.com/pkg/errors"
secp256k1 "github.com/tendermint/btcd/btcec"
"github.com/tendermint/tendermint/crypto"
@ -77,3 +79,9 @@ func (mock LedgerSECP256K1Mock) SignSECP256K1(derivationPath []uint32, message [
sig2 := btcec.Signature{R: sig.R, S: sig.S}
return sig2.Serialize(), nil
}
// ShowAddressSECP256K1 shows the address for the corresponding bip32 derivation path
func (mock LedgerSECP256K1Mock) ShowAddressSECP256K1(bip32Path []uint32, hrp string) error {
fmt.Printf("Request to show address for %v at %v", hrp, bip32Path)
return nil
}

View File

@ -5,12 +5,14 @@ import (
"os"
"github.com/btcsuite/btcd/btcec"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"
tmbtcec "github.com/tendermint/btcd/btcec"
tmcrypto "github.com/tendermint/tendermint/crypto"
tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
)
var (
@ -31,6 +33,7 @@ type (
Close() error
GetPublicKeySECP256K1([]uint32) ([]byte, error)
SignSECP256K1([]uint32, []byte) ([]byte, error)
ShowAddressSECP256K1([]uint32, string) error
}
// PrivKeyLedgerSecp256k1 implements PrivKey, calling the ledger nano we
@ -61,6 +64,26 @@ func NewPrivKeyLedgerSecp256k1(path hd.BIP44Params) (tmcrypto.PrivKey, error) {
return PrivKeyLedgerSecp256k1{pubKey, path}, nil
}
// LedgerShowAddress triggers a ledger device to show the corresponding address.
func LedgerShowAddress(path hd.BIP44Params, expectedPubKey tmcrypto.PubKey) error {
device, err := getLedgerDevice()
if err != nil {
return err
}
defer warnIfErrors(device.Close)
pubKey, err := getPubKey(device, path)
if err != nil {
return err
}
if pubKey != expectedPubKey {
return fmt.Errorf("pubkey does not match, Check this is the same device")
}
return device.ShowAddressSECP256K1(path.DerivationPath(), types.Bech32PrefixAccAddr)
}
// PubKey returns the cached public key.
func (pkl PrivKeyLedgerSecp256k1) PubKey() tmcrypto.PubKey {
return pkl.CachedPubKey