@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,525 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _ = require('./util/_');
|
|
4
|
+
var $ = require('./util/preconditions');
|
|
5
|
+
|
|
6
|
+
var BN = require('./crypto/bn');
|
|
7
|
+
var Base58 = require('./encoding/base58');
|
|
8
|
+
var Base58Check = require('./encoding/base58check');
|
|
9
|
+
var Hash = require('./crypto/hash');
|
|
10
|
+
var HDPrivateKey = require('./hdprivatekey');
|
|
11
|
+
var Network = require('./networks');
|
|
12
|
+
var Point = require('./crypto/point');
|
|
13
|
+
var PublicKey = require('./publickey');
|
|
14
|
+
|
|
15
|
+
var opcatErrors = require('./errors');
|
|
16
|
+
var errors = opcatErrors;
|
|
17
|
+
var hdErrors = opcatErrors.HDPublicKey;
|
|
18
|
+
var assert = require('assert');
|
|
19
|
+
|
|
20
|
+
var JSUtil = require('./util/js');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The representation of an hierarchically derived public key.
|
|
24
|
+
*
|
|
25
|
+
* See https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
|
26
|
+
*
|
|
27
|
+
* @constructor
|
|
28
|
+
* @param {Object|string|Buffer} arg
|
|
29
|
+
*/
|
|
30
|
+
function HDPublicKey(arg) {
|
|
31
|
+
if (arg instanceof HDPublicKey) {
|
|
32
|
+
return arg;
|
|
33
|
+
}
|
|
34
|
+
if (!(this instanceof HDPublicKey)) {
|
|
35
|
+
return new HDPublicKey(arg);
|
|
36
|
+
}
|
|
37
|
+
if (arg) {
|
|
38
|
+
if (_.isString(arg) || Buffer.isBuffer(arg)) {
|
|
39
|
+
var error = HDPublicKey.getSerializedError(arg);
|
|
40
|
+
if (!error) {
|
|
41
|
+
return this._buildFromSerialized(arg);
|
|
42
|
+
} else if (Buffer.isBuffer(arg) && !HDPublicKey.getSerializedError(arg.toString())) {
|
|
43
|
+
return this._buildFromSerialized(arg.toString());
|
|
44
|
+
} else {
|
|
45
|
+
if (error instanceof hdErrors.ArgumentIsPrivateExtended) {
|
|
46
|
+
return new HDPrivateKey(arg).hdPublicKey;
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
if (_.isObject(arg)) {
|
|
52
|
+
if (arg instanceof HDPrivateKey) {
|
|
53
|
+
return this._buildFromPrivate(arg);
|
|
54
|
+
} else {
|
|
55
|
+
return this._buildFromObject(arg);
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
throw new hdErrors.UnrecognizedArgument(arg);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
throw new hdErrors.MustSupplyArgument();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
HDPublicKey.fromHDPrivateKey = function (hdPrivateKey) {
|
|
67
|
+
return new HDPublicKey(hdPrivateKey);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Verifies that a given path is valid.
|
|
72
|
+
*
|
|
73
|
+
* @param {string|number} arg
|
|
74
|
+
* @return {boolean}
|
|
75
|
+
*/
|
|
76
|
+
HDPublicKey.isValidPath = function (arg) {
|
|
77
|
+
if (_.isString(arg)) {
|
|
78
|
+
var indexes = HDPrivateKey._getDerivationIndexes(arg);
|
|
79
|
+
return indexes !== null && _.every(indexes, HDPublicKey.isValidPath);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (_.isNumber(arg)) {
|
|
83
|
+
return arg >= 0 && arg < HDPublicKey.Hardened;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return false;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* WARNING: This method is deprecated. Use deriveChild instead.
|
|
91
|
+
*
|
|
92
|
+
*
|
|
93
|
+
* Get a derivated child based on a string or number.
|
|
94
|
+
*
|
|
95
|
+
* If the first argument is a string, it's parsed as the full path of
|
|
96
|
+
* derivation. Valid values for this argument include "m" (which returns the
|
|
97
|
+
* same public key), "m/0/1/40/2/1000".
|
|
98
|
+
*
|
|
99
|
+
* Note that hardened keys can't be derived from a public extended key.
|
|
100
|
+
*
|
|
101
|
+
* If the first argument is a number, the child with that index will be
|
|
102
|
+
* derived. See the example usage for clarification.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```javascript
|
|
106
|
+
* var parent = new HDPublicKey('xpub...');
|
|
107
|
+
* var child_0_1_2 = parent.derive(0).derive(1).derive(2);
|
|
108
|
+
* var copy_of_child_0_1_2 = parent.derive("m/0/1/2");
|
|
109
|
+
* assert(child_0_1_2.xprivkey === copy_of_child_0_1_2);
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @param {string|number} arg
|
|
113
|
+
*/
|
|
114
|
+
HDPublicKey.prototype.derive = function () {
|
|
115
|
+
throw new Error(
|
|
116
|
+
'derive has been deprecated. use deriveChild or, for the old way, deriveNonCompliantChild.',
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* WARNING: This method will not be officially supported until v1.0.0.
|
|
122
|
+
*
|
|
123
|
+
*
|
|
124
|
+
* Get a derivated child based on a string or number.
|
|
125
|
+
*
|
|
126
|
+
* If the first argument is a string, it's parsed as the full path of
|
|
127
|
+
* derivation. Valid values for this argument include "m" (which returns the
|
|
128
|
+
* same public key), "m/0/1/40/2/1000".
|
|
129
|
+
*
|
|
130
|
+
* Note that hardened keys can't be derived from a public extended key.
|
|
131
|
+
*
|
|
132
|
+
* If the first argument is a number, the child with that index will be
|
|
133
|
+
* derived. See the example usage for clarification.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```javascript
|
|
137
|
+
* var parent = new HDPublicKey('xpub...');
|
|
138
|
+
* var child_0_1_2 = parent.deriveChild(0).deriveChild(1).deriveChild(2);
|
|
139
|
+
* var copy_of_child_0_1_2 = parent.deriveChild("m/0/1/2");
|
|
140
|
+
* assert(child_0_1_2.xprivkey === copy_of_child_0_1_2);
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* @param {string|number} arg
|
|
144
|
+
*/
|
|
145
|
+
HDPublicKey.prototype.deriveChild = function (arg, hardened) {
|
|
146
|
+
if (_.isNumber(arg)) {
|
|
147
|
+
return this._deriveWithNumber(arg, hardened);
|
|
148
|
+
} else if (_.isString(arg)) {
|
|
149
|
+
return this._deriveFromString(arg);
|
|
150
|
+
} else {
|
|
151
|
+
throw new hdErrors.InvalidDerivationArgument(arg);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
HDPublicKey.prototype._deriveWithNumber = function (index, hardened) {
|
|
156
|
+
if (index >= HDPublicKey.Hardened || hardened) {
|
|
157
|
+
throw new hdErrors.InvalidIndexCantDeriveHardened();
|
|
158
|
+
}
|
|
159
|
+
if (index < 0) {
|
|
160
|
+
throw new hdErrors.InvalidPath(index);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
var indexBuffer = JSUtil.integerAsBuffer(index);
|
|
164
|
+
var data = Buffer.concat([this.publicKey.toBuffer(), indexBuffer]);
|
|
165
|
+
var hash = Hash.sha512hmac(data, this._buffers.chainCode);
|
|
166
|
+
var leftPart = BN.fromBuffer(hash.slice(0, 32), { size: 32 });
|
|
167
|
+
var chainCode = hash.slice(32, 64);
|
|
168
|
+
|
|
169
|
+
var publicKey;
|
|
170
|
+
try {
|
|
171
|
+
publicKey = PublicKey.fromPoint(Point.getG().mul(leftPart).add(this.publicKey.point));
|
|
172
|
+
} catch (e) {
|
|
173
|
+
return this._deriveWithNumber(index + 1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
var derived = new HDPublicKey({
|
|
177
|
+
network: this.network,
|
|
178
|
+
depth: this.depth + 1,
|
|
179
|
+
parentFingerPrint: this.fingerPrint,
|
|
180
|
+
childIndex: index,
|
|
181
|
+
chainCode: chainCode,
|
|
182
|
+
publicKey: publicKey,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return derived;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
HDPublicKey.prototype._deriveFromString = function (path) {
|
|
189
|
+
if (_.includes(path, "'")) {
|
|
190
|
+
throw new hdErrors.InvalidIndexCantDeriveHardened();
|
|
191
|
+
} else if (!HDPublicKey.isValidPath(path)) {
|
|
192
|
+
throw new hdErrors.InvalidPath(path);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
var indexes = HDPrivateKey._getDerivationIndexes(path);
|
|
196
|
+
var derived = indexes.reduce(function (prev, index) {
|
|
197
|
+
return prev._deriveWithNumber(index);
|
|
198
|
+
}, this);
|
|
199
|
+
|
|
200
|
+
return derived;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Verifies that a given serialized public key in base58 with checksum format
|
|
205
|
+
* is valid.
|
|
206
|
+
*
|
|
207
|
+
* @param {string|Buffer} data - the serialized public key
|
|
208
|
+
* @param {string|Network=} network - optional, if present, checks that the
|
|
209
|
+
* network provided matches the network serialized.
|
|
210
|
+
* @return {boolean}
|
|
211
|
+
*/
|
|
212
|
+
HDPublicKey.isValidSerialized = function (data, network) {
|
|
213
|
+
return _.isNull(HDPublicKey.getSerializedError(data, network));
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Checks what's the error that causes the validation of a serialized public key
|
|
218
|
+
* in base58 with checksum to fail.
|
|
219
|
+
*
|
|
220
|
+
* @param {string|Buffer} data - the serialized public key
|
|
221
|
+
* @param {string|Network=} network - optional, if present, checks that the
|
|
222
|
+
* network provided matches the network serialized.
|
|
223
|
+
* @return {errors|null}
|
|
224
|
+
*/
|
|
225
|
+
HDPublicKey.getSerializedError = function (data, network) {
|
|
226
|
+
if (!(_.isString(data) || Buffer.isBuffer(data))) {
|
|
227
|
+
return new hdErrors.UnrecognizedArgument('expected buffer or string');
|
|
228
|
+
}
|
|
229
|
+
if (!Base58.validCharacters(data)) {
|
|
230
|
+
return new errors.InvalidB58Char('(unknown)', data);
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
data = Base58Check.decode(data);
|
|
234
|
+
} catch (e) {
|
|
235
|
+
return new errors.InvalidB58Checksum(data);
|
|
236
|
+
}
|
|
237
|
+
if (data.length !== HDPublicKey.DataSize) {
|
|
238
|
+
return new hdErrors.InvalidLength(data);
|
|
239
|
+
}
|
|
240
|
+
if (!_.isUndefined(network)) {
|
|
241
|
+
var error = HDPublicKey._validateNetwork(data, network);
|
|
242
|
+
if (error) {
|
|
243
|
+
return error;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
var version = data.readUInt32BE(0);
|
|
247
|
+
if (version === Network.livenet.xprivkey || version === Network.testnet.xprivkey) {
|
|
248
|
+
return new hdErrors.ArgumentIsPrivateExtended();
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
HDPublicKey._validateNetwork = function (data, networkArg) {
|
|
254
|
+
var network = Network.get(networkArg);
|
|
255
|
+
if (!network) {
|
|
256
|
+
return new errors.InvalidNetworkArgument(networkArg);
|
|
257
|
+
}
|
|
258
|
+
var version = data.slice(HDPublicKey.VersionStart, HDPublicKey.VersionEnd);
|
|
259
|
+
if (version.readUInt32BE(0) !== network.xpubkey) {
|
|
260
|
+
return new errors.InvalidNetwork(version);
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
HDPublicKey.prototype._buildFromPrivate = function (arg) {
|
|
266
|
+
var args = _.clone(arg._buffers);
|
|
267
|
+
var point = Point.getG().mul(BN.fromBuffer(args.privateKey));
|
|
268
|
+
args.publicKey = Point.pointToCompressed(point);
|
|
269
|
+
args.version = JSUtil.integerAsBuffer(Network.get(args.version.readUInt32BE(0)).xpubkey);
|
|
270
|
+
args.privateKey = undefined;
|
|
271
|
+
args.checksum = undefined;
|
|
272
|
+
args.xprivkey = undefined;
|
|
273
|
+
return this._buildFromBuffers(args);
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
HDPublicKey.prototype._buildFromObject = function (arg) {
|
|
277
|
+
// TODO: Type validation
|
|
278
|
+
var buffers = {
|
|
279
|
+
version: arg.network ? JSUtil.integerAsBuffer(Network.get(arg.network).xpubkey) : arg.version,
|
|
280
|
+
depth: _.isNumber(arg.depth) ? Buffer.from([arg.depth & 0xff]) : arg.depth,
|
|
281
|
+
parentFingerPrint: _.isNumber(arg.parentFingerPrint)
|
|
282
|
+
? JSUtil.integerAsBuffer(arg.parentFingerPrint)
|
|
283
|
+
: arg.parentFingerPrint,
|
|
284
|
+
childIndex: _.isNumber(arg.childIndex)
|
|
285
|
+
? JSUtil.integerAsBuffer(arg.childIndex)
|
|
286
|
+
: arg.childIndex,
|
|
287
|
+
chainCode: _.isString(arg.chainCode) ? Buffer.from(arg.chainCode, 'hex') : arg.chainCode,
|
|
288
|
+
publicKey: _.isString(arg.publicKey)
|
|
289
|
+
? Buffer.from(arg.publicKey, 'hex')
|
|
290
|
+
: Buffer.isBuffer(arg.publicKey)
|
|
291
|
+
? arg.publicKey
|
|
292
|
+
: arg.publicKey.toBuffer(),
|
|
293
|
+
checksum: _.isNumber(arg.checksum) ? JSUtil.integerAsBuffer(arg.checksum) : arg.checksum,
|
|
294
|
+
};
|
|
295
|
+
return this._buildFromBuffers(buffers);
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
HDPublicKey.prototype._buildFromSerialized = function (arg) {
|
|
299
|
+
var decoded = Base58Check.decode(arg);
|
|
300
|
+
var buffers = {
|
|
301
|
+
version: decoded.slice(HDPublicKey.VersionStart, HDPublicKey.VersionEnd),
|
|
302
|
+
depth: decoded.slice(HDPublicKey.DepthStart, HDPublicKey.DepthEnd),
|
|
303
|
+
parentFingerPrint: decoded.slice(
|
|
304
|
+
HDPublicKey.ParentFingerPrintStart,
|
|
305
|
+
HDPublicKey.ParentFingerPrintEnd,
|
|
306
|
+
),
|
|
307
|
+
childIndex: decoded.slice(HDPublicKey.ChildIndexStart, HDPublicKey.ChildIndexEnd),
|
|
308
|
+
chainCode: decoded.slice(HDPublicKey.ChainCodeStart, HDPublicKey.ChainCodeEnd),
|
|
309
|
+
publicKey: decoded.slice(HDPublicKey.PublicKeyStart, HDPublicKey.PublicKeyEnd),
|
|
310
|
+
checksum: decoded.slice(HDPublicKey.ChecksumStart, HDPublicKey.ChecksumEnd),
|
|
311
|
+
xpubkey: arg,
|
|
312
|
+
};
|
|
313
|
+
return this._buildFromBuffers(buffers);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Receives a object with buffers in all the properties and populates the
|
|
318
|
+
* internal structure
|
|
319
|
+
*
|
|
320
|
+
* @param {Object} arg
|
|
321
|
+
* @param {buffer.Buffer} arg.version
|
|
322
|
+
* @param {buffer.Buffer} arg.depth
|
|
323
|
+
* @param {buffer.Buffer} arg.parentFingerPrint
|
|
324
|
+
* @param {buffer.Buffer} arg.childIndex
|
|
325
|
+
* @param {buffer.Buffer} arg.chainCode
|
|
326
|
+
* @param {buffer.Buffer} arg.publicKey
|
|
327
|
+
* @param {buffer.Buffer} arg.checksum
|
|
328
|
+
* @param {string=} arg.xpubkey - if set, don't recalculate the base58
|
|
329
|
+
* representation
|
|
330
|
+
* @return {HDPublicKey} this
|
|
331
|
+
*/
|
|
332
|
+
HDPublicKey.prototype._buildFromBuffers = function (arg) {
|
|
333
|
+
HDPublicKey._validateBufferArguments(arg);
|
|
334
|
+
|
|
335
|
+
JSUtil.defineImmutable(this, {
|
|
336
|
+
_buffers: arg,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
var sequence = [
|
|
340
|
+
arg.version,
|
|
341
|
+
arg.depth,
|
|
342
|
+
arg.parentFingerPrint,
|
|
343
|
+
arg.childIndex,
|
|
344
|
+
arg.chainCode,
|
|
345
|
+
arg.publicKey,
|
|
346
|
+
];
|
|
347
|
+
var concat = Buffer.concat(sequence);
|
|
348
|
+
var checksum = Base58Check.checksum(concat);
|
|
349
|
+
if (!arg.checksum || !arg.checksum.length) {
|
|
350
|
+
arg.checksum = checksum;
|
|
351
|
+
} else {
|
|
352
|
+
if (arg.checksum.toString('hex') !== checksum.toString('hex')) {
|
|
353
|
+
throw new errors.InvalidB58Checksum(concat, checksum);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
var network = Network.get(arg.version.readUInt32BE(0));
|
|
357
|
+
|
|
358
|
+
var xpubkey;
|
|
359
|
+
xpubkey = Base58Check.encode(Buffer.concat(sequence));
|
|
360
|
+
arg.xpubkey = Buffer.from(xpubkey);
|
|
361
|
+
|
|
362
|
+
var publicKey = new PublicKey(arg.publicKey, { network: network });
|
|
363
|
+
var size = HDPublicKey.ParentFingerPrintSize;
|
|
364
|
+
var fingerPrint = Hash.sha256ripemd160(publicKey.toBuffer()).slice(0, size);
|
|
365
|
+
|
|
366
|
+
JSUtil.defineImmutable(this, {
|
|
367
|
+
xpubkey: xpubkey,
|
|
368
|
+
network: network,
|
|
369
|
+
depth: arg.depth[0],
|
|
370
|
+
publicKey: publicKey,
|
|
371
|
+
fingerPrint: fingerPrint,
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
return this;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
HDPublicKey._validateBufferArguments = function (arg) {
|
|
378
|
+
var checkBuffer = function (name, size) {
|
|
379
|
+
var buff = arg[name];
|
|
380
|
+
assert(Buffer.isBuffer(buff), name + " argument is not a buffer, it's " + typeof buff);
|
|
381
|
+
assert(
|
|
382
|
+
buff.length === size,
|
|
383
|
+
name + ' has not the expected size: found ' + buff.length + ', expected ' + size,
|
|
384
|
+
);
|
|
385
|
+
};
|
|
386
|
+
checkBuffer('version', HDPublicKey.VersionSize);
|
|
387
|
+
checkBuffer('depth', HDPublicKey.DepthSize);
|
|
388
|
+
checkBuffer('parentFingerPrint', HDPublicKey.ParentFingerPrintSize);
|
|
389
|
+
checkBuffer('childIndex', HDPublicKey.ChildIndexSize);
|
|
390
|
+
checkBuffer('chainCode', HDPublicKey.ChainCodeSize);
|
|
391
|
+
checkBuffer('publicKey', HDPublicKey.PublicKeySize);
|
|
392
|
+
if (arg.checksum && arg.checksum.length) {
|
|
393
|
+
checkBuffer('checksum', HDPublicKey.CheckSumSize);
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
HDPublicKey.fromString = function (arg) {
|
|
398
|
+
$.checkArgument(_.isString(arg), 'No valid string was provided');
|
|
399
|
+
return new HDPublicKey(arg);
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
HDPublicKey.fromObject = function (arg) {
|
|
403
|
+
$.checkArgument(_.isObject(arg), 'No valid argument was provided');
|
|
404
|
+
return new HDPublicKey(arg);
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Returns the base58 checked representation of the public key
|
|
409
|
+
* @return {string} a string starting with "xpub..." in livenet
|
|
410
|
+
*/
|
|
411
|
+
HDPublicKey.prototype.toString = function () {
|
|
412
|
+
return this.xpubkey;
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Returns the console representation of this extended public key.
|
|
417
|
+
* @return string
|
|
418
|
+
*/
|
|
419
|
+
HDPublicKey.prototype.inspect = function () {
|
|
420
|
+
return '<HDPublicKey: ' + this.xpubkey + '>';
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Returns a plain JavaScript object with information to reconstruct a key.
|
|
425
|
+
*
|
|
426
|
+
* Fields are: <ul>
|
|
427
|
+
* <li> network: 'livenet' or 'testnet'
|
|
428
|
+
* <li> depth: a number from 0 to 255, the depth to the master extended key
|
|
429
|
+
* <li> fingerPrint: a number of 32 bits taken from the hash of the public key
|
|
430
|
+
* <li> fingerPrint: a number of 32 bits taken from the hash of this key's
|
|
431
|
+
* <li> parent's public key
|
|
432
|
+
* <li> childIndex: index with which this key was derived
|
|
433
|
+
* <li> chainCode: string in hexa encoding used for derivation
|
|
434
|
+
* <li> publicKey: string, hexa encoded, in compressed key format
|
|
435
|
+
* <li> checksum: this._buffers.checksum.readUInt32BE(0),
|
|
436
|
+
* <li> xpubkey: the string with the base58 representation of this extended key
|
|
437
|
+
* <li> checksum: the base58 checksum of xpubkey
|
|
438
|
+
* </ul>
|
|
439
|
+
*/
|
|
440
|
+
HDPublicKey.prototype.toObject = HDPublicKey.prototype.toJSON = function toObject() {
|
|
441
|
+
return {
|
|
442
|
+
network: Network.get(this._buffers.version.readUInt32BE(0)).name,
|
|
443
|
+
depth: this._buffers.depth[0],
|
|
444
|
+
fingerPrint: this.fingerPrint.readUInt32BE(0),
|
|
445
|
+
parentFingerPrint: this._buffers.parentFingerPrint.readUInt32BE(0),
|
|
446
|
+
childIndex: this._buffers.childIndex.readUInt32BE(0),
|
|
447
|
+
chainCode: this._buffers.chainCode.toString('hex'),
|
|
448
|
+
publicKey: this.publicKey.toString(),
|
|
449
|
+
checksum: this._buffers.checksum.readUInt32BE(0),
|
|
450
|
+
xpubkey: this.xpubkey,
|
|
451
|
+
};
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Create a HDPublicKey from a buffer argument
|
|
456
|
+
*
|
|
457
|
+
* @param {Buffer} arg
|
|
458
|
+
* @return {HDPublicKey}
|
|
459
|
+
*/
|
|
460
|
+
HDPublicKey.fromBuffer = function (arg) {
|
|
461
|
+
return new HDPublicKey(arg);
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Create a HDPublicKey from a hex string argument
|
|
466
|
+
*
|
|
467
|
+
* @param {Buffer} arg
|
|
468
|
+
* @return {HDPublicKey}
|
|
469
|
+
*/
|
|
470
|
+
HDPublicKey.fromHex = function (hex) {
|
|
471
|
+
return HDPublicKey.fromBuffer(Buffer.from(hex, 'hex'));
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Return a buffer representation of the xpubkey
|
|
476
|
+
*
|
|
477
|
+
* @return {Buffer}
|
|
478
|
+
*/
|
|
479
|
+
HDPublicKey.prototype.toBuffer = function () {
|
|
480
|
+
return Buffer.from(this._buffers.xpubkey);
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Return a hex string representation of the xpubkey
|
|
485
|
+
*
|
|
486
|
+
* @return {Buffer}
|
|
487
|
+
*/
|
|
488
|
+
HDPublicKey.prototype.toHex = function () {
|
|
489
|
+
return this.toBuffer().toString('hex');
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
HDPublicKey.Hardened = 0x80000000;
|
|
493
|
+
HDPublicKey.RootElementAlias = ['m', 'M'];
|
|
494
|
+
|
|
495
|
+
HDPublicKey.VersionSize = 4;
|
|
496
|
+
HDPublicKey.DepthSize = 1;
|
|
497
|
+
HDPublicKey.ParentFingerPrintSize = 4;
|
|
498
|
+
HDPublicKey.ChildIndexSize = 4;
|
|
499
|
+
HDPublicKey.ChainCodeSize = 32;
|
|
500
|
+
HDPublicKey.PublicKeySize = 33;
|
|
501
|
+
HDPublicKey.CheckSumSize = 4;
|
|
502
|
+
|
|
503
|
+
HDPublicKey.DataSize = 78;
|
|
504
|
+
HDPublicKey.SerializedByteSize = 82;
|
|
505
|
+
|
|
506
|
+
HDPublicKey.VersionStart = 0;
|
|
507
|
+
HDPublicKey.VersionEnd = HDPublicKey.VersionStart + HDPublicKey.VersionSize;
|
|
508
|
+
HDPublicKey.DepthStart = HDPublicKey.VersionEnd;
|
|
509
|
+
HDPublicKey.DepthEnd = HDPublicKey.DepthStart + HDPublicKey.DepthSize;
|
|
510
|
+
HDPublicKey.ParentFingerPrintStart = HDPublicKey.DepthEnd;
|
|
511
|
+
HDPublicKey.ParentFingerPrintEnd =
|
|
512
|
+
HDPublicKey.ParentFingerPrintStart + HDPublicKey.ParentFingerPrintSize;
|
|
513
|
+
HDPublicKey.ChildIndexStart = HDPublicKey.ParentFingerPrintEnd;
|
|
514
|
+
HDPublicKey.ChildIndexEnd = HDPublicKey.ChildIndexStart + HDPublicKey.ChildIndexSize;
|
|
515
|
+
HDPublicKey.ChainCodeStart = HDPublicKey.ChildIndexEnd;
|
|
516
|
+
HDPublicKey.ChainCodeEnd = HDPublicKey.ChainCodeStart + HDPublicKey.ChainCodeSize;
|
|
517
|
+
HDPublicKey.PublicKeyStart = HDPublicKey.ChainCodeEnd;
|
|
518
|
+
HDPublicKey.PublicKeyEnd = HDPublicKey.PublicKeyStart + HDPublicKey.PublicKeySize;
|
|
519
|
+
HDPublicKey.ChecksumStart = HDPublicKey.PublicKeyEnd;
|
|
520
|
+
HDPublicKey.ChecksumEnd = HDPublicKey.ChecksumStart + HDPublicKey.CheckSumSize;
|
|
521
|
+
|
|
522
|
+
assert(HDPublicKey.PublicKeyEnd === HDPublicKey.DataSize);
|
|
523
|
+
assert(HDPublicKey.ChecksumEnd === HDPublicKey.SerializedByteSize);
|
|
524
|
+
|
|
525
|
+
module.exports = HDPublicKey;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _ = require('../util/_');
|
|
4
|
+
var PrivateKey = require('../privatekey');
|
|
5
|
+
var PublicKey = require('../publickey');
|
|
6
|
+
var Address = require('../address');
|
|
7
|
+
var BufferWriter = require('../encoding/bufferwriter');
|
|
8
|
+
var ECDSA = require('../crypto/ecdsa');
|
|
9
|
+
var Signature = require('../crypto/signature');
|
|
10
|
+
var sha256sha256 = require('../crypto/hash').sha256sha256;
|
|
11
|
+
var JSUtil = require('../util/js');
|
|
12
|
+
var $ = require('../util/preconditions');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* constructs a new message to sign and verify.
|
|
16
|
+
*
|
|
17
|
+
* @param {String} message
|
|
18
|
+
* @returns {Message}
|
|
19
|
+
*/
|
|
20
|
+
var Message = function Message(message) {
|
|
21
|
+
if (!(this instanceof Message)) {
|
|
22
|
+
return new Message(message);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
$.checkArgument(
|
|
26
|
+
_.isString(message) || Buffer.isBuffer(message),
|
|
27
|
+
'First argument should be a string or Buffer',
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (_.isString(message)) {
|
|
31
|
+
this.messageBuffer = Buffer.from(message);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (Buffer.isBuffer(message)) {
|
|
35
|
+
this.messageBuffer = message;
|
|
36
|
+
}
|
|
37
|
+
return this;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
Message.sign = function (message, privateKey) {
|
|
41
|
+
return new Message(message).sign(privateKey);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
Message.verify = function (message, address, signature) {
|
|
45
|
+
return new Message(message).verify(address, signature);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
Message.MAGIC_BYTES = Buffer.from('Bitcoin Signed Message:\n');
|
|
49
|
+
|
|
50
|
+
Message.prototype.magicHash = function magicHash() {
|
|
51
|
+
var prefix1 = BufferWriter.varintBufNum(Message.MAGIC_BYTES.length);
|
|
52
|
+
var prefix2 = BufferWriter.varintBufNum(this.messageBuffer.length);
|
|
53
|
+
var buf = Buffer.concat([prefix1, Message.MAGIC_BYTES, prefix2, this.messageBuffer]);
|
|
54
|
+
var hash = sha256sha256(buf);
|
|
55
|
+
return hash;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
Message.prototype._sign = function _sign(privateKey) {
|
|
59
|
+
$.checkArgument(
|
|
60
|
+
privateKey instanceof PrivateKey,
|
|
61
|
+
'First argument should be an instance of PrivateKey',
|
|
62
|
+
);
|
|
63
|
+
var hash = this.magicHash();
|
|
64
|
+
return ECDSA.signWithCalcI(hash, privateKey);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Will sign a message with a given bitcoin private key.
|
|
69
|
+
*
|
|
70
|
+
* @param {PrivateKey} privateKey - An instance of PrivateKey
|
|
71
|
+
* @returns {String} A base64 encoded compact signature
|
|
72
|
+
*/
|
|
73
|
+
Message.prototype.sign = function sign(privateKey) {
|
|
74
|
+
var signature = this._sign(privateKey);
|
|
75
|
+
return signature.toCompact().toString('base64');
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
Message.prototype._verify = function _verify(publicKey, signature) {
|
|
79
|
+
$.checkArgument(
|
|
80
|
+
publicKey instanceof PublicKey,
|
|
81
|
+
'First argument should be an instance of PublicKey',
|
|
82
|
+
);
|
|
83
|
+
$.checkArgument(
|
|
84
|
+
signature instanceof Signature,
|
|
85
|
+
'Second argument should be an instance of Signature',
|
|
86
|
+
);
|
|
87
|
+
var hash = this.magicHash();
|
|
88
|
+
var verified = ECDSA.verify(hash, signature, publicKey);
|
|
89
|
+
if (!verified) {
|
|
90
|
+
this.error = 'The signature was invalid';
|
|
91
|
+
}
|
|
92
|
+
return verified;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Will return a boolean of the signature is valid for a given bitcoin address.
|
|
97
|
+
* If it isn't the specific reason is accessible via the "error" member.
|
|
98
|
+
*
|
|
99
|
+
* @param {Address|String} bitcoinAddress - A bitcoin address
|
|
100
|
+
* @param {String} signatureString - A base64 encoded compact signature
|
|
101
|
+
* @returns {Boolean}
|
|
102
|
+
*/
|
|
103
|
+
Message.prototype.verify = function verify(bitcoinAddress, signatureString) {
|
|
104
|
+
$.checkArgument(bitcoinAddress);
|
|
105
|
+
$.checkArgument(signatureString && _.isString(signatureString));
|
|
106
|
+
|
|
107
|
+
if (_.isString(bitcoinAddress)) {
|
|
108
|
+
bitcoinAddress = Address.fromString(bitcoinAddress);
|
|
109
|
+
}
|
|
110
|
+
var signature = Signature.fromCompact(Buffer.from(signatureString, 'base64'));
|
|
111
|
+
|
|
112
|
+
// recover the public key
|
|
113
|
+
var ecdsa = new ECDSA();
|
|
114
|
+
ecdsa.hashbuf = this.magicHash();
|
|
115
|
+
ecdsa.sig = signature;
|
|
116
|
+
var publicKey = ecdsa.toPublicKey();
|
|
117
|
+
|
|
118
|
+
var signatureAddress = Address.fromPublicKey(publicKey, bitcoinAddress.network);
|
|
119
|
+
|
|
120
|
+
// check that the recovered address and specified address match
|
|
121
|
+
if (bitcoinAddress.toString() !== signatureAddress.toString()) {
|
|
122
|
+
this.error = 'The signature did not match the message digest';
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return this._verify(publicKey, signature);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Instantiate a message from a message string
|
|
131
|
+
*
|
|
132
|
+
* @param {String} str - A string of the message
|
|
133
|
+
* @returns {Message} A new instance of a Message
|
|
134
|
+
*/
|
|
135
|
+
Message.fromString = function (str) {
|
|
136
|
+
return new Message(str);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Instantiate a message from JSON
|
|
141
|
+
*
|
|
142
|
+
* @param {String} json - An JSON string or Object with keys: message
|
|
143
|
+
* @returns {Message} A new instance of a Message
|
|
144
|
+
*/
|
|
145
|
+
Message.fromJSON = function fromJSON(json) {
|
|
146
|
+
if (JSUtil.isValidJSON(json)) {
|
|
147
|
+
json = JSON.parse(json);
|
|
148
|
+
}
|
|
149
|
+
return Message.fromObject(json);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @returns {Object} A plain object with the message information
|
|
154
|
+
*/
|
|
155
|
+
Message.prototype.toObject = function toObject() {
|
|
156
|
+
return {
|
|
157
|
+
messageHex: this.messageBuffer.toString('hex'),
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
Message.fromObject = function (obj) {
|
|
162
|
+
let messageBuffer = Buffer.from(obj.messageHex, 'hex');
|
|
163
|
+
return new Message(messageBuffer);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @returns {String} A JSON representation of the message information
|
|
168
|
+
*/
|
|
169
|
+
Message.prototype.toJSON = function toJSON() {
|
|
170
|
+
return JSON.stringify(this.toObject());
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Will return a the string representation of the message
|
|
175
|
+
*
|
|
176
|
+
* @returns {String} Message
|
|
177
|
+
*/
|
|
178
|
+
Message.prototype.toString = function () {
|
|
179
|
+
return this.messageBuffer.toString();
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Will return a string formatted for the console
|
|
184
|
+
*
|
|
185
|
+
* @returns {String} Message
|
|
186
|
+
*/
|
|
187
|
+
Message.prototype.inspect = function () {
|
|
188
|
+
return '<Message: ' + this.toString() + '>';
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
module.exports = Message;
|