UNPKG

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