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