UNPKG

12.1 kBJavaScriptView Raw
1'use strict'
2
3var _ = require('../lib/util/_')
4var assert = require('assert')
5var should = require('chai').should()
6var expect = require('chai').expect
7var bsv = require('..')
8var errors = bsv.errors
9var hdErrors = errors.HDPrivateKey
10var buffer = require('buffer')
11var Networks = bsv.Networks
12var JSUtil = require('../lib/util/js')
13var HDPrivateKey = bsv.HDPrivateKey
14var Base58Check = bsv.encoding.Base58Check
15
16var xprivkey = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'
17var json = '{"network":"livenet","depth":0,"fingerPrint":876747070,"parentFingerPrint":0,"childIndex":0,"chainCode":"873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508","privateKey":"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35","checksum":3883834737,"xprivkey":"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"}'
18describe('HDPrivate key interface', function () {
19 var expectFail = function (func, error) {
20 var got = null
21 try {
22 func()
23 } catch (e) {
24 got = e instanceof error
25 }
26 expect(got).to.equal(true)
27 }
28
29 var expectDerivationFail = function (argument, error) {
30 return expectFail(function () {
31 var privateKey = new HDPrivateKey(xprivkey)
32 privateKey.deriveChild(argument)
33 }, error)
34 }
35
36 var expectFailBuilding = function (argument, error) {
37 return expectFail(function () {
38 return new HDPrivateKey(argument)
39 }, error)
40 }
41
42 var expectSeedFail = function (argument, error) {
43 return expectFail(function () {
44 return HDPrivateKey.fromSeed(argument)
45 }, error)
46 }
47
48 it('should make a new private key from random', function () {
49 should.exist(new HDPrivateKey().xprivkey)
50 })
51
52 it('should make a new private key from random for testnet', function () {
53 var key = new HDPrivateKey('testnet')
54 should.exist(key.xprivkey)
55 key.network.name.should.equal('testnet')
56 })
57
58 it('should not be able to change read-only properties', function () {
59 var hdkey = new HDPrivateKey()
60 expect(function () {
61 hdkey.fingerPrint = 'notafingerprint'
62 }).to.throw(TypeError)
63 })
64
65 it('should error with an invalid checksum', function () {
66 expectFailBuilding(xprivkey + '1', errors.InvalidB58Checksum)
67 })
68
69 it('can be rebuilt from a json generated by itself', function () {
70 var regenerate = new HDPrivateKey(json)
71 regenerate.xprivkey.should.equal(xprivkey)
72 })
73
74 it('builds a json keeping the structure and same members', function () {
75 assert.deepStrictEqual(
76 new HDPrivateKey(json).toJSON(),
77 new HDPrivateKey(xprivkey).toJSON()
78 )
79 })
80
81 describe('instantiation', function () {
82 it('invalid argument: can not instantiate from a number', function () {
83 expectFailBuilding(1, hdErrors.UnrecognizedArgument)
84 })
85 it('allows no-new calling', function () {
86 HDPrivateKey(xprivkey).toString().should.equal(xprivkey)
87 })
88 it('allows the use of a copy constructor', function () {
89 HDPrivateKey(HDPrivateKey(xprivkey))
90 .xprivkey.should.equal(xprivkey)
91 })
92 })
93
94 describe('public key', function () {
95 var testnetKey = new HDPrivateKey('tprv8ZgxMBicQKsPdEeU2KiGFnUgRGriMnQxrwrg6FWCBg4jeiidHRyCCdA357kfkZiGaXEapWZsGDKikeeEbvgXo3UmEdbEKNdQH9VXESmGuUK')
96 var livenetKey = new HDPrivateKey('xprv9s21ZrQH143K3e39bnn1vyS7YFa1EAJAFGDoeHaSBsgBxgAkTEXeSx7xLvhNQNJxJwhzziWcK3znUFKRPRwWBPkKZ8ijUBa5YYpYPQmeBDX')
97
98 it('matches the network', function () {
99 testnetKey.publicKey.network.should.equal(Networks.testnet)
100 livenetKey.publicKey.network.should.equal(Networks.livenet)
101 })
102
103 it('cache for xpubkey works', function () {
104 var privateKey = new HDPrivateKey(xprivkey)
105 should.not.exist(privateKey._hdPublicKey)
106 privateKey.xpubkey.should.equal(privateKey.xpubkey)
107 should.exist(privateKey._hdPublicKey)
108 })
109 })
110
111 it('inspect() displays correctly', function () {
112 HDPrivateKey(xprivkey).inspect().should.equal('<HDPrivateKey: ' + xprivkey + '>')
113 })
114 it('fails when trying to derive with an invalid argument', function () {
115 expectDerivationFail([], hdErrors.InvalidDerivationArgument)
116 })
117
118 it('catches early invalid paths', function () {
119 expectDerivationFail('s', hdErrors.InvalidPath)
120 })
121
122 it('allows derivation of hardened keys by passing a very big number', function () {
123 var privateKey = new HDPrivateKey(xprivkey)
124 var derivedByNumber = privateKey.deriveChild(0x80000000)
125 var derivedByArgument = privateKey.deriveChild(0, true)
126 derivedByNumber.xprivkey.should.equal(derivedByArgument.xprivkey)
127 })
128
129 it('returns itself with \'m\' parameter', function () {
130 var privateKey = new HDPrivateKey(xprivkey)
131 privateKey.should.equal(privateKey.deriveChild('m'))
132 })
133
134 it('returns InvalidArgument if invalid data is given to getSerializedError', function () {
135 expect(
136 HDPrivateKey.getSerializedError(1) instanceof hdErrors.UnrecognizedArgument
137 ).to.equal(true)
138 })
139
140 it('returns InvalidLength if data of invalid length is given to getSerializedError', function () {
141 var b58s = Base58Check.encode(buffer.Buffer.from('onestring'))
142 expect(
143 HDPrivateKey.getSerializedError(b58s) instanceof hdErrors.InvalidLength
144 ).to.equal(true)
145 })
146
147 it('returns InvalidNetworkArgument if an invalid network is provided', function () {
148 expect(
149 HDPrivateKey.getSerializedError(xprivkey, 'invalidNetwork') instanceof errors.InvalidNetworkArgument
150 ).to.equal(true)
151 })
152
153 it('recognizes that the wrong network was asked for', function () {
154 expect(
155 HDPrivateKey.getSerializedError(xprivkey, 'testnet') instanceof errors.InvalidNetwork
156 ).to.equal(true)
157 })
158
159 it('recognizes the correct network', function () {
160 expect(HDPrivateKey.getSerializedError(xprivkey, 'livenet')).to.equal(null)
161 })
162
163 describe('on creation from seed', function () {
164 it('converts correctly from an hexa string', function () {
165 should.exist(HDPrivateKey.fromSeed('01234567890abcdef01234567890abcdef').xprivkey)
166 })
167 it('fails when argument is not a buffer or string', function () {
168 expectSeedFail(1, hdErrors.InvalidEntropyArgument)
169 })
170 it('fails when argument doesn\'t provide enough entropy', function () {
171 expectSeedFail('01', hdErrors.InvalidEntropyArgument.NotEnoughEntropy)
172 })
173 it('fails when argument provides too much entropy', function () {
174 var entropy = '0'
175 for (var i = 0; i < 129; i++) {
176 entropy += '1'
177 }
178 expectSeedFail(entropy, hdErrors.InvalidEntropyArgument.TooMuchEntropy)
179 })
180 })
181
182 it('correctly errors if an invalid checksum is provided', function () {
183 var privKey = new HDPrivateKey(xprivkey)
184 var error = null
185 try {
186 var buffers = privKey._buffers
187 buffers.checksum = JSUtil.integerAsBuffer(0)
188 new HDPrivateKey(buffers) //eslint-disable-line
189 } catch (e) {
190 error = e
191 }
192 expect(error instanceof errors.InvalidB58Checksum).to.equal(true)
193 })
194 it('correctly validates the checksum', function () {
195 var privKey = new HDPrivateKey(xprivkey)
196 expect(function () {
197 var buffers = privKey._buffers
198 return new HDPrivateKey(buffers)
199 }).to.not.throw()
200 })
201
202 it('shouldn\'t matter if derivations are made with strings or numbers', function () {
203 var privateKey = new HDPrivateKey(xprivkey)
204 var derivedByString = privateKey.deriveChild('m/0\'/1/2\'')
205 var derivedByNumber = privateKey.deriveChild(0, true).deriveChild(1).deriveChild(2, true)
206 derivedByNumber.xprivkey.should.equal(derivedByString.xprivkey)
207 })
208
209 describe('validates paths', function () {
210 it('validates correct paths', function () {
211 var valid
212
213 valid = HDPrivateKey.isValidPath('m/0\'/1/2\'')
214 valid.should.equal(true)
215
216 valid = HDPrivateKey.isValidPath('m')
217 valid.should.equal(true)
218
219 valid = HDPrivateKey.isValidPath(123, true)
220 valid.should.equal(true)
221
222 valid = HDPrivateKey.isValidPath(123)
223 valid.should.equal(true)
224
225 valid = HDPrivateKey.isValidPath(HDPrivateKey.Hardened + 123)
226 valid.should.equal(true)
227
228 valid = HDPrivateKey.isValidPath(HDPrivateKey.Hardened + 123, true)
229 valid.should.equal(true)
230 })
231
232 var invalid = [
233 'm/-1/12',
234 'bad path',
235 'K',
236 'm/',
237 'm/12asd',
238 'm/1/2//3'
239 ]
240
241 invalid.forEach(function (datum) {
242 it('rejects illegal path ' + datum, function () {
243 HDPrivateKey.isValidPath(datum).should.equal(false)
244 expect(HDPrivateKey._getDerivationIndexes(datum)).to.equal(null)
245 })
246 })
247
248 it('generates deriving indexes correctly', function () {
249 var indexes
250
251 indexes = HDPrivateKey._getDerivationIndexes('m/-1/12')
252 expect(indexes).to.equal(null)
253
254 indexes = HDPrivateKey._getDerivationIndexes('m/0/12/12\'')
255 indexes.should.eql([0, 12, HDPrivateKey.Hardened + 12])
256
257 indexes = HDPrivateKey._getDerivationIndexes('m/0/12/12\'')
258 indexes.should.eql([0, 12, HDPrivateKey.Hardened + 12])
259 })
260 })
261
262 describe('conversion to/from buffer', function () {
263 var str = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'
264 it('should roundtrip to/from a buffer', function () {
265 var priv = new HDPrivateKey(str)
266 var toBuffer = priv.toBuffer()
267 var fromBuffer = HDPrivateKey.fromBuffer(toBuffer)
268 var roundTrip = new HDPrivateKey(fromBuffer.toString())
269 roundTrip.xprivkey.should.equal(str)
270 })
271 })
272
273 describe('conversion to/from hex', function () {
274 var str = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'
275 it('should roundtrip to/from a buffer', function () {
276 var priv = new HDPrivateKey(str)
277 var toBuffer = priv.toBuffer()
278 var fromBuffer = HDPrivateKey.fromBuffer(toBuffer)
279 var roundTrip = new HDPrivateKey(fromBuffer.toString())
280 roundTrip.xprivkey.should.equal(str)
281 })
282 })
283
284 describe('from random', function () {
285 var str = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'
286 it('should roundtrip to/from a buffer', function () {
287 var xprv1 = new HDPrivateKey(str)
288 var xprv2 = HDPrivateKey.fromRandom()
289 var xprv3 = HDPrivateKey.fromRandom()
290 xprv1.toString().should.not.equal(xprv2.toString())
291 xprv2.toString().should.not.equal(xprv3.toString())
292 xprv1.toString().should.not.equal(xprv3.toString())
293 })
294 })
295
296 describe('conversion to plain object/json', function () {
297 var plainObject = {
298 'network': 'livenet',
299 'depth': 0,
300 'fingerPrint': 876747070,
301 'parentFingerPrint': 0,
302 'childIndex': 0,
303 'chainCode': '873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508',
304 'privateKey': 'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35',
305 'checksum': 3883834737,
306 'xprivkey': 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvN' +
307 'KmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'
308 }
309 it('toObject leaves no Buffer instances', function () {
310 var privKey = new HDPrivateKey(xprivkey)
311 var object = privKey.toObject()
312 _.each(_.values(object), function (value) {
313 expect(Buffer.isBuffer(value)).to.equal(false)
314 })
315 })
316 it('roundtrips toObject', function () {
317 expect(HDPrivateKey.fromObject(new HDPrivateKey(xprivkey).toObject()).xprivkey).to.equal(xprivkey)
318 })
319 it('roundtrips to JSON and to Object', function () {
320 var privkey = new HDPrivateKey(xprivkey)
321 expect(HDPrivateKey.fromObject(privkey.toJSON()).xprivkey).to.equal(xprivkey)
322 })
323 it('recovers state from JSON', function () {
324 new HDPrivateKey(JSON.stringify(plainObject)).xprivkey.should.equal(xprivkey)
325 })
326 it('recovers state from Object', function () {
327 new HDPrivateKey(plainObject).xprivkey.should.equal(xprivkey)
328 })
329 })
330})