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