2020-04-08 02:38:28 -07:00
|
|
|
package hd_test
|
2018-06-28 17:54:47 -07:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/hex"
|
|
|
|
"fmt"
|
2018-10-17 10:37:58 -07:00
|
|
|
"testing"
|
|
|
|
|
2020-04-08 02:38:28 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
2019-05-30 05:46:38 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/types"
|
|
|
|
|
2019-04-10 16:33:50 -07:00
|
|
|
bip39 "github.com/cosmos/go-bip39"
|
2019-02-05 08:22:56 -08:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
2018-06-28 17:54:47 -07:00
|
|
|
)
|
|
|
|
|
2018-10-17 10:37:58 -07:00
|
|
|
var defaultBIP39Passphrase = ""
|
|
|
|
|
|
|
|
// return bip39 seed with empty passphrase
|
|
|
|
func mnemonicToSeed(mnemonic string) []byte {
|
|
|
|
return bip39.NewSeed(mnemonic, defaultBIP39Passphrase)
|
|
|
|
}
|
|
|
|
|
2019-10-14 08:43:19 -07:00
|
|
|
// nolint:govet
|
2018-06-28 17:54:47 -07:00
|
|
|
func ExampleStringifyPathParams() {
|
2020-04-08 02:38:28 -07:00
|
|
|
path := hd.NewParams(44, 0, 0, false, 0)
|
2018-06-28 17:54:47 -07:00
|
|
|
fmt.Println(path.String())
|
2020-04-08 02:38:28 -07:00
|
|
|
path = hd.NewParams(44, 33, 7, true, 9)
|
2019-02-05 08:22:56 -08:00
|
|
|
fmt.Println(path.String())
|
|
|
|
// Output:
|
|
|
|
// 44'/0'/0'/0/0
|
|
|
|
// 44'/33'/7'/1/9
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStringifyFundraiserPathParams(t *testing.T) {
|
2020-04-08 02:38:28 -07:00
|
|
|
path := hd.NewFundraiserParams(4, types.CoinType, 22)
|
2019-02-05 08:22:56 -08:00
|
|
|
require.Equal(t, "44'/118'/4'/0/22", path.String())
|
|
|
|
|
2020-04-08 02:38:28 -07:00
|
|
|
path = hd.NewFundraiserParams(4, types.CoinType, 57)
|
2019-02-05 08:22:56 -08:00
|
|
|
require.Equal(t, "44'/118'/4'/0/57", path.String())
|
2019-05-30 05:46:38 -07:00
|
|
|
|
2020-04-08 02:38:28 -07:00
|
|
|
path = hd.NewFundraiserParams(4, 12345, 57)
|
2019-05-30 05:46:38 -07:00
|
|
|
require.Equal(t, "44'/12345'/4'/0/57", path.String())
|
2019-02-05 08:22:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestPathToArray(t *testing.T) {
|
2020-04-08 02:38:28 -07:00
|
|
|
path := hd.NewParams(44, 118, 1, false, 4)
|
2019-02-05 08:22:56 -08:00
|
|
|
require.Equal(t, "[44 118 1 0 4]", fmt.Sprintf("%v", path.DerivationPath()))
|
|
|
|
|
2020-04-08 02:38:28 -07:00
|
|
|
path = hd.NewParams(44, 118, 2, true, 15)
|
2019-02-05 08:22:56 -08:00
|
|
|
require.Equal(t, "[44 118 2 1 15]", fmt.Sprintf("%v", path.DerivationPath()))
|
2018-06-28 17:54:47 -07:00
|
|
|
}
|
|
|
|
|
2018-10-17 10:37:58 -07:00
|
|
|
func TestParamsFromPath(t *testing.T) {
|
|
|
|
goodCases := []struct {
|
2020-04-08 02:38:28 -07:00
|
|
|
params *hd.BIP44Params
|
2018-10-17 10:37:58 -07:00
|
|
|
path string
|
|
|
|
}{
|
2020-04-08 02:38:28 -07:00
|
|
|
{&hd.BIP44Params{44, 0, 0, false, 0}, "44'/0'/0'/0/0"},
|
|
|
|
{&hd.BIP44Params{44, 1, 0, false, 0}, "44'/1'/0'/0/0"},
|
|
|
|
{&hd.BIP44Params{44, 0, 1, false, 0}, "44'/0'/1'/0/0"},
|
|
|
|
{&hd.BIP44Params{44, 0, 0, true, 0}, "44'/0'/0'/1/0"},
|
|
|
|
{&hd.BIP44Params{44, 0, 0, false, 1}, "44'/0'/0'/0/1"},
|
|
|
|
{&hd.BIP44Params{44, 1, 1, true, 1}, "44'/1'/1'/1/1"},
|
|
|
|
{&hd.BIP44Params{44, 118, 52, true, 41}, "44'/118'/52'/1/41"},
|
2018-10-17 10:37:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, c := range goodCases {
|
2020-04-08 02:38:28 -07:00
|
|
|
params, err := hd.NewParamsFromPath(c.path)
|
2018-10-17 10:37:58 -07:00
|
|
|
errStr := fmt.Sprintf("%d %v", i, c)
|
|
|
|
assert.NoError(t, err, errStr)
|
|
|
|
assert.EqualValues(t, c.params, params, errStr)
|
|
|
|
assert.Equal(t, c.path, c.params.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
badCases := []struct {
|
|
|
|
path string
|
|
|
|
}{
|
|
|
|
{"43'/0'/0'/0/0"}, // doesnt start with 44
|
|
|
|
{"44'/1'/0'/0/0/5"}, // too many fields
|
|
|
|
{"44'/0'/1'/0"}, // too few fields
|
|
|
|
{"44'/0'/0'/2/0"}, // change field can only be 0/1
|
|
|
|
{"44/0'/0'/0/0"}, // first field needs '
|
|
|
|
{"44'/0/0'/0/0"}, // second field needs '
|
|
|
|
{"44'/0'/0/0/0"}, // third field needs '
|
|
|
|
{"44'/0'/0'/0'/0"}, // fourth field must not have '
|
|
|
|
{"44'/0'/0'/0/0'"}, // fifth field must not have '
|
|
|
|
{"44'/-1'/0'/0/0"}, // no negatives
|
|
|
|
{"44'/0'/0'/-1/0"}, // no negatives
|
2019-02-05 08:22:56 -08:00
|
|
|
{"a'/0'/0'/-1/0"}, // valid values
|
|
|
|
{"0/X/0'/-1/0"}, // valid values
|
|
|
|
{"44'/0'/X/-1/0"}, // valid values
|
|
|
|
{"44'/0'/0'/%/0"}, // valid values
|
|
|
|
{"44'/0'/0'/0/%"}, // valid values
|
2018-10-17 10:37:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, c := range badCases {
|
2020-04-08 02:38:28 -07:00
|
|
|
params, err := hd.NewParamsFromPath(c.path)
|
2018-10-17 10:37:58 -07:00
|
|
|
errStr := fmt.Sprintf("%d %v", i, c)
|
|
|
|
assert.Nil(t, params, errStr)
|
|
|
|
assert.Error(t, err, errStr)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-10-14 08:43:19 -07:00
|
|
|
// nolint:govet
|
2018-06-28 17:54:47 -07:00
|
|
|
func ExampleSomeBIP32TestVecs() {
|
|
|
|
|
2018-10-17 10:37:58 -07:00
|
|
|
seed := mnemonicToSeed("barrel original fuel morning among eternal " +
|
2018-06-28 17:54:47 -07:00
|
|
|
"filter ball stove pluck matrix mechanic")
|
2020-04-08 02:38:28 -07:00
|
|
|
master, ch := hd.ComputeMastersFromSeed(seed)
|
2018-06-28 17:54:47 -07:00
|
|
|
fmt.Println("keys from fundraiser test-vector (cosmos, bitcoin, ether)")
|
|
|
|
fmt.Println()
|
|
|
|
// cosmos
|
2020-04-08 02:38:28 -07:00
|
|
|
priv, err := hd.DerivePrivateKeyForPath(master, ch, types.FullFundraiserPath)
|
2019-02-05 08:22:56 -08:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println("INVALID")
|
|
|
|
} else {
|
|
|
|
fmt.Println(hex.EncodeToString(priv[:]))
|
|
|
|
}
|
2018-06-28 17:54:47 -07:00
|
|
|
// bitcoin
|
2020-04-08 02:38:28 -07:00
|
|
|
priv, err = hd.DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/0")
|
2019-02-05 08:22:56 -08:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println("INVALID")
|
|
|
|
} else {
|
|
|
|
fmt.Println(hex.EncodeToString(priv[:]))
|
|
|
|
}
|
2018-06-28 17:54:47 -07:00
|
|
|
// ether
|
2020-04-08 02:38:28 -07:00
|
|
|
priv, err = hd.DerivePrivateKeyForPath(master, ch, "44'/60'/0'/0/0")
|
2019-02-05 08:22:56 -08:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println("INVALID")
|
|
|
|
} else {
|
|
|
|
fmt.Println(hex.EncodeToString(priv[:]))
|
|
|
|
}
|
|
|
|
// INVALID
|
2020-04-08 02:38:28 -07:00
|
|
|
priv, err = hd.DerivePrivateKeyForPath(master, ch, "X/0'/0'/0/0")
|
2019-02-05 08:22:56 -08:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println("INVALID")
|
|
|
|
} else {
|
|
|
|
fmt.Println(hex.EncodeToString(priv[:]))
|
|
|
|
}
|
2020-04-08 02:38:28 -07:00
|
|
|
priv, err = hd.DerivePrivateKeyForPath(master, ch, "-44/0'/0'/0/0")
|
2019-02-05 08:22:56 -08:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println("INVALID")
|
|
|
|
} else {
|
|
|
|
fmt.Println(hex.EncodeToString(priv[:]))
|
|
|
|
}
|
2018-06-28 17:54:47 -07:00
|
|
|
|
|
|
|
fmt.Println()
|
|
|
|
fmt.Println("keys generated via https://coinomi.com/recovery-phrase-tool.html")
|
|
|
|
fmt.Println()
|
|
|
|
|
2018-10-17 10:37:58 -07:00
|
|
|
seed = mnemonicToSeed(
|
2018-06-28 17:54:47 -07:00
|
|
|
"advice process birth april short trust crater change bacon monkey medal garment " +
|
|
|
|
"gorilla ranch hour rival razor call lunar mention taste vacant woman sister")
|
2020-04-08 02:38:28 -07:00
|
|
|
master, ch = hd.ComputeMastersFromSeed(seed)
|
|
|
|
priv, _ = hd.DerivePrivateKeyForPath(master, ch, "44'/1'/1'/0/4")
|
2018-06-28 17:54:47 -07:00
|
|
|
fmt.Println(hex.EncodeToString(priv[:]))
|
|
|
|
|
2018-10-17 10:37:58 -07:00
|
|
|
seed = mnemonicToSeed("idea naive region square margin day captain habit " +
|
2018-06-28 17:54:47 -07:00
|
|
|
"gun second farm pact pulse someone armed")
|
2020-04-08 02:38:28 -07:00
|
|
|
master, ch = hd.ComputeMastersFromSeed(seed)
|
|
|
|
priv, _ = hd.DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/420")
|
2018-06-28 17:54:47 -07:00
|
|
|
fmt.Println(hex.EncodeToString(priv[:]))
|
|
|
|
|
|
|
|
fmt.Println()
|
|
|
|
fmt.Println("BIP 32 example")
|
|
|
|
fmt.Println()
|
|
|
|
|
|
|
|
// bip32 path: m/0/7
|
2018-10-17 10:37:58 -07:00
|
|
|
seed = mnemonicToSeed("monitor flock loyal sick object grunt duty ride develop assault harsh history")
|
2020-04-08 02:38:28 -07:00
|
|
|
master, ch = hd.ComputeMastersFromSeed(seed)
|
|
|
|
priv, _ = hd.DerivePrivateKeyForPath(master, ch, "0/7")
|
2018-06-28 17:54:47 -07:00
|
|
|
fmt.Println(hex.EncodeToString(priv[:]))
|
|
|
|
|
|
|
|
// Output: keys from fundraiser test-vector (cosmos, bitcoin, ether)
|
|
|
|
//
|
|
|
|
// bfcb217c058d8bbafd5e186eae936106ca3e943889b0b4a093ae13822fd3170c
|
|
|
|
// e77c3de76965ad89997451de97b95bb65ede23a6bf185a55d80363d92ee37c3d
|
|
|
|
// 7fc4d8a8146dea344ba04c593517d3f377fa6cded36cd55aee0a0bb968e651bc
|
2019-02-05 08:22:56 -08:00
|
|
|
// INVALID
|
|
|
|
// INVALID
|
2018-06-28 17:54:47 -07:00
|
|
|
//
|
|
|
|
// keys generated via https://coinomi.com/recovery-phrase-tool.html
|
|
|
|
//
|
|
|
|
// a61f10c5fecf40c084c94fa54273b6f5d7989386be4a37669e6d6f7b0169c163
|
|
|
|
// 32c4599843de3ef161a629a461d12c60b009b676c35050be5f7ded3a3b23501f
|
|
|
|
//
|
|
|
|
// BIP 32 example
|
|
|
|
//
|
|
|
|
// c4c11d8c03625515905d7e89d25dfc66126fbc629ecca6db489a1a72fc4bda78
|
|
|
|
}
|
2020-04-08 02:38:28 -07:00
|
|
|
|
|
|
|
func TestCreateHDPath(t *testing.T) {
|
|
|
|
type args struct {
|
|
|
|
coinType uint32
|
|
|
|
account uint32
|
|
|
|
index uint32
|
|
|
|
}
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
args args
|
|
|
|
want hd.BIP44Params
|
|
|
|
}{
|
|
|
|
{"44'/0'/0'/0/0", args{0, 0, 0}, hd.BIP44Params{Purpose: 44}},
|
|
|
|
{"44'/114'/0'/0/0", args{114, 0, 0}, hd.BIP44Params{Purpose: 44, CoinType: 114, Account: 0, AddressIndex: 0}},
|
|
|
|
{"44'/114'/1'/1/0", args{114, 1, 1}, hd.BIP44Params{Purpose: 44, CoinType: 114, Account: 1, AddressIndex: 1}},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
tt := tt
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
tt := tt
|
|
|
|
require.Equal(t, tt.want, *hd.CreateHDPath(tt.args.coinType, tt.args.account, tt.args.index))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|