@opcat-labs/opcat 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.yaml +3 -0
- package/index.d.ts +1541 -0
- package/index.js +74 -0
- package/lib/address.js +478 -0
- package/lib/block/block.js +277 -0
- package/lib/block/blockheader.js +295 -0
- package/lib/block/index.js +4 -0
- package/lib/block/merkleblock.js +323 -0
- package/lib/bn.js +3423 -0
- package/lib/crypto/bn.js +278 -0
- package/lib/crypto/ecdsa.js +339 -0
- package/lib/crypto/hash.browser.js +171 -0
- package/lib/crypto/hash.js +2 -0
- package/lib/crypto/hash.node.js +171 -0
- package/lib/crypto/point.js +221 -0
- package/lib/crypto/random.browser.js +28 -0
- package/lib/crypto/random.js +2 -0
- package/lib/crypto/random.node.js +11 -0
- package/lib/crypto/signature.js +325 -0
- package/lib/encoding/base58.js +111 -0
- package/lib/encoding/base58check.js +121 -0
- package/lib/encoding/bufferreader.js +212 -0
- package/lib/encoding/bufferwriter.js +140 -0
- package/lib/encoding/decode-asm.js +24 -0
- package/lib/encoding/decode-hex.js +32 -0
- package/lib/encoding/decode-script-chunks.js +43 -0
- package/lib/encoding/encode-hex.js +284 -0
- package/lib/encoding/is-hex.js +7 -0
- package/lib/encoding/varint.js +75 -0
- package/lib/errors/index.js +54 -0
- package/lib/errors/spec.js +314 -0
- package/lib/hash-cache.js +50 -0
- package/lib/hdprivatekey.js +678 -0
- package/lib/hdpublickey.js +525 -0
- package/lib/message/message.js +191 -0
- package/lib/mnemonic/mnemonic.js +303 -0
- package/lib/mnemonic/pbkdf2.browser.js +68 -0
- package/lib/mnemonic/pbkdf2.js +2 -0
- package/lib/mnemonic/pbkdf2.node.js +68 -0
- package/lib/mnemonic/words/chinese.js +2054 -0
- package/lib/mnemonic/words/english.js +2054 -0
- package/lib/mnemonic/words/french.js +2054 -0
- package/lib/mnemonic/words/index.js +8 -0
- package/lib/mnemonic/words/italian.js +2054 -0
- package/lib/mnemonic/words/japanese.js +2054 -0
- package/lib/mnemonic/words/spanish.js +2054 -0
- package/lib/networks.js +379 -0
- package/lib/opcode.js +255 -0
- package/lib/privatekey.js +374 -0
- package/lib/publickey.js +386 -0
- package/lib/script/index.js +5 -0
- package/lib/script/interpreter.js +1834 -0
- package/lib/script/script.js +1074 -0
- package/lib/script/stack.js +109 -0
- package/lib/script/write-i32-le.js +17 -0
- package/lib/script/write-push-data.js +35 -0
- package/lib/script/write-u16-le.js +12 -0
- package/lib/script/write-u32-le.js +16 -0
- package/lib/script/write-u64-le.js +24 -0
- package/lib/script/write-u8-le.js +8 -0
- package/lib/script/write-varint.js +46 -0
- package/lib/transaction/index.js +7 -0
- package/lib/transaction/input/index.js +5 -0
- package/lib/transaction/input/input.js +354 -0
- package/lib/transaction/input/multisig.js +242 -0
- package/lib/transaction/input/publickey.js +100 -0
- package/lib/transaction/input/publickeyhash.js +118 -0
- package/lib/transaction/output.js +231 -0
- package/lib/transaction/sighash.js +167 -0
- package/lib/transaction/signature.js +97 -0
- package/lib/transaction/transaction.js +1639 -0
- package/lib/transaction/unspentoutput.js +113 -0
- package/lib/util/_.js +47 -0
- package/lib/util/js.js +90 -0
- package/lib/util/preconditions.js +33 -0
- package/package.json +26 -0
- package/test/address.js +509 -0
- package/test/block/block.js +251 -0
- package/test/block/blockheader.js +275 -0
- package/test/block/merklebloack.js +211 -0
- package/test/crypto/bn.js +177 -0
- package/test/crypto/ecdsa.js +391 -0
- package/test/crypto/hash.browser.js +135 -0
- package/test/crypto/hash.js +136 -0
- package/test/crypto/point.js +224 -0
- package/test/crypto/random.js +32 -0
- package/test/crypto/signature.js +409 -0
- package/test/data/bip69.json +215 -0
- package/test/data/bitcoind/base58_keys_invalid.json +52 -0
- package/test/data/bitcoind/base58_keys_valid.json +335 -0
- package/test/data/bitcoind/blocks.json +22 -0
- package/test/data/bitcoind/script_tests.json +3822 -0
- package/test/data/bitcoind/sig_canonical.json +7 -0
- package/test/data/bitcoind/sig_noncanonical.json +36 -0
- package/test/data/bitcoind/tx_invalid.json +445 -0
- package/test/data/bitcoind/tx_valid.json +44 -0
- package/test/data/blk86756-testnet.dat +0 -0
- package/test/data/blk86756-testnet.js +14 -0
- package/test/data/blk86756-testnet.json +684 -0
- package/test/data/block.hex +1 -0
- package/test/data/ecdsa.json +230 -0
- package/test/data/merkleblocks.js +488 -0
- package/test/data/messages.json +22 -0
- package/test/data/sighash.json +12 -0
- package/test/data/tx_creation.json +95 -0
- package/test/encoding/base58.js +131 -0
- package/test/encoding/base58check.js +136 -0
- package/test/encoding/bufferreader.js +337 -0
- package/test/encoding/bufferwriter.js +172 -0
- package/test/encoding/varint.js +104 -0
- package/test/hashCache.js +67 -0
- package/test/hdkeys.js +445 -0
- package/test/hdprivatekey.js +332 -0
- package/test/hdpublickey.js +304 -0
- package/test/index.js +16 -0
- package/test/message/message.js +204 -0
- package/test/mnemonic/data/fixtures.json +300 -0
- package/test/mnemonic/mnemonic.js +259 -0
- package/test/mnemonic/mocha.opts +1 -0
- package/test/mnemonic/pbkdf2.test.js +59 -0
- package/test/networks.js +159 -0
- package/test/opcode.js +161 -0
- package/test/privatekey.js +439 -0
- package/test/publickey.js +554 -0
- package/test/script/interpreter.js +734 -0
- package/test/script/script.js +1437 -0
- package/test/transaction/deserialize.js +34 -0
- package/test/transaction/input/input.js +90 -0
- package/test/transaction/input/multisig.js +90 -0
- package/test/transaction/input/publickey.js +68 -0
- package/test/transaction/input/publickeyhash.js +51 -0
- package/test/transaction/output.js +185 -0
- package/test/transaction/sighash.js +65 -0
- package/test/transaction/signature.js +114 -0
- package/test/transaction/transaction.js +1109 -0
- package/test/transaction/unspentoutput.js +110 -0
- package/test/util/js.js +76 -0
- package/test/util/preconditions.js +79 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _ = require('../lib/util/_');
|
|
4
|
+
var assert = require('assert');
|
|
5
|
+
var should = require('chai').should();
|
|
6
|
+
var expect = require('chai').expect;
|
|
7
|
+
var opcat = require('..');
|
|
8
|
+
var errors = opcat.errors;
|
|
9
|
+
var hdErrors = errors.HDPrivateKey;
|
|
10
|
+
var buffer = require('buffer');
|
|
11
|
+
var Networks = opcat.Networks;
|
|
12
|
+
var JSUtil = require('../lib/util/js');
|
|
13
|
+
var HDPrivateKey = opcat.HDPrivateKey;
|
|
14
|
+
var Base58Check = opcat.encoding.Base58Check;
|
|
15
|
+
|
|
16
|
+
var xprivkey =
|
|
17
|
+
'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
|
|
18
|
+
var json =
|
|
19
|
+
'{"network":"livenet","depth":0,"fingerPrint":876747070,"parentFingerPrint":0,"childIndex":0,"chainCode":"873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508","privateKey":"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35","checksum":3883834737,"xprivkey":"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"}';
|
|
20
|
+
describe('HDPrivate key interface', function () {
|
|
21
|
+
var expectFail = function (func, error) {
|
|
22
|
+
var got = null;
|
|
23
|
+
try {
|
|
24
|
+
func();
|
|
25
|
+
} catch (e) {
|
|
26
|
+
got = e instanceof error;
|
|
27
|
+
}
|
|
28
|
+
expect(got).to.equal(true);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
var expectDerivationFail = function (argument, error) {
|
|
32
|
+
return expectFail(function () {
|
|
33
|
+
var privateKey = new HDPrivateKey(xprivkey);
|
|
34
|
+
privateKey.deriveChild(argument);
|
|
35
|
+
}, error);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
var expectFailBuilding = function (argument, error) {
|
|
39
|
+
return expectFail(function () {
|
|
40
|
+
return new HDPrivateKey(argument);
|
|
41
|
+
}, error);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
var expectSeedFail = function (argument, error) {
|
|
45
|
+
return expectFail(function () {
|
|
46
|
+
return HDPrivateKey.fromSeed(argument);
|
|
47
|
+
}, error);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
it('should make a new private key from random', function () {
|
|
51
|
+
should.exist(new HDPrivateKey().xprivkey);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should make a new private key from random for testnet', function () {
|
|
55
|
+
var key = new HDPrivateKey('testnet');
|
|
56
|
+
should.exist(key.xprivkey);
|
|
57
|
+
key.network.name.should.equal('testnet');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should not be able to change read-only properties', function () {
|
|
61
|
+
var hdkey = new HDPrivateKey();
|
|
62
|
+
expect(function () {
|
|
63
|
+
hdkey.fingerPrint = 'notafingerprint';
|
|
64
|
+
}).to.throw(TypeError);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should error with an invalid checksum', function () {
|
|
68
|
+
expectFailBuilding(xprivkey + '1', errors.InvalidB58Checksum);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('can be rebuilt from a json generated by itself', function () {
|
|
72
|
+
var regenerate = new HDPrivateKey(json);
|
|
73
|
+
regenerate.xprivkey.should.equal(xprivkey);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('builds a json keeping the structure and same members', function () {
|
|
77
|
+
assert.deepStrictEqual(new HDPrivateKey(json).toJSON(), new HDPrivateKey(xprivkey).toJSON());
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('instantiation', function () {
|
|
81
|
+
it('invalid argument: can not instantiate from a number', function () {
|
|
82
|
+
expectFailBuilding(1, hdErrors.UnrecognizedArgument);
|
|
83
|
+
});
|
|
84
|
+
it('allows no-new calling', function () {
|
|
85
|
+
HDPrivateKey(xprivkey).toString().should.equal(xprivkey);
|
|
86
|
+
});
|
|
87
|
+
it('allows the use of a copy constructor', function () {
|
|
88
|
+
HDPrivateKey(HDPrivateKey(xprivkey)).xprivkey.should.equal(xprivkey);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('public key', function () {
|
|
93
|
+
var testnetKey = new HDPrivateKey(
|
|
94
|
+
'tprv8ZgxMBicQKsPdEeU2KiGFnUgRGriMnQxrwrg6FWCBg4jeiidHRyCCdA357kfkZiGaXEapWZsGDKikeeEbvgXo3UmEdbEKNdQH9VXESmGuUK',
|
|
95
|
+
);
|
|
96
|
+
var livenetKey = new HDPrivateKey(
|
|
97
|
+
'xprv9s21ZrQH143K3e39bnn1vyS7YFa1EAJAFGDoeHaSBsgBxgAkTEXeSx7xLvhNQNJxJwhzziWcK3znUFKRPRwWBPkKZ8ijUBa5YYpYPQmeBDX',
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
it('matches the network', function () {
|
|
101
|
+
testnetKey.publicKey.network.should.equal(Networks.testnet);
|
|
102
|
+
livenetKey.publicKey.network.should.equal(Networks.livenet);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('cache for xpubkey works', function () {
|
|
106
|
+
var privateKey = new HDPrivateKey(xprivkey);
|
|
107
|
+
should.not.exist(privateKey._hdPublicKey);
|
|
108
|
+
privateKey.xpubkey.should.equal(privateKey.xpubkey);
|
|
109
|
+
should.exist(privateKey._hdPublicKey);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('inspect() displays correctly', function () {
|
|
114
|
+
HDPrivateKey(xprivkey)
|
|
115
|
+
.inspect()
|
|
116
|
+
.should.equal('<HDPrivateKey: ' + xprivkey + '>');
|
|
117
|
+
});
|
|
118
|
+
it('fails when trying to derive with an invalid argument', function () {
|
|
119
|
+
expectDerivationFail([], hdErrors.InvalidDerivationArgument);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('catches early invalid paths', function () {
|
|
123
|
+
expectDerivationFail('s', hdErrors.InvalidPath);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('allows derivation of hardened keys by passing a very big number', function () {
|
|
127
|
+
var privateKey = new HDPrivateKey(xprivkey);
|
|
128
|
+
var derivedByNumber = privateKey.deriveChild(0x80000000);
|
|
129
|
+
var derivedByArgument = privateKey.deriveChild(0, true);
|
|
130
|
+
derivedByNumber.xprivkey.should.equal(derivedByArgument.xprivkey);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("returns itself with 'm' parameter", function () {
|
|
134
|
+
var privateKey = new HDPrivateKey(xprivkey);
|
|
135
|
+
privateKey.should.equal(privateKey.deriveChild('m'));
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('returns InvalidArgument if invalid data is given to getSerializedError', function () {
|
|
139
|
+
expect(HDPrivateKey.getSerializedError(1) instanceof hdErrors.UnrecognizedArgument).to.equal(
|
|
140
|
+
true,
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('returns InvalidLength if data of invalid length is given to getSerializedError', function () {
|
|
145
|
+
var b58s = Base58Check.encode(buffer.Buffer.from('onestring'));
|
|
146
|
+
expect(HDPrivateKey.getSerializedError(b58s) instanceof hdErrors.InvalidLength).to.equal(true);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('returns InvalidNetworkArgument if an invalid network is provided', function () {
|
|
150
|
+
expect(
|
|
151
|
+
HDPrivateKey.getSerializedError(xprivkey, 'invalidNetwork') instanceof
|
|
152
|
+
errors.InvalidNetworkArgument,
|
|
153
|
+
).to.equal(true);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('recognizes that the wrong network was asked for', function () {
|
|
157
|
+
expect(
|
|
158
|
+
HDPrivateKey.getSerializedError(xprivkey, 'testnet') instanceof errors.InvalidNetwork,
|
|
159
|
+
).to.equal(true);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('recognizes the correct network', function () {
|
|
163
|
+
expect(HDPrivateKey.getSerializedError(xprivkey, 'livenet')).to.equal(null);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('on creation from seed', function () {
|
|
167
|
+
it('converts correctly from an hexa string', function () {
|
|
168
|
+
should.exist(HDPrivateKey.fromSeed('01234567890abcdef01234567890abcdef').xprivkey);
|
|
169
|
+
});
|
|
170
|
+
it('fails when argument is not a buffer or string', function () {
|
|
171
|
+
expectSeedFail(1, hdErrors.InvalidEntropyArgument);
|
|
172
|
+
});
|
|
173
|
+
it("fails when argument doesn't provide enough entropy", function () {
|
|
174
|
+
expectSeedFail('01', hdErrors.InvalidEntropyArgument.NotEnoughEntropy);
|
|
175
|
+
});
|
|
176
|
+
it('fails when argument provides too much entropy', function () {
|
|
177
|
+
var entropy = '0';
|
|
178
|
+
for (var i = 0; i < 129; i++) {
|
|
179
|
+
entropy += '1';
|
|
180
|
+
}
|
|
181
|
+
expectSeedFail(entropy, hdErrors.InvalidEntropyArgument.TooMuchEntropy);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('correctly errors if an invalid checksum is provided', function () {
|
|
186
|
+
var privKey = new HDPrivateKey(xprivkey);
|
|
187
|
+
var error = null;
|
|
188
|
+
try {
|
|
189
|
+
var buffers = privKey._buffers;
|
|
190
|
+
buffers.checksum = JSUtil.integerAsBuffer(0);
|
|
191
|
+
new HDPrivateKey(buffers);
|
|
192
|
+
} catch (e) {
|
|
193
|
+
error = e;
|
|
194
|
+
}
|
|
195
|
+
expect(error instanceof errors.InvalidB58Checksum).to.equal(true);
|
|
196
|
+
});
|
|
197
|
+
it('correctly validates the checksum', function () {
|
|
198
|
+
var privKey = new HDPrivateKey(xprivkey);
|
|
199
|
+
expect(function () {
|
|
200
|
+
var buffers = privKey._buffers;
|
|
201
|
+
return new HDPrivateKey(buffers);
|
|
202
|
+
}).to.not.throw();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("shouldn't matter if derivations are made with strings or numbers", function () {
|
|
206
|
+
var privateKey = new HDPrivateKey(xprivkey);
|
|
207
|
+
var derivedByString = privateKey.deriveChild("m/0'/1/2'");
|
|
208
|
+
var derivedByNumber = privateKey.deriveChild(0, true).deriveChild(1).deriveChild(2, true);
|
|
209
|
+
derivedByNumber.xprivkey.should.equal(derivedByString.xprivkey);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('validates paths', function () {
|
|
213
|
+
it('validates correct paths', function () {
|
|
214
|
+
var valid;
|
|
215
|
+
|
|
216
|
+
valid = HDPrivateKey.isValidPath("m/0'/1/2'");
|
|
217
|
+
valid.should.equal(true);
|
|
218
|
+
|
|
219
|
+
valid = HDPrivateKey.isValidPath('m');
|
|
220
|
+
valid.should.equal(true);
|
|
221
|
+
|
|
222
|
+
valid = HDPrivateKey.isValidPath(123, true);
|
|
223
|
+
valid.should.equal(true);
|
|
224
|
+
|
|
225
|
+
valid = HDPrivateKey.isValidPath(123);
|
|
226
|
+
valid.should.equal(true);
|
|
227
|
+
|
|
228
|
+
valid = HDPrivateKey.isValidPath(HDPrivateKey.Hardened + 123);
|
|
229
|
+
valid.should.equal(true);
|
|
230
|
+
|
|
231
|
+
valid = HDPrivateKey.isValidPath(HDPrivateKey.Hardened + 123, true);
|
|
232
|
+
valid.should.equal(true);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
var invalid = ['m/-1/12', 'bad path', 'K', 'm/', 'm/12asd', 'm/1/2//3'];
|
|
236
|
+
|
|
237
|
+
invalid.forEach(function (datum) {
|
|
238
|
+
it('rejects illegal path ' + datum, function () {
|
|
239
|
+
HDPrivateKey.isValidPath(datum).should.equal(false);
|
|
240
|
+
expect(HDPrivateKey._getDerivationIndexes(datum)).to.equal(null);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('generates deriving indexes correctly', function () {
|
|
245
|
+
var indexes;
|
|
246
|
+
|
|
247
|
+
indexes = HDPrivateKey._getDerivationIndexes('m/-1/12');
|
|
248
|
+
expect(indexes).to.equal(null);
|
|
249
|
+
|
|
250
|
+
indexes = HDPrivateKey._getDerivationIndexes("m/0/12/12'");
|
|
251
|
+
indexes.should.eql([0, 12, HDPrivateKey.Hardened + 12]);
|
|
252
|
+
|
|
253
|
+
indexes = HDPrivateKey._getDerivationIndexes("m/0/12/12'");
|
|
254
|
+
indexes.should.eql([0, 12, HDPrivateKey.Hardened + 12]);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
describe('conversion to/from buffer', function () {
|
|
259
|
+
var str =
|
|
260
|
+
'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
|
|
261
|
+
it('should roundtrip to/from a buffer', function () {
|
|
262
|
+
var priv = new HDPrivateKey(str);
|
|
263
|
+
var toBuffer = priv.toBuffer();
|
|
264
|
+
var fromBuffer = HDPrivateKey.fromBuffer(toBuffer);
|
|
265
|
+
var roundTrip = new HDPrivateKey(fromBuffer.toString());
|
|
266
|
+
roundTrip.xprivkey.should.equal(str);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
describe('conversion to/from hex', function () {
|
|
271
|
+
var str =
|
|
272
|
+
'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
|
|
273
|
+
it('should roundtrip to/from a buffer', function () {
|
|
274
|
+
var priv = new HDPrivateKey(str);
|
|
275
|
+
var toBuffer = priv.toBuffer();
|
|
276
|
+
var fromBuffer = HDPrivateKey.fromBuffer(toBuffer);
|
|
277
|
+
var roundTrip = new HDPrivateKey(fromBuffer.toString());
|
|
278
|
+
roundTrip.xprivkey.should.equal(str);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
describe('from random', function () {
|
|
283
|
+
var str =
|
|
284
|
+
'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
|
|
285
|
+
it('should roundtrip to/from a buffer', function () {
|
|
286
|
+
var xprv1 = new HDPrivateKey(str);
|
|
287
|
+
var xprv2 = HDPrivateKey.fromRandom();
|
|
288
|
+
var xprv3 = HDPrivateKey.fromRandom();
|
|
289
|
+
xprv1.toString().should.not.equal(xprv2.toString());
|
|
290
|
+
xprv2.toString().should.not.equal(xprv3.toString());
|
|
291
|
+
xprv1.toString().should.not.equal(xprv3.toString());
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe('conversion to plain object/json', function () {
|
|
296
|
+
var plainObject = {
|
|
297
|
+
network: 'livenet',
|
|
298
|
+
depth: 0,
|
|
299
|
+
fingerPrint: 876747070,
|
|
300
|
+
parentFingerPrint: 0,
|
|
301
|
+
childIndex: 0,
|
|
302
|
+
chainCode: '873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508',
|
|
303
|
+
privateKey: 'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35',
|
|
304
|
+
checksum: 3883834737,
|
|
305
|
+
xprivkey:
|
|
306
|
+
'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(
|
|
318
|
+
xprivkey,
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
it('roundtrips to JSON and to Object', function () {
|
|
322
|
+
var privkey = new HDPrivateKey(xprivkey);
|
|
323
|
+
expect(HDPrivateKey.fromObject(privkey.toJSON()).xprivkey).to.equal(xprivkey);
|
|
324
|
+
});
|
|
325
|
+
it('recovers state from JSON', function () {
|
|
326
|
+
new HDPrivateKey(JSON.stringify(plainObject)).xprivkey.should.equal(xprivkey);
|
|
327
|
+
});
|
|
328
|
+
it('recovers state from Object', function () {
|
|
329
|
+
new HDPrivateKey(plainObject).xprivkey.should.equal(xprivkey);
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
});
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var assert = require('assert');
|
|
4
|
+
require('chai').should();
|
|
5
|
+
var expect = require('chai').expect;
|
|
6
|
+
var opcat = require('..');
|
|
7
|
+
var buffer = require('buffer');
|
|
8
|
+
var errors = opcat.errors;
|
|
9
|
+
var hdErrors = opcat.errors.HDPublicKey;
|
|
10
|
+
var JSUtil = require('../lib/util/js');
|
|
11
|
+
var HDPrivateKey = opcat.HDPrivateKey;
|
|
12
|
+
var HDPublicKey = opcat.HDPublicKey;
|
|
13
|
+
var Base58Check = opcat.encoding.Base58Check;
|
|
14
|
+
var Networks = opcat.Networks;
|
|
15
|
+
|
|
16
|
+
var xprivkey =
|
|
17
|
+
'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
|
|
18
|
+
var xpubkey =
|
|
19
|
+
'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8';
|
|
20
|
+
var xpubkeyTestnet =
|
|
21
|
+
'tpubD6NzVbkrYhZ4WZaiWHz59q5EQ61bd6dUYfU4ggRWAtNAyyYRNWT6ktJ7UHJEXURvTfTfskFQmK7Ff4FRkiRN5wQH8nkGAb6aKB4Yyeqsw5m';
|
|
22
|
+
var json =
|
|
23
|
+
'{"network":"livenet","depth":0,"fingerPrint":876747070,"parentFingerPrint":0,"childIndex":0,"chainCode":"873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508","publicKey":"0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2","checksum":2873572129,"xpubkey":"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"}';
|
|
24
|
+
var derived01200000 =
|
|
25
|
+
'xpub6BqyndF6rkBNTV6LXwiY8Pco8aqctqq7tGEUdA8fmGDTnDJphn2fmxr3eM8Lm3m8TrNUsLbEjHvpa3adBU18YpEx4tp2Zp6nqax3mQkudhX';
|
|
26
|
+
|
|
27
|
+
describe('HDPublicKey interface', function () {
|
|
28
|
+
var expectFail = function (func, errorType) {
|
|
29
|
+
(function () {
|
|
30
|
+
func();
|
|
31
|
+
}).should.throw(errorType);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
var expectDerivationFail = function (argument, error) {
|
|
35
|
+
(function () {
|
|
36
|
+
var pubkey = new HDPublicKey(xpubkey);
|
|
37
|
+
pubkey.deriveChild(argument);
|
|
38
|
+
}).should.throw(error);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
var expectFailBuilding = function (argument, error) {
|
|
42
|
+
(function () {
|
|
43
|
+
return new HDPublicKey(argument);
|
|
44
|
+
}).should.throw(error);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
describe('creation formats', function () {
|
|
48
|
+
it('returns same argument if already an instance of HDPublicKey', function () {
|
|
49
|
+
var publicKey = new HDPublicKey(xpubkey);
|
|
50
|
+
publicKey.should.equal(new HDPublicKey(publicKey));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns the correct xpubkey for a xprivkey', function () {
|
|
54
|
+
var publicKey = new HDPublicKey(xprivkey);
|
|
55
|
+
publicKey.xpubkey.should.equal(xpubkey);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('allows to call the argument with no "new" keyword', function () {
|
|
59
|
+
HDPublicKey(xpubkey).xpubkey.should.equal(new HDPublicKey(xpubkey).xpubkey);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("fails when user doesn't supply an argument", function () {
|
|
63
|
+
expectFailBuilding(null, hdErrors.MustSupplyArgument);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should not be able to change read-only properties', function () {
|
|
67
|
+
var publicKey = new HDPublicKey(xprivkey);
|
|
68
|
+
expect(function () {
|
|
69
|
+
publicKey.fingerPrint = 'notafingerprint';
|
|
70
|
+
}).to.throw(TypeError);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("doesn't recognize an invalid argument", function () {
|
|
74
|
+
expectFailBuilding(1, hdErrors.UnrecognizedArgument);
|
|
75
|
+
expectFailBuilding(true, hdErrors.UnrecognizedArgument);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('xpubkey string serialization errors', function () {
|
|
79
|
+
it('fails on invalid length', function () {
|
|
80
|
+
expectFailBuilding(
|
|
81
|
+
Base58Check.encode(buffer.Buffer.from([1, 2, 3])),
|
|
82
|
+
hdErrors.InvalidLength,
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
it('fails on invalid base58 encoding', function () {
|
|
86
|
+
expectFailBuilding(xpubkey + '1', errors.InvalidB58Checksum);
|
|
87
|
+
});
|
|
88
|
+
it('user can ask if a string is valid', function () {
|
|
89
|
+
HDPublicKey.isValidSerialized(xpubkey).should.equal(true);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('can be generated from a json', function () {
|
|
94
|
+
expect(new HDPublicKey(JSON.parse(json)).xpubkey).to.equal(xpubkey);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('can generate a json that has a particular structure', function () {
|
|
98
|
+
assert.deepStrictEqual(
|
|
99
|
+
new HDPublicKey(JSON.parse(json)).toJSON(),
|
|
100
|
+
new HDPublicKey(xpubkey).toJSON(),
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('builds from a buffer object', function () {
|
|
105
|
+
new HDPublicKey(new HDPublicKey(xpubkey)._buffers).xpubkey.should.equal(xpubkey);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('checks the checksum', function () {
|
|
109
|
+
var buffers = new HDPublicKey(xpubkey)._buffers;
|
|
110
|
+
buffers.checksum = JSUtil.integerAsBuffer(1);
|
|
111
|
+
expectFail(function () {
|
|
112
|
+
return new HDPublicKey(buffers);
|
|
113
|
+
}, errors.InvalidB58Checksum);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('error checking on serialization', function () {
|
|
118
|
+
var compareType = function (a, b) {
|
|
119
|
+
expect(a instanceof b).to.equal(true);
|
|
120
|
+
};
|
|
121
|
+
it('throws invalid argument when argument is not a string or buffer', function () {
|
|
122
|
+
compareType(HDPublicKey.getSerializedError(1), hdErrors.UnrecognizedArgument);
|
|
123
|
+
});
|
|
124
|
+
it('if a network is provided, validates that data corresponds to it', function () {
|
|
125
|
+
compareType(HDPublicKey.getSerializedError(xpubkey, 'testnet'), errors.InvalidNetwork);
|
|
126
|
+
});
|
|
127
|
+
it('recognizes invalid network arguments', function () {
|
|
128
|
+
compareType(
|
|
129
|
+
HDPublicKey.getSerializedError(xpubkey, 'invalid'),
|
|
130
|
+
errors.InvalidNetworkArgument,
|
|
131
|
+
);
|
|
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
|
+
it('should roundtrip to an equivalent object', function () {
|
|
158
|
+
var pubKey = new HDPublicKey(xpubkey);
|
|
159
|
+
var toBuffer = pubKey.toBuffer();
|
|
160
|
+
var fromBuffer = HDPublicKey.fromBuffer(toBuffer);
|
|
161
|
+
var roundTrip = new HDPublicKey(fromBuffer.toBuffer());
|
|
162
|
+
roundTrip.xpubkey.should.equal(xpubkey);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('conversion to/from hex', function () {
|
|
167
|
+
it('should roundtrip to an equivalent object', function () {
|
|
168
|
+
var pubKey = new HDPublicKey(xpubkey);
|
|
169
|
+
var toHex = pubKey.toHex();
|
|
170
|
+
var fromHex = HDPublicKey.fromHex(toHex);
|
|
171
|
+
var roundTrip = new HDPublicKey(fromHex.toBuffer());
|
|
172
|
+
roundTrip.xpubkey.should.equal(xpubkey);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('from hdprivatekey', function () {
|
|
177
|
+
var str =
|
|
178
|
+
'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
|
|
179
|
+
it('should roundtrip to/from a buffer', function () {
|
|
180
|
+
var xprv1 = new HDPrivateKey(str);
|
|
181
|
+
var xprv2 = HDPrivateKey.fromRandom();
|
|
182
|
+
var xprv3 = HDPrivateKey.fromRandom();
|
|
183
|
+
var xpub1 = HDPublicKey.fromHDPrivateKey(xprv1);
|
|
184
|
+
var xpub2 = HDPublicKey.fromHDPrivateKey(xprv2);
|
|
185
|
+
var xpub3 = HDPublicKey.fromHDPrivateKey(xprv3);
|
|
186
|
+
xpub1
|
|
187
|
+
.toString()
|
|
188
|
+
.should.equal(
|
|
189
|
+
'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8',
|
|
190
|
+
);
|
|
191
|
+
xpub1.toString().should.not.equal(xpub2.toString());
|
|
192
|
+
xpub1.toString().should.not.equal(xpub3.toString());
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe('conversion to different formats', function () {
|
|
197
|
+
var plainObject = {
|
|
198
|
+
network: 'livenet',
|
|
199
|
+
depth: 0,
|
|
200
|
+
fingerPrint: 876747070,
|
|
201
|
+
parentFingerPrint: 0,
|
|
202
|
+
childIndex: 0,
|
|
203
|
+
chainCode: '873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508',
|
|
204
|
+
publicKey: '0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2',
|
|
205
|
+
checksum: 2873572129,
|
|
206
|
+
xpubkey:
|
|
207
|
+
'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8',
|
|
208
|
+
};
|
|
209
|
+
it('roundtrips to JSON and to Object', function () {
|
|
210
|
+
var pubkey = new HDPublicKey(xpubkey);
|
|
211
|
+
expect(HDPublicKey.fromObject(pubkey.toJSON()).xpubkey).to.equal(xpubkey);
|
|
212
|
+
});
|
|
213
|
+
it('recovers state from Object', function () {
|
|
214
|
+
new HDPublicKey(plainObject).xpubkey.should.equal(xpubkey);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('derivation', function () {
|
|
219
|
+
it('derivation is the same whether deriving with number or string', function () {
|
|
220
|
+
var pubkey = new HDPublicKey(xpubkey);
|
|
221
|
+
var derived1 = pubkey.deriveChild(0).deriveChild(1).deriveChild(200000);
|
|
222
|
+
var derived2 = pubkey.deriveChild('m/0/1/200000');
|
|
223
|
+
derived1.xpubkey.should.equal(derived01200000);
|
|
224
|
+
derived2.xpubkey.should.equal(derived01200000);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('allows special parameters m, M', function () {
|
|
228
|
+
var expectDerivationSuccess = function (argument) {
|
|
229
|
+
new HDPublicKey(xpubkey).deriveChild(argument).xpubkey.should.equal(xpubkey);
|
|
230
|
+
};
|
|
231
|
+
expectDerivationSuccess('m');
|
|
232
|
+
expectDerivationSuccess('M');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("doesn't allow object arguments for derivation", function () {
|
|
236
|
+
expectFail(function () {
|
|
237
|
+
return new HDPublicKey(xpubkey).deriveChild({});
|
|
238
|
+
}, hdErrors.InvalidDerivationArgument);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('needs first argument for derivation', function () {
|
|
242
|
+
expectFail(function () {
|
|
243
|
+
return new HDPublicKey(xpubkey).deriveChild('s');
|
|
244
|
+
}, hdErrors.InvalidPath);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("doesn't allow other parameters like m' or M' or \"s\"", function () {
|
|
248
|
+
expectDerivationFail("m'", hdErrors.InvalidIndexCantDeriveHardened);
|
|
249
|
+
expectDerivationFail("M'", hdErrors.InvalidIndexCantDeriveHardened);
|
|
250
|
+
expectDerivationFail('1', hdErrors.InvalidPath);
|
|
251
|
+
expectDerivationFail('S', hdErrors.InvalidPath);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("can't derive hardened keys", function () {
|
|
255
|
+
expectFail(function () {
|
|
256
|
+
return new HDPublicKey(xpubkey).deriveChild(HDPublicKey.Hardened);
|
|
257
|
+
}, hdErrors.InvalidIndexCantDeriveHardened);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("can't derive hardened keys via second argument", function () {
|
|
261
|
+
expectFail(function () {
|
|
262
|
+
return new HDPublicKey(xpubkey).deriveChild(5, true);
|
|
263
|
+
}, hdErrors.InvalidIndexCantDeriveHardened);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('validates correct paths', function () {
|
|
267
|
+
var valid;
|
|
268
|
+
|
|
269
|
+
valid = HDPublicKey.isValidPath('m/123/12');
|
|
270
|
+
valid.should.equal(true);
|
|
271
|
+
|
|
272
|
+
valid = HDPublicKey.isValidPath('m');
|
|
273
|
+
valid.should.equal(true);
|
|
274
|
+
|
|
275
|
+
valid = HDPublicKey.isValidPath(123);
|
|
276
|
+
valid.should.equal(true);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('rejects illegal paths', function () {
|
|
280
|
+
var valid;
|
|
281
|
+
|
|
282
|
+
valid = HDPublicKey.isValidPath('m/-1/12');
|
|
283
|
+
valid.should.equal(false);
|
|
284
|
+
|
|
285
|
+
valid = HDPublicKey.isValidPath("m/0'/12");
|
|
286
|
+
valid.should.equal(false);
|
|
287
|
+
|
|
288
|
+
valid = HDPublicKey.isValidPath('m/8000000000/12');
|
|
289
|
+
valid.should.equal(false);
|
|
290
|
+
|
|
291
|
+
valid = HDPublicKey.isValidPath('bad path');
|
|
292
|
+
valid.should.equal(false);
|
|
293
|
+
|
|
294
|
+
valid = HDPublicKey.isValidPath(-1);
|
|
295
|
+
valid.should.equal(false);
|
|
296
|
+
|
|
297
|
+
valid = HDPublicKey.isValidPath(8000000000);
|
|
298
|
+
valid.should.equal(false);
|
|
299
|
+
|
|
300
|
+
valid = HDPublicKey.isValidPath(HDPublicKey.Hardened);
|
|
301
|
+
valid.should.equal(false);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|
package/test/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var should = require('chai').should();
|
|
4
|
+
var opcat = require('../');
|
|
5
|
+
|
|
6
|
+
describe('#versionGuard', function () {
|
|
7
|
+
it('global.opcat should be defined', function () {
|
|
8
|
+
should.equal(global.opcat, opcat.version);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('throw an error if version is already defined', function () {
|
|
12
|
+
(function () {
|
|
13
|
+
opcat.versionGuard('version');
|
|
14
|
+
}).should.not.throw('More than one instance of opcat');
|
|
15
|
+
});
|
|
16
|
+
});
|