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