1 | 'use strict'
|
2 |
|
3 | var chai = require('chai')
|
4 | var should = chai.should()
|
5 | var expect = chai.expect
|
6 |
|
7 | var bsv = require('..')
|
8 | var BN = bsv.crypto.BN
|
9 | var Point = bsv.crypto.Point
|
10 | var PrivateKey = bsv.PrivateKey
|
11 | var Networks = bsv.Networks
|
12 | var Base58Check = bsv.encoding.Base58Check
|
13 |
|
14 | var validbase58 = require('./data/bitcoind/base58_keys_valid.json')
|
15 | var invalidbase58 = require('./data/bitcoind/base58_keys_invalid.json')
|
16 |
|
17 | describe('PrivateKey', function () {
|
18 | var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a'
|
19 | var hex2 = '8080808080808080808080808080808080808080808080808080808080808080'
|
20 | var buf = Buffer.from(hex, 'hex')
|
21 | var wifTestnet = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG'
|
22 | var wifTestnetUncompressed = '92jJzK4tbURm1C7udQXxeCBvXHoHJstDXRxAMouPG1k1XUaXdsu'
|
23 | var wifLivenet = 'L2Gkw3kKJ6N24QcDuH4XDqt9cTqsKTVNDGz1CRZhk9cq4auDUbJy'
|
24 | var wifLivenetUncompressed = '5JxgQaFM1FMd38cd14e3mbdxsdSa9iM2BV6DHBYsvGzxkTNQ7Un'
|
25 | var wifNamecoin = '74pxNKNpByQ2kMow4d9kF6Z77BYeKztQNLq3dSyU4ES1K5KLNiz'
|
26 |
|
27 | it('should create a new random private key', function () {
|
28 | var a = new PrivateKey()
|
29 | should.exist(a)
|
30 | should.exist(a.bn)
|
31 | var b = PrivateKey()
|
32 | should.exist(b)
|
33 | should.exist(b.bn)
|
34 | })
|
35 |
|
36 | it('should create a privatekey from hexa string', function () {
|
37 | var a = new PrivateKey(hex2)
|
38 | should.exist(a)
|
39 | should.exist(a.bn)
|
40 | })
|
41 |
|
42 | it('should create a new random testnet private key with only one argument', function () {
|
43 | var a = new PrivateKey(Networks.testnet)
|
44 | should.exist(a)
|
45 | should.exist(a.bn)
|
46 | })
|
47 |
|
48 | it('should create a private key from a custom network WIF string', function () {
|
49 | var nmc = {
|
50 | name: 'namecoin',
|
51 | alias: 'namecoin',
|
52 | pubkeyhash: 0x34,
|
53 | privatekey: 0xB4,
|
54 |
|
55 | scripthash: 0x08,
|
56 | xpubkey: 0x0278b20e,
|
57 | xprivkey: 0x0278ade4,
|
58 | networkMagic: 0xf9beb4fe,
|
59 | port: 20001,
|
60 | dnsSeeds: [
|
61 | 'localhost',
|
62 | 'mynet.localhost'
|
63 | ]
|
64 | }
|
65 | Networks.add(nmc)
|
66 | var nmcNet = Networks.get('namecoin')
|
67 | var a = new PrivateKey(wifNamecoin, nmcNet)
|
68 | should.exist(a)
|
69 | should.exist(a.bn)
|
70 | Networks.remove(nmcNet)
|
71 | })
|
72 |
|
73 | it('should create a new random testnet private key with empty data', function () {
|
74 | var a = new PrivateKey(null, Networks.testnet)
|
75 | should.exist(a)
|
76 | should.exist(a.bn)
|
77 | })
|
78 |
|
79 | it('should create a private key from WIF string', function () {
|
80 | var a = new PrivateKey('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m')
|
81 | should.exist(a)
|
82 | should.exist(a.bn)
|
83 | })
|
84 |
|
85 | it('should create a private key from WIF buffer', function () {
|
86 | var a = new PrivateKey(Base58Check.decode('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'))
|
87 | should.exist(a)
|
88 | should.exist(a.bn)
|
89 | })
|
90 |
|
91 | describe('bitcoind compliance', function () {
|
92 | validbase58.map(function (d) {
|
93 | if (d[2].isPrivkey) {
|
94 | it('should instantiate WIF private key ' + d[0] + ' with correct properties', function () {
|
95 | var network = Networks.livenet
|
96 | if (d[2].isTestnet) {
|
97 | network = Networks.testnet
|
98 | }
|
99 | var key = new PrivateKey(d[0])
|
100 | key.compressed.should.equal(d[2].isCompressed)
|
101 | key.network.should.equal(network)
|
102 | })
|
103 | }
|
104 | })
|
105 | invalidbase58.map(function (d) {
|
106 | it('should describe input ' + d[0].slice(0, 10) + '... as invalid', function () {
|
107 | expect(function () {
|
108 | return new PrivateKey(d[0])
|
109 | }).to.throw(Error)
|
110 | })
|
111 | })
|
112 | })
|
113 |
|
114 | describe('instantiation', function () {
|
115 | it('should not be able to instantiate private key greater than N', function () {
|
116 | expect(function () {
|
117 | return new PrivateKey(Point.getN())
|
118 | }).to.throw('Number must be less than N')
|
119 | })
|
120 |
|
121 | it('should not be able to instantiate private key because of network mismatch', function () {
|
122 | expect(function () {
|
123 | return new PrivateKey('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m', 'testnet')
|
124 | }).to.throw('Private key network mismatch')
|
125 | })
|
126 |
|
127 | it('should not be able to instantiate private key WIF is too long', function () {
|
128 | expect(function () {
|
129 | var buf = Base58Check.decode('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m')
|
130 | var buf2 = Buffer.concat([buf, Buffer.alloc(0x01)])
|
131 | return new PrivateKey(buf2)
|
132 | }).to.throw('Length of buffer must be 33 (uncompressed) or 34 (compressed')
|
133 | })
|
134 |
|
135 | it('should not be able to instantiate private key WIF because of unknown network byte', function () {
|
136 | expect(function () {
|
137 | var buf = Base58Check.decode('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m')
|
138 | var buf2 = Buffer.concat([Buffer.from('ff', 'hex'), buf.slice(1, 33)])
|
139 | return new PrivateKey(buf2)
|
140 | }).to.throw('Invalid network')
|
141 | })
|
142 |
|
143 | it('should not be able to instantiate private key WIF because of network mismatch', function () {
|
144 | expect(function () {
|
145 | new PrivateKey(wifNamecoin, 'testnet')
|
146 | }).to.throw('Invalid network')
|
147 | })
|
148 |
|
149 | it('can be instantiated from a hex string', function () {
|
150 | var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'
|
151 | var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'
|
152 | var privkey = new PrivateKey(privhex)
|
153 | privkey.publicKey.toString().should.equal(pubhex)
|
154 | })
|
155 |
|
156 | it('should not be able to instantiate because of unrecognized data', function () {
|
157 | expect(function () {
|
158 | return new PrivateKey(new Error())
|
159 | }).to.throw('First argument is an unrecognized data type.')
|
160 | })
|
161 |
|
162 | it('should not be able to instantiate with unknown network', function () {
|
163 | expect(function () {
|
164 | return new PrivateKey(new BN(2), 'unknown')
|
165 | }).to.throw('Must specify the network ("livenet" or "testnet")')
|
166 | })
|
167 |
|
168 | it('should not create a zero private key', function () {
|
169 | expect(function () {
|
170 | var bn = new BN(0)
|
171 | return new PrivateKey(bn)
|
172 | }).to.throw(TypeError)
|
173 | })
|
174 |
|
175 | it('should create a livenet private key', function () {
|
176 | var privkey = new PrivateKey(BN.fromBuffer(buf), 'livenet')
|
177 | privkey.toWIF().should.equal(wifLivenet)
|
178 | })
|
179 |
|
180 | it('should create a default network private key', function () {
|
181 |
|
182 | var network = Networks.defaultNetwork
|
183 | Networks.defaultNetwork = Networks.livenet
|
184 | var a = new PrivateKey(BN.fromBuffer(buf))
|
185 | a.network.should.equal(Networks.livenet)
|
186 |
|
187 | Networks.defaultNetwork = Networks.testnet
|
188 | var b = new PrivateKey(BN.fromBuffer(buf))
|
189 | b.network.should.equal(Networks.testnet)
|
190 |
|
191 | Networks.defaultNetwork = network
|
192 | })
|
193 |
|
194 | it('returns the same instance if a PrivateKey is provided (immutable)', function () {
|
195 | var privkey = new PrivateKey()
|
196 | new PrivateKey(privkey).should.equal(privkey)
|
197 | })
|
198 | })
|
199 |
|
200 | describe('#json/object', function () {
|
201 | it('should input/output json', function () {
|
202 | var json = JSON.stringify({
|
203 | bn: '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a',
|
204 | compressed: false,
|
205 | network: 'livenet'
|
206 | })
|
207 | var key = PrivateKey.fromObject(JSON.parse(json))
|
208 | JSON.stringify(key).should.equal(json)
|
209 | })
|
210 |
|
211 | it('should input/output json', function () {
|
212 | var json = JSON.stringify({
|
213 | bn: '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a',
|
214 | compressed: false,
|
215 | network: 'livenet'
|
216 | })
|
217 | var key = PrivateKey.fromJSON(JSON.parse(json))
|
218 | JSON.stringify(key).should.equal(json)
|
219 | })
|
220 |
|
221 | it('input json should correctly initialize network field', function () {
|
222 | ['livenet', 'testnet', 'mainnet'].forEach(function (net) {
|
223 | var pk = PrivateKey.fromObject({
|
224 | bn: '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a',
|
225 | compressed: false,
|
226 | network: net
|
227 | })
|
228 | pk.network.should.be.deep.equal(Networks.get(net))
|
229 | })
|
230 | })
|
231 |
|
232 | it('fails on invalid argument', function () {
|
233 | expect(function () {
|
234 | return PrivateKey.fromJSON('¹')
|
235 | }).to.throw()
|
236 | })
|
237 |
|
238 | it('also accepts an object as argument', function () {
|
239 | expect(function () {
|
240 | return PrivateKey.fromObject(new PrivateKey().toObject())
|
241 | }).to.not.throw()
|
242 | })
|
243 |
|
244 | it('also accepts an object as argument', function () {
|
245 | expect(function () {
|
246 | return PrivateKey.fromObject(new PrivateKey().toJSON())
|
247 | }).to.not.throw()
|
248 | })
|
249 | })
|
250 |
|
251 | it('coverage: public key cache', function () {
|
252 | expect(function () {
|
253 | var privateKey = new PrivateKey()
|
254 | var publicKey = privateKey.publicKey
|
255 | return publicKey
|
256 | }).to.not.throw()
|
257 | })
|
258 |
|
259 | describe('#toString', function () {
|
260 | it('should output this address correctly', function () {
|
261 | var privkey = PrivateKey.fromWIF(wifLivenetUncompressed)
|
262 | privkey.toWIF().should.equal(wifLivenetUncompressed)
|
263 | })
|
264 | })
|
265 |
|
266 | describe('#toAddress', function () {
|
267 | it('should output this known livenet address correctly', function () {
|
268 | var privkey = PrivateKey.fromWIF('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m')
|
269 | var address = privkey.toAddress()
|
270 | address.toString().should.equal('1A6ut1tWnUq1SEQLMr4ttDh24wcbJ5o9TT')
|
271 | })
|
272 |
|
273 | it('should output this known testnet address correctly', function () {
|
274 | var privkey = PrivateKey.fromWIF('cR4qogdN9UxLZJXCNFNwDRRZNeLRWuds9TTSuLNweFVjiaE4gPaq')
|
275 | var address = privkey.toAddress()
|
276 | address.toString().should.equal('mtX8nPZZdJ8d3QNLRJ1oJTiEi26Sj6LQXS')
|
277 | })
|
278 |
|
279 | it('creates network specific address', function () {
|
280 | var pk = PrivateKey.fromWIF('cR4qogdN9UxLZJXCNFNwDRRZNeLRWuds9TTSuLNweFVjiaE4gPaq')
|
281 | pk.toAddress(Networks.livenet).network.name.should.equal(Networks.livenet.name)
|
282 | pk.toAddress(Networks.testnet).network.name.should.equal(Networks.testnet.name)
|
283 | })
|
284 | })
|
285 |
|
286 | describe('#inspect', function () {
|
287 | it('should output known livenet address for console', function () {
|
288 | var privkey = PrivateKey.fromWIF('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m')
|
289 | privkey.inspect().should.equal(
|
290 | '<PrivateKey: b9de6e778fe92aa7edb69395556f843f1dce0448350112e14906efc2a80fa61a, network: livenet>'
|
291 | )
|
292 | })
|
293 |
|
294 | it('should output known testnet address for console', function () {
|
295 | var privkey = PrivateKey.fromWIF('cR4qogdN9UxLZJXCNFNwDRRZNeLRWuds9TTSuLNweFVjiaE4gPaq')
|
296 | privkey.inspect().should.equal(
|
297 | '<PrivateKey: 67fd2209ce4a95f6f1d421ab3fbea47ada13df11b73b30c4d9a9f78cc80651ac, network: testnet>'
|
298 | )
|
299 | })
|
300 |
|
301 | it('outputs "uncompressed" for uncompressed imported WIFs', function () {
|
302 | var privkey = PrivateKey.fromWIF(wifLivenetUncompressed)
|
303 | privkey.inspect().should.equal('<PrivateKey: 96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a, network: livenet, uncompressed>')
|
304 | })
|
305 | })
|
306 |
|
307 | describe('#getValidationError', function () {
|
308 | it('should get an error because private key greater than N', function () {
|
309 | var n = Point.getN()
|
310 | var a = PrivateKey.getValidationError(n)
|
311 | a.message.should.equal('Number must be less than N')
|
312 | })
|
313 |
|
314 | it('should validate as false because private key greater than N', function () {
|
315 | var n = Point.getN()
|
316 | var a = PrivateKey.isValid(n)
|
317 | a.should.equal(false)
|
318 | })
|
319 |
|
320 | it('should recognize that undefined is an invalid private key', function () {
|
321 | PrivateKey.isValid().should.equal(false)
|
322 | })
|
323 |
|
324 | it('should validate as true', function () {
|
325 | var a = PrivateKey.isValid('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m')
|
326 | a.should.equal(true)
|
327 | })
|
328 | })
|
329 |
|
330 | describe('buffer serialization', function () {
|
331 | it('returns an expected value when creating a PrivateKey from a buffer', function () {
|
332 | var privkey = new PrivateKey(BN.fromBuffer(buf), 'livenet')
|
333 | privkey.toHex().should.equal(buf.toString('hex'))
|
334 | })
|
335 |
|
336 | it('roundtrips correctly when using toBuffer/fromBuffer', function () {
|
337 | var privkey = new PrivateKey(BN.fromBuffer(buf))
|
338 | var toBuffer = new PrivateKey(privkey.toBuffer())
|
339 | var fromBuffer = PrivateKey.fromBuffer(toBuffer.toBuffer())
|
340 | fromBuffer.toHex().should.equal(privkey.toHex())
|
341 | })
|
342 | })
|
343 |
|
344 | describe('#toBigNumber', function () {
|
345 | it('should output known BN', function () {
|
346 | var a = BN.fromBuffer(buf)
|
347 | var privkey = new PrivateKey(a, 'livenet')
|
348 | var b = privkey.toBigNumber()
|
349 | b.toString('hex').should.equal(a.toString('hex'))
|
350 | })
|
351 | })
|
352 |
|
353 | describe('#fromRandom', function () {
|
354 | it('should set bn gt 0 and lt n, and should be compressed', function () {
|
355 | var privkey = PrivateKey.fromRandom()
|
356 | privkey.bn.gt(new BN(0)).should.equal(true)
|
357 | privkey.bn.lt(Point.getN()).should.equal(true)
|
358 | privkey.compressed.should.equal(true)
|
359 | })
|
360 | })
|
361 |
|
362 | describe('#fromWIF', function () {
|
363 | it('should parse this compressed testnet address correctly', function () {
|
364 | var privkey = PrivateKey.fromWIF(wifLivenet)
|
365 | privkey.toWIF().should.equal(wifLivenet)
|
366 | })
|
367 | })
|
368 |
|
369 | describe('#toWIF', function () {
|
370 | it('should parse this compressed testnet address correctly', function () {
|
371 | var privkey = PrivateKey.fromWIF(wifTestnet)
|
372 | privkey.toWIF().should.equal(wifTestnet)
|
373 | })
|
374 | })
|
375 |
|
376 | describe('#fromString', function () {
|
377 | it('should parse this uncompressed testnet address correctly', function () {
|
378 | var privkey = PrivateKey.fromString(wifTestnetUncompressed)
|
379 | privkey.toWIF().should.equal(wifTestnetUncompressed)
|
380 | })
|
381 | })
|
382 |
|
383 | describe('#toString', function () {
|
384 | it('should parse this uncompressed livenet address correctly', function () {
|
385 | var privkey = PrivateKey.fromString(wifLivenetUncompressed)
|
386 | privkey.toHex().should.equal('96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a')
|
387 | })
|
388 | })
|
389 |
|
390 | describe('#toPublicKey', function () {
|
391 | it('should convert this known PrivateKey to known PublicKey', function () {
|
392 | var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'
|
393 | var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'
|
394 | var privkey = new PrivateKey(new BN(Buffer.from(privhex, 'hex')))
|
395 | var pubkey = privkey.toPublicKey()
|
396 | pubkey.toString().should.equal(pubhex)
|
397 | })
|
398 |
|
399 | it('should have a "publicKey" property', function () {
|
400 | var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'
|
401 | var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'
|
402 | var privkey = new PrivateKey(new BN(Buffer.from(privhex, 'hex')))
|
403 | privkey.publicKey.toString().should.equal(pubhex)
|
404 | })
|
405 |
|
406 | it('should convert this known PrivateKey to known PublicKey and preserve compressed=true', function () {
|
407 | var privwif = 'L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'
|
408 | var privkey = new PrivateKey(privwif, 'livenet')
|
409 | var pubkey = privkey.toPublicKey()
|
410 | pubkey.compressed.should.equal(true)
|
411 | })
|
412 |
|
413 | it('should convert this known PrivateKey to known PublicKey and preserve compressed=false', function () {
|
414 | var privwif = '92jJzK4tbURm1C7udQXxeCBvXHoHJstDXRxAMouPG1k1XUaXdsu'
|
415 | var privkey = new PrivateKey(privwif, 'testnet')
|
416 | var pubkey = privkey.toPublicKey()
|
417 | pubkey.compressed.should.equal(false)
|
418 | })
|
419 | })
|
420 |
|
421 | it('creates an address as expected from WIF, livenet', function () {
|
422 | var privkey = new PrivateKey('5J2NYGstJg7aJQEqNwYp4enG5BSfFdKXVTtBLvHicnRGD5kjxi6')
|
423 | privkey.publicKey.toAddress().toString().should.equal('135bwugFCmhmNU3SeCsJeTqvo5ViymgwZ9')
|
424 | })
|
425 |
|
426 | it('creates an address as expected from WIF, testnet', function () {
|
427 | var privkey = new PrivateKey('92VYMmwFLXRwXn5688edGxYYgMFsc3fUXYhGp17WocQhU6zG1kd')
|
428 | privkey.publicKey.toAddress().toString().should.equal('moiAvLUw16qgrwhFGo1eDnXHC2wPMYiv7Y')
|
429 | })
|
430 | })
|