github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/hd/hdpath_test.go (about)

     1  package hd
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/gnolang/gno/tm2/pkg/crypto/bip39"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  var defaultBIP39Passphrase = ""
    14  
    15  // return bip39 seed with empty passphrase
    16  func mnemonicToSeed(mnemonic string) []byte {
    17  	return bip39.NewSeed(mnemonic, defaultBIP39Passphrase)
    18  }
    19  
    20  func ExampleStringifyPathParams() {
    21  	path := NewParams(44, 0, 0, false, 0)
    22  	fmt.Println(path.String())
    23  	path = NewParams(44, 33, 7, true, 9)
    24  	fmt.Println(path.String())
    25  	// Output:
    26  	// 44'/0'/0'/0/0
    27  	// 44'/33'/7'/1/9
    28  }
    29  
    30  // from cosmos-sdk/types/address.go
    31  const (
    32  	testCoinType           = 118
    33  	testFullFundraiserPath = "44'/118'/0'/0/0"
    34  )
    35  
    36  func TestStringifyFundraiserPathParams(t *testing.T) {
    37  	t.Parallel()
    38  
    39  	path := NewFundraiserParams(4, testCoinType, 22)
    40  	require.Equal(t, "44'/118'/4'/0/22", path.String())
    41  
    42  	path = NewFundraiserParams(4, testCoinType, 57)
    43  	require.Equal(t, "44'/118'/4'/0/57", path.String())
    44  
    45  	path = NewFundraiserParams(4, 12345, 57)
    46  	require.Equal(t, "44'/12345'/4'/0/57", path.String())
    47  }
    48  
    49  func TestPathToArray(t *testing.T) {
    50  	t.Parallel()
    51  
    52  	path := NewParams(44, 118, 1, false, 4)
    53  	require.Equal(t, "[44 118 1 0 4]", fmt.Sprintf("%v", path.DerivationPath()))
    54  
    55  	path = NewParams(44, 118, 2, true, 15)
    56  	require.Equal(t, "[44 118 2 1 15]", fmt.Sprintf("%v", path.DerivationPath()))
    57  }
    58  
    59  func TestParamsFromPath(t *testing.T) {
    60  	t.Parallel()
    61  
    62  	goodCases := []struct {
    63  		params *BIP44Params
    64  		path   string
    65  	}{
    66  		{&BIP44Params{44, 0, 0, false, 0}, "44'/0'/0'/0/0"},
    67  		{&BIP44Params{44, 1, 0, false, 0}, "44'/1'/0'/0/0"},
    68  		{&BIP44Params{44, 0, 1, false, 0}, "44'/0'/1'/0/0"},
    69  		{&BIP44Params{44, 0, 0, true, 0}, "44'/0'/0'/1/0"},
    70  		{&BIP44Params{44, 0, 0, false, 1}, "44'/0'/0'/0/1"},
    71  		{&BIP44Params{44, 1, 1, true, 1}, "44'/1'/1'/1/1"},
    72  		{&BIP44Params{44, 118, 52, true, 41}, "44'/118'/52'/1/41"},
    73  	}
    74  
    75  	for i, c := range goodCases {
    76  		params, err := NewParamsFromPath(c.path)
    77  		errStr := fmt.Sprintf("%d %v", i, c)
    78  		assert.NoError(t, err, errStr)
    79  		assert.EqualValues(t, c.params, params, errStr)
    80  		assert.Equal(t, c.path, c.params.String())
    81  	}
    82  
    83  	badCases := []struct {
    84  		path string
    85  	}{
    86  		{"43'/0'/0'/0/0"},   // doesn't start with 44
    87  		{"44'/1'/0'/0/0/5"}, // too many fields
    88  		{"44'/0'/1'/0"},     // too few fields
    89  		{"44'/0'/0'/2/0"},   // change field can only be 0/1
    90  		{"44/0'/0'/0/0"},    // first field needs '
    91  		{"44'/0/0'/0/0"},    // second field needs '
    92  		{"44'/0'/0/0/0"},    // third field needs '
    93  		{"44'/0'/0'/0'/0"},  // fourth field must not have '
    94  		{"44'/0'/0'/0/0'"},  // fifth field must not have '
    95  		{"44'/-1'/0'/0/0"},  // no negatives
    96  		{"44'/0'/0'/-1/0"},  // no negatives
    97  		{"a'/0'/0'/-1/0"},   // valid values
    98  		{"0/X/0'/-1/0"},     // valid values
    99  		{"44'/0'/X/-1/0"},   // valid values
   100  		{"44'/0'/0'/%/0"},   // valid values
   101  		{"44'/0'/0'/0/%"},   // valid values
   102  	}
   103  
   104  	for i, c := range badCases {
   105  		params, err := NewParamsFromPath(c.path)
   106  		errStr := fmt.Sprintf("%d %v", i, c)
   107  		assert.Nil(t, params, errStr)
   108  		assert.Error(t, err, errStr)
   109  	}
   110  }
   111  
   112  func ExampleSomeBIP32TestVecs() {
   113  	seed := mnemonicToSeed("barrel original fuel morning among eternal " +
   114  		"filter ball stove pluck matrix mechanic")
   115  	master, ch := ComputeMastersFromSeed(seed)
   116  	fmt.Println("keys from fundraiser test-vector (cosmos, bitcoin, ether)")
   117  	fmt.Println()
   118  	// cosmos
   119  	priv, err := DerivePrivateKeyForPath(master, ch, testFullFundraiserPath)
   120  	if err != nil {
   121  		fmt.Println("INVALID")
   122  	} else {
   123  		fmt.Println(hex.EncodeToString(priv[:]))
   124  	}
   125  	// bitcoin
   126  	priv, err = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/0")
   127  	if err != nil {
   128  		fmt.Println("INVALID")
   129  	} else {
   130  		fmt.Println(hex.EncodeToString(priv[:]))
   131  	}
   132  	// ether
   133  	priv, err = DerivePrivateKeyForPath(master, ch, "44'/60'/0'/0/0")
   134  	if err != nil {
   135  		fmt.Println("INVALID")
   136  	} else {
   137  		fmt.Println(hex.EncodeToString(priv[:]))
   138  	}
   139  	// INVALID
   140  	priv, err = DerivePrivateKeyForPath(master, ch, "X/0'/0'/0/0")
   141  	if err != nil {
   142  		fmt.Println("INVALID")
   143  	} else {
   144  		fmt.Println(hex.EncodeToString(priv[:]))
   145  	}
   146  	priv, err = DerivePrivateKeyForPath(master, ch, "-44/0'/0'/0/0")
   147  	if err != nil {
   148  		fmt.Println("INVALID")
   149  	} else {
   150  		fmt.Println(hex.EncodeToString(priv[:]))
   151  	}
   152  
   153  	fmt.Println()
   154  	fmt.Println("keys generated via https://coinomi.com/recovery-phrase-tool.html")
   155  	fmt.Println()
   156  
   157  	seed = mnemonicToSeed(
   158  		"advice process birth april short trust crater change bacon monkey medal garment " +
   159  			"gorilla ranch hour rival razor call lunar mention taste vacant woman sister")
   160  	master, ch = ComputeMastersFromSeed(seed)
   161  	priv, _ = DerivePrivateKeyForPath(master, ch, "44'/1'/1'/0/4")
   162  	fmt.Println(hex.EncodeToString(priv[:]))
   163  
   164  	seed = mnemonicToSeed("idea naive region square margin day captain habit " +
   165  		"gun second farm pact pulse someone armed")
   166  	master, ch = ComputeMastersFromSeed(seed)
   167  	priv, _ = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/420")
   168  	fmt.Println(hex.EncodeToString(priv[:]))
   169  
   170  	fmt.Println()
   171  	fmt.Println("BIP 32 example")
   172  	fmt.Println()
   173  
   174  	// bip32 path: m/0/7
   175  	seed = mnemonicToSeed("monitor flock loyal sick object grunt duty ride develop assault harsh history")
   176  	master, ch = ComputeMastersFromSeed(seed)
   177  	priv, _ = DerivePrivateKeyForPath(master, ch, "0/7")
   178  	fmt.Println(hex.EncodeToString(priv[:]))
   179  
   180  	// Output: keys from fundraiser test-vector (cosmos, bitcoin, ether)
   181  	//
   182  	// bfcb217c058d8bbafd5e186eae936106ca3e943889b0b4a093ae13822fd3170c
   183  	// e77c3de76965ad89997451de97b95bb65ede23a6bf185a55d80363d92ee37c3d
   184  	// 7fc4d8a8146dea344ba04c593517d3f377fa6cded36cd55aee0a0bb968e651bc
   185  	// INVALID
   186  	// INVALID
   187  	//
   188  	// keys generated via https://coinomi.com/recovery-phrase-tool.html
   189  	//
   190  	// a61f10c5fecf40c084c94fa54273b6f5d7989386be4a37669e6d6f7b0169c163
   191  	// 32c4599843de3ef161a629a461d12c60b009b676c35050be5f7ded3a3b23501f
   192  	//
   193  	// BIP 32 example
   194  	//
   195  	// c4c11d8c03625515905d7e89d25dfc66126fbc629ecca6db489a1a72fc4bda78
   196  }