github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/crypto/keys/hd/hdpath_test.go (about) 1 package hd 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "testing" 7 8 "github.com/stretchr/testify/require" 9 10 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 11 12 bip39 "github.com/cosmos/go-bip39" 13 ) 14 15 var defaultBIP39Passphrase = "" 16 17 // return bip39 seed with empty passphrase 18 func mnemonicToSeed(mnemonic string) []byte { 19 return bip39.NewSeed(mnemonic, defaultBIP39Passphrase) 20 } 21 22 func TestStringifyFundraiserPathParams(t *testing.T) { 23 path := NewFundraiserParams(4, types.CoinType, 22) 24 require.Equal(t, "m/44'/118'/4'/0/22", path.String()) 25 26 path = NewFundraiserParams(4, types.CoinType, 57) 27 require.Equal(t, "m/44'/118'/4'/0/57", path.String()) 28 29 path = NewFundraiserParams(4, 12345, 57) 30 require.Equal(t, "m/44'/12345'/4'/0/57", path.String()) 31 } 32 33 func TestPathToArray(t *testing.T) { 34 path := NewParams(44, 118, 1, false, 4) 35 require.Equal(t, "[44 118 1 0 4]", fmt.Sprintf("%v", path.DerivationPath())) 36 37 path = NewParams(44, 118, 2, true, 15) 38 require.Equal(t, "[44 118 2 1 15]", fmt.Sprintf("%v", path.DerivationPath())) 39 } 40 41 func TestParamsFromPath(t *testing.T) { 42 goodCases := []struct { 43 params *BIP44Params 44 path string 45 }{ 46 {&BIP44Params{44, 0, 0, false, 0}, "m/44'/0'/0'/0/0"}, 47 {&BIP44Params{44, 1, 0, false, 0}, "m/44'/1'/0'/0/0"}, 48 {&BIP44Params{44, 0, 1, false, 0}, "m/44'/0'/1'/0/0"}, 49 {&BIP44Params{44, 0, 0, true, 0}, "m/44'/0'/0'/1/0"}, 50 {&BIP44Params{44, 0, 0, false, 1}, "m/44'/0'/0'/0/1"}, 51 {&BIP44Params{44, 1, 1, true, 1}, "m/44'/1'/1'/1/1"}, 52 {&BIP44Params{44, 118, 52, true, 41}, "m/44'/118'/52'/1/41"}, 53 } 54 55 for i, c := range goodCases { 56 params, err := NewParamsFromPath(c.path) 57 errStr := fmt.Sprintf("%d %v", i, c) 58 require.NoError(t, err, errStr) 59 require.EqualValues(t, c.params, params, errStr) 60 require.Equal(t, c.path, c.params.String()) 61 } 62 63 badCases := []struct { 64 path string 65 }{ 66 {"m/43'/0'/0'/0/0"}, // doesn't start with 44 67 {"m/44'/1'/0'/0/0/5"}, // too many fields 68 {"m/44'/0'/1'/0"}, // too few fields 69 {"m/44'/0'/0'/2/0"}, // change field can only be 0/1 70 {"m/44/0'/0'/0/0"}, // first field needs ' 71 {"m/44'/0/0'/0/0"}, // second field needs ' 72 {"m/44'/0'/0/0/0"}, // third field needs ' 73 {"m/44'/0'/0'/0'/0"}, // fourth field must not have ' 74 {"m/44'/0'/0'/0/0'"}, // fifth field must not have ' 75 {"m/44'/-1'/0'/0/0"}, // no negatives 76 {"m/44'/0'/0'/-1/0"}, // no negatives 77 {"m/a'/0'/0'/-1/0"}, // invalid values 78 {"m/0/X/0'/-1/0"}, // invalid values 79 {"m/44'/0'/X/-1/0"}, // invalid values 80 {"m/44'/0'/0'/%/0"}, // invalid values 81 {"m/44'/0'/0'/0/%"}, // invalid values 82 {"m44'0'0'00"}, // no separators 83 {" /44'/0'/0'/0/0"}, // blank first component 84 } 85 86 for i, c := range badCases { 87 params, err := NewParamsFromPath(c.path) 88 errStr := fmt.Sprintf("%d %v", i, c) 89 require.Nil(t, params, errStr) 90 require.Error(t, err, errStr) 91 } 92 93 } 94 95 // Tests to ensure that any index value is in the range [0, max(int32)] as per 96 // the extended keys specification. If the index belongs to that of a hardened key, 97 // its 0x80000000 bit will be set, so we can still accept values in [0, max(int32)] and then 98 // increase its value as deriveKeyPath already augments. 99 // See issue https://github.com/cosmos/cosmos-sdk/issues/7627. 100 func TestDeriveHDPathRange(t *testing.T) { 101 seed := mnemonicToSeed("I am become Death, the destroyer of worlds!") 102 103 tests := []struct { 104 path string 105 wantErr string 106 }{ 107 { 108 path: "m/1'/2147483648/0'/0/0", 109 wantErr: "out of range", 110 }, 111 { 112 path: "m/2147483648'/1/0/0", 113 wantErr: "out of range", 114 }, 115 { 116 path: "m/2147483648'/2147483648/0'/0/0", 117 wantErr: "out of range", 118 }, 119 { 120 path: "m/1'/-5/0'/0/0", 121 wantErr: "invalid syntax", 122 }, 123 { 124 path: "m/-2147483646'/1/0/0", 125 wantErr: "invalid syntax", 126 }, 127 { 128 path: "m/-2147483648'/-2147483648/0'/0/0", 129 wantErr: "invalid syntax", 130 }, 131 { 132 path: "m44'118'0'00", 133 wantErr: "path 'm44'118'0'00' doesn't contain '/' separators", 134 }, 135 { 136 path: "", 137 wantErr: "path '' doesn't contain '/' separators", 138 }, 139 { 140 // Should pass. 141 path: "m/1'/2147483647'/1/0'/0/0", 142 }, 143 { 144 // Should pass. 145 path: "1'/2147483647'/1/0'/0/0", 146 }, 147 } 148 149 for _, tt := range tests { 150 tt := tt 151 t.Run(tt.path, func(t *testing.T) { 152 master, ch := ComputeMastersFromSeed(seed) 153 _, err := DerivePrivateKeyForPath(master, ch, tt.path) 154 155 if tt.wantErr == "" { 156 require.NoError(t, err, "unexpected error") 157 } else { 158 require.Error(t, err, "expected a report of an int overflow") 159 require.Contains(t, err.Error(), tt.wantErr) 160 } 161 }) 162 } 163 } 164 165 func ExampleNewParams() { 166 path := NewParams(44, 0, 0, false, 0) 167 fmt.Println(path.String()) 168 path = NewParams(44, 33, 7, true, 9) 169 fmt.Println(path.String()) 170 // Output: 171 // m/44'/0'/0'/0/0 172 // m/44'/33'/7'/1/9 173 } 174 175 func ExampleDerivePrivateKeyForPath() { 176 seed := mnemonicToSeed("barrel original fuel morning among eternal " + 177 "filter ball stove pluck matrix mechanic") 178 master, ch := ComputeMastersFromSeed(seed) 179 fmt.Println("keys from fundraiser test-vector (cosmos, bitcoin, ether)") 180 fmt.Println() 181 // cosmos 182 priv, err := DerivePrivateKeyForPath(master, ch, types.FullFundraiserPath) 183 if err != nil { 184 fmt.Println("INVALID") 185 } else { 186 fmt.Println(hex.EncodeToString(priv[:])) 187 } 188 // bitcoin 189 priv, err = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/0") 190 if err != nil { 191 fmt.Println("INVALID") 192 } else { 193 fmt.Println(hex.EncodeToString(priv[:])) 194 } 195 // ether 196 priv, err = DerivePrivateKeyForPath(master, ch, "44'/60'/0'/0/0") 197 if err != nil { 198 fmt.Println("INVALID") 199 } else { 200 fmt.Println(hex.EncodeToString(priv[:])) 201 } 202 // INVALID 203 priv, err = DerivePrivateKeyForPath(master, ch, "X/0'/0'/0/0") 204 if err != nil { 205 fmt.Println("INVALID") 206 } else { 207 fmt.Println(hex.EncodeToString(priv[:])) 208 } 209 priv, err = DerivePrivateKeyForPath(master, ch, "-44/0'/0'/0/0") 210 if err != nil { 211 fmt.Println("INVALID") 212 } else { 213 fmt.Println(hex.EncodeToString(priv[:])) 214 } 215 216 fmt.Println() 217 fmt.Println("keys generated via https://coinomi.com/recovery-phrase-tool.html") 218 fmt.Println() 219 220 seed = mnemonicToSeed( 221 "advice process birth april short trust crater change bacon monkey medal garment " + 222 "gorilla ranch hour rival razor call lunar mention taste vacant woman sister") 223 master, ch = ComputeMastersFromSeed(seed) 224 priv, _ = DerivePrivateKeyForPath(master, ch, "44'/1'/1'/0/4") 225 fmt.Println(hex.EncodeToString(priv[:])) 226 227 seed = mnemonicToSeed("idea naive region square margin day captain habit " + 228 "gun second farm pact pulse someone armed") 229 master, ch = ComputeMastersFromSeed(seed) 230 priv, _ = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/420") 231 fmt.Println(hex.EncodeToString(priv[:])) 232 233 fmt.Println() 234 fmt.Println("BIP 32 example") 235 fmt.Println() 236 237 // bip32 path: m/0/7 238 seed = mnemonicToSeed("monitor flock loyal sick object grunt duty ride develop assault harsh history") 239 master, ch = ComputeMastersFromSeed(seed) 240 priv, _ = DerivePrivateKeyForPath(master, ch, "0/7") 241 fmt.Println(hex.EncodeToString(priv[:])) 242 243 // Output: keys from fundraiser test-vector (cosmos, bitcoin, ether) 244 // 245 // bfcb217c058d8bbafd5e186eae936106ca3e943889b0b4a093ae13822fd3170c 246 // e77c3de76965ad89997451de97b95bb65ede23a6bf185a55d80363d92ee37c3d 247 // 7fc4d8a8146dea344ba04c593517d3f377fa6cded36cd55aee0a0bb968e651bc 248 // INVALID 249 // INVALID 250 // 251 // keys generated via https://coinomi.com/recovery-phrase-tool.html 252 // 253 // a61f10c5fecf40c084c94fa54273b6f5d7989386be4a37669e6d6f7b0169c163 254 // 32c4599843de3ef161a629a461d12c60b009b676c35050be5f7ded3a3b23501f 255 // 256 // BIP 32 example 257 // 258 // c4c11d8c03625515905d7e89d25dfc66126fbc629ecca6db489a1a72fc4bda78 259 }