@opcat-labs/opcat 1.0.1 → 1.0.2
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/.cjs2esm.json +18 -0
- package/.mocharc.yaml +1 -1
- package/CHANGELOG.md +6 -0
- package/README.md +6 -0
- package/{lib/address.js → cjs/address.cjs} +77 -72
- package/cjs/block/block.cjs +332 -0
- package/{lib/block/blockheader.js → cjs/block/blockheader.cjs} +8 -7
- package/cjs/block/index.cjs +2 -0
- package/{lib/block/merkleblock.js → cjs/block/merkleblock.cjs} +23 -15
- package/cjs/bn.cjs +3411 -0
- package/{lib/crypto/bn.js → cjs/crypto/bn.cjs} +3 -3
- package/{lib/crypto/ecdsa.js → cjs/crypto/ecdsa.cjs} +150 -14
- package/{lib/crypto/hash.node.js → cjs/crypto/hash.cjs} +13 -2
- package/cjs/crypto/index.cjs +16 -0
- package/{lib/crypto/point.js → cjs/crypto/point.cjs} +11 -4
- package/cjs/crypto/random.cjs +18 -0
- package/{lib/crypto/signature.js → cjs/crypto/signature.cjs} +158 -8
- package/{lib/encoding/base58.js → cjs/encoding/base58.cjs} +58 -2
- package/cjs/encoding/base58check.cjs +192 -0
- package/cjs/encoding/bufferreader.cjs +333 -0
- package/cjs/encoding/bufferwriter.cjs +244 -0
- package/{lib/encoding/decode-asm.js → cjs/encoding/decode-asm.cjs} +4 -4
- package/{lib/encoding/decode-hex.js → cjs/encoding/decode-hex.cjs} +1 -1
- package/cjs/encoding/index.cjs +14 -0
- package/cjs/encoding/varint.cjs +116 -0
- package/{lib/errors/index.js → cjs/errors/index.cjs} +9 -9
- package/{lib/errors/spec.js → cjs/errors/spec.cjs} +2 -2
- package/cjs/hash-cache.cjs +98 -0
- package/{lib/hdprivatekey.js → cjs/hdprivatekey.cjs} +232 -140
- package/{lib/hdpublickey.js → cjs/hdpublickey.cjs} +120 -93
- package/cjs/index.cjs +94 -0
- package/cjs/interpreter/index.cjs +2 -0
- package/cjs/interpreter/interpreter.cjs +1988 -0
- package/{lib/script/stack.js → cjs/interpreter/stack.cjs} +9 -2
- package/{lib/message/message.js → cjs/message/message.cjs} +62 -25
- package/cjs/mnemonic/index.cjs +3 -0
- package/{lib/mnemonic/mnemonic.js → cjs/mnemonic/mnemonic.cjs} +44 -13
- package/{lib/mnemonic/pbkdf2.node.js → cjs/mnemonic/pbkdf2.cjs} +9 -2
- package/cjs/mnemonic/words/index.cjs +66 -0
- package/cjs/network.cjs +13 -0
- package/cjs/networks.cjs +321 -0
- package/{lib/opcode.js → cjs/opcode.cjs} +69 -5
- package/cjs/privatekey.cjs +422 -0
- package/{lib/publickey.js → cjs/publickey.cjs} +14 -16
- package/cjs/script/index.cjs +2 -0
- package/{lib/script/script.js → cjs/script/script.cjs} +322 -67
- package/cjs/transaction/index.cjs +5 -0
- package/cjs/transaction/input/index.cjs +34 -0
- package/cjs/transaction/input/input.cjs +396 -0
- package/{lib/transaction/input/multisig.js → cjs/transaction/input/multisig.cjs} +112 -18
- package/{lib/transaction/input/publickey.js → cjs/transaction/input/publickey.cjs} +29 -19
- package/{lib/transaction/input/publickeyhash.js → cjs/transaction/input/publickeyhash.cjs} +25 -17
- package/{lib/transaction/output.js → cjs/transaction/output.cjs} +100 -15
- package/cjs/transaction/sighash.cjs +187 -0
- package/{lib/transaction/signature.js → cjs/transaction/signature.cjs} +30 -6
- package/cjs/transaction/transaction.cjs +2000 -0
- package/{lib/transaction/unspentoutput.js → cjs/transaction/unspentoutput.cjs} +5 -5
- package/cjs/util/derivation.cjs +53 -0
- package/cjs/util/index.cjs +11 -0
- package/cjs/util/js.cjs +95 -0
- package/{lib/util/preconditions.js → cjs/util/preconditions.cjs} +2 -2
- package/esm/address.js +483 -0
- package/{lib → esm}/block/block.js +82 -27
- package/esm/block/blockheader.js +296 -0
- package/esm/block/index.js +2 -0
- package/esm/block/merkleblock.js +331 -0
- package/esm/bn.js +3411 -0
- package/esm/crypto/bn.js +278 -0
- package/esm/crypto/ecdsa.js +475 -0
- package/{lib/crypto/hash.browser.js → esm/crypto/hash.js} +18 -7
- package/esm/crypto/index.js +16 -0
- package/esm/crypto/point.js +228 -0
- package/esm/crypto/random.js +18 -0
- package/esm/crypto/signature.js +475 -0
- package/esm/encoding/base58.js +167 -0
- package/esm/encoding/base58check.js +192 -0
- package/esm/encoding/bufferreader.js +333 -0
- package/esm/encoding/bufferwriter.js +243 -0
- package/esm/encoding/decode-asm.js +24 -0
- package/esm/encoding/decode-hex.js +32 -0
- package/esm/encoding/decode-script-chunks.js +43 -0
- package/esm/encoding/encode-hex.js +284 -0
- package/esm/encoding/index.js +14 -0
- package/esm/encoding/is-hex.js +7 -0
- package/esm/encoding/varint.js +116 -0
- package/esm/errors/index.js +54 -0
- package/esm/errors/spec.js +314 -0
- package/esm/hash-cache.js +98 -0
- package/esm/hdprivatekey.js +768 -0
- package/esm/hdpublickey.js +549 -0
- package/esm/index.js +66 -0
- package/esm/interpreter/index.js +2 -0
- package/{lib/script → esm/interpreter}/interpreter.js +219 -66
- package/esm/interpreter/stack.js +116 -0
- package/esm/message/message.js +228 -0
- package/esm/mnemonic/index.js +3 -0
- package/esm/mnemonic/mnemonic.js +332 -0
- package/{lib/mnemonic/pbkdf2.browser.js → esm/mnemonic/pbkdf2.js} +13 -6
- package/esm/mnemonic/words/chinese.js +2054 -0
- package/esm/mnemonic/words/english.js +2054 -0
- package/esm/mnemonic/words/french.js +2054 -0
- package/esm/mnemonic/words/index.js +66 -0
- package/esm/mnemonic/words/italian.js +2054 -0
- package/esm/mnemonic/words/japanese.js +2054 -0
- package/esm/mnemonic/words/spanish.js +2054 -0
- package/esm/network.js +13 -0
- package/{lib → esm}/networks.js +61 -120
- package/esm/opcode.js +319 -0
- package/{lib → esm}/privatekey.js +76 -28
- package/esm/publickey.js +384 -0
- package/esm/script/index.js +2 -0
- package/esm/script/script.js +1329 -0
- package/esm/script/write-i32-le.js +17 -0
- package/esm/script/write-push-data.js +35 -0
- package/esm/script/write-u16-le.js +12 -0
- package/esm/script/write-u32-le.js +16 -0
- package/esm/script/write-u64-le.js +24 -0
- package/esm/script/write-u8-le.js +8 -0
- package/esm/script/write-varint.js +46 -0
- package/esm/transaction/index.js +5 -0
- package/esm/transaction/input/index.js +33 -0
- package/{lib → esm}/transaction/input/input.js +132 -90
- package/esm/transaction/input/multisig.js +335 -0
- package/esm/transaction/input/publickey.js +108 -0
- package/esm/transaction/input/publickeyhash.js +124 -0
- package/esm/transaction/output.js +316 -0
- package/{lib → esm}/transaction/sighash.js +42 -22
- package/esm/transaction/signature.js +120 -0
- package/{lib → esm}/transaction/transaction.js +522 -163
- package/esm/transaction/unspentoutput.js +112 -0
- package/esm/util/_.js +47 -0
- package/esm/util/derivation.js +53 -0
- package/esm/util/index.js +12 -0
- package/esm/util/js.js +95 -0
- package/esm/util/preconditions.js +33 -0
- package/fixup.cjs +17 -0
- package/package.json +18 -4
- package/test/{address.js → address.cjs} +14 -43
- package/test/block/{block.js → block.cjs} +3 -5
- package/test/block/{blockheader.js → blockheader.cjs} +2 -2
- package/test/block/{merklebloack.js → merklebloack.cjs} +2 -2
- package/test/crypto/{ecdsa.js → ecdsa.cjs} +9 -9
- package/test/crypto/{hash.browser.js → hash.browser.cjs} +2 -1
- package/test/crypto/{signature.js → signature.cjs} +2 -2
- package/test/data/bitcoind/script_tests.json +5 -5
- package/test/{hashCache.js → hashCache.cjs} +2 -1
- package/test/{hdkeys.js → hdkeys.cjs} +4 -2
- package/test/{hdprivatekey.js → hdprivatekey.cjs} +7 -6
- package/test/{hdpublickey.js → hdpublickey.cjs} +2 -7
- package/test/mnemonic/{pbkdf2.test.js → pbkdf2.test.cjs} +2 -2
- package/test/{networks.js → networks.cjs} +12 -31
- package/test/{publickey.js → publickey.cjs} +2 -2
- package/test/script/{interpreter.js → interpreter.cjs} +5 -5
- package/test/script/{script.js → script.cjs} +8 -2
- package/test/transaction/{deserialize.js → deserialize.cjs} +2 -2
- package/test/transaction/input/{input.js → input.cjs} +1 -1
- package/test/transaction/input/{multisig.js → multisig.cjs} +2 -1
- package/test/transaction/input/{publickeyhash.js → publickeyhash.cjs} +1 -1
- package/test/transaction/{sighash.js → sighash.cjs} +1 -1
- package/test/transaction/{transaction.js → transaction.cjs} +2 -2
- package/tsconfig.json +13 -0
- package/types/address.d.cts +252 -0
- package/types/block/block.d.cts +139 -0
- package/types/block/blockheader.d.cts +125 -0
- package/types/block/index.d.cts +2 -0
- package/types/block/merkleblock.d.cts +95 -0
- package/types/bn.d.cts +202 -0
- package/types/crypto/bn.d.cts +2 -0
- package/types/crypto/ecdsa.d.cts +187 -0
- package/types/crypto/hash.d.cts +118 -0
- package/types/crypto/index.d.cts +7 -0
- package/types/crypto/point.d.cts +134 -0
- package/types/crypto/random.d.cts +13 -0
- package/types/crypto/signature.d.cts +160 -0
- package/types/encoding/base58.d.cts +106 -0
- package/types/encoding/base58check.d.cts +107 -0
- package/types/encoding/bufferreader.d.cts +164 -0
- package/types/encoding/bufferwriter.d.cts +126 -0
- package/types/encoding/decode-asm.d.cts +2 -0
- package/types/encoding/decode-hex.d.cts +2 -0
- package/types/encoding/decode-script-chunks.d.cts +14 -0
- package/types/encoding/encode-hex.d.cts +2 -0
- package/types/encoding/index.d.cts +6 -0
- package/types/encoding/is-hex.d.cts +2 -0
- package/types/encoding/varint.d.cts +66 -0
- package/types/errors/index.d.cts +4 -0
- package/types/errors/spec.d.cts +22 -0
- package/types/hash-cache.d.cts +65 -0
- package/types/hdprivatekey.d.cts +281 -0
- package/types/hdpublickey.d.cts +240 -0
- package/types/index.d.cts +26 -0
- package/types/interpreter/index.d.cts +2 -0
- package/types/interpreter/interpreter.d.cts +228 -0
- package/types/interpreter/stack.d.cts +35 -0
- package/types/message/message.d.cts +110 -0
- package/types/mnemonic/index.d.cts +2 -0
- package/types/mnemonic/mnemonic.d.cts +171 -0
- package/types/mnemonic/pbkdf2.d.cts +14 -0
- package/types/mnemonic/words/chinese.d.cts +2 -0
- package/types/mnemonic/words/english.d.cts +2 -0
- package/types/mnemonic/words/french.d.cts +2 -0
- package/types/mnemonic/words/index.d.cts +22 -0
- package/types/mnemonic/words/italian.d.cts +2 -0
- package/types/mnemonic/words/japanese.d.cts +2 -0
- package/types/mnemonic/words/spanish.d.cts +2 -0
- package/types/network.d.cts +11 -0
- package/types/networks.d.cts +76 -0
- package/types/opcode.d.cts +93 -0
- package/types/privatekey.d.cts +169 -0
- package/types/publickey.d.cts +202 -0
- package/types/script/index.d.cts +2 -0
- package/types/script/script.d.cts +449 -0
- package/types/script/write-i32-le.d.cts +2 -0
- package/types/script/write-push-data.d.cts +2 -0
- package/types/script/write-u16-le.d.cts +2 -0
- package/types/script/write-u32-le.d.cts +2 -0
- package/types/script/write-u64-le.d.cts +2 -0
- package/types/script/write-u8-le.d.cts +2 -0
- package/types/script/write-varint.d.cts +2 -0
- package/types/transaction/index.d.cts +2 -0
- package/types/transaction/input/index.d.cts +2 -0
- package/types/transaction/input/input.d.cts +178 -0
- package/types/transaction/input/multisig.d.cts +127 -0
- package/types/transaction/input/publickey.d.cts +44 -0
- package/types/transaction/input/publickeyhash.d.cts +45 -0
- package/types/transaction/output.d.cts +118 -0
- package/types/transaction/sighash.d.cts +61 -0
- package/types/transaction/signature.d.cts +43 -0
- package/types/transaction/transaction.d.cts +716 -0
- package/types/transaction/unspentoutput.d.cts +83 -0
- package/types/util/_.d.cts +26 -0
- package/types/util/derivation.d.cts +21 -0
- package/types/util/index.d.cts +5 -0
- package/types/util/js.d.cts +50 -0
- package/types/util/preconditions.d.cts +3 -0
- package/index.d.ts +0 -1541
- package/index.js +0 -74
- package/lib/block/index.js +0 -4
- package/lib/bn.js +0 -3423
- package/lib/crypto/hash.js +0 -2
- package/lib/crypto/random.browser.js +0 -28
- package/lib/crypto/random.js +0 -2
- package/lib/crypto/random.node.js +0 -11
- package/lib/encoding/base58check.js +0 -121
- package/lib/encoding/bufferreader.js +0 -212
- package/lib/encoding/bufferwriter.js +0 -140
- package/lib/encoding/varint.js +0 -75
- package/lib/hash-cache.js +0 -50
- package/lib/mnemonic/pbkdf2.js +0 -2
- package/lib/mnemonic/words/index.js +0 -8
- package/lib/script/index.js +0 -5
- package/lib/transaction/index.js +0 -7
- package/lib/transaction/input/index.js +0 -5
- package/lib/util/js.js +0 -90
- /package/{lib/encoding/decode-script-chunks.js → cjs/encoding/decode-script-chunks.cjs} +0 -0
- /package/{lib/encoding/encode-hex.js → cjs/encoding/encode-hex.cjs} +0 -0
- /package/{lib/encoding/is-hex.js → cjs/encoding/is-hex.cjs} +0 -0
- /package/{lib/mnemonic/words/chinese.js → cjs/mnemonic/words/chinese.cjs} +0 -0
- /package/{lib/mnemonic/words/english.js → cjs/mnemonic/words/english.cjs} +0 -0
- /package/{lib/mnemonic/words/french.js → cjs/mnemonic/words/french.cjs} +0 -0
- /package/{lib/mnemonic/words/italian.js → cjs/mnemonic/words/italian.cjs} +0 -0
- /package/{lib/mnemonic/words/japanese.js → cjs/mnemonic/words/japanese.cjs} +0 -0
- /package/{lib/mnemonic/words/spanish.js → cjs/mnemonic/words/spanish.cjs} +0 -0
- /package/{lib/script/write-i32-le.js → cjs/script/write-i32-le.cjs} +0 -0
- /package/{lib/script/write-push-data.js → cjs/script/write-push-data.cjs} +0 -0
- /package/{lib/script/write-u16-le.js → cjs/script/write-u16-le.cjs} +0 -0
- /package/{lib/script/write-u32-le.js → cjs/script/write-u32-le.cjs} +0 -0
- /package/{lib/script/write-u64-le.js → cjs/script/write-u64-le.cjs} +0 -0
- /package/{lib/script/write-u8-le.js → cjs/script/write-u8-le.cjs} +0 -0
- /package/{lib/script/write-varint.js → cjs/script/write-varint.cjs} +0 -0
- /package/{lib/util/_.js → cjs/util/_.cjs} +0 -0
- /package/test/crypto/{bn.js → bn.cjs} +0 -0
- /package/test/crypto/{hash.js → hash.cjs} +0 -0
- /package/test/crypto/{point.js → point.cjs} +0 -0
- /package/test/crypto/{random.js → random.cjs} +0 -0
- /package/test/data/{blk86756-testnet.js → blk86756-testnet.cjs} +0 -0
- /package/test/data/{merkleblocks.js → merkleblocks.cjs} +0 -0
- /package/test/encoding/{base58.js → base58.cjs} +0 -0
- /package/test/encoding/{base58check.js → base58check.cjs} +0 -0
- /package/test/encoding/{bufferreader.js → bufferreader.cjs} +0 -0
- /package/test/encoding/{bufferwriter.js → bufferwriter.cjs} +0 -0
- /package/test/encoding/{varint.js → varint.cjs} +0 -0
- /package/test/{index.js → index.cjs} +0 -0
- /package/test/message/{message.js → message.cjs} +0 -0
- /package/test/mnemonic/{mnemonic.js → mnemonic.cjs} +0 -0
- /package/test/{opcode.js → opcode.cjs} +0 -0
- /package/test/{privatekey.js → privatekey.cjs} +0 -0
- /package/test/transaction/input/{publickey.js → publickey.cjs} +0 -0
- /package/test/transaction/{output.js → output.cjs} +0 -0
- /package/test/transaction/{signature.js → signature.cjs} +0 -0
- /package/test/transaction/{unspentoutput.js → unspentoutput.cjs} +0 -0
- /package/test/util/{js.js → js.cjs} +0 -0
- /package/test/util/{preconditions.js → preconditions.cjs} +0 -0
|
@@ -0,0 +1,1329 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import Address from '../address.js';
|
|
4
|
+
import BufferWriter from '../encoding/bufferwriter.js';
|
|
5
|
+
import Hash from '../crypto/hash.js';
|
|
6
|
+
import Opcode from '../opcode.js';
|
|
7
|
+
import PublicKey from '../publickey.js';
|
|
8
|
+
import Signature from '../crypto/signature.js';
|
|
9
|
+
import Networks from '../networks.js';
|
|
10
|
+
import $ from '../util/preconditions.js';
|
|
11
|
+
import _ from '../util/_.js';
|
|
12
|
+
import errors from '../errors/index.js';
|
|
13
|
+
import JSUtil from '../util/js.js';
|
|
14
|
+
import decodeScriptChunks from '../encoding/decode-script-chunks.js';
|
|
15
|
+
import decodeASM from '../encoding/decode-asm.js';
|
|
16
|
+
import encodeHex from '../encoding/encode-hex.js';
|
|
17
|
+
|
|
18
|
+
// These WeakMap caches allow the objects themselves to maintain their immutability
|
|
19
|
+
const SCRIPT_TO_CHUNKS_CACHE = new WeakMap();
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A bitcoin transaction script. Each transaction's inputs and outputs
|
|
23
|
+
* has a script that is evaluated to validate it's spending.
|
|
24
|
+
*
|
|
25
|
+
* See https://en.bitcoin.it/wiki/Script
|
|
26
|
+
*
|
|
27
|
+
* @constructor
|
|
28
|
+
* @param {Object|string|Buffer} [from] optional data to populate script
|
|
29
|
+
*/
|
|
30
|
+
function Script(from) {
|
|
31
|
+
if (!(this instanceof Script)) {
|
|
32
|
+
return new Script(from);
|
|
33
|
+
}
|
|
34
|
+
this.buffer = Buffer.from([]);
|
|
35
|
+
|
|
36
|
+
if (Buffer.isBuffer(from)) {
|
|
37
|
+
return Script.fromBuffer(from);
|
|
38
|
+
} else if (from instanceof Address) {
|
|
39
|
+
return Script.fromAddress(from);
|
|
40
|
+
} else if (from instanceof Script) {
|
|
41
|
+
return Script.fromBuffer(from.toBuffer());
|
|
42
|
+
} else if (_.isString(from)) {
|
|
43
|
+
return Script.fromString(from);
|
|
44
|
+
} else if (_.isObject(from) && _.isArray(from.chunks)) {
|
|
45
|
+
return Script.fromChunks(from.chunks);
|
|
46
|
+
} else if (_.isObject(from) && Buffer.isBuffer(from.buffer)) {
|
|
47
|
+
return Script.fromBuffer(from.buffer);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Sets the script content from an object.
|
|
53
|
+
* @param {Object} obj - The source object containing either chunks array or buffer.
|
|
54
|
+
* @param {Array} [obj.chunks] - Optional array of chunks to create script from.
|
|
55
|
+
* @param {Buffer} [obj.buffer] - Optional buffer containing script data.
|
|
56
|
+
* @returns {Script} Returns the script instance for chaining.
|
|
57
|
+
* @throws Will throw if argument is invalid (not object or missing required buffer).
|
|
58
|
+
*/
|
|
59
|
+
Script.prototype.set = function (obj) {
|
|
60
|
+
$.checkArgument(_.isObject(obj));
|
|
61
|
+
if (obj.chunks && _.isArray(obj.chunks)) {
|
|
62
|
+
var s = Script.fromChunks(obj.chunks);
|
|
63
|
+
this.buffer = s.buffer;
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
$.checkArgument(Buffer.isBuffer(obj.buffer));
|
|
68
|
+
this.buffer = obj.buffer;
|
|
69
|
+
return this;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a Script instance from a Buffer.
|
|
74
|
+
* @param {Buffer} buffer - The buffer containing the script data.
|
|
75
|
+
* @returns {Script} A new Script instance with the provided buffer.
|
|
76
|
+
* @throws {Error} Throws if the input is not a Buffer.
|
|
77
|
+
*/
|
|
78
|
+
Script.fromBuffer = function (buffer) {
|
|
79
|
+
$.checkArgument(Buffer.isBuffer(buffer));
|
|
80
|
+
var script = new Script();
|
|
81
|
+
script.buffer = buffer;
|
|
82
|
+
return script;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Creates a Script instance from an array of opcode chunks.
|
|
87
|
+
* Handles different pushdata opcodes (OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4)
|
|
88
|
+
* by writing appropriate length prefixes before the buffer data.
|
|
89
|
+
* @param {Array} chunks - Array of opcode chunks containing opcodenum and optional buf/len
|
|
90
|
+
* @returns {Script} A new Script instance with compiled buffer
|
|
91
|
+
*/
|
|
92
|
+
Script.fromChunks = function (chunks) {
|
|
93
|
+
var script = new Script();
|
|
94
|
+
|
|
95
|
+
const bw = new BufferWriter();
|
|
96
|
+
|
|
97
|
+
for (let index = 0; index < chunks.length; index++) {
|
|
98
|
+
const chunk = chunks[index];
|
|
99
|
+
bw.writeUInt8(chunk.opcodenum);
|
|
100
|
+
if (chunk.buf) {
|
|
101
|
+
if (chunk.opcodenum < Opcode.OP_PUSHDATA1) {
|
|
102
|
+
bw.write(chunk.buf);
|
|
103
|
+
} else if (chunk.opcodenum === Opcode.OP_PUSHDATA1) {
|
|
104
|
+
bw.writeUInt8(chunk.len);
|
|
105
|
+
bw.write(chunk.buf);
|
|
106
|
+
} else if (chunk.opcodenum === Opcode.OP_PUSHDATA2) {
|
|
107
|
+
bw.writeUInt16LE(chunk.len);
|
|
108
|
+
bw.write(chunk.buf);
|
|
109
|
+
} else if (chunk.opcodenum === Opcode.OP_PUSHDATA4) {
|
|
110
|
+
bw.writeUInt32LE(chunk.len);
|
|
111
|
+
bw.write(chunk.buf);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
script.buffer = bw.toBuffer();
|
|
117
|
+
return script;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Returns the underlying buffer of the script.
|
|
122
|
+
* @returns {Buffer} The script's buffer data.
|
|
123
|
+
*/
|
|
124
|
+
Script.prototype.toBuffer = function () {
|
|
125
|
+
return this.buffer;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Creates a Script instance from ASM (Assembly) formatted string.
|
|
130
|
+
* @param {string} str - ASM formatted string to decode
|
|
131
|
+
* @returns {Script} Script instance created from decoded ASM
|
|
132
|
+
*/
|
|
133
|
+
Script.fromASM = function (str) {
|
|
134
|
+
return Script.fromBuffer(decodeASM(str));
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Creates a Script instance from a hex string.
|
|
139
|
+
* @param {string} str - Hex string to convert to Script.
|
|
140
|
+
* @returns {Script} New Script instance created from the hex string.
|
|
141
|
+
*/
|
|
142
|
+
Script.fromHex = function (str) {
|
|
143
|
+
return new Script(Buffer.from(str, 'hex'));
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Converts a string representation of a script into a Script object.
|
|
148
|
+
* Handles hex strings, empty strings, and space-separated opcode tokens.
|
|
149
|
+
* For pushdata operations (OP_PUSHDATA1/2/4), validates format and length.
|
|
150
|
+
* Throws errors for invalid script formats or data lengths.
|
|
151
|
+
* @param {string} str - The script string to parse (hex or opcode tokens)
|
|
152
|
+
* @returns {Script} The constructed Script object
|
|
153
|
+
* @throws {Error} When script format is invalid or data lengths don't match
|
|
154
|
+
*/
|
|
155
|
+
Script.fromString = function (str) {
|
|
156
|
+
if (JSUtil.isHexa(str) || str.length === 0) {
|
|
157
|
+
return new Script(Buffer.from(str, 'hex'));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
var chunks = [];
|
|
161
|
+
|
|
162
|
+
var tokens = str.split(' ');
|
|
163
|
+
var i = 0;
|
|
164
|
+
while (i < tokens.length) {
|
|
165
|
+
var token = tokens[i];
|
|
166
|
+
var opcode = Opcode(token);
|
|
167
|
+
var opcodenum = opcode.toNumber();
|
|
168
|
+
|
|
169
|
+
if (_.isUndefined(opcodenum)) {
|
|
170
|
+
opcodenum = parseInt(token);
|
|
171
|
+
if (opcodenum > 0 && opcodenum < Opcode.OP_PUSHDATA1) {
|
|
172
|
+
var buf = Buffer.from(tokens[i + 1].slice(2), 'hex');
|
|
173
|
+
if (buf.length !== opcodenum) {
|
|
174
|
+
throw new Error('Invalid script buf len: ' + JSON.stringify(str));
|
|
175
|
+
}
|
|
176
|
+
chunks.push({
|
|
177
|
+
buf: Buffer.from(tokens[i + 1].slice(2), 'hex'),
|
|
178
|
+
len: opcodenum,
|
|
179
|
+
opcodenum: opcodenum,
|
|
180
|
+
});
|
|
181
|
+
i = i + 2;
|
|
182
|
+
} else {
|
|
183
|
+
throw new Error('Invalid script: ' + JSON.stringify(str));
|
|
184
|
+
}
|
|
185
|
+
} else if (
|
|
186
|
+
opcodenum === Opcode.OP_PUSHDATA1 ||
|
|
187
|
+
opcodenum === Opcode.OP_PUSHDATA2 ||
|
|
188
|
+
opcodenum === Opcode.OP_PUSHDATA4
|
|
189
|
+
) {
|
|
190
|
+
if (tokens[i + 2].slice(0, 2) !== '0x') {
|
|
191
|
+
throw new Error('Pushdata data must start with 0x');
|
|
192
|
+
}
|
|
193
|
+
chunks.push({
|
|
194
|
+
buf: Buffer.from(tokens[i + 2].slice(2), 'hex'),
|
|
195
|
+
len: parseInt(tokens[i + 1]),
|
|
196
|
+
opcodenum: opcodenum,
|
|
197
|
+
});
|
|
198
|
+
i = i + 3;
|
|
199
|
+
} else {
|
|
200
|
+
chunks.push({
|
|
201
|
+
opcodenum: opcodenum,
|
|
202
|
+
});
|
|
203
|
+
i = i + 1;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return Script.fromChunks(chunks);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Gets a portion of the script's buffer as a new buffer.
|
|
211
|
+
* @param {number} [start] - The beginning index of the specified portion of the buffer.
|
|
212
|
+
* @param {number} [end] - The end index of the specified portion of the buffer.
|
|
213
|
+
* @returns {Buffer} A new Buffer that contains the specified portion of the original buffer.
|
|
214
|
+
*/
|
|
215
|
+
Script.prototype.slice = function (start, end) {
|
|
216
|
+
return this.buffer.slice(start, end);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Gets the chunks associated with the Script instance.
|
|
221
|
+
* @memberof Script.prototype
|
|
222
|
+
* @name chunks
|
|
223
|
+
* @type {Array}
|
|
224
|
+
*/
|
|
225
|
+
Object.defineProperty(Script.prototype, 'chunks', {
|
|
226
|
+
get() {
|
|
227
|
+
if (SCRIPT_TO_CHUNKS_CACHE.has(this)) return SCRIPT_TO_CHUNKS_CACHE.get(this);
|
|
228
|
+
const chunks = decodeScriptChunks(this.buffer);
|
|
229
|
+
SCRIPT_TO_CHUNKS_CACHE.set(this, chunks);
|
|
230
|
+
return chunks;
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Gets the length of the script in bytes.
|
|
236
|
+
* @memberof Script.prototype
|
|
237
|
+
* @name length
|
|
238
|
+
* @type {number}
|
|
239
|
+
*/
|
|
240
|
+
Object.defineProperty(Script.prototype, 'length', {
|
|
241
|
+
get() {
|
|
242
|
+
return this.buffer.length;
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Converts a script chunk to a string representation based on the given type.
|
|
248
|
+
* Handles both data chunks and opcode chunks, with special formatting for ASM output.
|
|
249
|
+
*
|
|
250
|
+
* @param {Object} chunk - The script chunk to convert, containing opcodenum and optional buf/len
|
|
251
|
+
* @param {string} type - The output type ('asm' or other)
|
|
252
|
+
* @returns {string} The formatted string representation of the chunk
|
|
253
|
+
* @private
|
|
254
|
+
*/
|
|
255
|
+
Script.prototype._chunkToString = function (chunk, type) {
|
|
256
|
+
var opcodenum = chunk.opcodenum;
|
|
257
|
+
var asm = type === 'asm';
|
|
258
|
+
var str = '';
|
|
259
|
+
if (!chunk.buf) {
|
|
260
|
+
// no data chunk
|
|
261
|
+
if (typeof Opcode.reverseMap[opcodenum] !== 'undefined') {
|
|
262
|
+
if (asm) {
|
|
263
|
+
// A few cases where the opcode name differs from reverseMap
|
|
264
|
+
// aside from 1 to 16 data pushes.
|
|
265
|
+
if (opcodenum === 0) {
|
|
266
|
+
// OP_0 -> 0
|
|
267
|
+
str = str + ' 0';
|
|
268
|
+
} else if (opcodenum === 79) {
|
|
269
|
+
// OP_1NEGATE -> 1
|
|
270
|
+
str = str + ' -1';
|
|
271
|
+
} else {
|
|
272
|
+
str = str + ' ' + Opcode(opcodenum).toString();
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
str = str + ' ' + Opcode(opcodenum).toString();
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
var numstr = opcodenum.toString(16);
|
|
279
|
+
if (numstr.length % 2 !== 0) {
|
|
280
|
+
numstr = '0' + numstr;
|
|
281
|
+
}
|
|
282
|
+
if (asm) {
|
|
283
|
+
str = str + ' ' + numstr;
|
|
284
|
+
} else {
|
|
285
|
+
str = str + ' ' + '0x' + numstr;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
// data chunk
|
|
290
|
+
if (
|
|
291
|
+
!asm &&
|
|
292
|
+
(opcodenum === Opcode.OP_PUSHDATA1 ||
|
|
293
|
+
opcodenum === Opcode.OP_PUSHDATA2 ||
|
|
294
|
+
opcodenum === Opcode.OP_PUSHDATA4)
|
|
295
|
+
) {
|
|
296
|
+
str = str + ' ' + Opcode(opcodenum).toString();
|
|
297
|
+
}
|
|
298
|
+
if (chunk.len > 0) {
|
|
299
|
+
if (asm) {
|
|
300
|
+
str = str + ' ' + chunk.buf.toString('hex');
|
|
301
|
+
} else {
|
|
302
|
+
str = str + ' ' + chunk.len + ' ' + '0x' + chunk.buf.toString('hex');
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return str;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Converts the script chunks to ASM (Assembly) format string representation.
|
|
311
|
+
* Iterates through each chunk and appends its ASM string representation.
|
|
312
|
+
* @returns {string} The ASM formatted string (excluding the first character).
|
|
313
|
+
*/
|
|
314
|
+
Script.prototype.toASM = function () {
|
|
315
|
+
var str = '';
|
|
316
|
+
var chunks = this.chunks;
|
|
317
|
+
for (var i = 0; i < chunks.length; i++) {
|
|
318
|
+
var chunk = this.chunks[i];
|
|
319
|
+
str += this._chunkToString(chunk, 'asm');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return str.substr(1);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Converts the script's chunks to a string representation.
|
|
327
|
+
* Iterates through each chunk and appends its string representation,
|
|
328
|
+
* then removes the leading character from the result.
|
|
329
|
+
* @returns {string} The concatenated string of all chunks.
|
|
330
|
+
*/
|
|
331
|
+
Script.prototype.toString = function () {
|
|
332
|
+
var str = '';
|
|
333
|
+
for (var i = 0; i < this.chunks.length; i++) {
|
|
334
|
+
var chunk = this.chunks[i];
|
|
335
|
+
str += this._chunkToString(chunk);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return str.substr(1);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Converts the script's buffer to a hexadecimal string.
|
|
343
|
+
* @returns {string} Hex-encoded representation of the script buffer.
|
|
344
|
+
*/
|
|
345
|
+
Script.prototype.toHex = function () {
|
|
346
|
+
return encodeHex(this.buffer);
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Custom inspect method for Script instances.
|
|
351
|
+
* @returns {string} String representation of the Script object in format '<Script: [content]>'.
|
|
352
|
+
*/
|
|
353
|
+
Script.prototype.inspect = function () {
|
|
354
|
+
return '<Script: ' + this.toString() + '>';
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
// script classification methods
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Checks if the script is a standard public key hash output script (P2PKH).
|
|
362
|
+
* @returns {boolean} True if the script matches the P2PKH pattern:
|
|
363
|
+
* - OP_DUP
|
|
364
|
+
* - OP_HASH160
|
|
365
|
+
* - 20-byte hash
|
|
366
|
+
* - OP_EQUALVERIFY
|
|
367
|
+
* - OP_CHECKSIG
|
|
368
|
+
*/
|
|
369
|
+
Script.prototype.isPublicKeyHashOut = function () {
|
|
370
|
+
return !!(
|
|
371
|
+
this.chunks.length === 5 &&
|
|
372
|
+
this.chunks[0].opcodenum === Opcode.OP_DUP &&
|
|
373
|
+
this.chunks[1].opcodenum === Opcode.OP_HASH160 &&
|
|
374
|
+
this.chunks[2].buf &&
|
|
375
|
+
this.chunks[2].buf.length === 20 &&
|
|
376
|
+
this.chunks[3].opcodenum === Opcode.OP_EQUALVERIFY &&
|
|
377
|
+
this.chunks[4].opcodenum === Opcode.OP_CHECKSIG
|
|
378
|
+
);
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Checks if the script contains a valid public key hash.
|
|
384
|
+
* @returns {boolean} True if the script has exactly 2 chunks (signature and public key),
|
|
385
|
+
* the signature starts with 0x30, and the public key has a valid version
|
|
386
|
+
* and length (65 bytes for versions 0x04/0x06/0x07, 33 bytes for 0x02/0x03).
|
|
387
|
+
*/
|
|
388
|
+
Script.prototype.isPublicKeyHashIn = function () {
|
|
389
|
+
if (this.chunks.length === 2) {
|
|
390
|
+
var signatureBuf = this.chunks[0].buf;
|
|
391
|
+
var pubkeyBuf = this.chunks[1].buf;
|
|
392
|
+
if (
|
|
393
|
+
signatureBuf &&
|
|
394
|
+
signatureBuf.length &&
|
|
395
|
+
signatureBuf[0] === 0x30 &&
|
|
396
|
+
pubkeyBuf &&
|
|
397
|
+
pubkeyBuf.length
|
|
398
|
+
) {
|
|
399
|
+
var version = pubkeyBuf[0];
|
|
400
|
+
if ((version === 0x04 || version === 0x06 || version === 0x07) && pubkeyBuf.length === 65) {
|
|
401
|
+
return true;
|
|
402
|
+
} else if ((version === 0x03 || version === 0x02) && pubkeyBuf.length === 33) {
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return false;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Gets the public key from a script output.
|
|
412
|
+
* @returns {Buffer} The public key buffer.
|
|
413
|
+
* @throws {Error} If the script is not a public key output.
|
|
414
|
+
*/
|
|
415
|
+
Script.prototype.getPublicKey = function () {
|
|
416
|
+
$.checkState(this.isPublicKeyOut(), "Can't retrieve PublicKey from a non-PK output");
|
|
417
|
+
return this.chunks[0].buf;
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Retrieves the PublicKeyHash from a script output.
|
|
422
|
+
* @returns {Buffer} The PublicKeyHash buffer.
|
|
423
|
+
* @throws {Error} If the script output is not a PublicKeyHash output.
|
|
424
|
+
*/
|
|
425
|
+
Script.prototype.getPublicKeyHash = function () {
|
|
426
|
+
$.checkState(this.isPublicKeyHashOut(), "Can't retrieve PublicKeyHash from a non-PKH output");
|
|
427
|
+
return this.chunks[2].buf;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Checks if the script is a standard public key output script.
|
|
433
|
+
* @returns {boolean} True if the script matches the standard public key output format:
|
|
434
|
+
* - Contains exactly 2 chunks
|
|
435
|
+
* - First chunk is a valid public key buffer (65 bytes for uncompressed, 33 bytes for compressed)
|
|
436
|
+
* - Second chunk is OP_CHECKSIG opcode
|
|
437
|
+
*/
|
|
438
|
+
Script.prototype.isPublicKeyOut = function () {
|
|
439
|
+
if (
|
|
440
|
+
this.chunks.length === 2 &&
|
|
441
|
+
this.chunks[0].buf &&
|
|
442
|
+
this.chunks[0].buf.length &&
|
|
443
|
+
this.chunks[1].opcodenum === Opcode.OP_CHECKSIG
|
|
444
|
+
) {
|
|
445
|
+
var pubkeyBuf = this.chunks[0].buf;
|
|
446
|
+
var version = pubkeyBuf[0];
|
|
447
|
+
var isVersion = false;
|
|
448
|
+
if ((version === 0x04 || version === 0x06 || version === 0x07) && pubkeyBuf.length === 65) {
|
|
449
|
+
isVersion = true;
|
|
450
|
+
} else if ((version === 0x03 || version === 0x02) && pubkeyBuf.length === 33) {
|
|
451
|
+
isVersion = true;
|
|
452
|
+
}
|
|
453
|
+
if (isVersion) {
|
|
454
|
+
return PublicKey.isValid(pubkeyBuf);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return false;
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Checks if the script contains a valid public key signature.
|
|
463
|
+
* @returns {boolean} True if the script has exactly one chunk that starts with 0x30 (DER signature marker), false otherwise.
|
|
464
|
+
*/
|
|
465
|
+
Script.prototype.isPublicKeyIn = function () {
|
|
466
|
+
if (this.chunks.length === 1) {
|
|
467
|
+
var signatureBuf = this.chunks[0].buf;
|
|
468
|
+
if (signatureBuf && signatureBuf.length && signatureBuf[0] === 0x30) {
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return false;
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Checks if the script is a multisig output script.
|
|
478
|
+
* @returns {boolean} True if the script matches the multisig output pattern:
|
|
479
|
+
* - Has more than 3 chunks
|
|
480
|
+
* - First chunk is a small integer opcode
|
|
481
|
+
* - Middle chunks are all buffers
|
|
482
|
+
* - Second-to-last chunk is a small integer opcode
|
|
483
|
+
* - Last chunk is OP_CHECKMULTISIG
|
|
484
|
+
*/
|
|
485
|
+
Script.prototype.isMultisigOut = function () {
|
|
486
|
+
return (
|
|
487
|
+
this.chunks.length > 3 &&
|
|
488
|
+
Opcode.isSmallIntOp(this.chunks[0].opcodenum) &&
|
|
489
|
+
this.chunks.slice(1, this.chunks.length - 2).every(function (obj) {
|
|
490
|
+
return obj.buf && Buffer.isBuffer(obj.buf);
|
|
491
|
+
}) &&
|
|
492
|
+
Opcode.isSmallIntOp(this.chunks[this.chunks.length - 2].opcodenum) &&
|
|
493
|
+
this.chunks[this.chunks.length - 1].opcodenum === Opcode.OP_CHECKMULTISIG
|
|
494
|
+
);
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Decodes a multisig output script into its components.
|
|
500
|
+
* @returns {Object} An object containing:
|
|
501
|
+
* - m {number} The required number of signatures (m-of-n)
|
|
502
|
+
* - n {number} The total number of public keys
|
|
503
|
+
* - pubkeys {Buffer[]} Array of public keys involved in the multisig
|
|
504
|
+
*/
|
|
505
|
+
Script.prototype.decodeMultisigOut = function () {
|
|
506
|
+
$.checkState(this.isMultisigOut(), "Can't decode a non-multisig output script");
|
|
507
|
+
const OP_INT_BASE = Opcode.OP_RESERVED; // OP_1 - 1
|
|
508
|
+
const m = this.chunks[0].opcodenum - OP_INT_BASE
|
|
509
|
+
const n = (this.chunks[0][this.chunks[0].length - 2]) - OP_INT_BASE;
|
|
510
|
+
const pubkeys = this.chunks.slice(1, -2).map((chunk) => chunk.buf);
|
|
511
|
+
|
|
512
|
+
return {
|
|
513
|
+
m,
|
|
514
|
+
n,
|
|
515
|
+
pubkeys
|
|
516
|
+
};
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Checks if the script is a multisig input script.
|
|
522
|
+
* @returns {boolean} True if the script is a valid multisig input (starts with OP_0 and has valid DER signatures).
|
|
523
|
+
*/
|
|
524
|
+
Script.prototype.isMultisigIn = function () {
|
|
525
|
+
return (
|
|
526
|
+
this.chunks.length >= 2 &&
|
|
527
|
+
this.chunks[0].opcodenum === 0 &&
|
|
528
|
+
this.chunks.slice(1, this.chunks.length).every(function (obj) {
|
|
529
|
+
return obj.buf && Buffer.isBuffer(obj.buf) && Signature.isTxDER(obj.buf);
|
|
530
|
+
})
|
|
531
|
+
);
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Checks if the script is a data-only output script (OP_RETURN followed by push-only data).
|
|
537
|
+
* @returns {boolean} True if the script is a valid data-only output, false otherwise.
|
|
538
|
+
*/
|
|
539
|
+
Script.prototype.isDataOut = function () {
|
|
540
|
+
var step1 = this.buffer.length >= 1 && this.buffer[0] === Opcode.OP_RETURN;
|
|
541
|
+
if (!step1) return false;
|
|
542
|
+
var buffer = this.buffer.slice(1);
|
|
543
|
+
var script2 = new Script({ buffer: buffer });
|
|
544
|
+
return script2.isPushOnly();
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Checks if the script is a safe data output script.
|
|
549
|
+
* A safe data output script must start with OP_FALSE followed by a valid data output script.
|
|
550
|
+
* @returns {boolean} True if the script is a safe data output, false otherwise.
|
|
551
|
+
*/
|
|
552
|
+
Script.prototype.isSafeDataOut = function () {
|
|
553
|
+
if (this.buffer.length < 2) {
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
if (this.buffer[0] !== Opcode.OP_FALSE) {
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
var buffer = this.buffer.slice(1);
|
|
560
|
+
var script2 = new Script({ buffer });
|
|
561
|
+
return script2.isDataOut();
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Retrieve the associated data for this script.
|
|
566
|
+
* In the case of a pay to public key hash, return the hash.
|
|
567
|
+
* In the case of safe OP_RETURN data, return an array of buffers
|
|
568
|
+
* In the case of a standard deprecated OP_RETURN, return the data
|
|
569
|
+
* @returns {Buffer}
|
|
570
|
+
*/
|
|
571
|
+
Script.prototype.getData = function () {
|
|
572
|
+
if (this.isSafeDataOut()) {
|
|
573
|
+
var chunks = this.chunks.slice(2);
|
|
574
|
+
var buffers = chunks.map((chunk) => chunk.buf);
|
|
575
|
+
return buffers;
|
|
576
|
+
}
|
|
577
|
+
if (this.isDataOut()) {
|
|
578
|
+
if (_.isUndefined(this.chunks[1])) {
|
|
579
|
+
return Buffer.alloc(0);
|
|
580
|
+
} else {
|
|
581
|
+
return Buffer.from(this.chunks[1].buf);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
if (this.isPublicKeyHashOut()) {
|
|
585
|
+
return Buffer.from(this.chunks[2].buf);
|
|
586
|
+
}
|
|
587
|
+
throw new Error('Unrecognized script type to get data from');
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Checks if the script consists only of push operations (OP_0 to OP_16) or data push operations (OP_PUSHDATA1/2/4).
|
|
593
|
+
* @returns {boolean} True if all chunks are push operations, false otherwise.
|
|
594
|
+
*/
|
|
595
|
+
Script.prototype.isPushOnly = function () {
|
|
596
|
+
return _.every(this.chunks, function (chunk) {
|
|
597
|
+
return (
|
|
598
|
+
chunk.opcodenum <= Opcode.OP_16 ||
|
|
599
|
+
chunk.opcodenum === Opcode.OP_PUSHDATA1 ||
|
|
600
|
+
chunk.opcodenum === Opcode.OP_PUSHDATA2 ||
|
|
601
|
+
chunk.opcodenum === Opcode.OP_PUSHDATA4
|
|
602
|
+
);
|
|
603
|
+
});
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Registry for script types.
|
|
608
|
+
* @namespace
|
|
609
|
+
*/
|
|
610
|
+
Script.types = {};
|
|
611
|
+
/**
|
|
612
|
+
* Represents the 'Unknown' type constant for script types.
|
|
613
|
+
* @type {string}
|
|
614
|
+
*/
|
|
615
|
+
Script.types.UNKNOWN = 'Unknown';
|
|
616
|
+
/**
|
|
617
|
+
* Defines the display text for the public key output script type.
|
|
618
|
+
* @type {string}
|
|
619
|
+
*/
|
|
620
|
+
Script.types.PUBKEY_OUT = 'Pay to public key';
|
|
621
|
+
/**
|
|
622
|
+
* Script type constant for spending from a public key input.
|
|
623
|
+
* @type {string}
|
|
624
|
+
*/
|
|
625
|
+
Script.types.PUBKEY_IN = 'Spend from public key';
|
|
626
|
+
/**
|
|
627
|
+
* Script type constant for Pay to Public Key Hash (P2PKH) output.
|
|
628
|
+
* @type {string}
|
|
629
|
+
*/
|
|
630
|
+
Script.types.PUBKEYHASH_OUT = 'Pay to public key hash';
|
|
631
|
+
/**
|
|
632
|
+
* Constant defining the display string for public key hash input script type.
|
|
633
|
+
* @type {string}
|
|
634
|
+
*/
|
|
635
|
+
Script.types.PUBKEYHASH_IN = 'Spend from public key hash';
|
|
636
|
+
/**
|
|
637
|
+
* Script type constant for Pay to Script Hash (P2SH) output.
|
|
638
|
+
* @type {string}
|
|
639
|
+
*/
|
|
640
|
+
Script.types.SCRIPTHASH_OUT = 'Pay to script hash';
|
|
641
|
+
/**
|
|
642
|
+
* Constant defining the string representation for spending from a script hash input type.
|
|
643
|
+
* @type {string}
|
|
644
|
+
*/
|
|
645
|
+
Script.types.SCRIPTHASH_IN = 'Spend from script hash';
|
|
646
|
+
/**
|
|
647
|
+
* Multisig output script type identifier.
|
|
648
|
+
* @type {string}
|
|
649
|
+
*/
|
|
650
|
+
Script.types.MULTISIG_OUT = 'Pay to multisig';
|
|
651
|
+
/**
|
|
652
|
+
* Multisig input script type - represents spending from a multisig address.
|
|
653
|
+
*/
|
|
654
|
+
Script.types.MULTISIG_IN = 'Spend from multisig';
|
|
655
|
+
/**
|
|
656
|
+
* Constant defining the type string for data output operations.
|
|
657
|
+
* @type {string}
|
|
658
|
+
*/
|
|
659
|
+
Script.types.DATA_OUT = 'Data push';
|
|
660
|
+
/**
|
|
661
|
+
* Constant defining the type string for safe data output operations.
|
|
662
|
+
* @type {string}
|
|
663
|
+
*/
|
|
664
|
+
Script.types.SAFE_DATA_OUT = 'Safe data push';
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* @returns {object} The Script type if it is a known form,
|
|
669
|
+
* or Script.UNKNOWN if it isn't
|
|
670
|
+
*/
|
|
671
|
+
Script.prototype.classify = function () {
|
|
672
|
+
if (this._isInput) {
|
|
673
|
+
return this.classifyInput();
|
|
674
|
+
} else if (this._isOutput) {
|
|
675
|
+
return this.classifyOutput();
|
|
676
|
+
} else {
|
|
677
|
+
var outputType = this.classifyOutput();
|
|
678
|
+
return outputType !== Script.types.UNKNOWN ? outputType : this.classifyInput();
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
Script.outputIdentifiers = {};
|
|
683
|
+
Script.outputIdentifiers.PUBKEY_OUT = Script.prototype.isPublicKeyOut;
|
|
684
|
+
Script.outputIdentifiers.PUBKEYHASH_OUT = Script.prototype.isPublicKeyHashOut;
|
|
685
|
+
Script.outputIdentifiers.MULTISIG_OUT = Script.prototype.isMultisigOut;
|
|
686
|
+
Script.outputIdentifiers.DATA_OUT = Script.prototype.isDataOut;
|
|
687
|
+
Script.outputIdentifiers.SAFE_DATA_OUT = Script.prototype.isSafeDataOut;
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* @returns {object} The Script type if it is a known form,
|
|
691
|
+
* or Script.UNKNOWN if it isn't
|
|
692
|
+
*/
|
|
693
|
+
Script.prototype.classifyOutput = function () {
|
|
694
|
+
for (var type in Script.outputIdentifiers) {
|
|
695
|
+
if (Script.outputIdentifiers[type].bind(this)()) {
|
|
696
|
+
return Script.types[type];
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return Script.types.UNKNOWN;
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
Script.inputIdentifiers = {};
|
|
703
|
+
Script.inputIdentifiers.PUBKEY_IN = Script.prototype.isPublicKeyIn;
|
|
704
|
+
Script.inputIdentifiers.PUBKEYHASH_IN = Script.prototype.isPublicKeyHashIn;
|
|
705
|
+
Script.inputIdentifiers.MULTISIG_IN = Script.prototype.isMultisigIn;
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* @returns {object} The Script type if it is a known form,
|
|
709
|
+
* or Script.UNKNOWN if it isn't
|
|
710
|
+
*/
|
|
711
|
+
Script.prototype.classifyInput = function () {
|
|
712
|
+
for (var type in Script.inputIdentifiers) {
|
|
713
|
+
if (Script.inputIdentifiers[type].bind(this)()) {
|
|
714
|
+
return Script.types[type];
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return Script.types.UNKNOWN;
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* @returns {boolean} if script is one of the known types
|
|
722
|
+
*/
|
|
723
|
+
Script.prototype.isStandard = function () {
|
|
724
|
+
// TODO: Add BIP62 compliance
|
|
725
|
+
return this.classify() !== Script.types.UNKNOWN;
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
// Script construction methods
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Adds a script element at the start of the script.
|
|
732
|
+
* @param {*} obj a string, number, Opcode, Buffer, or object to add
|
|
733
|
+
* @returns {Script} this script instance
|
|
734
|
+
*/
|
|
735
|
+
Script.prototype.prepend = function (obj) {
|
|
736
|
+
this._addByType(obj, true);
|
|
737
|
+
return this;
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Compares this script with another script for equality.
|
|
743
|
+
* @param {Script} script - The script to compare with.
|
|
744
|
+
* @returns {boolean} True if the scripts have identical buffer contents, false otherwise.
|
|
745
|
+
* @throws {Error} If the provided argument is not a Script instance.
|
|
746
|
+
*/
|
|
747
|
+
Script.prototype.equals = function (script) {
|
|
748
|
+
$.checkState(script instanceof Script, 'Must provide another script');
|
|
749
|
+
if (this.buffer.length !== script.buffer.length) {
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
var i;
|
|
753
|
+
for (i = 0; i < this.buffer.length; i++) {
|
|
754
|
+
if (this.buffer[i] !== script.buffer[i]) {
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return true;
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Adds a script element to the end of the script.
|
|
764
|
+
* @param {Object} obj - The object to add.
|
|
765
|
+
* @returns {Script} Returns the script instance for chaining.
|
|
766
|
+
*/
|
|
767
|
+
Script.prototype.add = function (obj) {
|
|
768
|
+
this._addByType(obj, false);
|
|
769
|
+
return this;
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Adds a script element to the script by type.
|
|
774
|
+
* Handles strings, numbers, Opcode instances, Buffers, Script instances, or objects.
|
|
775
|
+
* @param {string|number|Opcode|Buffer|Script|Object} obj - The element to add
|
|
776
|
+
* @param {boolean} [prepend=false] - Whether to prepend (true) or append (false)
|
|
777
|
+
* @throws {Error} If the input is an invalid script chunk
|
|
778
|
+
* @private
|
|
779
|
+
*/
|
|
780
|
+
Script.prototype._addByType = function (obj, prepend) {
|
|
781
|
+
if (typeof obj === 'string') {
|
|
782
|
+
this._addOpcode(obj, prepend);
|
|
783
|
+
} else if (typeof obj === 'number') {
|
|
784
|
+
this._addOpcode(obj, prepend);
|
|
785
|
+
} else if (obj instanceof Opcode) {
|
|
786
|
+
this._addOpcode(obj, prepend);
|
|
787
|
+
} else if (Buffer.isBuffer(obj)) {
|
|
788
|
+
this._addBuffer(obj, prepend);
|
|
789
|
+
} else if (obj instanceof Script) {
|
|
790
|
+
this._insertAtPosition(obj.buffer, prepend);
|
|
791
|
+
} else if (typeof obj === 'object') {
|
|
792
|
+
var s = Script.fromChunks([obj]);
|
|
793
|
+
this._insertAtPosition(s.toBuffer(), prepend);
|
|
794
|
+
} else {
|
|
795
|
+
throw new Error('Invalid script chunk');
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Inserts a buffer at the specified position in the script's buffer.
|
|
801
|
+
* @param {Buffer} buf - The buffer to insert.
|
|
802
|
+
* @param {boolean} prepend - If true, inserts before the existing buffer; otherwise appends after.
|
|
803
|
+
* @private
|
|
804
|
+
*/
|
|
805
|
+
Script.prototype._insertAtPosition = function (buf, prepend) {
|
|
806
|
+
var bw = new BufferWriter();
|
|
807
|
+
|
|
808
|
+
if (prepend) {
|
|
809
|
+
bw.write(buf);
|
|
810
|
+
bw.write(this.buffer);
|
|
811
|
+
} else {
|
|
812
|
+
bw.write(this.buffer);
|
|
813
|
+
bw.write(buf);
|
|
814
|
+
}
|
|
815
|
+
this.buffer = bw.toBuffer();
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Adds an opcode to the script.
|
|
820
|
+
* @param {number|Opcode|string} opcode - The opcode to add (can be a number, Opcode instance, or string).
|
|
821
|
+
* @param {boolean} [prepend=false] - Whether to prepend the opcode (true) or append it (false).
|
|
822
|
+
* @returns {Script} Returns the script instance for chaining.
|
|
823
|
+
* @throws {errors.Script.InvalidOpcode} Throws if the opcode value exceeds 255.
|
|
824
|
+
*/
|
|
825
|
+
Script.prototype._addOpcode = function (opcode, prepend) {
|
|
826
|
+
var op;
|
|
827
|
+
if (typeof opcode === 'number') {
|
|
828
|
+
op = opcode;
|
|
829
|
+
} else if (opcode instanceof Opcode) {
|
|
830
|
+
op = opcode.toNumber();
|
|
831
|
+
} else {
|
|
832
|
+
op = Opcode(opcode).toNumber();
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// OP_INVALIDOPCODE
|
|
836
|
+
if (op > 255) {
|
|
837
|
+
throw new errors.Script.InvalidOpcode(op);
|
|
838
|
+
}
|
|
839
|
+
this._insertAtPosition(
|
|
840
|
+
Script.fromChunks([
|
|
841
|
+
{
|
|
842
|
+
opcodenum: op,
|
|
843
|
+
},
|
|
844
|
+
]).toBuffer(),
|
|
845
|
+
prepend,
|
|
846
|
+
);
|
|
847
|
+
return this;
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Adds a buffer to the script with appropriate opcode based on buffer length.
|
|
852
|
+
* Handles different buffer sizes by using corresponding pushdata opcodes.
|
|
853
|
+
* @param {Buffer} buf - The buffer to add to the script
|
|
854
|
+
* @param {boolean} [prepend] - Whether to prepend the buffer (default: append)
|
|
855
|
+
* @returns {Script} Returns the script instance for chaining
|
|
856
|
+
* @throws {Error} If buffer length exceeds maximum allowed size (2^32)
|
|
857
|
+
*/
|
|
858
|
+
Script.prototype._addBuffer = function (buf, prepend) {
|
|
859
|
+
var bw = new BufferWriter();
|
|
860
|
+
var opcodenum;
|
|
861
|
+
var len = buf.length;
|
|
862
|
+
if (len === 0) {
|
|
863
|
+
opcodenum = 0;
|
|
864
|
+
bw.writeUInt8(opcodenum);
|
|
865
|
+
} else if (len > 0 && len < Opcode.OP_PUSHDATA1) {
|
|
866
|
+
opcodenum = len;
|
|
867
|
+
bw.writeUInt8(opcodenum);
|
|
868
|
+
bw.write(buf);
|
|
869
|
+
} else if (len < Math.pow(2, 8)) {
|
|
870
|
+
opcodenum = Opcode.OP_PUSHDATA1;
|
|
871
|
+
bw.writeUInt8(opcodenum);
|
|
872
|
+
bw.writeUInt8(len);
|
|
873
|
+
bw.write(buf);
|
|
874
|
+
} else if (len < Math.pow(2, 16)) {
|
|
875
|
+
opcodenum = Opcode.OP_PUSHDATA2;
|
|
876
|
+
bw.writeUInt8(opcodenum);
|
|
877
|
+
bw.writeUInt16LE(len);
|
|
878
|
+
bw.write(buf);
|
|
879
|
+
} else if (len < Math.pow(2, 32)) {
|
|
880
|
+
opcodenum = Opcode.OP_PUSHDATA4;
|
|
881
|
+
bw.writeUInt8(opcodenum);
|
|
882
|
+
bw.writeUInt32LE(len);
|
|
883
|
+
bw.write(buf);
|
|
884
|
+
} else {
|
|
885
|
+
throw new Error("You can't push that much data");
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
this._insertAtPosition(bw.toBuffer(), prepend);
|
|
889
|
+
return this;
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Creates a shallow copy of the Script instance.
|
|
894
|
+
* @returns {Script} A new Script instance with the same buffer content.
|
|
895
|
+
*/
|
|
896
|
+
Script.prototype.clone = function () {
|
|
897
|
+
return Script.fromBuffer(this.buffer.slice());
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Removes all OP_CODESEPARATOR opcodes from the script chunks.
|
|
902
|
+
* Updates the script buffer with the filtered chunks and clears the cache.
|
|
903
|
+
* @returns {Script} The modified script instance for chaining.
|
|
904
|
+
*/
|
|
905
|
+
Script.prototype.removeCodeseparators = function () {
|
|
906
|
+
var chunks = [];
|
|
907
|
+
for (var i = 0; i < this.chunks.length; i++) {
|
|
908
|
+
if (this.chunks[i].opcodenum !== Opcode.OP_CODESEPARATOR) {
|
|
909
|
+
chunks.push(this.chunks[i]);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
SCRIPT_TO_CHUNKS_CACHE.delete(this);
|
|
913
|
+
|
|
914
|
+
this.buffer = Script.fromChunks(chunks).toBuffer();
|
|
915
|
+
return this;
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
/**
|
|
919
|
+
* If the script does not contain any OP_CODESEPARATOR, Return all scripts
|
|
920
|
+
* If the script contains any OP_CODESEPARATOR, the scriptCode is the script but removing everything up to and including the last executed OP_CODESEPARATOR before the signature checking opcode being executed
|
|
921
|
+
* @param {n} The {n}th codeseparator in the script
|
|
922
|
+
*
|
|
923
|
+
* @returns {Script} Subset of script starting at the {n}th codeseparator
|
|
924
|
+
*/
|
|
925
|
+
Script.prototype.subScript = function (n) {
|
|
926
|
+
var idx = 0;
|
|
927
|
+
|
|
928
|
+
for (var i = 0; i < this.chunks.length; i++) {
|
|
929
|
+
if (this.chunks[i].opcodenum === Opcode.OP_CODESEPARATOR) {
|
|
930
|
+
if (idx === n) {
|
|
931
|
+
return Script.fromChunks(this.chunks.slice(i + 1));
|
|
932
|
+
} else {
|
|
933
|
+
idx++;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
return this;
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Builds a multisig output script from given public keys and threshold.
|
|
944
|
+
* @param {Array} publicKeys - Array of public keys to include in the multisig
|
|
945
|
+
* @param {number} threshold - Minimum number of signatures required
|
|
946
|
+
* @param {Object} [opts] - Optional parameters
|
|
947
|
+
* @param {boolean} [opts.noSorting] - If true, skips sorting of public keys
|
|
948
|
+
* @returns {Script} The constructed multisig script
|
|
949
|
+
*/
|
|
950
|
+
Script.buildMultisigOut = function (publicKeys, threshold, opts) {
|
|
951
|
+
$.checkArgument(
|
|
952
|
+
threshold <= publicKeys.length,
|
|
953
|
+
'Number of required signatures must be less than or equal to the number of public keys',
|
|
954
|
+
);
|
|
955
|
+
opts = opts || {};
|
|
956
|
+
var script = new Script();
|
|
957
|
+
script.add(Opcode.smallInt(threshold));
|
|
958
|
+
publicKeys = _.map(publicKeys, PublicKey);
|
|
959
|
+
var sorted = publicKeys;
|
|
960
|
+
if (!opts.noSorting) {
|
|
961
|
+
sorted = publicKeys
|
|
962
|
+
.map((k) => k.toString('hex'))
|
|
963
|
+
.sort()
|
|
964
|
+
.map((k) => new PublicKey(k));
|
|
965
|
+
}
|
|
966
|
+
for (var i = 0; i < sorted.length; i++) {
|
|
967
|
+
var publicKey = sorted[i];
|
|
968
|
+
script.add(publicKey.toBuffer());
|
|
969
|
+
}
|
|
970
|
+
script.add(Opcode.smallInt(publicKeys.length));
|
|
971
|
+
script.add(Opcode.OP_CHECKMULTISIG);
|
|
972
|
+
return script;
|
|
973
|
+
};
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* A new Multisig input script for the given public keys, requiring m of those public keys to spend
|
|
977
|
+
*
|
|
978
|
+
* @param {PublicKey[]} pubkeys list of all public keys controlling the output
|
|
979
|
+
* @param {number} threshold amount of required signatures to spend the output
|
|
980
|
+
* @param {Array} signatures and array of signature buffers to append to the script
|
|
981
|
+
* @param {Object=} opts
|
|
982
|
+
* @param {boolean=} opts.noSorting don't sort the given public keys before creating the script (false by default)
|
|
983
|
+
* @param {Script=} opts.cachedMultisig don't recalculate the redeemScript
|
|
984
|
+
*
|
|
985
|
+
* @returns {Script}
|
|
986
|
+
*/
|
|
987
|
+
Script.buildMultisigIn = function (pubkeys, threshold, signatures, opts) {
|
|
988
|
+
$.checkArgument(_.isArray(pubkeys));
|
|
989
|
+
$.checkArgument(_.isNumber(threshold));
|
|
990
|
+
$.checkArgument(_.isArray(signatures));
|
|
991
|
+
opts = opts || {};
|
|
992
|
+
var s = new Script();
|
|
993
|
+
s.add(Opcode.OP_0);
|
|
994
|
+
_.each(signatures, function (signature) {
|
|
995
|
+
$.checkArgument(Buffer.isBuffer(signature), 'Signatures must be an array of Buffers');
|
|
996
|
+
// TODO: allow signatures to be an array of Signature objects
|
|
997
|
+
s.add(signature);
|
|
998
|
+
});
|
|
999
|
+
return s;
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* Builds a standard P2PKH (Pay-to-Public-Key-Hash) script for a given recipient.
|
|
1005
|
+
* @param {PublicKey|Address|string} to - Recipient's public key, address, or address string
|
|
1006
|
+
* @returns {Script} A P2PKH script with the format: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
|
|
1007
|
+
* @throws {Error} If 'to' argument is undefined or invalid type
|
|
1008
|
+
*/
|
|
1009
|
+
Script.buildPublicKeyHashOut = function (to) {
|
|
1010
|
+
$.checkArgument(!_.isUndefined(to));
|
|
1011
|
+
$.checkArgument(to instanceof PublicKey || to instanceof Address || _.isString(to));
|
|
1012
|
+
if (to instanceof PublicKey) {
|
|
1013
|
+
to = to.toAddress();
|
|
1014
|
+
} else if (_.isString(to)) {
|
|
1015
|
+
to = new Address(to);
|
|
1016
|
+
}
|
|
1017
|
+
var s = new Script();
|
|
1018
|
+
s.add(Opcode.OP_DUP)
|
|
1019
|
+
.add(Opcode.OP_HASH160)
|
|
1020
|
+
.add(to.hashBuffer)
|
|
1021
|
+
.add(Opcode.OP_EQUALVERIFY)
|
|
1022
|
+
.add(Opcode.OP_CHECKSIG);
|
|
1023
|
+
s._network = to.network;
|
|
1024
|
+
return s;
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Builds a standard P2PK (Pay-to-Public-Key) script output.
|
|
1030
|
+
* @param {PublicKey} pubkey - The public key to create the script for
|
|
1031
|
+
* @returns {Script} A new script containing the public key and OP_CHECKSIG opcode
|
|
1032
|
+
*/
|
|
1033
|
+
Script.buildPublicKeyOut = function (pubkey) {
|
|
1034
|
+
$.checkArgument(pubkey instanceof PublicKey);
|
|
1035
|
+
var s = new Script();
|
|
1036
|
+
s.add(pubkey.toBuffer()).add(Opcode.OP_CHECKSIG);
|
|
1037
|
+
return s;
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* @returns {Script} a new OP_RETURN script with data
|
|
1042
|
+
* @param {string|Buffer|Array} data - the data to embed in the output - it is a string, buffer, or array of strings or buffers
|
|
1043
|
+
* @param {string} encoding - the type of encoding of the string(s)
|
|
1044
|
+
*/
|
|
1045
|
+
Script.buildDataOut = function (data, encoding) {
|
|
1046
|
+
$.checkArgument(
|
|
1047
|
+
_.isUndefined(data) || _.isString(data) || _.isArray(data) || Buffer.isBuffer(data),
|
|
1048
|
+
);
|
|
1049
|
+
var datas = data;
|
|
1050
|
+
if (!_.isArray(datas)) {
|
|
1051
|
+
datas = [data];
|
|
1052
|
+
}
|
|
1053
|
+
var s = new Script();
|
|
1054
|
+
s.add(Opcode.OP_RETURN);
|
|
1055
|
+
for (let data of datas) {
|
|
1056
|
+
$.checkArgument(_.isUndefined(data) || _.isString(data) || Buffer.isBuffer(data));
|
|
1057
|
+
if (_.isString(data)) {
|
|
1058
|
+
data = Buffer.from(data, encoding);
|
|
1059
|
+
}
|
|
1060
|
+
if (!_.isUndefined(data)) {
|
|
1061
|
+
s.add(data);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return s;
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* @returns {Script} a new OP_RETURN script with data
|
|
1069
|
+
* @param {string|Buffer|Array} data - the data to embed in the output - it is a string, buffer, or array of strings or buffers
|
|
1070
|
+
* @param {string} encoding - the type of encoding of the string(s)
|
|
1071
|
+
*/
|
|
1072
|
+
Script.buildSafeDataOut = function (data, encoding) {
|
|
1073
|
+
var s2 = Script.buildDataOut(data, encoding);
|
|
1074
|
+
var s1 = new Script();
|
|
1075
|
+
s1.add(Opcode.OP_FALSE);
|
|
1076
|
+
s1.add(s2);
|
|
1077
|
+
return s1;
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* Builds a scriptSig (a script for an input) that signs a public key output script.
|
|
1083
|
+
*
|
|
1084
|
+
* @param {Signature|Buffer} signature - a Signature object, or the signature in DER canonical encoding
|
|
1085
|
+
* @param {number} [sigtype] - the type of the signature (defaults to SIGHASH_ALL)
|
|
1086
|
+
*/
|
|
1087
|
+
Script.buildPublicKeyIn = function (signature, sigtype) {
|
|
1088
|
+
$.checkArgument(signature instanceof Signature || Buffer.isBuffer(signature));
|
|
1089
|
+
$.checkArgument(_.isUndefined(sigtype) || _.isNumber(sigtype));
|
|
1090
|
+
if (signature instanceof Signature) {
|
|
1091
|
+
signature = signature.toBuffer();
|
|
1092
|
+
}
|
|
1093
|
+
var script = new Script();
|
|
1094
|
+
script.add(Buffer.concat([signature, Buffer.from([(sigtype || Signature.SIGHASH_ALL) & 0xff])]));
|
|
1095
|
+
return script;
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* Builds a scriptSig (a script for an input) that signs a public key hash
|
|
1100
|
+
* output script.
|
|
1101
|
+
*
|
|
1102
|
+
* @param {Buffer|string|PublicKey} publicKey
|
|
1103
|
+
* @param {Signature|Buffer} signature - a Signature object, or the signature in DER canonical encoding
|
|
1104
|
+
* @param {number} [sigtype] - the type of the signature (defaults to SIGHASH_ALL)
|
|
1105
|
+
*/
|
|
1106
|
+
Script.buildPublicKeyHashIn = function (publicKey, signature, sigtype) {
|
|
1107
|
+
$.checkArgument(signature instanceof Signature || Buffer.isBuffer(signature));
|
|
1108
|
+
$.checkArgument(_.isUndefined(sigtype) || _.isNumber(sigtype));
|
|
1109
|
+
if (signature instanceof Signature) {
|
|
1110
|
+
signature = signature.toBuffer();
|
|
1111
|
+
}
|
|
1112
|
+
var script = new Script()
|
|
1113
|
+
.add(Buffer.concat([signature, Buffer.from([(sigtype || Signature.SIGHASH_ALL) & 0xff])]))
|
|
1114
|
+
.add(new PublicKey(publicKey).toBuffer());
|
|
1115
|
+
return script;
|
|
1116
|
+
};
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
/**
|
|
1120
|
+
* Creates and returns an empty Script instance.
|
|
1121
|
+
* @returns {Script} A new empty Script object.
|
|
1122
|
+
*/
|
|
1123
|
+
Script.empty = function () {
|
|
1124
|
+
return new Script();
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
/**
|
|
1129
|
+
* Creates a Script from an address.
|
|
1130
|
+
* @param {Address|string} address - The address to convert to a script.
|
|
1131
|
+
* @returns {Script} A Pay-to-PublicKeyHash (P2PKH) script for the given address.
|
|
1132
|
+
* @throws {errors.Script.UnrecognizedAddress} If the address type is not supported.
|
|
1133
|
+
*/
|
|
1134
|
+
Script.fromAddress = function (address) {
|
|
1135
|
+
address = Address(address);
|
|
1136
|
+
if (address.isPayToPublicKeyHash()) {
|
|
1137
|
+
return Script.buildPublicKeyHashOut(address);
|
|
1138
|
+
}
|
|
1139
|
+
throw new errors.Script.UnrecognizedAddress(address);
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* Gets address information for the script.
|
|
1145
|
+
* For input scripts, returns input address info.
|
|
1146
|
+
* For output scripts, returns output address info.
|
|
1147
|
+
* For general scripts, tries output address info first, falls back to input if not available.
|
|
1148
|
+
* @returns {Object} Address information object
|
|
1149
|
+
*/
|
|
1150
|
+
Script.prototype.getAddressInfo = function () {
|
|
1151
|
+
if (this._isInput) {
|
|
1152
|
+
return this._getInputAddressInfo();
|
|
1153
|
+
} else if (this._isOutput) {
|
|
1154
|
+
return this._getOutputAddressInfo();
|
|
1155
|
+
} else {
|
|
1156
|
+
var info = this._getOutputAddressInfo();
|
|
1157
|
+
if (!info) {
|
|
1158
|
+
return this._getInputAddressInfo();
|
|
1159
|
+
}
|
|
1160
|
+
return info;
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
|
|
1164
|
+
|
|
1165
|
+
/**
|
|
1166
|
+
* Gets the output address information from the script.
|
|
1167
|
+
* @returns {Object|boolean} An object containing the hash buffer and address type if the script is a public key hash output, otherwise false.
|
|
1168
|
+
* @property {Buffer} info.hashBuffer - The hash buffer of the address.
|
|
1169
|
+
* @property {number} info.type - The type of the address (Address.PayToPublicKeyHash).
|
|
1170
|
+
*/
|
|
1171
|
+
Script.prototype._getOutputAddressInfo = function () {
|
|
1172
|
+
var info = {};
|
|
1173
|
+
if (this.isPublicKeyHashOut()) {
|
|
1174
|
+
info.hashBuffer = this.getData();
|
|
1175
|
+
info.type = Address.PayToPublicKeyHash;
|
|
1176
|
+
} else {
|
|
1177
|
+
return false;
|
|
1178
|
+
}
|
|
1179
|
+
return info;
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* Will return the associated input scriptSig address information object
|
|
1184
|
+
* @return {Address|boolean}
|
|
1185
|
+
* @private
|
|
1186
|
+
*/
|
|
1187
|
+
Script.prototype._getInputAddressInfo = function () {
|
|
1188
|
+
var info = {};
|
|
1189
|
+
if (this.isPublicKeyHashIn()) {
|
|
1190
|
+
// hash the publickey found in the scriptSig
|
|
1191
|
+
info.hashBuffer = Hash.sha256ripemd160(this.chunks[1].buf);
|
|
1192
|
+
info.type = Address.PayToPublicKeyHash;
|
|
1193
|
+
} else {
|
|
1194
|
+
return false;
|
|
1195
|
+
}
|
|
1196
|
+
return info;
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* Converts the script to an Address object for the specified network.
|
|
1202
|
+
* @param {string|Network} [network] - optianal, the network name or identifier.
|
|
1203
|
+
* @returns {Address} The derived Address object.
|
|
1204
|
+
* @throws {errors.Script.CantDeriveAddress} If address information cannot be derived from the script.
|
|
1205
|
+
*/
|
|
1206
|
+
Script.prototype.toAddress = function (network) {
|
|
1207
|
+
var info = this.getAddressInfo();
|
|
1208
|
+
if (!info) {
|
|
1209
|
+
throw new errors.Script.CantDeriveAddress(this);
|
|
1210
|
+
}
|
|
1211
|
+
info.network = Networks.get(network) || this._network || Networks.defaultNetwork;
|
|
1212
|
+
return new Address(info);
|
|
1213
|
+
};
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* Finds and deletes a matching script chunk from the current script.
|
|
1217
|
+
* Analogous to bitcoind's FindAndDelete. Find and delete equivalent chunks,
|
|
1218
|
+
* typically used with push data chunks. Note that this will find and delete
|
|
1219
|
+
* not just the same data, but the same data with the same push data op as
|
|
1220
|
+
* produced by default. i.e., if a pushdata in a tx does not use the minimal
|
|
1221
|
+
* pushdata op, then when you try to remove the data it is pushing, it will not
|
|
1222
|
+
* be removed, because they do not use the same pushdata op.
|
|
1223
|
+
* @param {Script} script - The script chunk to find and delete.
|
|
1224
|
+
* @returns {Script} The modified script instance after deletion.
|
|
1225
|
+
*/
|
|
1226
|
+
Script.prototype.findAndDelete = function (script) {
|
|
1227
|
+
var buf = script.toBuffer();
|
|
1228
|
+
var hex = buf.toString('hex');
|
|
1229
|
+
var chunks = this.chunks;
|
|
1230
|
+
for (var i = 0; i < chunks.length; i++) {
|
|
1231
|
+
var script2 = Script.fromChunks([chunks[i]]);
|
|
1232
|
+
var buf2 = script2.toBuffer();
|
|
1233
|
+
var hex2 = buf2.toString('hex');
|
|
1234
|
+
if (hex === hex2) {
|
|
1235
|
+
chunks.splice(i, 1);
|
|
1236
|
+
this.buffer = Script.fromChunks(chunks).toBuffer();
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
return this;
|
|
1240
|
+
};
|
|
1241
|
+
|
|
1242
|
+
|
|
1243
|
+
/**
|
|
1244
|
+
* Checks if a script chunk uses the minimal push operation possible.
|
|
1245
|
+
*
|
|
1246
|
+
* @param {number} i - Index of the chunk to check
|
|
1247
|
+
* @returns {boolean} True if the chunk uses minimal push operation, false otherwise
|
|
1248
|
+
*
|
|
1249
|
+
* The function verifies if the chunk could have been represented with:
|
|
1250
|
+
* - OP_0 for empty buffer
|
|
1251
|
+
* - OP_1 to OP_16 for single-byte values 1-16
|
|
1252
|
+
* - OP_1NEGATE for 0x81
|
|
1253
|
+
* - Direct push for buffers ≤75 bytes
|
|
1254
|
+
* - OP_PUSHDATA1 for buffers ≤255 bytes
|
|
1255
|
+
* - OP_PUSHDATA2 for buffers ≤65535 bytes
|
|
1256
|
+
*/
|
|
1257
|
+
Script.prototype.checkMinimalPush = function (i) {
|
|
1258
|
+
var chunk = this.chunks[i];
|
|
1259
|
+
var buf = chunk.buf;
|
|
1260
|
+
var opcodenum = chunk.opcodenum;
|
|
1261
|
+
if (!buf) {
|
|
1262
|
+
return true;
|
|
1263
|
+
}
|
|
1264
|
+
if (buf.length === 0) {
|
|
1265
|
+
// Could have used OP_0.
|
|
1266
|
+
return opcodenum === Opcode.OP_0;
|
|
1267
|
+
} else if (buf.length === 1 && buf[0] >= 1 && buf[0] <= 16) {
|
|
1268
|
+
// Could have used OP_1 .. OP_16.
|
|
1269
|
+
return opcodenum === Opcode.OP_1 + (buf[0] - 1);
|
|
1270
|
+
} else if (buf.length === 1 && buf[0] === 0x81) {
|
|
1271
|
+
// Could have used OP_1NEGATE
|
|
1272
|
+
return opcodenum === Opcode.OP_1NEGATE;
|
|
1273
|
+
} else if (buf.length <= 75) {
|
|
1274
|
+
// Could have used a direct push (opcode indicating number of bytes pushed + those bytes).
|
|
1275
|
+
return opcodenum === buf.length;
|
|
1276
|
+
} else if (buf.length <= 255) {
|
|
1277
|
+
// Could have used OP_PUSHDATA.
|
|
1278
|
+
return opcodenum === Opcode.OP_PUSHDATA1;
|
|
1279
|
+
} else if (buf.length <= 65535) {
|
|
1280
|
+
// Could have used OP_PUSHDATA2.
|
|
1281
|
+
return opcodenum === Opcode.OP_PUSHDATA2;
|
|
1282
|
+
}
|
|
1283
|
+
return true;
|
|
1284
|
+
};
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* Comes from bitcoind's script DecodeOP_N function
|
|
1288
|
+
* @param {number} opcode
|
|
1289
|
+
* @returns {number} numeric value in range of 0 to 16
|
|
1290
|
+
* @private
|
|
1291
|
+
*/
|
|
1292
|
+
Script.prototype._decodeOP_N = function (opcode) {
|
|
1293
|
+
if (opcode === Opcode.OP_0) {
|
|
1294
|
+
return 0;
|
|
1295
|
+
} else if (opcode >= Opcode.OP_1 && opcode <= Opcode.OP_16) {
|
|
1296
|
+
return opcode - (Opcode.OP_1 - 1);
|
|
1297
|
+
} else {
|
|
1298
|
+
throw new Error('Invalid opcode: ' + JSON.stringify(opcode));
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* Counts the number of signature operations in the script.
|
|
1305
|
+
* @param {boolean} [accurate=true] - Whether to count accurately for OP_CHECKMULTISIG(VERIFY).
|
|
1306
|
+
* @returns {number} The total count of signature operations.
|
|
1307
|
+
*/
|
|
1308
|
+
Script.prototype.getSignatureOperationsCount = function (accurate) {
|
|
1309
|
+
accurate = _.isUndefined(accurate) ? true : accurate;
|
|
1310
|
+
var self = this;
|
|
1311
|
+
var n = 0;
|
|
1312
|
+
var lastOpcode = Opcode.OP_INVALIDOPCODE;
|
|
1313
|
+
_.each(self.chunks, function getChunk(chunk) {
|
|
1314
|
+
var opcode = chunk.opcodenum;
|
|
1315
|
+
if (opcode === Opcode.OP_CHECKSIG || opcode === Opcode.OP_CHECKSIGVERIFY) {
|
|
1316
|
+
n++;
|
|
1317
|
+
} else if (opcode === Opcode.OP_CHECKMULTISIG || opcode === Opcode.OP_CHECKMULTISIGVERIFY) {
|
|
1318
|
+
if (accurate && lastOpcode >= Opcode.OP_1 && lastOpcode <= Opcode.OP_16) {
|
|
1319
|
+
n += self._decodeOP_N(lastOpcode);
|
|
1320
|
+
} else {
|
|
1321
|
+
n += 20;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
lastOpcode = opcode;
|
|
1325
|
+
});
|
|
1326
|
+
return n;
|
|
1327
|
+
};
|
|
1328
|
+
|
|
1329
|
+
export default Script;
|