UNPKG

10 kBJavaScriptView Raw
1'use strict';
2
3/* jshint unused: false */
4var _ = require('lodash');
5var assert = require('assert');
6var should = require('chai').should();
7var expect = require('chai').expect;
8var bitcore = require('..');
9var buffer = require('buffer');
10var errors = bitcore.errors;
11var hdErrors = bitcore.errors.HDPublicKey;
12var BufferUtil = bitcore.util.buffer;
13var HDPrivateKey = bitcore.HDPrivateKey;
14var HDPublicKey = bitcore.HDPublicKey;
15var Base58Check = bitcore.encoding.Base58Check;
16var Networks = bitcore.Networks;
17
18var xprivkey = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
19var xpubkey = 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8';
20var xpubkeyTestnet = 'tpubD6NzVbkrYhZ4WZaiWHz59q5EQ61bd6dUYfU4ggRWAtNAyyYRNWT6ktJ7UHJEXURvTfTfskFQmK7Ff4FRkiRN5wQH8nkGAb6aKB4Yyeqsw5m';
21var json = '{"network":"livenet","depth":0,"fingerPrint":876747070,"parentFingerPrint":0,"childIndex":0,"chainCode":"873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508","publicKey":"0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2","checksum":-1421395167,"xpubkey":"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"}';
22var derived_0_1_200000 = 'xpub6BqyndF6rkBNTV6LXwiY8Pco8aqctqq7tGEUdA8fmGDTnDJphn2fmxr3eM8Lm3m8TrNUsLbEjHvpa3adBU18YpEx4tp2Zp6nqax3mQkudhX';
23
24describe('HDPublicKey interface', function() {
25
26 var expectFail = function(func, errorType) {
27 (function() {
28 func();
29 }).should.throw(errorType);
30 };
31
32 var expectDerivationFail = function(argument, error) {
33 (function() {
34 var pubkey = new HDPublicKey(xpubkey);
35 pubkey.derive(argument);
36 }).should.throw(error);
37 };
38
39 var expectFailBuilding = function(argument, error) {
40 (function() {
41 return new HDPublicKey(argument);
42 }).should.throw(error);
43 };
44
45 describe('creation formats', function() {
46
47 it('returns same argument if already an instance of HDPublicKey', function() {
48 var publicKey = new HDPublicKey(xpubkey);
49 publicKey.should.equal(new HDPublicKey(publicKey));
50 });
51
52 it('returns the correct xpubkey for a xprivkey', function() {
53 var publicKey = new HDPublicKey(xprivkey);
54 publicKey.xpubkey.should.equal(xpubkey);
55 });
56
57 it('allows to call the argument with no "new" keyword', function() {
58 HDPublicKey(xpubkey).xpubkey.should.equal(new HDPublicKey(xpubkey).xpubkey);
59 });
60
61 it('fails when user doesn\'t supply an argument', function() {
62 expectFailBuilding(null, hdErrors.MustSupplyArgument);
63 });
64
65 it('should not be able to change read-only properties', function() {
66 var publicKey = new HDPublicKey(xprivkey);
67 expect(function() {
68 publicKey.fingerPrint = 'notafingerprint';
69 }).to.throw(TypeError);
70 });
71
72 it('doesn\'t recognize an invalid argument', function() {
73 expectFailBuilding(1, hdErrors.UnrecognizedArgument);
74 expectFailBuilding(true, hdErrors.UnrecognizedArgument);
75 });
76
77
78 describe('xpubkey string serialization errors', function() {
79 it('fails on invalid length', function() {
80 expectFailBuilding(
81 Base58Check.encode(Buffer.from([1, 2, 3])),
82 hdErrors.InvalidLength
83 );
84 });
85 it('fails on invalid base58 encoding', function() {
86 expectFailBuilding(
87 xpubkey + '1',
88 errors.InvalidB58Checksum
89 );
90 });
91 it('user can ask if a string is valid', function() {
92 (HDPublicKey.isValidSerialized(xpubkey)).should.equal(true);
93 });
94 });
95
96 it('can be generated from a json', function() {
97 expect(new HDPublicKey(JSON.parse(json)).xpubkey).to.equal(xpubkey);
98 });
99
100 it('can generate a json that has a particular structure', function() {
101 assert(_.isEqual(
102 new HDPublicKey(JSON.parse(json)).toJSON(),
103 new HDPublicKey(xpubkey).toJSON()
104 ));
105 });
106
107 it('builds from a buffer object', function() {
108 (new HDPublicKey(new HDPublicKey(xpubkey)._buffers)).xpubkey.should.equal(xpubkey);
109 });
110
111 it('checks the checksum', function() {
112 var buffers = new HDPublicKey(xpubkey)._buffers;
113 buffers.checksum = BufferUtil.integerAsBuffer(1);
114 expectFail(function() {
115 return new HDPublicKey(buffers);
116 }, errors.InvalidB58Checksum);
117 });
118 });
119
120 describe('error checking on serialization', function() {
121 var compareType = function(a, b) {
122 expect(a instanceof b).to.equal(true);
123 };
124 it('throws invalid argument when argument is not a string or buffer', function() {
125 compareType(HDPublicKey.getSerializedError(1), hdErrors.UnrecognizedArgument);
126 });
127 it('if a network is provided, validates that data corresponds to it', function() {
128 compareType(HDPublicKey.getSerializedError(xpubkey, 'testnet'), errors.InvalidNetwork);
129 });
130 it('recognizes invalid network arguments', function() {
131 compareType(HDPublicKey.getSerializedError(xpubkey, 'invalid'), errors.InvalidNetworkArgument);
132 });
133 it('recognizes a valid network', function() {
134 expect(HDPublicKey.getSerializedError(xpubkey, 'livenet')).to.equal(null);
135 });
136 });
137
138 it('toString() returns the same value as .xpubkey', function() {
139 var pubKey = new HDPublicKey(xpubkey);
140 pubKey.toString().should.equal(pubKey.xpubkey);
141 });
142
143 it('publicKey property matches network', function() {
144 var livenet = new HDPublicKey(xpubkey);
145 var testnet = new HDPublicKey(xpubkeyTestnet);
146
147 livenet.publicKey.network.should.equal(Networks.livenet);
148 testnet.publicKey.network.should.equal(Networks.testnet);
149 });
150
151 it('inspect() displays correctly', function() {
152 var pubKey = new HDPublicKey(xpubkey);
153 pubKey.inspect().should.equal('<HDPublicKey: ' + pubKey.xpubkey + '>');
154 });
155
156 describe('conversion to/from buffer', function() {
157
158 it('should roundtrip to an equivalent object', function() {
159 var pubKey = new HDPublicKey(xpubkey);
160 var toBuffer = pubKey.toBuffer();
161 var fromBuffer = HDPublicKey.fromBuffer(toBuffer);
162 var roundTrip = new HDPublicKey(fromBuffer.toBuffer());
163 roundTrip.xpubkey.should.equal(xpubkey);
164 });
165 });
166
167 describe('conversion to different formats', function() {
168 var plainObject = {
169 'network':'livenet',
170 'depth':0,
171 'fingerPrint':876747070,
172 'parentFingerPrint':0,
173 'childIndex':0,
174 'chainCode':'873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508',
175 'publicKey':'0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2',
176 'checksum':-1421395167,
177 'xpubkey':'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8'
178 };
179 it('roundtrips to JSON and to Object', function() {
180 var pubkey = new HDPublicKey(xpubkey);
181 expect(HDPublicKey.fromObject(pubkey.toJSON()).xpubkey).to.equal(xpubkey);
182 });
183 it('recovers state from Object', function() {
184 new HDPublicKey(plainObject).xpubkey.should.equal(xpubkey);
185 });
186 });
187
188 describe('derivation', function() {
189 it('derivation is the same whether deriving with number or string', function() {
190 var pubkey = new HDPublicKey(xpubkey);
191 var derived1 = pubkey.derive(0).derive(1).derive(200000);
192 var derived2 = pubkey.derive('m/0/1/200000');
193 derived1.xpubkey.should.equal(derived_0_1_200000);
194 derived2.xpubkey.should.equal(derived_0_1_200000);
195 });
196
197 it('allows special parameters m, M', function() {
198 var expectDerivationSuccess = function(argument) {
199 new HDPublicKey(xpubkey).derive(argument).xpubkey.should.equal(xpubkey);
200 };
201 expectDerivationSuccess('m');
202 expectDerivationSuccess('M');
203 });
204
205 it('doesn\'t allow object arguments for derivation', function() {
206 expectFail(function() {
207 return new HDPublicKey(xpubkey).derive({});
208 }, hdErrors.InvalidDerivationArgument);
209 });
210
211 it('needs first argument for derivation', function() {
212 expectFail(function() {
213 return new HDPublicKey(xpubkey).derive('s');
214 }, hdErrors.InvalidPath);
215 });
216
217 it('doesn\'t allow other parameters like m\' or M\' or "s"', function() {
218 /* jshint quotmark: double */
219 expectDerivationFail("m'", hdErrors.InvalidIndexCantDeriveHardened);
220 expectDerivationFail("M'", hdErrors.InvalidIndexCantDeriveHardened);
221 expectDerivationFail("1", hdErrors.InvalidPath);
222 expectDerivationFail("S", hdErrors.InvalidPath);
223 });
224
225 it('can\'t derive hardened keys', function() {
226 expectFail(function() {
227 return new HDPublicKey(xpubkey).derive(HDPublicKey.Hardened);
228 }, hdErrors.InvalidIndexCantDeriveHardened);
229 });
230
231 it('can\'t derive hardened keys via second argument', function() {
232 expectFail(function() {
233 return new HDPublicKey(xpubkey).derive(5, true);
234 }, hdErrors.InvalidIndexCantDeriveHardened);
235 });
236
237 it('validates correct paths', function() {
238 var valid;
239
240 valid = HDPublicKey.isValidPath('m/123/12');
241 valid.should.equal(true);
242
243 valid = HDPublicKey.isValidPath('m');
244 valid.should.equal(true);
245
246 valid = HDPublicKey.isValidPath(123);
247 valid.should.equal(true);
248 });
249
250 it('rejects illegal paths', function() {
251 var valid;
252
253 valid = HDPublicKey.isValidPath('m/-1/12');
254 valid.should.equal(false);
255
256 valid = HDPublicKey.isValidPath("m/0'/12");
257 valid.should.equal(false);
258
259 valid = HDPublicKey.isValidPath("m/8000000000/12");
260 valid.should.equal(false);
261
262 valid = HDPublicKey.isValidPath('bad path');
263 valid.should.equal(false);
264
265 valid = HDPublicKey.isValidPath(-1);
266 valid.should.equal(false);
267
268 valid = HDPublicKey.isValidPath(8000000000);
269 valid.should.equal(false);
270
271 valid = HDPublicKey.isValidPath(HDPublicKey.Hardened);
272 valid.should.equal(false);
273 });
274 });
275});