Configurable Bip44 CoinType & HdPath for SDK users (#4300)

Closes: #4144
This commit is contained in:
Frank Yang 2019-05-30 20:46:38 +08:00 committed by Alessio Treglia
parent 5cae008fbe
commit 1db10b0033
11 changed files with 60 additions and 28 deletions

View File

@ -0,0 +1 @@
#4144 Allow for configurable BIP44 HD path and coin type.

View File

@ -25,12 +25,6 @@ import (
"github.com/btcsuite/btcd/btcec"
)
// BIP44Prefix is the parts of the BIP32 HD path that are fixed by what we used during the fundraiser.
const (
BIP44Prefix = "44'/118'/"
FullFundraiserPath = BIP44Prefix + "0'/0/0"
)
// BIP44Params wraps BIP 44 params (5 level BIP 32 path).
// To receive a canonical string representation ala
// m / purpose' / coinType' / account' / change / addressIndex
@ -130,10 +124,10 @@ func isHardened(field string) bool {
}
// NewFundraiserParams creates a BIP 44 parameter object from the params:
// m / 44' / 118' / account' / 0 / address_index
// m / 44' / coinType' / account' / 0 / address_index
// The fixed parameters (purpose', coin_type', and change) are determined by what was used in the fundraiser.
func NewFundraiserParams(account uint32, addressIdx uint32) *BIP44Params {
return NewParams(44, 118, account, false, addressIdx)
func NewFundraiserParams(account, coinType, addressIdx uint32) *BIP44Params {
return NewParams(44, coinType, account, false, addressIdx)
}
// DerivationPath returns the BIP44 fields as an array.

View File

@ -5,6 +5,8 @@ import (
"fmt"
"testing"
"github.com/cosmos/cosmos-sdk/types"
bip39 "github.com/cosmos/go-bip39"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -29,11 +31,14 @@ func ExampleStringifyPathParams() {
}
func TestStringifyFundraiserPathParams(t *testing.T) {
path := NewFundraiserParams(4, 22)
path := NewFundraiserParams(4, types.CoinType, 22)
require.Equal(t, "44'/118'/4'/0/22", path.String())
path = NewFundraiserParams(4, 57)
path = NewFundraiserParams(4, types.CoinType, 57)
require.Equal(t, "44'/118'/4'/0/57", path.String())
path = NewFundraiserParams(4, 12345, 57)
require.Equal(t, "44'/12345'/4'/0/57", path.String())
}
func TestPathToArray(t *testing.T) {
@ -105,7 +110,7 @@ func ExampleSomeBIP32TestVecs() {
fmt.Println("keys from fundraiser test-vector (cosmos, bitcoin, ether)")
fmt.Println()
// cosmos
priv, err := DerivePrivateKeyForPath(master, ch, FullFundraiserPath)
priv, err := DerivePrivateKeyForPath(master, ch, types.FullFundraiserPath)
if err != nil {
fmt.Println("INVALID")
} else {

View File

@ -115,13 +115,15 @@ func (kb dbKeybase) CreateMnemonic(name string, language Language, passwd string
}
seed := bip39.NewSeed(mnemonic, DefaultBIP39Passphrase)
info, err = kb.persistDerivedKey(seed, passwd, name, hd.FullFundraiserPath)
fullFundraiserPath := types.GetConfig().GetFullFundraiserPath()
info, err = kb.persistDerivedKey(seed, passwd, name, fullFundraiserPath)
return
}
// CreateAccount converts a mnemonic to a private key and persists it, encrypted with the given password.
func (kb dbKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error) {
hdPath := hd.NewFundraiserParams(account, index)
coinType := types.GetConfig().GetCoinType()
hdPath := hd.NewFundraiserParams(account, coinType, index)
return kb.Derive(name, mnemonic, bip39Passwd, encryptPasswd, *hdPath)
}
@ -142,7 +144,8 @@ func (kb dbKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, acco
return nil, ErrUnsupportedSigningAlgo
}
hdPath := hd.NewFundraiserParams(account, index)
coinType := types.GetConfig().GetCoinType()
hdPath := hd.NewFundraiserParams(account, coinType, index)
priv, _, err := crypto.NewPrivKeyLedgerSecp256k1(*hdPath, hrp)
if err != nil {
return nil, err

View File

@ -383,7 +383,7 @@ func TestSeedPhrase(t *testing.T) {
require.NotNil(t, err)
// let us re-create it from the mnemonic-phrase
params := *hd.NewFundraiserParams(0, 0)
params := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
newInfo, err := cstore.Derive(n2, mnemonic, DefaultBIP39Passphrase, p2, params)
require.NoError(t, err)
require.Equal(t, n2, newInfo.GetName())
@ -406,8 +406,8 @@ func ExampleNew() {
// return info here just like in List
fmt.Println(bob.GetName())
}
cstore.CreateMnemonic("Alice", English, "secret", sec)
cstore.CreateMnemonic("Carl", English, "mitm", sec)
_, _, _ = cstore.CreateMnemonic("Alice", English, "secret", sec)
_, _, _ = cstore.CreateMnemonic("Carl", English, "mitm", sec)
info, _ := cstore.List()
for _, i := range info {
fmt.Println(i.GetName())

View File

@ -335,7 +335,7 @@ func TestLazySeedPhrase(t *testing.T) {
require.NotNil(t, err)
// let us re-create it from the mnemonic-phrase
params := *hd.NewFundraiserParams(0, 0)
params := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
newInfo, err := kb.Derive(n2, mnemonic, DefaultBIP39Passphrase, p2, params)
require.NoError(t, err)
require.Equal(t, n2, newInfo.GetName())

View File

@ -19,7 +19,7 @@ func Test_writeReadLedgerInfo(t *testing.T) {
lInfo := ledgerInfo{
"some_name",
tmpKey,
*hd.NewFundraiserParams(5, 1)}
*hd.NewFundraiserParams(5, types.CoinType, 1)}
assert.Equal(t, TypeLedger, lInfo.GetType())
path, err := lInfo.GetPath()

View File

@ -41,7 +41,7 @@ func (mock LedgerSECP256K1Mock) GetPublicKeySECP256K1(derivationPath []uint32) (
if derivationPath[0] != 44 {
return nil, errors.New("Invalid derivation path")
}
if derivationPath[1] != 118 {
if derivationPath[1] != types.CoinType {
return nil, errors.New("Invalid derivation path")
}

View File

@ -24,7 +24,7 @@ func TestLedgerErrorHandling(t *testing.T) {
}
func TestPublicKeyUnsafe(t *testing.T) {
path := *hd.NewFundraiserParams(0, 0)
path := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path)
require.Nil(t, err, "%s", err)
require.NotNil(t, priv)
@ -63,7 +63,7 @@ func TestPublicKeyUnsafeHDPath(t *testing.T) {
// Check with device
for i := uint32(0); i < 10; i++ {
path := *hd.NewFundraiserParams(0, i)
path := *hd.NewFundraiserParams(0, sdk.CoinType, i)
fmt.Printf("Checking keys at %v\n", path)
priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path)
@ -99,7 +99,7 @@ func TestPublicKeyUnsafeHDPath(t *testing.T) {
}
func TestPublicKeySafe(t *testing.T) {
path := *hd.NewFundraiserParams(0, 0)
path := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
priv, addr, err := NewPrivKeyLedgerSecp256k1(path, "cosmos")
require.Nil(t, err, "%s", err)
@ -154,7 +154,7 @@ func TestPublicKeyHDPath(t *testing.T) {
// Check with device
for i := uint32(0); i < 10; i++ {
path := *hd.NewFundraiserParams(0, i)
path := *hd.NewFundraiserParams(0, sdk.CoinType, i)
fmt.Printf("Checking keys at %v\n", path)
priv, addr, err := NewPrivKeyLedgerSecp256k1(path, "cosmos")
@ -208,7 +208,7 @@ func TestSignaturesHD(t *testing.T) {
for account := uint32(0); account < 100; account += 30 {
msg := getFakeTx(account)
path := *hd.NewFundraiserParams(account, account/5)
path := *hd.NewFundraiserParams(account, sdk.CoinType, account/5)
fmt.Printf("Checking signature at %v --- PLEASE REVIEW AND ACCEPT IN THE DEVICE\n", path)
priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path)
@ -225,7 +225,7 @@ func TestSignaturesHD(t *testing.T) {
func TestRealLedgerSecp256k1(t *testing.T) {
msg := getFakeTx(50)
path := *hd.NewFundraiserParams(0, 0)
path := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path)
require.Nil(t, err, "%s", err)

View File

@ -20,6 +20,13 @@ const (
// Bech32PrefixAccAddr defines the Bech32 prefix of an account's address
Bech32MainPrefix = "cosmos"
// Atom in https://github.com/satoshilabs/slips/blob/master/slip-0044.md
CoinType = 118
// BIP44Prefix is the parts of the BIP32 HD path that are fixed by
// what we used during the fundraiser.
FullFundraiserPath = "44'/118'/0'/0/0"
// PrefixAccount is the prefix for account keys
PrefixAccount = "acc"
// PrefixValidator is the prefix for validator keys

View File

@ -10,6 +10,8 @@ type Config struct {
mtx sync.RWMutex
sealed bool
bech32AddressPrefix map[string]string
coinType uint32
fullFundraiserPath string
txEncoder TxEncoder
addressVerifier func([]byte) error
}
@ -26,7 +28,9 @@ var (
"validator_pub": Bech32PrefixValPub,
"consensus_pub": Bech32PrefixConsPub,
},
txEncoder: nil,
coinType: CoinType,
fullFundraiserPath: FullFundraiserPath,
txEncoder: nil,
}
)
@ -81,6 +85,16 @@ func (config *Config) SetAddressVerifier(addressVerifier func([]byte) error) {
config.addressVerifier = addressVerifier
}
func (config *Config) SetCoinType(coinType uint32) {
config.assertNotSealed()
config.coinType = coinType
}
func (config *Config) SetFullFundraiserPath(fullFundraiserPath string) {
config.assertNotSealed()
config.fullFundraiserPath = fullFundraiserPath
}
// Seal seals the config such that the config state could not be modified further
func (config *Config) Seal() *Config {
config.mtx.Lock()
@ -129,3 +143,11 @@ func (config *Config) GetTxEncoder() TxEncoder {
func (config *Config) GetAddressVerifier() func([]byte) error {
return config.addressVerifier
}
func (config *Config) GetCoinType() uint32 {
return config.coinType
}
func (config *Config) GetFullFundraiserPath() string {
return config.fullFundraiserPath
}