Merge PR #3670: CLI support for showing bech32 addresses in Ledger devices
This commit is contained in:
parent
c96d8f3e81
commit
3eb0acda88
6
Gopkg.lock
generated
6
Gopkg.lock
generated
@ -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"
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/zondax/ledger-cosmos-go"
|
||||
version = "=v0.9.7"
|
||||
version = "=v0.9.8"
|
||||
|
||||
## deps without releases:
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user