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