github.com/particl/partsuite_partutil@v0.0.0-20170924205752-117d65fa91c0/hdkeychain/extendedkey_test.go (about) 1 // Copyright (c) 2014-2017 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package hdkeychain 6 7 // References: 8 // [BIP32]: BIP0032 - Hierarchical Deterministic Wallets 9 // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki 10 11 import ( 12 "bytes" 13 "encoding/hex" 14 "errors" 15 "math" 16 "reflect" 17 "testing" 18 19 "github.com/particl/partsuite_partd/chaincfg" 20 ) 21 22 // TestBIP0032Vectors tests the vectors provided by [BIP32] to ensure the 23 // derivation works as intended. 24 func TestBIP0032Vectors(t *testing.T) { 25 // The master seeds for each of the two test vectors in [BIP32]. 26 testVec1MasterHex := "000102030405060708090a0b0c0d0e0f" 27 testVec2MasterHex := "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" 28 testVec3MasterHex := "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be" 29 hkStart := uint32(0x80000000) 30 31 tests := []struct { 32 name string 33 master string 34 path []uint32 35 wantPub string 36 wantPriv string 37 net *chaincfg.Params 38 }{ 39 // Test vector 1 40 { 41 name: "test vector 1 chain m", 42 master: testVec1MasterHex, 43 path: []uint32{}, 44 wantPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 45 wantPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", 46 net: &chaincfg.MainNetParams, 47 }, 48 { 49 name: "test vector 1 chain m/0H", 50 master: testVec1MasterHex, 51 path: []uint32{hkStart}, 52 wantPub: "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", 53 wantPriv: "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", 54 net: &chaincfg.MainNetParams, 55 }, 56 { 57 name: "test vector 1 chain m/0H/1", 58 master: testVec1MasterHex, 59 path: []uint32{hkStart, 1}, 60 wantPub: "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ", 61 wantPriv: "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", 62 net: &chaincfg.MainNetParams, 63 }, 64 { 65 name: "test vector 1 chain m/0H/1/2H", 66 master: testVec1MasterHex, 67 path: []uint32{hkStart, 1, hkStart + 2}, 68 wantPub: "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", 69 wantPriv: "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", 70 net: &chaincfg.MainNetParams, 71 }, 72 { 73 name: "test vector 1 chain m/0H/1/2H/2", 74 master: testVec1MasterHex, 75 path: []uint32{hkStart, 1, hkStart + 2, 2}, 76 wantPub: "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV", 77 wantPriv: "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", 78 net: &chaincfg.MainNetParams, 79 }, 80 { 81 name: "test vector 1 chain m/0H/1/2H/2/1000000000", 82 master: testVec1MasterHex, 83 path: []uint32{hkStart, 1, hkStart + 2, 2, 1000000000}, 84 wantPub: "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy", 85 wantPriv: "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", 86 net: &chaincfg.MainNetParams, 87 }, 88 89 // Test vector 2 90 { 91 name: "test vector 2 chain m", 92 master: testVec2MasterHex, 93 path: []uint32{}, 94 wantPub: "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", 95 wantPriv: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", 96 net: &chaincfg.MainNetParams, 97 }, 98 { 99 name: "test vector 2 chain m/0", 100 master: testVec2MasterHex, 101 path: []uint32{0}, 102 wantPub: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", 103 wantPriv: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", 104 net: &chaincfg.MainNetParams, 105 }, 106 { 107 name: "test vector 2 chain m/0/2147483647H", 108 master: testVec2MasterHex, 109 path: []uint32{0, hkStart + 2147483647}, 110 wantPub: "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a", 111 wantPriv: "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", 112 net: &chaincfg.MainNetParams, 113 }, 114 { 115 name: "test vector 2 chain m/0/2147483647H/1", 116 master: testVec2MasterHex, 117 path: []uint32{0, hkStart + 2147483647, 1}, 118 wantPub: "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon", 119 wantPriv: "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", 120 net: &chaincfg.MainNetParams, 121 }, 122 { 123 name: "test vector 2 chain m/0/2147483647H/1/2147483646H", 124 master: testVec2MasterHex, 125 path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646}, 126 wantPub: "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", 127 wantPriv: "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", 128 net: &chaincfg.MainNetParams, 129 }, 130 { 131 name: "test vector 2 chain m/0/2147483647H/1/2147483646H/2", 132 master: testVec2MasterHex, 133 path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646, 2}, 134 wantPub: "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", 135 wantPriv: "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", 136 net: &chaincfg.MainNetParams, 137 }, 138 139 // Test vector 3 140 { 141 name: "test vector 3 chain m", 142 master: testVec3MasterHex, 143 path: []uint32{}, 144 wantPub: "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13", 145 wantPriv: "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6", 146 net: &chaincfg.MainNetParams, 147 }, 148 { 149 name: "test vector 3 chain m/0H", 150 master: testVec3MasterHex, 151 path: []uint32{hkStart}, 152 wantPub: "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y", 153 wantPriv: "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L", 154 net: &chaincfg.MainNetParams, 155 }, 156 157 // Test vector 1 - Testnet 158 { 159 name: "test vector 1 chain m - testnet", 160 master: testVec1MasterHex, 161 path: []uint32{}, 162 wantPub: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp", 163 wantPriv: "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m", 164 net: &chaincfg.TestNet3Params, 165 }, 166 { 167 name: "test vector 1 chain m/0H - testnet", 168 master: testVec1MasterHex, 169 path: []uint32{hkStart}, 170 wantPub: "tpubD8eQVK4Kdxg3gHrF62jGP7dKVCoYiEB8dFSpuTawkL5YxTus5j5pf83vaKnii4bc6v2NVEy81P2gYrJczYne3QNNwMTS53p5uzDyHvnw2jm", 171 wantPriv: "tprv8bxNLu25VazNnppTCP4fyhyCvBHcYtzE3wr3cwYeL4HA7yf6TLGEUdS4QC1vLT63TkjRssqJe4CvGNEC8DzW5AoPUw56D1Ayg6HY4oy8QZ9", 172 net: &chaincfg.TestNet3Params, 173 }, 174 { 175 name: "test vector 1 chain m/0H/1 - testnet", 176 master: testVec1MasterHex, 177 path: []uint32{hkStart, 1}, 178 wantPub: "tpubDApXh6cD2fZ7WjtgpHd8yrWyYaneiFuRZa7fVjMkgxsmC1QzoXW8cgx9zQFJ81Jx4deRGfRE7yXA9A3STsxXj4CKEZJHYgpMYikkas9DBTP", 179 wantPriv: "tprv8e8VYgZxtHsSdGrtvdxYaSrryZGiYviWzGWtDDKTGh5NMXAEB8gYSCLHpFCywNs5uqV7ghRjimALQJkRFZnUrLHpzi2pGkwqLtbubgWuQ8q", 180 net: &chaincfg.TestNet3Params, 181 }, 182 { 183 name: "test vector 1 chain m/0H/1/2H - testnet", 184 master: testVec1MasterHex, 185 path: []uint32{hkStart, 1, hkStart + 2}, 186 wantPub: "tpubDDRojdS4jYQXNugn4t2WLrZ7mjfAyoVQu7MLk4eurqFCbrc7cHLZX8W5YRS8ZskGR9k9t3PqVv68bVBjAyW4nWM9pTGRddt3GQftg6MVQsm", 187 wantPriv: "tprv8gjmbDPpbAirVSezBEMuwSu1Ci9EpUJWKokZTYccSZSomNMLytWyLdtDNHRbucNaRJWWHANf9AzEdWVAqahfyRjVMKbNRhBmxAM8EJr7R15", 188 net: &chaincfg.TestNet3Params, 189 }, 190 { 191 name: "test vector 1 chain m/0H/1/2H/2 - testnet", 192 master: testVec1MasterHex, 193 path: []uint32{hkStart, 1, hkStart + 2, 2}, 194 wantPub: "tpubDFfCa4Z1v25WTPAVm9EbEMiRrYwucPocLbEe12BPBGooxxEUg42vihy1DkRWyftztTsL23snYezF9uXjGGwGW6pQjEpcTpmsH6ajpf4CVPn", 195 wantPriv: "tprv8iyAReWmmePqZv8hsVZzpx4KHXRyT4chmHdriW95m11R8Tyi3fDLYDM93bq4NGn1V6eCu5cE3zSQ6hPd31F2ApKXkZgTyn1V78pHjkq1V2v", 196 net: &chaincfg.TestNet3Params, 197 }, 198 { 199 name: "test vector 1 chain m/0H/1/2H/2/1000000000 - testnet", 200 master: testVec1MasterHex, 201 path: []uint32{hkStart, 1, hkStart + 2, 2, 1000000000}, 202 wantPub: "tpubDHNy3kAG39ThyiwwsgoKY4iRenXDRtce8qdCFJZXPMCJg5dsCUHayp84raLTpvyiNA9sXPob5rgqkKvkN8S7MMyXbnEhGJMW64Cf4vFAoaF", 203 wantPriv: "tprv8kgvuL81tmn36Fv9z38j8f4K5m1HGZRjZY2QxnXDy5PuqbP6a5TzoKWCgTcGHBu66W3TgSbAu2yX6sPza5FkHmy564Sh6gmCPUNeUt4yj2x", 204 net: &chaincfg.TestNet3Params, 205 }, 206 } 207 208 tests: 209 for i, test := range tests { 210 masterSeed, err := hex.DecodeString(test.master) 211 if err != nil { 212 t.Errorf("DecodeString #%d (%s): unexpected error: %v", 213 i, test.name, err) 214 continue 215 } 216 217 extKey, err := NewMaster(masterSeed, test.net) 218 if err != nil { 219 t.Errorf("NewMaster #%d (%s): unexpected error when "+ 220 "creating new master key: %v", i, test.name, 221 err) 222 continue 223 } 224 225 for _, childNum := range test.path { 226 var err error 227 extKey, err = extKey.Child(childNum) 228 if err != nil { 229 t.Errorf("err: %v", err) 230 continue tests 231 } 232 } 233 234 if extKey.Depth() != uint8(len(test.path)) { 235 t.Errorf("Depth of key %d should match fixture path") 236 continue 237 } 238 239 privStr := extKey.String() 240 if privStr != test.wantPriv { 241 t.Errorf("Serialize #%d (%s): mismatched serialized "+ 242 "private extended key -- got: %s, want: %s", i, 243 test.name, privStr, test.wantPriv) 244 continue 245 } 246 247 pubKey, err := extKey.Neuter() 248 if err != nil { 249 t.Errorf("Neuter #%d (%s): unexpected error: %v ", i, 250 test.name, err) 251 continue 252 } 253 254 // Neutering a second time should have no effect. 255 pubKey, err = pubKey.Neuter() 256 if err != nil { 257 t.Errorf("Neuter #%d (%s): unexpected error: %v", i, 258 test.name, err) 259 return 260 } 261 262 pubStr := pubKey.String() 263 if pubStr != test.wantPub { 264 t.Errorf("Neuter #%d (%s): mismatched serialized "+ 265 "public extended key -- got: %s, want: %s", i, 266 test.name, pubStr, test.wantPub) 267 continue 268 } 269 } 270 } 271 272 // TestPrivateDerivation tests several vectors which derive private keys from 273 // other private keys works as intended. 274 func TestPrivateDerivation(t *testing.T) { 275 // The private extended keys for test vectors in [BIP32]. 276 testVec1MasterPrivKey := "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" 277 testVec2MasterPrivKey := "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U" 278 279 tests := []struct { 280 name string 281 master string 282 path []uint32 283 wantPriv string 284 }{ 285 // Test vector 1 286 { 287 name: "test vector 1 chain m", 288 master: testVec1MasterPrivKey, 289 path: []uint32{}, 290 wantPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", 291 }, 292 { 293 name: "test vector 1 chain m/0", 294 master: testVec1MasterPrivKey, 295 path: []uint32{0}, 296 wantPriv: "xprv9uHRZZhbkedL37eZEnyrNsQPFZYRAvjy5rt6M1nbEkLSo378x1CQQLo2xxBvREwiK6kqf7GRNvsNEchwibzXaV6i5GcsgyjBeRguXhKsi4R", 297 }, 298 { 299 name: "test vector 1 chain m/0/1", 300 master: testVec1MasterPrivKey, 301 path: []uint32{0, 1}, 302 wantPriv: "xprv9ww7sMFLzJMzy7bV1qs7nGBxgKYrgcm3HcJvGb4yvNhT9vxXC7eX7WVULzCfxucFEn2TsVvJw25hH9d4mchywguGQCZvRgsiRaTY1HCqN8G", 303 }, 304 { 305 name: "test vector 1 chain m/0/1/2", 306 master: testVec1MasterPrivKey, 307 path: []uint32{0, 1, 2}, 308 wantPriv: "xprv9xrdP7iD2L1YZCgR9AecDgpDMZSTzP5KCfUykGXgjBxLgp1VFHsEeL3conzGAkbc1MigG1o8YqmfEA2jtkPdf4vwMaGJC2YSDbBTPAjfRUi", 309 }, 310 { 311 name: "test vector 1 chain m/0/1/2/2", 312 master: testVec1MasterPrivKey, 313 path: []uint32{0, 1, 2, 2}, 314 wantPriv: "xprvA2J8Hq4eiP7xCEBP7gzRJGJnd9CHTkEU6eTNMrZ6YR7H5boik8daFtDZxmJDfdMSKHwroCfAfsBKWWidRfBQjpegy6kzXSkQGGoMdWKz5Xh", 315 }, 316 { 317 name: "test vector 1 chain m/0/1/2/2/1000000000", 318 master: testVec1MasterPrivKey, 319 path: []uint32{0, 1, 2, 2, 1000000000}, 320 wantPriv: "xprvA3XhazxncJqJsQcG85Gg61qwPQKiobAnWjuPpjKhExprZjfse6nErRwTMwGe6uGWXPSykZSTiYb2TXAm7Qhwj8KgRd2XaD21Styu6h6AwFz", 321 }, 322 323 // Test vector 2 324 { 325 name: "test vector 2 chain m", 326 master: testVec2MasterPrivKey, 327 path: []uint32{}, 328 wantPriv: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", 329 }, 330 { 331 name: "test vector 2 chain m/0", 332 master: testVec2MasterPrivKey, 333 path: []uint32{0}, 334 wantPriv: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", 335 }, 336 { 337 name: "test vector 2 chain m/0/2147483647", 338 master: testVec2MasterPrivKey, 339 path: []uint32{0, 2147483647}, 340 wantPriv: "xprv9wSp6B7cXJWXZRpDbxkFg3ry2fuSyUfvboJ5Yi6YNw7i1bXmq9QwQ7EwMpeG4cK2pnMqEx1cLYD7cSGSCtruGSXC6ZSVDHugMsZgbuY62m6", 341 }, 342 { 343 name: "test vector 2 chain m/0/2147483647/1", 344 master: testVec2MasterPrivKey, 345 path: []uint32{0, 2147483647, 1}, 346 wantPriv: "xprv9ysS5br6UbWCRCJcggvpUNMyhVWgD7NypY9gsVTMYmuRtZg8izyYC5Ey4T931WgWbfJwRDwfVFqV3b29gqHDbuEpGcbzf16pdomk54NXkSm", 347 }, 348 { 349 name: "test vector 2 chain m/0/2147483647/1/2147483646", 350 master: testVec2MasterPrivKey, 351 path: []uint32{0, 2147483647, 1, 2147483646}, 352 wantPriv: "xprvA2LfeWWwRCxh4iqigcDMnUf2E3nVUFkntc93nmUYBtb9rpSPYWa8MY3x9ZHSLZkg4G84UefrDruVK3FhMLSJsGtBx883iddHNuH1LNpRrEp", 353 }, 354 { 355 name: "test vector 2 chain m/0/2147483647/1/2147483646/2", 356 master: testVec2MasterPrivKey, 357 path: []uint32{0, 2147483647, 1, 2147483646, 2}, 358 wantPriv: "xprvA48ALo8BDjcRET68R5RsPzF3H7WeyYYtHcyUeLRGBPHXu6CJSGjwW7dWoeUWTEzT7LG3qk6Eg6x2ZoqD8gtyEFZecpAyvchksfLyg3Zbqam", 359 }, 360 361 // Custom tests to trigger specific conditions. 362 { 363 // Seed 000000000000000000000000000000da. 364 name: "Derived privkey with zero high byte m/0", 365 master: "xprv9s21ZrQH143K4FR6rNeqEK4EBhRgLjWLWhA3pw8iqgAKk82ypz58PXbrzU19opYcxw8JDJQF4id55PwTsN1Zv8Xt6SKvbr2KNU5y8jN8djz", 366 path: []uint32{0}, 367 wantPriv: "xprv9uC5JqtViMmgcAMUxcsBCBFA7oYCNs4bozPbyvLfddjHou4rMiGEHipz94xNaPb1e4f18TRoPXfiXx4C3cDAcADqxCSRSSWLvMBRWPctSN9", 368 }, 369 } 370 371 tests: 372 for i, test := range tests { 373 extKey, err := NewKeyFromString(test.master) 374 if err != nil { 375 t.Errorf("NewKeyFromString #%d (%s): unexpected error "+ 376 "creating extended key: %v", i, test.name, 377 err) 378 continue 379 } 380 381 for _, childNum := range test.path { 382 var err error 383 extKey, err = extKey.Child(childNum) 384 if err != nil { 385 t.Errorf("err: %v", err) 386 continue tests 387 } 388 } 389 390 privStr := extKey.String() 391 if privStr != test.wantPriv { 392 t.Errorf("Child #%d (%s): mismatched serialized "+ 393 "private extended key -- got: %s, want: %s", i, 394 test.name, privStr, test.wantPriv) 395 continue 396 } 397 } 398 } 399 400 // TestPublicDerivation tests several vectors which derive public keys from 401 // other public keys works as intended. 402 func TestPublicDerivation(t *testing.T) { 403 // The public extended keys for test vectors in [BIP32]. 404 testVec1MasterPubKey := "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" 405 testVec2MasterPubKey := "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB" 406 407 tests := []struct { 408 name string 409 master string 410 path []uint32 411 wantPub string 412 }{ 413 // Test vector 1 414 { 415 name: "test vector 1 chain m", 416 master: testVec1MasterPubKey, 417 path: []uint32{}, 418 wantPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 419 }, 420 { 421 name: "test vector 1 chain m/0", 422 master: testVec1MasterPubKey, 423 path: []uint32{0}, 424 wantPub: "xpub68Gmy5EVb2BdFbj2LpWrk1M7obNuaPTpT5oh9QCCo5sRfqSHVYWex97WpDZzszdzHzxXDAzPLVSwybe4uPYkSk4G3gnrPqqkV9RyNzAcNJ1", 425 }, 426 { 427 name: "test vector 1 chain m/0/1", 428 master: testVec1MasterPubKey, 429 path: []uint32{0, 1}, 430 wantPub: "xpub6AvUGrnEpfvJBbfx7sQ89Q8hEMPM65UteqEX4yUbUiES2jHfjexmfJoxCGSwFMZiPBaKQT1RiKWrKfuDV4vpgVs4Xn8PpPTR2i79rwHd4Zr", 431 }, 432 { 433 name: "test vector 1 chain m/0/1/2", 434 master: testVec1MasterPubKey, 435 path: []uint32{0, 1, 2}, 436 wantPub: "xpub6BqyndF6rhZqmgktFCBcapkwubGxPqoAZtQaYewJHXVKZcLdnqBVC8N6f6FSHWUghjuTLeubWyQWfJdk2G3tGgvgj3qngo4vLTnnSjAZckv", 437 }, 438 { 439 name: "test vector 1 chain m/0/1/2/2", 440 master: testVec1MasterPubKey, 441 path: []uint32{0, 1, 2, 2}, 442 wantPub: "xpub6FHUhLbYYkgFQiFrDiXRfQFXBB2msCxKTsNyAExi6keFxQ8sHfwpogY3p3s1ePSpUqLNYks5T6a3JqpCGszt4kxbyq7tUoFP5c8KWyiDtPp", 443 }, 444 { 445 name: "test vector 1 chain m/0/1/2/2/1000000000", 446 master: testVec1MasterPubKey, 447 path: []uint32{0, 1, 2, 2, 1000000000}, 448 wantPub: "xpub6GX3zWVgSgPc5tgjE6ogT9nfwSADD3tdsxpzd7jJoJMqSY12Be6VQEFwDCp6wAQoZsH2iq5nNocHEaVDxBcobPrkZCjYW3QUmoDYzMFBDu9", 449 }, 450 451 // Test vector 2 452 { 453 name: "test vector 2 chain m", 454 master: testVec2MasterPubKey, 455 path: []uint32{}, 456 wantPub: "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", 457 }, 458 { 459 name: "test vector 2 chain m/0", 460 master: testVec2MasterPubKey, 461 path: []uint32{0}, 462 wantPub: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", 463 }, 464 { 465 name: "test vector 2 chain m/0/2147483647", 466 master: testVec2MasterPubKey, 467 path: []uint32{0, 2147483647}, 468 wantPub: "xpub6ASAVgeWMg4pmutghzHG3BohahjwNwPmy2DgM6W9wGegtPrvNgjBwuZRD7hSDFhYfunq8vDgwG4ah1gVzZysgp3UsKz7VNjCnSUJJ5T4fdD", 469 }, 470 { 471 name: "test vector 2 chain m/0/2147483647/1", 472 master: testVec2MasterPubKey, 473 path: []uint32{0, 2147483647, 1}, 474 wantPub: "xpub6CrnV7NzJy4VdgP5niTpqWJiFXMAca6qBm5Hfsry77SQmN1HGYHnjsZSujoHzdxf7ZNK5UVrmDXFPiEW2ecwHGWMFGUxPC9ARipss9rXd4b", 475 }, 476 { 477 name: "test vector 2 chain m/0/2147483647/1/2147483646", 478 master: testVec2MasterPubKey, 479 path: []uint32{0, 2147483647, 1, 2147483646}, 480 wantPub: "xpub6FL2423qFaWzHCvBndkN9cbkn5cysiUeFq4eb9t9kE88jcmY63tNuLNRzpHPdAM4dUpLhZ7aUm2cJ5zF7KYonf4jAPfRqTMTRBNkQL3Tfta", 481 }, 482 { 483 name: "test vector 2 chain m/0/2147483647/1/2147483646/2", 484 master: testVec2MasterPubKey, 485 path: []uint32{0, 2147483647, 1, 2147483646, 2}, 486 wantPub: "xpub6H7WkJf547AiSwAbX6xsm8Bmq9M9P1Gjequ5SipsjipWmtXSyp4C3uwzewedGEgAMsDy4jEvNTWtxLyqqHY9C12gaBmgUdk2CGmwachwnWK", 487 }, 488 } 489 490 tests: 491 for i, test := range tests { 492 extKey, err := NewKeyFromString(test.master) 493 if err != nil { 494 t.Errorf("NewKeyFromString #%d (%s): unexpected error "+ 495 "creating extended key: %v", i, test.name, 496 err) 497 continue 498 } 499 500 for _, childNum := range test.path { 501 var err error 502 extKey, err = extKey.Child(childNum) 503 if err != nil { 504 t.Errorf("err: %v", err) 505 continue tests 506 } 507 } 508 509 pubStr := extKey.String() 510 if pubStr != test.wantPub { 511 t.Errorf("Child #%d (%s): mismatched serialized "+ 512 "public extended key -- got: %s, want: %s", i, 513 test.name, pubStr, test.wantPub) 514 continue 515 } 516 } 517 } 518 519 // TestGenenerateSeed ensures the GenerateSeed function works as intended. 520 func TestGenenerateSeed(t *testing.T) { 521 wantErr := errors.New("seed length must be between 128 and 512 bits") 522 523 tests := []struct { 524 name string 525 length uint8 526 err error 527 }{ 528 // Test various valid lengths. 529 {name: "16 bytes", length: 16}, 530 {name: "17 bytes", length: 17}, 531 {name: "20 bytes", length: 20}, 532 {name: "32 bytes", length: 32}, 533 {name: "64 bytes", length: 64}, 534 535 // Test invalid lengths. 536 {name: "15 bytes", length: 15, err: wantErr}, 537 {name: "65 bytes", length: 65, err: wantErr}, 538 } 539 540 for i, test := range tests { 541 seed, err := GenerateSeed(test.length) 542 if !reflect.DeepEqual(err, test.err) { 543 t.Errorf("GenerateSeed #%d (%s): unexpected error -- "+ 544 "want %v, got %v", i, test.name, test.err, err) 545 continue 546 } 547 548 if test.err == nil && len(seed) != int(test.length) { 549 t.Errorf("GenerateSeed #%d (%s): length mismatch -- "+ 550 "got %d, want %d", i, test.name, len(seed), 551 test.length) 552 continue 553 } 554 } 555 } 556 557 // TestExtendedKeyAPI ensures the API on the ExtendedKey type works as intended. 558 func TestExtendedKeyAPI(t *testing.T) { 559 tests := []struct { 560 name string 561 extKey string 562 isPrivate bool 563 parentFP uint32 564 privKey string 565 privKeyErr error 566 pubKey string 567 address string 568 }{ 569 { 570 name: "test vector 1 master node private", 571 extKey: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", 572 isPrivate: true, 573 parentFP: 0, 574 privKey: "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", 575 pubKey: "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2", 576 address: "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma", 577 }, 578 { 579 name: "test vector 1 chain m/0H/1/2H public", 580 extKey: "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", 581 isPrivate: false, 582 parentFP: 3203769081, 583 privKeyErr: ErrNotPrivExtKey, 584 pubKey: "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2", 585 address: "1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x", 586 }, 587 } 588 589 for i, test := range tests { 590 key, err := NewKeyFromString(test.extKey) 591 if err != nil { 592 t.Errorf("NewKeyFromString #%d (%s): unexpected "+ 593 "error: %v", i, test.name, err) 594 continue 595 } 596 597 if key.IsPrivate() != test.isPrivate { 598 t.Errorf("IsPrivate #%d (%s): mismatched key type -- "+ 599 "want private %v, got private %v", i, test.name, 600 test.isPrivate, key.IsPrivate()) 601 continue 602 } 603 604 parentFP := key.ParentFingerprint() 605 if parentFP != test.parentFP { 606 t.Errorf("ParentFingerprint #%d (%s): mismatched "+ 607 "parent fingerprint -- want %d, got %d", i, 608 test.name, test.parentFP, parentFP) 609 continue 610 } 611 612 serializedKey := key.String() 613 if serializedKey != test.extKey { 614 t.Errorf("String #%d (%s): mismatched serialized key "+ 615 "-- want %s, got %s", i, test.name, test.extKey, 616 serializedKey) 617 continue 618 } 619 620 privKey, err := key.ECPrivKey() 621 if !reflect.DeepEqual(err, test.privKeyErr) { 622 t.Errorf("ECPrivKey #%d (%s): mismatched error: want "+ 623 "%v, got %v", i, test.name, test.privKeyErr, err) 624 continue 625 } 626 if test.privKeyErr == nil { 627 privKeyStr := hex.EncodeToString(privKey.Serialize()) 628 if privKeyStr != test.privKey { 629 t.Errorf("ECPrivKey #%d (%s): mismatched "+ 630 "private key -- want %s, got %s", i, 631 test.name, test.privKey, privKeyStr) 632 continue 633 } 634 } 635 636 pubKey, err := key.ECPubKey() 637 if err != nil { 638 t.Errorf("ECPubKey #%d (%s): unexpected error: %v", i, 639 test.name, err) 640 continue 641 } 642 pubKeyStr := hex.EncodeToString(pubKey.SerializeCompressed()) 643 if pubKeyStr != test.pubKey { 644 t.Errorf("ECPubKey #%d (%s): mismatched public key -- "+ 645 "want %s, got %s", i, test.name, test.pubKey, 646 pubKeyStr) 647 continue 648 } 649 650 addr, err := key.Address(&chaincfg.MainNetParams) 651 if err != nil { 652 t.Errorf("Address #%d (%s): unexpected error: %v", i, 653 test.name, err) 654 continue 655 } 656 if addr.EncodeAddress() != test.address { 657 t.Errorf("Address #%d (%s): mismatched address -- want "+ 658 "%s, got %s", i, test.name, test.address, 659 addr.EncodeAddress()) 660 continue 661 } 662 } 663 } 664 665 // TestNet ensures the network related APIs work as intended. 666 func TestNet(t *testing.T) { 667 tests := []struct { 668 name string 669 key string 670 origNet *chaincfg.Params 671 newNet *chaincfg.Params 672 newPriv string 673 newPub string 674 isPrivate bool 675 }{ 676 // Private extended keys. 677 { 678 name: "mainnet -> simnet", 679 key: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", 680 origNet: &chaincfg.MainNetParams, 681 newNet: &chaincfg.SimNetParams, 682 newPriv: "sprv8Erh3X3hFeKunvVdAGQQtambRPapECWiTDtvsTGdyrhzhbYgnSZajRRWbihzvq4AM4ivm6uso31VfKaukwJJUs3GYihXP8ebhMb3F2AHu3P", 683 newPub: "spub4Tr3T2ab61tD1Qa6GHwRFiiKyRRJdfEZpSpXfqgFYCEyaPsqKysqHDjzSzMJSiUEGbcsG3w2SLMoTqn44B8x6u3MLRRkYfACTUBnHK79THk", 684 isPrivate: true, 685 }, 686 { 687 name: "simnet -> mainnet", 688 key: "sprv8Erh3X3hFeKunvVdAGQQtambRPapECWiTDtvsTGdyrhzhbYgnSZajRRWbihzvq4AM4ivm6uso31VfKaukwJJUs3GYihXP8ebhMb3F2AHu3P", 689 origNet: &chaincfg.SimNetParams, 690 newNet: &chaincfg.MainNetParams, 691 newPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", 692 newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 693 isPrivate: true, 694 }, 695 { 696 name: "mainnet -> regtest", 697 key: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", 698 origNet: &chaincfg.MainNetParams, 699 newNet: &chaincfg.RegressionNetParams, 700 newPriv: "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m", 701 newPub: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp", 702 isPrivate: true, 703 }, 704 { 705 name: "regtest -> mainnet", 706 key: "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m", 707 origNet: &chaincfg.RegressionNetParams, 708 newNet: &chaincfg.MainNetParams, 709 newPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", 710 newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 711 isPrivate: true, 712 }, 713 714 // Public extended keys. 715 { 716 name: "mainnet -> simnet", 717 key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 718 origNet: &chaincfg.MainNetParams, 719 newNet: &chaincfg.SimNetParams, 720 newPub: "spub4Tr3T2ab61tD1Qa6GHwRFiiKyRRJdfEZpSpXfqgFYCEyaPsqKysqHDjzSzMJSiUEGbcsG3w2SLMoTqn44B8x6u3MLRRkYfACTUBnHK79THk", 721 isPrivate: false, 722 }, 723 { 724 name: "simnet -> mainnet", 725 key: "spub4Tr3T2ab61tD1Qa6GHwRFiiKyRRJdfEZpSpXfqgFYCEyaPsqKysqHDjzSzMJSiUEGbcsG3w2SLMoTqn44B8x6u3MLRRkYfACTUBnHK79THk", 726 origNet: &chaincfg.SimNetParams, 727 newNet: &chaincfg.MainNetParams, 728 newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 729 isPrivate: false, 730 }, 731 { 732 name: "mainnet -> regtest", 733 key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 734 origNet: &chaincfg.MainNetParams, 735 newNet: &chaincfg.RegressionNetParams, 736 newPub: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp", 737 isPrivate: false, 738 }, 739 { 740 name: "regtest -> mainnet", 741 key: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp", 742 origNet: &chaincfg.RegressionNetParams, 743 newNet: &chaincfg.MainNetParams, 744 newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 745 isPrivate: false, 746 }, 747 } 748 749 for i, test := range tests { 750 extKey, err := NewKeyFromString(test.key) 751 if err != nil { 752 t.Errorf("NewKeyFromString #%d (%s): unexpected error "+ 753 "creating extended key: %v", i, test.name, 754 err) 755 continue 756 } 757 758 if !extKey.IsForNet(test.origNet) { 759 t.Errorf("IsForNet #%d (%s): key is not for expected "+ 760 "network %v", i, test.name, test.origNet.Name) 761 continue 762 } 763 764 extKey.SetNet(test.newNet) 765 if !extKey.IsForNet(test.newNet) { 766 t.Errorf("SetNet/IsForNet #%d (%s): key is not for "+ 767 "expected network %v", i, test.name, 768 test.newNet.Name) 769 continue 770 } 771 772 if test.isPrivate { 773 privStr := extKey.String() 774 if privStr != test.newPriv { 775 t.Errorf("Serialize #%d (%s): mismatched serialized "+ 776 "private extended key -- got: %s, want: %s", i, 777 test.name, privStr, test.newPriv) 778 continue 779 } 780 781 extKey, err = extKey.Neuter() 782 if err != nil { 783 t.Errorf("Neuter #%d (%s): unexpected error: %v ", i, 784 test.name, err) 785 continue 786 } 787 } 788 789 pubStr := extKey.String() 790 if pubStr != test.newPub { 791 t.Errorf("Neuter #%d (%s): mismatched serialized "+ 792 "public extended key -- got: %s, want: %s", i, 793 test.name, pubStr, test.newPub) 794 continue 795 } 796 } 797 } 798 799 // TestErrors performs some negative tests for various invalid cases to ensure 800 // the errors are handled properly. 801 func TestErrors(t *testing.T) { 802 // Should get an error when seed has too few bytes. 803 net := &chaincfg.MainNetParams 804 _, err := NewMaster(bytes.Repeat([]byte{0x00}, 15), net) 805 if err != ErrInvalidSeedLen { 806 t.Fatalf("NewMaster: mismatched error -- got: %v, want: %v", 807 err, ErrInvalidSeedLen) 808 } 809 810 // Should get an error when seed has too many bytes. 811 _, err = NewMaster(bytes.Repeat([]byte{0x00}, 65), net) 812 if err != ErrInvalidSeedLen { 813 t.Fatalf("NewMaster: mismatched error -- got: %v, want: %v", 814 err, ErrInvalidSeedLen) 815 } 816 817 // Generate a new key and neuter it to a public extended key. 818 seed, err := GenerateSeed(RecommendedSeedLen) 819 if err != nil { 820 t.Fatalf("GenerateSeed: unexpected error: %v", err) 821 } 822 extKey, err := NewMaster(seed, net) 823 if err != nil { 824 t.Fatalf("NewMaster: unexpected error: %v", err) 825 } 826 pubKey, err := extKey.Neuter() 827 if err != nil { 828 t.Fatalf("Neuter: unexpected error: %v", err) 829 } 830 831 // Deriving a hardened child extended key should fail from a public key. 832 _, err = pubKey.Child(HardenedKeyStart) 833 if err != ErrDeriveHardFromPublic { 834 t.Fatalf("Child: mismatched error -- got: %v, want: %v", 835 err, ErrDeriveHardFromPublic) 836 } 837 838 // NewKeyFromString failure tests. 839 tests := []struct { 840 name string 841 key string 842 err error 843 neuter bool 844 neuterErr error 845 }{ 846 { 847 name: "invalid key length", 848 key: "xpub1234", 849 err: ErrInvalidKeyLen, 850 }, 851 { 852 name: "bad checksum", 853 key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EBygr15", 854 err: ErrBadChecksum, 855 }, 856 { 857 name: "pubkey not on curve", 858 key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ1hr9Rwbk95YadvBkQXxzHBSngB8ndpW6QH7zhhsXZ2jHyZqPjk", 859 err: errors.New("pubkey isn't on secp256k1 curve"), 860 }, 861 { 862 name: "unsupported version", 863 key: "xbad4LfUL9eKmA66w2GJdVMqhvDmYGJpTGjWRAtjHqoUY17sGaymoMV9Cm3ocn9Ud6Hh2vLFVC7KSKCRVVrqc6dsEdsTjRV1WUmkK85YEUujAPX", 864 err: nil, 865 neuter: true, 866 neuterErr: chaincfg.ErrUnknownHDKeyID, 867 }, 868 } 869 870 for i, test := range tests { 871 extKey, err := NewKeyFromString(test.key) 872 if !reflect.DeepEqual(err, test.err) { 873 t.Errorf("NewKeyFromString #%d (%s): mismatched error "+ 874 "-- got: %v, want: %v", i, test.name, err, 875 test.err) 876 continue 877 } 878 879 if test.neuter { 880 _, err := extKey.Neuter() 881 if !reflect.DeepEqual(err, test.neuterErr) { 882 t.Errorf("Neuter #%d (%s): mismatched error "+ 883 "-- got: %v, want: %v", i, test.name, 884 err, test.neuterErr) 885 continue 886 } 887 } 888 } 889 } 890 891 // TestZero ensures that zeroing an extended key works as intended. 892 func TestZero(t *testing.T) { 893 tests := []struct { 894 name string 895 master string 896 extKey string 897 net *chaincfg.Params 898 }{ 899 // Test vector 1 900 { 901 name: "test vector 1 chain m", 902 master: "000102030405060708090a0b0c0d0e0f", 903 extKey: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", 904 net: &chaincfg.MainNetParams, 905 }, 906 907 // Test vector 2 908 { 909 name: "test vector 2 chain m", 910 master: "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", 911 extKey: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", 912 net: &chaincfg.MainNetParams, 913 }, 914 } 915 916 // Use a closure to test that a key is zeroed since the tests create 917 // keys in different ways and need to test the same things multiple 918 // times. 919 testZeroed := func(i int, testName string, key *ExtendedKey) bool { 920 // Zeroing a key should result in it no longer being private 921 if key.IsPrivate() { 922 t.Errorf("IsPrivate #%d (%s): mismatched key type -- "+ 923 "want private %v, got private %v", i, testName, 924 false, key.IsPrivate()) 925 return false 926 } 927 928 parentFP := key.ParentFingerprint() 929 if parentFP != 0 { 930 t.Errorf("ParentFingerprint #%d (%s): mismatched "+ 931 "parent fingerprint -- want %d, got %d", i, 932 testName, 0, parentFP) 933 return false 934 } 935 936 wantKey := "zeroed extended key" 937 serializedKey := key.String() 938 if serializedKey != wantKey { 939 t.Errorf("String #%d (%s): mismatched serialized key "+ 940 "-- want %s, got %s", i, testName, wantKey, 941 serializedKey) 942 return false 943 } 944 945 wantErr := ErrNotPrivExtKey 946 _, err := key.ECPrivKey() 947 if !reflect.DeepEqual(err, wantErr) { 948 t.Errorf("ECPrivKey #%d (%s): mismatched error: want "+ 949 "%v, got %v", i, testName, wantErr, err) 950 return false 951 } 952 953 wantErr = errors.New("pubkey string is empty") 954 _, err = key.ECPubKey() 955 if !reflect.DeepEqual(err, wantErr) { 956 t.Errorf("ECPubKey #%d (%s): mismatched error: want "+ 957 "%v, got %v", i, testName, wantErr, err) 958 return false 959 } 960 961 wantAddr := "1HT7xU2Ngenf7D4yocz2SAcnNLW7rK8d4E" 962 addr, err := key.Address(&chaincfg.MainNetParams) 963 if err != nil { 964 t.Errorf("Addres s #%d (%s): unexpected error: %v", i, 965 testName, err) 966 return false 967 } 968 if addr.EncodeAddress() != wantAddr { 969 t.Errorf("Address #%d (%s): mismatched address -- want "+ 970 "%s, got %s", i, testName, wantAddr, 971 addr.EncodeAddress()) 972 return false 973 } 974 975 return true 976 } 977 978 for i, test := range tests { 979 // Create new key from seed and get the neutered version. 980 masterSeed, err := hex.DecodeString(test.master) 981 if err != nil { 982 t.Errorf("DecodeString #%d (%s): unexpected error: %v", 983 i, test.name, err) 984 continue 985 } 986 key, err := NewMaster(masterSeed, test.net) 987 if err != nil { 988 t.Errorf("NewMaster #%d (%s): unexpected error when "+ 989 "creating new master key: %v", i, test.name, 990 err) 991 continue 992 } 993 neuteredKey, err := key.Neuter() 994 if err != nil { 995 t.Errorf("Neuter #%d (%s): unexpected error: %v", i, 996 test.name, err) 997 continue 998 } 999 1000 // Ensure both non-neutered and neutered keys are zeroed 1001 // properly. 1002 key.Zero() 1003 if !testZeroed(i, test.name+" from seed not neutered", key) { 1004 continue 1005 } 1006 neuteredKey.Zero() 1007 if !testZeroed(i, test.name+" from seed neutered", key) { 1008 continue 1009 } 1010 1011 // Deserialize key and get the neutered version. 1012 key, err = NewKeyFromString(test.extKey) 1013 if err != nil { 1014 t.Errorf("NewKeyFromString #%d (%s): unexpected "+ 1015 "error: %v", i, test.name, err) 1016 continue 1017 } 1018 neuteredKey, err = key.Neuter() 1019 if err != nil { 1020 t.Errorf("Neuter #%d (%s): unexpected error: %v", i, 1021 test.name, err) 1022 continue 1023 } 1024 1025 // Ensure both non-neutered and neutered keys are zeroed 1026 // properly. 1027 key.Zero() 1028 if !testZeroed(i, test.name+" deserialized not neutered", key) { 1029 continue 1030 } 1031 neuteredKey.Zero() 1032 if !testZeroed(i, test.name+" deserialized neutered", key) { 1033 continue 1034 } 1035 } 1036 } 1037 1038 // TestMaximumDepth ensures that attempting to retrieve a child key when already 1039 // at the maximum depth is not allowed. The serialization of a BIP32 key uses 1040 // uint8 to encode the depth. This implicitly bounds the depth of the tree to 1041 // 255 derivations. Here we test that an error is returned after 'max uint8'. 1042 func TestMaximumDepth(t *testing.T) { 1043 net := &chaincfg.MainNetParams 1044 extKey, err := NewMaster([]byte(`abcd1234abcd1234abcd1234abcd1234`), net) 1045 if err != nil { 1046 t.Fatalf("NewMaster: unexpected error: %v", err) 1047 } 1048 1049 for i := uint8(0); i < math.MaxUint8; i++ { 1050 if extKey.Depth() != i { 1051 t.Fatalf("extendedkey depth %d should match expected value %d", 1052 extKey.Depth(), i) 1053 } 1054 newKey, err := extKey.Child(1) 1055 if err != nil { 1056 t.Fatalf("Child: unexpected error: %v", err) 1057 } 1058 extKey = newKey 1059 } 1060 1061 noKey, err := extKey.Child(1) 1062 if err != ErrDeriveBeyondMaxDepth { 1063 t.Fatalf("Child: mismatched error: want %v, got %v", 1064 ErrDeriveBeyondMaxDepth, err) 1065 } 1066 if noKey != nil { 1067 t.Fatal("Child: deriving 256th key should not succeed") 1068 } 1069 }