Adding support for Ledger Cosmos App v1.5 (#4227)
This PR adds support for the latest version of the Cosmos App (v.1.5). The app is not been released yet by Ledger but the PR is backwards compatible. We can later remove backwards compatibility and enforce v1.5 only. When creating a new account, `gaiacli` now shows the account/index and address in the device and requires user confirmation. Related PRs: https://github.com/cosmos/ledger-cosmos-go/pull/3 https://github.com/cosmos/ledger-cosmos-go/pull/4 https://github.com/cosmos/ledger-cosmos-go/pull/5 https://github.com/cosmos/ledger-cosmos-go/pull/6 Changes in the app can be found here: https://github.com/LedgerHQ/ledger-app-cosmos/pull/5
This commit is contained in:
parent
f0f7b7dab7
commit
1306a25e42
|
@ -0,0 +1 @@
|
|||
#4227 Support for Ledger App v1.5
|
|
@ -171,9 +171,10 @@ func runAddCmd(_ *cobra.Command, args []string) error {
|
|||
account := uint32(viper.GetInt(flagAccount))
|
||||
index := uint32(viper.GetInt(flagIndex))
|
||||
|
||||
// If we're using ledger, only thing we need is the path. So generate key and we're done.
|
||||
// If we're using ledger, only thing we need is the path and the bech32 prefix.
|
||||
if viper.GetBool(client.FlagUseLedger) {
|
||||
info, err := kb.CreateLedger(name, keys.Secp256k1, account, index)
|
||||
bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix()
|
||||
info, err := kb.CreateLedger(name, keys.Secp256k1, bech32PrefixAccAddr, account, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ 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 indicates that the information should be shown in the device
|
||||
FlagDevice = "device"
|
||||
|
||||
flagMultiSigThreshold = "multisig-threshold"
|
||||
|
@ -48,7 +48,7 @@ consisting of all the keys provided by name and multisig threshold.`,
|
|||
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().BoolP(FlagDevice, "d", false, "Output the address in a ledger device")
|
||||
cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures")
|
||||
cmd.Flags().BoolP(flagShowMultiSig, "m", false, "Output multisig pubkey constituents, threshold, and weights")
|
||||
cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response")
|
||||
|
|
|
@ -137,18 +137,19 @@ func (kb dbKeybase) Derive(name, mnemonic, bip39Passphrase, encryptPasswd string
|
|||
|
||||
// CreateLedger creates a new locally-stored reference to a Ledger keypair
|
||||
// It returns the created key info and an error if the Ledger could not be queried
|
||||
func (kb dbKeybase) CreateLedger(name string, algo SigningAlgo, account uint32, index uint32) (Info, error) {
|
||||
func (kb dbKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (Info, error) {
|
||||
if algo != Secp256k1 {
|
||||
return nil, ErrUnsupportedSigningAlgo
|
||||
}
|
||||
|
||||
hdPath := hd.NewFundraiserParams(account, index)
|
||||
priv, err := crypto.NewPrivKeyLedgerSecp256k1(*hdPath)
|
||||
priv, _, err := crypto.NewPrivKeyLedgerSecp256k1(*hdPath, hrp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pub := priv.PubKey()
|
||||
|
||||
// Note: Once Cosmos App v1.3.1 is compulsory, it could be possible to check that pubkey and addr match
|
||||
return kb.writeLedgerKey(name, pub, *hdPath), nil
|
||||
}
|
||||
|
||||
|
@ -246,7 +247,7 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub t
|
|||
|
||||
case ledgerInfo:
|
||||
linfo := info.(ledgerInfo)
|
||||
priv, err = crypto.NewPrivKeyLedgerSecp256k1(linfo.Path)
|
||||
priv, err = crypto.NewPrivKeyLedgerSecp256k1Unsafe(linfo.Path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ func TestCreateAccountInvalidMnemonic(t *testing.T) {
|
|||
|
||||
func TestCreateLedgerUnsupportedAlgo(t *testing.T) {
|
||||
kb := NewInMemory()
|
||||
_, err := kb.CreateLedger("some_account", Ed25519, 0, 1)
|
||||
_, err := kb.CreateLedger("some_account", Ed25519, "cosmos", 0, 1)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "unsupported signing algo: only secp256k1 is supported", err.Error())
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func TestCreateLedger(t *testing.T) {
|
|||
// test_cover does not compile some dependencies so ledger is disabled
|
||||
// test_unit may add a ledger mock
|
||||
// both cases are acceptable
|
||||
ledger, err := kb.CreateLedger("some_account", Secp256k1, 3, 1)
|
||||
ledger, err := kb.CreateLedger("some_account", Secp256k1, "cosmos", 3, 1)
|
||||
|
||||
if err != nil {
|
||||
assert.Error(t, err)
|
||||
|
|
|
@ -106,14 +106,14 @@ func (lkb lazyKeybase) Derive(name, mnemonic, bip39Passwd, encryptPasswd string,
|
|||
return newDbKeybase(db).Derive(name, mnemonic, bip39Passwd, encryptPasswd, params)
|
||||
}
|
||||
|
||||
func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, account uint32, index uint32) (info Info, err error) {
|
||||
func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error) {
|
||||
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
return newDbKeybase(db).CreateLedger(name, algo, account, index)
|
||||
return newDbKeybase(db).CreateLedger(name, algo, hrp, account, index)
|
||||
}
|
||||
|
||||
func (lkb lazyKeybase) CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error) {
|
||||
|
|
|
@ -35,7 +35,7 @@ type Keybase interface {
|
|||
Derive(name, mnemonic, bip39Passwd, encryptPasswd string, params hd.BIP44Params) (Info, error)
|
||||
|
||||
// CreateLedger creates, stores, and returns a new Ledger key reference
|
||||
CreateLedger(name string, algo SigningAlgo, account uint32, index uint32) (info Info, err error)
|
||||
CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error)
|
||||
|
||||
// CreateOffline creates, stores, and returns a new offline key reference
|
||||
CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error)
|
||||
|
|
|
@ -4,15 +4,17 @@ package crypto
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
bip39 "github.com/cosmos/go-bip39"
|
||||
"github.com/pkg/errors"
|
||||
secp256k1 "github.com/tendermint/btcd/btcec"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/go-bip39"
|
||||
|
||||
secp256k1 "github.com/tendermint/btcd/btcec"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
)
|
||||
|
||||
// If ledger support (build tag) has been enabled, which implies a CGO dependency,
|
||||
|
@ -31,6 +33,8 @@ func (mock LedgerSECP256K1Mock) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetPublicKeySECP256K1 mocks a ledger device
|
||||
// as per the original API, it returns an uncompressed key
|
||||
func (mock LedgerSECP256K1Mock) GetPublicKeySECP256K1(derivationPath []uint32) ([]byte, error) {
|
||||
if derivationPath[0] != 44 {
|
||||
return nil, errors.New("Invalid derivation path")
|
||||
|
@ -56,6 +60,28 @@ func (mock LedgerSECP256K1Mock) GetPublicKeySECP256K1(derivationPath []uint32) (
|
|||
return pubkeyObject.SerializeUncompressed(), nil
|
||||
}
|
||||
|
||||
// GetAddressPubKeySECP256K1 mocks a ledger device
|
||||
// as per the original API, it returns a compressed key and a bech32 address
|
||||
func (mock LedgerSECP256K1Mock) GetAddressPubKeySECP256K1(derivationPath []uint32, hrp string) ([]byte, string, error) {
|
||||
pk, err := mock.GetPublicKeySECP256K1(derivationPath)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// re-serialize in the 33-byte compressed format
|
||||
cmp, err := btcec.ParsePubKey(pk[:], btcec.S256())
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("error parsing public key: %v", err)
|
||||
}
|
||||
|
||||
var compressedPublicKey tmsecp256k1.PubKeySecp256k1
|
||||
copy(compressedPublicKey[:], cmp.SerializeCompressed())
|
||||
|
||||
// Generate the bech32 addr using existing tmcrypto/etc.
|
||||
addr := types.AccAddress(compressedPublicKey.Address()).String()
|
||||
return pk, addr, err
|
||||
}
|
||||
|
||||
func (mock LedgerSECP256K1Mock) SignSECP256K1(derivationPath []uint32, message []byte) ([]byte, error) {
|
||||
path := hd.NewParams(derivationPath[0], derivationPath[1], derivationPath[2], derivationPath[3] != 0, derivationPath[4])
|
||||
seed, err := bip39.NewSeedWithErrorChecking(tests.TestMnemonic, "")
|
||||
|
|
|
@ -5,12 +5,11 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"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"
|
||||
|
@ -28,13 +27,15 @@ type (
|
|||
// dependencies when Ledger support is potentially not enabled.
|
||||
discoverLedgerFn func() (LedgerSECP256K1, error)
|
||||
|
||||
// LedgerSECP256K1 reflects an interface a Ledger API must implement for
|
||||
// the SECP256K1 scheme.
|
||||
// LedgerSECP256K1 reflects an interface a Ledger API must implement for SECP256K1
|
||||
LedgerSECP256K1 interface {
|
||||
Close() error
|
||||
// Returns an uncompressed pubkey
|
||||
GetPublicKeySECP256K1([]uint32) ([]byte, error)
|
||||
// Returns a compressed pubkey and bech32 address (requires user confirmation)
|
||||
GetAddressPubKeySECP256K1([]uint32, string) ([]byte, string, error)
|
||||
// Signs a message (requires user confirmation)
|
||||
SignSECP256K1([]uint32, []byte) ([]byte, error)
|
||||
ShowAddressSECP256K1([]uint32, string) error
|
||||
}
|
||||
|
||||
// PrivKeyLedgerSecp256k1 implements PrivKey, calling the ledger nano we
|
||||
|
@ -48,16 +49,19 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
// NewPrivKeyLedgerSecp256k1 will generate a new key and store the public key
|
||||
// for later use.
|
||||
func NewPrivKeyLedgerSecp256k1(path hd.BIP44Params) (tmcrypto.PrivKey, error) {
|
||||
// NewPrivKeyLedgerSecp256k1Unsafe will generate a new key and store the public key for later use.
|
||||
//
|
||||
// This function is marked as unsafe as it will retrieve a pubkey without user verification.
|
||||
// It can only be used to verify a pubkey but never to create new accounts/keys. In that case,
|
||||
// please refer to NewPrivKeyLedgerSecp256k1
|
||||
func NewPrivKeyLedgerSecp256k1Unsafe(path hd.BIP44Params) (tmcrypto.PrivKey, error) {
|
||||
device, err := getLedgerDevice()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer warnIfErrors(device.Close)
|
||||
|
||||
pubKey, err := getPubKey(device, path)
|
||||
pubKey, err := getPubKeyUnsafe(device, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -65,24 +69,21 @@ 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 {
|
||||
// NewPrivKeyLedgerSecp256k1 will generate a new key and store the public key for later use.
|
||||
// The request will require user confirmation and will show account and index in the device
|
||||
func NewPrivKeyLedgerSecp256k1(path hd.BIP44Params, hrp string) (tmcrypto.PrivKey, string, error) {
|
||||
device, err := getLedgerDevice()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, "", err
|
||||
}
|
||||
defer warnIfErrors(device.Close)
|
||||
|
||||
pubKey, err := getPubKey(device, path)
|
||||
pubKey, addr, err := getPubKeyAddrSafe(device, path, hrp)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if pubKey != expectedPubKey {
|
||||
return fmt.Errorf("pubkey does not match, Check this is the same device")
|
||||
}
|
||||
|
||||
return device.ShowAddressSECP256K1(path.DerivationPath(), types.Bech32PrefixAccAddr)
|
||||
return PrivKeyLedgerSecp256k1{pubKey, path}, addr, nil
|
||||
}
|
||||
|
||||
// PubKey returns the cached public key.
|
||||
|
@ -101,6 +102,35 @@ func (pkl PrivKeyLedgerSecp256k1) Sign(message []byte) ([]byte, error) {
|
|||
return sign(device, pkl, message)
|
||||
}
|
||||
|
||||
// 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 := getPubKeyUnsafe(device, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pubKey != expectedPubKey {
|
||||
return fmt.Errorf("the key's pubkey does not match with the one retrieved from Ledger. Check that the HD path and device are the correct ones")
|
||||
}
|
||||
|
||||
pubKey2, _, err := getPubKeyAddrSafe(device, path, types.Bech32PrefixAccAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pubKey2 != expectedPubKey {
|
||||
return fmt.Errorf("the key's pubkey does not match with the one retrieved from Ledger. Check that the HD path and device are the correct ones")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateKey allows us to verify the sanity of a public key after loading it
|
||||
// from disk.
|
||||
func (pkl PrivKeyLedgerSecp256k1) ValidateKey() error {
|
||||
|
@ -162,7 +192,7 @@ func getLedgerDevice() (LedgerSECP256K1, error) {
|
|||
}
|
||||
|
||||
func validateKey(device LedgerSECP256K1, pkl PrivKeyLedgerSecp256k1) error {
|
||||
pub, err := getPubKey(device, pkl.Path)
|
||||
pub, err := getPubKeyUnsafe(device, pkl.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -194,10 +224,15 @@ func sign(device LedgerSECP256K1, pkl PrivKeyLedgerSecp256k1, msg []byte) ([]byt
|
|||
return convertDERtoBER(sig)
|
||||
}
|
||||
|
||||
// getPubKey reads the pubkey the ledger itself
|
||||
// getPubKeyUnsafe reads the pubkey from a ledger device
|
||||
//
|
||||
// This function is marked as unsafe as it will retrieve a pubkey without user verification
|
||||
// It can only be used to verify a pubkey but never to create new accounts/keys. In that case,
|
||||
// please refer to getPubKeyAddrSafe
|
||||
//
|
||||
// since this involves IO, it may return an error, which is not exposed
|
||||
// in the PubKey interface, so this function allows better error handling
|
||||
func getPubKey(device LedgerSECP256K1, path hd.BIP44Params) (tmcrypto.PubKey, error) {
|
||||
func getPubKeyUnsafe(device LedgerSECP256K1, path hd.BIP44Params) (tmcrypto.PubKey, error) {
|
||||
publicKey, err := device.GetPublicKeySECP256K1(path.DerivationPath())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("please open Cosmos app on the Ledger device - error: %v", err)
|
||||
|
@ -214,3 +249,27 @@ func getPubKey(device LedgerSECP256K1, path hd.BIP44Params) (tmcrypto.PubKey, er
|
|||
|
||||
return compressedPublicKey, nil
|
||||
}
|
||||
|
||||
// getPubKeyAddr reads the pubkey and the address from a ledger device.
|
||||
// This function is marked as Safe as it will require user confirmation and
|
||||
// account and index will be shown in the device.
|
||||
//
|
||||
// Since this involves IO, it may return an error, which is not exposed
|
||||
// in the PubKey interface, so this function allows better error handling.
|
||||
func getPubKeyAddrSafe(device LedgerSECP256K1, path hd.BIP44Params, hrp string) (tmcrypto.PubKey, string, error) {
|
||||
publicKey, addr, err := device.GetAddressPubKeySECP256K1(path.DerivationPath(), hrp)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("address %s rejected", addr)
|
||||
}
|
||||
|
||||
// re-serialize in the 33-byte compressed format
|
||||
cmp, err := btcec.ParsePubKey(publicKey[:], btcec.S256())
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("error parsing public key: %v", err)
|
||||
}
|
||||
|
||||
var compressedPublicKey tmsecp256k1.PubKeySecp256k1
|
||||
copy(compressedPublicKey[:], cmp.SerializeCompressed())
|
||||
|
||||
return compressedPublicKey, addr, nil
|
||||
}
|
||||
|
|
|
@ -19,27 +19,31 @@ func TestLedgerErrorHandling(t *testing.T) {
|
|||
// first, try to generate a key, must return an error
|
||||
// (no panic)
|
||||
path := *hd.NewParams(44, 555, 0, false, 0)
|
||||
_, err := NewPrivKeyLedgerSecp256k1(path)
|
||||
_, err := NewPrivKeyLedgerSecp256k1Unsafe(path)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestPublicKey(t *testing.T) {
|
||||
func TestPublicKeyUnsafe(t *testing.T) {
|
||||
path := *hd.NewFundraiserParams(0, 0)
|
||||
priv, err := NewPrivKeyLedgerSecp256k1(path)
|
||||
priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path)
|
||||
require.Nil(t, err, "%s", err)
|
||||
require.NotNil(t, priv)
|
||||
|
||||
require.Equal(t, "eb5ae98721034fef9cd7c4c63588d3b03feb5281b9d232cba34d6f3d71aee59211ffbfe1fe87",
|
||||
fmt.Sprintf("%x", priv.PubKey().Bytes()),
|
||||
"Is your device using test mnemonic: %s ?", tests.TestMnemonic)
|
||||
|
||||
pubKeyAddr, err := sdk.Bech32ifyAccPub(priv.PubKey())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0",
|
||||
pubKeyAddr, "Is your device using test mnemonic: %s ?", tests.TestMnemonic)
|
||||
|
||||
require.Equal(t, "5075624b6579536563703235366b317b303334464546394344374334433633353838443342303"+
|
||||
"3464542353238314239443233324342413334443646334437314145453539323131464642464531464538377d",
|
||||
fmt.Sprintf("%x", priv.PubKey()))
|
||||
addr := sdk.AccAddress(priv.PubKey().Address()).String()
|
||||
require.Equal(t, "cosmos1w34k53py5v5xyluazqpq65agyajavep2rflq6h",
|
||||
addr, "Is your device using test mnemonic: %s ?", tests.TestMnemonic)
|
||||
}
|
||||
|
||||
func TestPublicKeyHDPath(t *testing.T) {
|
||||
func TestPublicKeyUnsafeHDPath(t *testing.T) {
|
||||
expectedAnswers := []string{
|
||||
"cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0",
|
||||
"cosmospub1addwnpepqfsdqjr68h7wjg5wacksmqaypasnra232fkgu5sxdlnlu8j22ztxvlqvd65",
|
||||
|
@ -62,7 +66,7 @@ func TestPublicKeyHDPath(t *testing.T) {
|
|||
path := *hd.NewFundraiserParams(0, i)
|
||||
fmt.Printf("Checking keys at %v\n", path)
|
||||
|
||||
priv, err := NewPrivKeyLedgerSecp256k1(path)
|
||||
priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path)
|
||||
require.Nil(t, err, "%s", err)
|
||||
require.NotNil(t, priv)
|
||||
|
||||
|
@ -94,6 +98,104 @@ func TestPublicKeyHDPath(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPublicKeySafe(t *testing.T) {
|
||||
path := *hd.NewFundraiserParams(0, 0)
|
||||
priv, addr, err := NewPrivKeyLedgerSecp256k1(path, "cosmos")
|
||||
|
||||
require.Nil(t, err, "%s", err)
|
||||
require.NotNil(t, priv)
|
||||
|
||||
require.Equal(t, "eb5ae98721034fef9cd7c4c63588d3b03feb5281b9d232cba34d6f3d71aee59211ffbfe1fe87",
|
||||
fmt.Sprintf("%x", priv.PubKey().Bytes()),
|
||||
"Is your device using test mnemonic: %s ?", tests.TestMnemonic)
|
||||
|
||||
pubKeyAddr, err := sdk.Bech32ifyAccPub(priv.PubKey())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0",
|
||||
pubKeyAddr, "Is your device using test mnemonic: %s ?", tests.TestMnemonic)
|
||||
|
||||
require.Equal(t, "cosmos1w34k53py5v5xyluazqpq65agyajavep2rflq6h",
|
||||
addr, "Is your device using test mnemonic: %s ?", tests.TestMnemonic)
|
||||
|
||||
addr2 := sdk.AccAddress(priv.PubKey().Address()).String()
|
||||
require.Equal(t, addr, addr2)
|
||||
}
|
||||
|
||||
func TestPublicKeyHDPath(t *testing.T) {
|
||||
expectedPubKeys := []string{
|
||||
"cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0",
|
||||
"cosmospub1addwnpepqfsdqjr68h7wjg5wacksmqaypasnra232fkgu5sxdlnlu8j22ztxvlqvd65",
|
||||
"cosmospub1addwnpepqw3xwqun6q43vtgw6p4qspq7srvxhcmvq4jrx5j5ma6xy3r7k6dtxmrkh3d",
|
||||
"cosmospub1addwnpepqvez9lrp09g8w7gkv42y4yr5p6826cu28ydrhrujv862yf4njmqyyjr4pjs",
|
||||
"cosmospub1addwnpepq06hw3enfrtmq8n67teytcmtnrgcr0yntmyt25kdukfjkerdc7lqg32rcz7",
|
||||
"cosmospub1addwnpepqg3trf2gd0s2940nckrxherwqhgmm6xd5h4pcnrh4x7y35h6yafmcpk5qns",
|
||||
"cosmospub1addwnpepqdm6rjpx6wsref8wjn7ym6ntejet430j4szpngfgc20caz83lu545vuv8hp",
|
||||
"cosmospub1addwnpepqvdhtjzy2wf44dm03jxsketxc07vzqwvt3vawqqtljgsr9s7jvydjmt66ew",
|
||||
"cosmospub1addwnpepqwystfpyxwcava7v3t7ndps5xzu6s553wxcxzmmnxevlzvwrlqpzz695nw9",
|
||||
"cosmospub1addwnpepqw970u6gjqkccg9u3rfj99857wupj2z9fqfzy2w7e5dd7xn7kzzgkgqch0r",
|
||||
}
|
||||
|
||||
expectedAddrs := []string{
|
||||
"cosmos1w34k53py5v5xyluazqpq65agyajavep2rflq6h",
|
||||
"cosmos19ewxwemt6uahejvwf44u7dh6tq859tkyvarh2q",
|
||||
"cosmos1a07dzdjgjsntxpp75zg7cgatgq0udh3pcdcxm3",
|
||||
"cosmos1qvw52lmn9gpvem8welghrkc52m3zczyhlqjsl7",
|
||||
"cosmos17m78ka80fqkkw2c4ww0v4xm5nsu2drgrlm8mn2",
|
||||
"cosmos1ferh9ll9c452d2p8k2v7heq084guygkn43up9e",
|
||||
"cosmos10vf3sxmjg96rqq36axcphzfsl74dsntuehjlw5",
|
||||
"cosmos1cq83av8cmnar79h0rg7duh9gnr7wkh228a7fxg",
|
||||
"cosmos1dszhfrt226jy5rsre7e48vw9tgwe90uerfyefa",
|
||||
"cosmos1734d7qsylzrdt05muhqqtpd90j8mp4y6rzch8l",
|
||||
}
|
||||
|
||||
const numIters = 10
|
||||
|
||||
privKeys := make([]tmcrypto.PrivKey, numIters)
|
||||
|
||||
// Check with device
|
||||
for i := uint32(0); i < 10; i++ {
|
||||
path := *hd.NewFundraiserParams(0, i)
|
||||
fmt.Printf("Checking keys at %v\n", path)
|
||||
|
||||
priv, addr, err := NewPrivKeyLedgerSecp256k1(path, "cosmos")
|
||||
require.Nil(t, err, "%s", err)
|
||||
require.NotNil(t, addr)
|
||||
require.NotNil(t, priv)
|
||||
|
||||
addr2 := sdk.AccAddress(priv.PubKey().Address()).String()
|
||||
require.Equal(t, addr2, addr)
|
||||
require.Equal(t,
|
||||
expectedAddrs[i], addr,
|
||||
"Is your device using test mnemonic: %s ?", tests.TestMnemonic)
|
||||
|
||||
// Check other methods
|
||||
require.NoError(t, priv.(PrivKeyLedgerSecp256k1).ValidateKey())
|
||||
tmp := priv.(PrivKeyLedgerSecp256k1)
|
||||
(&tmp).AssertIsPrivKeyInner()
|
||||
|
||||
pubKeyAddr, err := sdk.Bech32ifyAccPub(priv.PubKey())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t,
|
||||
expectedPubKeys[i], pubKeyAddr,
|
||||
"Is your device using test mnemonic: %s ?", tests.TestMnemonic)
|
||||
|
||||
// Store and restore
|
||||
serializedPk := priv.Bytes()
|
||||
require.NotNil(t, serializedPk)
|
||||
require.True(t, len(serializedPk) >= 50)
|
||||
|
||||
privKeys[i] = priv
|
||||
}
|
||||
|
||||
// Now check equality
|
||||
for i := 0; i < 10; i++ {
|
||||
for j := 0; j < 10; j++ {
|
||||
require.Equal(t, i == j, privKeys[i].Equals(privKeys[j]))
|
||||
require.Equal(t, i == j, privKeys[j].Equals(privKeys[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getFakeTx(accountNumber uint32) []byte {
|
||||
tmp := fmt.Sprintf(
|
||||
`{"account_number":"%d","chain_id":"1234","fee":{"amount":[{"amount":"150","denom":"atom"}],"gas":"5000"},"memo":"memo","msgs":[[""]],"sequence":"6"}`,
|
||||
|
@ -109,7 +211,7 @@ func TestSignaturesHD(t *testing.T) {
|
|||
path := *hd.NewFundraiserParams(account, account/5)
|
||||
fmt.Printf("Checking signature at %v --- PLEASE REVIEW AND ACCEPT IN THE DEVICE\n", path)
|
||||
|
||||
priv, err := NewPrivKeyLedgerSecp256k1(path)
|
||||
priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path)
|
||||
require.Nil(t, err, "%s", err)
|
||||
|
||||
pub := priv.PubKey()
|
||||
|
@ -124,7 +226,7 @@ func TestSignaturesHD(t *testing.T) {
|
|||
func TestRealLedgerSecp256k1(t *testing.T) {
|
||||
msg := getFakeTx(50)
|
||||
path := *hd.NewFundraiserParams(0, 0)
|
||||
priv, err := NewPrivKeyLedgerSecp256k1(path)
|
||||
priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path)
|
||||
require.Nil(t, err, "%s", err)
|
||||
|
||||
pub := priv.PubKey()
|
||||
|
|
8
go.mod
8
go.mod
|
@ -7,8 +7,7 @@ require (
|
|||
github.com/bgentry/speakeasy v0.1.0
|
||||
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d
|
||||
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8
|
||||
github.com/cosmos/ledger-cosmos-go v0.9.11
|
||||
github.com/cosmos/ledger-go v0.9.1 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.10.2
|
||||
github.com/fortytw2/leaktest v1.3.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.1.1
|
||||
|
@ -27,7 +26,7 @@ require (
|
|||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95 // indirect
|
||||
github.com/otiai10/mint v1.2.3 // indirect
|
||||
github.com/pelletier/go-toml v1.2.0
|
||||
github.com/pkg/errors v0.8.0
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/prometheus/client_golang v0.9.2 // indirect
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
|
||||
github.com/prometheus/common v0.2.0 // indirect
|
||||
|
@ -41,13 +40,12 @@ require (
|
|||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.3
|
||||
github.com/spf13/viper v1.0.3
|
||||
github.com/stretchr/testify v1.2.2
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3 // indirect
|
||||
github.com/tendermint/btcd v0.1.1
|
||||
github.com/tendermint/go-amino v0.14.1
|
||||
github.com/tendermint/iavl v0.12.1
|
||||
github.com/tendermint/tendermint v0.31.5
|
||||
github.com/zondax/hid v0.9.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793
|
||||
google.golang.org/grpc v1.19.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -28,11 +28,12 @@ github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJ
|
|||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 h1:Iwin12wRQtyZhH6FV3ykFcdGNlYEzoeR0jN8Vn+JWsI=
|
||||
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
|
||||
github.com/cosmos/ledger-cosmos-go v0.9.11 h1:bkcIKqaM6evicjkSP+Le8HDLXt9P+MqGRnGiMUC20m4=
|
||||
github.com/cosmos/ledger-cosmos-go v0.9.11/go.mod h1:RWldjvUf4Hfi46ti/8etBH3eQ2rOqqz2hstdzROQSHo=
|
||||
github.com/cosmos/ledger-cosmos-go v0.10.2 h1:B8JlCtl6otXi5PxY3Y7OF+HLhjLYvsemo4nl23wPQ9Y=
|
||||
github.com/cosmos/ledger-cosmos-go v0.10.2/go.mod h1:TOLCJf4/WyTm7uw3OKurqANyQ66yoJEH9D14o0cpRlU=
|
||||
github.com/cosmos/ledger-go v0.9.1 h1:bRIamtlWShVk1THw52NdCPHxtBxKnauglSB23mH1/w8=
|
||||
github.com/cosmos/ledger-go v0.9.1/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
|
@ -100,6 +101,8 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181
|
|||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
|
@ -134,9 +137,12 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
|||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.0.3 h1:z5LPUc2iz8VLT5Cw1UyrESG6FUUnOGecYGY08BLKSuc=
|
||||
github.com/spf13/viper v1.0.3/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3 h1:sAlSBRDl4psFR3ysKXRSE8ss6Mt90+ma1zRTroTNBJA=
|
||||
github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||
github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s=
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tendermint/go-amino"
|
||||
|
||||
sdkclient "github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||
"github.com/cosmos/cosmos-sdk/x/mint/client/cli"
|
||||
sdkclient "github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||
"github.com/cosmos/cosmos-sdk/x/mint/client/cli"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tendermint/go-amino"
|
||||
)
|
||||
|
||||
type ModuleClient struct {
|
||||
|
|
Loading…
Reference in New Issue