@opcat-labs/opcat 1.0.0 → 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 +13 -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 +40 -26
- 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,1988 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _ = require('../util/_.cjs');
|
|
4
|
+
|
|
5
|
+
var Script = require('../script/index.cjs');
|
|
6
|
+
var Opcode = require('../opcode.cjs');
|
|
7
|
+
var BN = require('../crypto/bn.cjs');
|
|
8
|
+
var Hash = require('../crypto/hash.cjs');
|
|
9
|
+
var Signature = require('../crypto/signature.cjs');
|
|
10
|
+
var PublicKey = require('../publickey.cjs');
|
|
11
|
+
var Stack = require('./stack.cjs');
|
|
12
|
+
var Transaction = require('../transaction/index.cjs');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Bitcoin transactions contain scripts. Each input has a script called the
|
|
16
|
+
* scriptSig, and each output has a script called the scriptPubkey. To validate
|
|
17
|
+
* an input, the input's script is concatenated with the referenced output script,
|
|
18
|
+
* and the result is executed. If at the end of execution the stack contains a
|
|
19
|
+
* "true" value, then the transaction is valid.
|
|
20
|
+
*
|
|
21
|
+
* The primary way to use this class is via the verify function.
|
|
22
|
+
* e.g., Interpreter().verify( ... );
|
|
23
|
+
* @constructor
|
|
24
|
+
* @param {Object} [obj] - Optional object to initialize the interpreter with.
|
|
25
|
+
* @returns {Interpreter} A new Interpreter instance.
|
|
26
|
+
*/
|
|
27
|
+
function Interpreter(obj) {
|
|
28
|
+
if (!(this instanceof Interpreter)) {
|
|
29
|
+
return new Interpreter(obj);
|
|
30
|
+
}
|
|
31
|
+
if (obj) {
|
|
32
|
+
this.initialize();
|
|
33
|
+
this.set(obj);
|
|
34
|
+
} else {
|
|
35
|
+
this.initialize();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Verifies a Script by executing it and returns true if it is valid.
|
|
41
|
+
* This function needs to be provided with the scriptSig and the scriptPubkey
|
|
42
|
+
* separately.
|
|
43
|
+
* @param {Script} scriptSig - the script's first part (corresponding to the tx input)
|
|
44
|
+
* @param {Script} scriptPubkey - the script's last part (corresponding to the tx output)
|
|
45
|
+
* @param {Transaction=} tx - the Transaction containing the scriptSig in one input (used
|
|
46
|
+
* to check signature validity for some opcodes like OP_CHECKSIG)
|
|
47
|
+
* @param {number} nin - index of the transaction input containing the scriptSig verified.
|
|
48
|
+
* @param {number} flags - evaluation flags. See Interpreter.SCRIPT_* constants
|
|
49
|
+
* @param {number} satoshisBN - amount in satoshis of the input to be verified (when FORKID signhash is used)
|
|
50
|
+
*
|
|
51
|
+
* Translated from bitcoind's VerifyScript
|
|
52
|
+
*/
|
|
53
|
+
Interpreter.prototype.verify = function (
|
|
54
|
+
scriptSig,
|
|
55
|
+
scriptPubkey,
|
|
56
|
+
tx,
|
|
57
|
+
nin,
|
|
58
|
+
flags,
|
|
59
|
+
satoshisBN,
|
|
60
|
+
) {
|
|
61
|
+
|
|
62
|
+
if (_.isUndefined(tx)) {
|
|
63
|
+
tx = new Transaction();
|
|
64
|
+
}
|
|
65
|
+
if (_.isUndefined(nin)) {
|
|
66
|
+
nin = 0;
|
|
67
|
+
}
|
|
68
|
+
if (_.isUndefined(flags)) {
|
|
69
|
+
flags = 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.set({
|
|
73
|
+
script: scriptSig,
|
|
74
|
+
tx: tx,
|
|
75
|
+
nin: nin,
|
|
76
|
+
flags: flags,
|
|
77
|
+
satoshisBN: satoshisBN,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if ((flags & Interpreter.SCRIPT_VERIFY_SIGPUSHONLY) !== 0 && !scriptSig.isPushOnly()) {
|
|
81
|
+
this.errstr = 'SCRIPT_ERR_SIG_PUSHONLY';
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// evaluate scriptSig
|
|
86
|
+
if (!this.evaluate('scriptSig')) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
var stack = this.stack;
|
|
91
|
+
this.initialize();
|
|
92
|
+
this.set({
|
|
93
|
+
script: scriptPubkey,
|
|
94
|
+
stack: stack,
|
|
95
|
+
tx: tx,
|
|
96
|
+
nin: nin,
|
|
97
|
+
flags: flags,
|
|
98
|
+
satoshisBN: satoshisBN,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// evaluate scriptPubkey
|
|
102
|
+
if (!this.evaluate('scriptPubkey')) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (this.stack.length === 0) {
|
|
107
|
+
this.errstr = 'SCRIPT_ERR_EVAL_FALSE_NO_RESULT';
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
var buf = this.stack.stacktop(-1);
|
|
112
|
+
if (!Interpreter.castToBool(buf)) {
|
|
113
|
+
this.errstr = 'SCRIPT_ERR_EVAL_FALSE_IN_STACK';
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return true;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Initializes the interpreter instance with default values.
|
|
123
|
+
* Sets up empty stacks, resets program counter and execution flags,
|
|
124
|
+
* and initializes state tracking variables for script execution.
|
|
125
|
+
*/
|
|
126
|
+
Interpreter.prototype.initialize = function () {
|
|
127
|
+
this.stack = new Stack([]);
|
|
128
|
+
this.altstack = new Stack([]);
|
|
129
|
+
this.pc = 0;
|
|
130
|
+
this.pbegincodehash = 0;
|
|
131
|
+
this.nOpCount = 0;
|
|
132
|
+
this.vfExec = [];
|
|
133
|
+
this.vfElse = [];
|
|
134
|
+
this.errstr = '';
|
|
135
|
+
this.flags = 0;
|
|
136
|
+
// if OP_RETURN is found in executed branches after genesis is activated,
|
|
137
|
+
// we still have to check if the rest of the script is valid
|
|
138
|
+
this.nonTopLevelReturnAfterGenesis = false;
|
|
139
|
+
this.returned = false;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Updates the interpreter's state with provided values.
|
|
144
|
+
* @param {Object} obj - Object containing properties to update
|
|
145
|
+
* @param {Buffer} [obj.script] - Script buffer
|
|
146
|
+
* @param {Object} [obj.tx] - Transaction object
|
|
147
|
+
* @param {boolean} [obj.nin] - Non-input flag
|
|
148
|
+
* @param {BN} [obj.satoshisBN] - Satoshis as BN.js instance
|
|
149
|
+
* @param {Array} [obj.stack] - Main stack
|
|
150
|
+
* @param {Array} [obj.altstack] - Alternate stack
|
|
151
|
+
* @param {number} [obj.pc] - Program counter
|
|
152
|
+
* @param {number} [obj.pbegincodehash] - Begin code hash position
|
|
153
|
+
* @param {number} [obj.nOpCount] - Operation count
|
|
154
|
+
* @param {Array} [obj.vfExec] - Execution flags
|
|
155
|
+
* @param {Array} [obj.vfElse] - Else flags
|
|
156
|
+
* @param {string} [obj.errstr] - Error string
|
|
157
|
+
* @param {number} [obj.flags] - Interpreter flags
|
|
158
|
+
*/
|
|
159
|
+
Interpreter.prototype.set = function (obj) {
|
|
160
|
+
this.script = obj.script || this.script;
|
|
161
|
+
this.tx = obj.tx || this.tx;
|
|
162
|
+
this.nin = typeof obj.nin !== 'undefined' ? obj.nin : this.nin;
|
|
163
|
+
this.satoshisBN = obj.satoshisBN || this.satoshisBN;
|
|
164
|
+
this.stack = obj.stack || this.stack;
|
|
165
|
+
this.altstack = obj.altstack || this.altstack;
|
|
166
|
+
this.pc = typeof obj.pc !== 'undefined' ? obj.pc : this.pc;
|
|
167
|
+
this.pbegincodehash =
|
|
168
|
+
typeof obj.pbegincodehash !== 'undefined' ? obj.pbegincodehash : this.pbegincodehash;
|
|
169
|
+
this.nOpCount = typeof obj.nOpCount !== 'undefined' ? obj.nOpCount : this.nOpCount;
|
|
170
|
+
this.vfExec = obj.vfExec || this.vfExec;
|
|
171
|
+
this.vfElse = obj.vfElse || this.vfElse;
|
|
172
|
+
this.errstr = obj.errstr || this.errstr;
|
|
173
|
+
this.flags = typeof obj.flags !== 'undefined' ? obj.flags : this.flags;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Returns a subset of the script starting from the most recent OP_CODESEPARATOR.
|
|
178
|
+
* @returns {Script} A new Script instance containing the sliced chunks.
|
|
179
|
+
*/
|
|
180
|
+
Interpreter.prototype.subscript = function () {
|
|
181
|
+
// Subset of script starting at the most recent codeseparator
|
|
182
|
+
// CScript scriptCode(pbegincodehash, pend);
|
|
183
|
+
return Script.fromChunks(this.script.chunks.slice(this.pbegincodehash));
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
Interpreter.getTrue = () => Buffer.from([1]);
|
|
187
|
+
Interpreter.getFalse = () => Buffer.from([]);
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Maximum allowed size for script elements in the interpreter.
|
|
191
|
+
* Set to the maximum safe integer value by default.
|
|
192
|
+
*/
|
|
193
|
+
Interpreter.MAX_SCRIPT_ELEMENT_SIZE = Number.MAX_SAFE_INTEGER;
|
|
194
|
+
/**
|
|
195
|
+
* The maximum allowed size for an element in the interpreter.
|
|
196
|
+
* @type {number}
|
|
197
|
+
*/
|
|
198
|
+
Interpreter.MAXIMUM_ELEMENT_SIZE = Number.MAX_SAFE_INTEGER;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The threshold value used to distinguish between block height and timestamp in locktime.
|
|
202
|
+
* Values below this threshold are interpreted as block numbers, while values equal or above are treated as timestamps.
|
|
203
|
+
*/
|
|
204
|
+
Interpreter.LOCKTIME_THRESHOLD = 500000000;
|
|
205
|
+
/**
|
|
206
|
+
* The locktime threshold value as a BN.js BigNumber instance.
|
|
207
|
+
* @type {BN}
|
|
208
|
+
*/
|
|
209
|
+
Interpreter.LOCKTIME_THRESHOLD_BN = new BN(Interpreter.LOCKTIME_THRESHOLD);
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Flag indicating no script verification rules should be enforced.
|
|
213
|
+
* flags taken from bitcoind
|
|
214
|
+
* bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104
|
|
215
|
+
* @constant {number}
|
|
216
|
+
*/
|
|
217
|
+
Interpreter.SCRIPT_VERIFY_NONE = 0;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
|
|
221
|
+
* Passing a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) to checksig causes that pubkey to be
|
|
222
|
+
* skipped (not softfork safe: this flag can widen the validity of OP_CHECKSIG OP_NOT).
|
|
223
|
+
*/
|
|
224
|
+
Interpreter.SCRIPT_VERIFY_STRICTENC = 1 << 1;
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1)
|
|
229
|
+
*/
|
|
230
|
+
Interpreter.SCRIPT_VERIFY_DERSIG = 1 << 2;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
|
|
234
|
+
* (softfork safe, BIP62 rule 5).
|
|
235
|
+
*/
|
|
236
|
+
Interpreter.SCRIPT_VERIFY_LOW_S = 1 << 3;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* verify dummy stack item consumed by CHECKMULTISIG is of zero-length (softfork safe, BIP62 rule 7).
|
|
240
|
+
*/
|
|
241
|
+
Interpreter.SCRIPT_VERIFY_NULLDUMMY = 1 << 4;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Using a non-push operator in the scriptSig causes script failure (softfork safe, BIP62 rule 2).
|
|
245
|
+
*/
|
|
246
|
+
Interpreter.SCRIPT_VERIFY_SIGPUSHONLY = 1 << 5;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
|
|
250
|
+
* pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
|
|
251
|
+
* any other push causes the script to fail (BIP62 rule 3).
|
|
252
|
+
* In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
|
|
253
|
+
* (softfork safe)
|
|
254
|
+
*
|
|
255
|
+
*/
|
|
256
|
+
Interpreter.SCRIPT_VERIFY_MINIMALDATA = 1 << 6;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Discourage use of NOPs reserved for upgrades (NOP1-10)
|
|
260
|
+
*
|
|
261
|
+
* Provided so that nodes can avoid accepting or mining transactions
|
|
262
|
+
* containing executed NOP's whose meaning may change after a soft-fork,
|
|
263
|
+
* thus rendering the script invalid; with this flag set executing
|
|
264
|
+
* discouraged NOPs fails the script. This verification flag will never be
|
|
265
|
+
* a mandatory flag applied to scripts in a block. NOPs that are not
|
|
266
|
+
* executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
|
|
267
|
+
*/
|
|
268
|
+
Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = 1 << 7;
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* CLTV See BIP65 for details.
|
|
274
|
+
*/
|
|
275
|
+
Interpreter.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = 1 << 9;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* support CHECKSEQUENCEVERIFY opcode
|
|
279
|
+
* See BIP112 for details
|
|
280
|
+
*/
|
|
281
|
+
Interpreter.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = 1 << 10;
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Flag indicating that script verification should fail if a null input is encountered.
|
|
286
|
+
* This is a bitmask value (1 << 14) used in script verification rules.
|
|
287
|
+
*/
|
|
288
|
+
Interpreter.SCRIPT_VERIFY_NULLFAIL = 1 << 14;
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Flag indicating that compressed public key type should be verified in scripts.
|
|
292
|
+
* This is a bit flag used in script verification (1 << 15).
|
|
293
|
+
* @type {number}
|
|
294
|
+
*/
|
|
295
|
+
Interpreter.SCRIPT_VERIFY_COMPRESSED_PUBKEYTYPE = 1 << 15;
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Enable new opcodes.
|
|
300
|
+
* @type {number}
|
|
301
|
+
*/
|
|
302
|
+
Interpreter.SCRIPT_ENABLE_MONOLITH_OPCODES = 1 << 18;
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Are the Magnetic upgrade opcodes enabled?
|
|
307
|
+
* @type {number}
|
|
308
|
+
*/
|
|
309
|
+
Interpreter.SCRIPT_ENABLE_MAGNETIC_OPCODES = 1 << 19;
|
|
310
|
+
|
|
311
|
+
/* Below flags apply in the context of BIP 68 */
|
|
312
|
+
/**
|
|
313
|
+
* If this flag set, CTxIn::nSequence is NOT interpreted as a relative
|
|
314
|
+
* lock-time.
|
|
315
|
+
* @type {number}
|
|
316
|
+
*/
|
|
317
|
+
Interpreter.SEQUENCE_LOCKTIME_DISABLE_FLAG = 1 << 31;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* If CTxIn::nSequence encodes a relative lock-time and this flag is set,
|
|
321
|
+
* the relative lock-time has units of 512 seconds, otherwise it specifies
|
|
322
|
+
* blocks with a granularity of 1.
|
|
323
|
+
* @type {number}
|
|
324
|
+
*/
|
|
325
|
+
Interpreter.SEQUENCE_LOCKTIME_TYPE_FLAG = 1 << 22;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* If CTxIn::nSequence encodes a relative lock-time, this mask is applied to
|
|
329
|
+
* extract that lock-time from the sequence field.
|
|
330
|
+
* @type {number}
|
|
331
|
+
*/
|
|
332
|
+
Interpreter.SEQUENCE_LOCKTIME_MASK = 0x0000ffff;
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Maximum allowed script size for the interpreter, set to the largest safe integer in JavaScript.
|
|
336
|
+
* @type {number}
|
|
337
|
+
*/
|
|
338
|
+
Interpreter.MAX_SCRIPT_SIZE = Number.MAX_SAFE_INTEGER;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Maximum number of opcodes that can be executed before stopping the interpreter.
|
|
342
|
+
* Defaults to Number.MAX_SAFE_INTEGER (effectively unlimited).
|
|
343
|
+
* @type {number}
|
|
344
|
+
*/
|
|
345
|
+
Interpreter.MAX_OPCODE_COUNT = Number.MAX_SAFE_INTEGER;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Default flags used by the interpreter.
|
|
349
|
+
* @type {number}
|
|
350
|
+
*/
|
|
351
|
+
Interpreter.DEFAULT_FLAGS =
|
|
352
|
+
Interpreter.SCRIPT_ENABLE_MAGNETIC_OPCODES |
|
|
353
|
+
Interpreter.SCRIPT_ENABLE_MONOLITH_OPCODES |
|
|
354
|
+
Interpreter.SCRIPT_VERIFY_STRICTENC |
|
|
355
|
+
Interpreter.SCRIPT_VERIFY_LOW_S |
|
|
356
|
+
Interpreter.SCRIPT_VERIFY_NULLFAIL |
|
|
357
|
+
Interpreter.SCRIPT_VERIFY_DERSIG |
|
|
358
|
+
Interpreter.SCRIPT_VERIFY_MINIMALDATA |
|
|
359
|
+
Interpreter.SCRIPT_VERIFY_NULLDUMMY |
|
|
360
|
+
Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
|
|
361
|
+
Interpreter.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
|
|
362
|
+
Interpreter.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Casts a buffer to a boolean value.
|
|
367
|
+
* Returns true if any byte in the buffer is non-zero (except for the special case of negative zero).
|
|
368
|
+
* Returns false if all bytes are zero or if the last byte is 0x80 (negative zero case).
|
|
369
|
+
* @param {Buffer} buf - The input buffer to check
|
|
370
|
+
* @returns {boolean} The boolean representation of the buffer
|
|
371
|
+
*/
|
|
372
|
+
Interpreter.castToBool = function (buf) {
|
|
373
|
+
for (var i = 0; i < buf.length; i++) {
|
|
374
|
+
if (buf[i] !== 0) {
|
|
375
|
+
// can be negative zero
|
|
376
|
+
if (i === buf.length - 1 && buf[i] === 0x80) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return false;
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Checks if a signature encoding is valid according to the interpreter's flags.
|
|
388
|
+
* - For empty signatures: always valid (used for compact invalid signatures in CHECK(MULTI)SIG)
|
|
389
|
+
* - With DERSIG/STRICTENC flags: validates DER encoding and strict encoding rules
|
|
390
|
+
* - With LOW_S flag: ensures signature uses low S value
|
|
391
|
+
* - With STRICTENC flag: validates defined hash type
|
|
392
|
+
* @param {Buffer} buf - The signature buffer to validate
|
|
393
|
+
* @returns {boolean} True if valid, false otherwise (sets errstr on failure)
|
|
394
|
+
*/
|
|
395
|
+
Interpreter.prototype.checkSignatureEncoding = function (buf) {
|
|
396
|
+
var sig;
|
|
397
|
+
|
|
398
|
+
// Empty signature. Not strictly DER encoded, but allowed to provide a
|
|
399
|
+
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
|
400
|
+
if (buf.length === 0) {
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (
|
|
405
|
+
(this.flags &
|
|
406
|
+
(Interpreter.SCRIPT_VERIFY_DERSIG |
|
|
407
|
+
Interpreter.SCRIPT_VERIFY_LOW_S |
|
|
408
|
+
Interpreter.SCRIPT_VERIFY_STRICTENC)) !==
|
|
409
|
+
0 &&
|
|
410
|
+
!Signature.isTxDER(buf)
|
|
411
|
+
) {
|
|
412
|
+
this.errstr = 'SCRIPT_ERR_SIG_DER_INVALID_FORMAT';
|
|
413
|
+
return false;
|
|
414
|
+
} else if ((this.flags & Interpreter.SCRIPT_VERIFY_LOW_S) !== 0) {
|
|
415
|
+
sig = Signature.fromTxFormat(buf);
|
|
416
|
+
if (!sig.hasLowS()) {
|
|
417
|
+
this.errstr = 'SCRIPT_ERR_SIG_DER_HIGH_S';
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
} else if ((this.flags & Interpreter.SCRIPT_VERIFY_STRICTENC) !== 0) {
|
|
421
|
+
sig = Signature.fromTxFormat(buf);
|
|
422
|
+
if (!sig.hasDefinedHashtype()) {
|
|
423
|
+
this.errstr = 'SCRIPT_ERR_SIG_HASHTYPE';
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return true;
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Checks if the provided public key buffer is valid according to strict encoding rules.
|
|
434
|
+
* Sets an error message if validation fails under SCRIPT_VERIFY_STRICTENC flag.
|
|
435
|
+
* @param {Buffer} buf - The public key buffer to validate.
|
|
436
|
+
* @returns {boolean} True if valid, false otherwise (with error string set).
|
|
437
|
+
*/
|
|
438
|
+
Interpreter.prototype.checkPubkeyEncoding = function (buf) {
|
|
439
|
+
if ((this.flags & Interpreter.SCRIPT_VERIFY_STRICTENC) !== 0 && !PublicKey.isValid(buf)) {
|
|
440
|
+
this.errstr = 'SCRIPT_ERR_PUBKEYTYPE';
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
return true;
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Checks if a buffer is minimally encoded (see https://github.com/bitcoincashorg/spec/blob/master/may-2018-reenabled-opcodes.md#op_bin2num) as a number.
|
|
448
|
+
* @param {Buffer} buf - The buffer to check.
|
|
449
|
+
* @param {number} [nMaxNumSize=Interpreter.MAXIMUM_ELEMENT_SIZE] - Maximum allowed size for the buffer.
|
|
450
|
+
* @returns {boolean} True if the buffer is minimally encoded, false otherwise.
|
|
451
|
+
* @private
|
|
452
|
+
*/
|
|
453
|
+
Interpreter._isMinimallyEncoded = function (buf, nMaxNumSize) {
|
|
454
|
+
nMaxNumSize = nMaxNumSize || Interpreter.MAXIMUM_ELEMENT_SIZE;
|
|
455
|
+
if (buf.length > nMaxNumSize) {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (buf.length > 0) {
|
|
460
|
+
// Check that the number is encoded with the minimum possible number
|
|
461
|
+
// of bytes.
|
|
462
|
+
//
|
|
463
|
+
// If the most-significant-byte - excluding the sign bit - is zero
|
|
464
|
+
// then we're not minimal. Note how this test also rejects the
|
|
465
|
+
// negative-zero encoding, 0x80.
|
|
466
|
+
if ((buf[buf.length - 1] & 0x7f) === 0) {
|
|
467
|
+
// One exception: if there's more than one byte and the most
|
|
468
|
+
// significant bit of the second-most-significant-byte is set it
|
|
469
|
+
// would conflict with the sign bit. An example of this case is
|
|
470
|
+
// +-255, which encode to 0xff00 and 0xff80 respectively.
|
|
471
|
+
// (big-endian).
|
|
472
|
+
if (buf.length <= 1 || (buf[buf.length - 2] & 0x80) === 0) {
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return true;
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Minimally encodes a buffer by removing unnecessary trailing zeros.
|
|
483
|
+
*
|
|
484
|
+
* This function implements minimal encoding rules for script numbers:
|
|
485
|
+
* - Empty buffer remains empty
|
|
486
|
+
* - Last byte must not be 0x00 or 0x80 unless necessary
|
|
487
|
+
* - Single zero byte encodes as empty buffer
|
|
488
|
+
* - Preserves sign bit when trimming
|
|
489
|
+
*
|
|
490
|
+
* @param {Buffer} buf - The input buffer to encode
|
|
491
|
+
* @returns {Buffer} Minimally encoded buffer (may be empty)
|
|
492
|
+
*/
|
|
493
|
+
Interpreter._minimallyEncode = function (buf) {
|
|
494
|
+
if (buf.length === 0) {
|
|
495
|
+
return buf;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// If the last byte is not 0x00 or 0x80, we are minimally encoded.
|
|
499
|
+
var last = buf[buf.length - 1];
|
|
500
|
+
if (last & 0x7f) {
|
|
501
|
+
return buf;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// If the script is one byte long, then we have a zero, which encodes as an
|
|
505
|
+
// empty array.
|
|
506
|
+
if (buf.length === 1) {
|
|
507
|
+
return Buffer.from('');
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// If the next byte has it sign bit set, then we are minimaly encoded.
|
|
511
|
+
if (buf[buf.length - 2] & 0x80) {
|
|
512
|
+
return buf;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// We are not minimally encoded, we need to figure out how much to trim.
|
|
516
|
+
for (var i = buf.length - 1; i > 0; i--) {
|
|
517
|
+
// We found a non zero byte, time to encode.
|
|
518
|
+
if (buf[i - 1] !== 0) {
|
|
519
|
+
if (buf[i - 1] & 0x80) {
|
|
520
|
+
// We found a byte with it sign bit set so we need one more
|
|
521
|
+
// byte.
|
|
522
|
+
buf[i++] = last;
|
|
523
|
+
} else {
|
|
524
|
+
// the sign bit is clear, we can use it.
|
|
525
|
+
buf[i - 1] |= last;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return buf.slice(0, i);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// If we found the whole thing is zeros, then we have a zero.
|
|
533
|
+
return Buffer.from('');
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Evaluates a script by executing each opcode step-by-step.
|
|
539
|
+
* Performs size checks on the script and stacks before execution.
|
|
540
|
+
*
|
|
541
|
+
* Based on bitcoind's EvalScript function, with the inner loop moved to `Interpreter.prototype.step()`
|
|
542
|
+
* bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104
|
|
543
|
+
* @param {string} scriptType - The type of script being evaluated
|
|
544
|
+
* @returns {boolean} True if evaluation succeeds, false if any error occurs
|
|
545
|
+
* @throws {Error} If an unknown error occurs during evaluation
|
|
546
|
+
*/
|
|
547
|
+
Interpreter.prototype.evaluate = function (scriptType) {
|
|
548
|
+
// TODO: script size should be configurable. no magic numbers
|
|
549
|
+
if (this.script.toBuffer().length > Interpreter.MAX_SCRIPT_SIZE) {
|
|
550
|
+
this.errstr = 'SCRIPT_ERR_SCRIPT_SIZE';
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
try {
|
|
555
|
+
while (!this.returned && this.pc < this.script.chunks.length) {
|
|
556
|
+
// fExec: if the opcode will be executed, i.e., not in a false branch
|
|
557
|
+
let thisStep = {
|
|
558
|
+
pc: this.pc,
|
|
559
|
+
fExec: this.vfExec.indexOf(false) === -1,
|
|
560
|
+
opcode: Opcode.fromNumber(this.script.chunks[this.pc].opcodenum),
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
var fSuccess = this.step(scriptType);
|
|
564
|
+
|
|
565
|
+
this._callbackStep(thisStep);
|
|
566
|
+
|
|
567
|
+
if (!fSuccess) {
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Size limits
|
|
573
|
+
if (this.stack.length + this.altstack.length > 1000) {
|
|
574
|
+
this.errstr = 'SCRIPT_ERR_STACK_SIZE';
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
} catch (e) {
|
|
578
|
+
this.errstr = 'SCRIPT_ERR_UNKNOWN_ERROR: ' + e;
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (this.vfExec.length > 0) {
|
|
583
|
+
this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL';
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return true;
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Handles step callback execution for the interpreter.
|
|
592
|
+
* @private
|
|
593
|
+
* @param {Object} thisStep - The current step object to pass to the listener
|
|
594
|
+
* @throws {Error} Logs any errors that occur during callback execution
|
|
595
|
+
*/
|
|
596
|
+
Interpreter.prototype._callbackStep = function (thisStep) {
|
|
597
|
+
if (typeof this.stepListener === 'function') {
|
|
598
|
+
try {
|
|
599
|
+
this.stepListener(thisStep);
|
|
600
|
+
} catch (err) {
|
|
601
|
+
console.log(`Error in Step callback:${err}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Handles stack callbacks by invoking the registered stack listener function.
|
|
609
|
+
* If an error occurs during callback execution, logs the error along with PC and opcode details.
|
|
610
|
+
* @param {Stack} stack - The current execution stack
|
|
611
|
+
* @param {number} pc - Program counter value
|
|
612
|
+
* @param {string} scriptType - Type of script being executed
|
|
613
|
+
* @private
|
|
614
|
+
*/
|
|
615
|
+
Interpreter.prototype._callbackStack = function (stack, pc, scriptType) {
|
|
616
|
+
if (typeof this.stackListener === 'function') {
|
|
617
|
+
try {
|
|
618
|
+
this.stackListener(stack, pc, scriptType);
|
|
619
|
+
} catch (err) {
|
|
620
|
+
var chunk = this.script.chunks[pc];
|
|
621
|
+
console.error(
|
|
622
|
+
`Error: ${err} in _updateStack pc: ${pc}, opcode ${Opcode.fromNumber(
|
|
623
|
+
chunk.opcodenum,
|
|
624
|
+
).toSafeString()}`,
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Checks a locktime parameter with the transaction's locktime.
|
|
632
|
+
* There are two times of nLockTime: lock-by-blockheight and lock-by-blocktime,
|
|
633
|
+
* distinguished by whether nLockTime < LOCKTIME_THRESHOLD = 500000000
|
|
634
|
+
*
|
|
635
|
+
* See the corresponding code on bitcoin core:
|
|
636
|
+
* https://github.com/bitcoin/bitcoin/blob/ffd75adce01a78b3461b3ff05bcc2b530a9ce994/src/script/interpreter.cpp#L1129
|
|
637
|
+
*
|
|
638
|
+
* @param {BN} nLockTime the locktime read from the script
|
|
639
|
+
* @return {boolean} true if the transaction's locktime is less than or equal to
|
|
640
|
+
* the transaction's locktime
|
|
641
|
+
*/
|
|
642
|
+
Interpreter.prototype.checkLockTime = function (nLockTime) {
|
|
643
|
+
// We want to compare apples to apples, so fail the script
|
|
644
|
+
// unless the type of nLockTime being tested is the same as
|
|
645
|
+
// the nLockTime in the transaction.
|
|
646
|
+
if (
|
|
647
|
+
!(
|
|
648
|
+
(this.tx.nLockTime < Interpreter.LOCKTIME_THRESHOLD &&
|
|
649
|
+
nLockTime.lt(Interpreter.LOCKTIME_THRESHOLD_BN)) ||
|
|
650
|
+
(this.tx.nLockTime >= Interpreter.LOCKTIME_THRESHOLD &&
|
|
651
|
+
nLockTime.gte(Interpreter.LOCKTIME_THRESHOLD_BN))
|
|
652
|
+
)
|
|
653
|
+
) {
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Now that we know we're comparing apples-to-apples, the
|
|
658
|
+
// comparison is a simple numeric one.
|
|
659
|
+
if (nLockTime.gt(new BN(this.tx.nLockTime))) {
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Finally the nLockTime feature can be disabled and thus
|
|
664
|
+
// CHECKLOCKTIMEVERIFY bypassed if every txin has been
|
|
665
|
+
// finalized by setting nSequence to maxint. The
|
|
666
|
+
// transaction would be allowed into the blockchain, making
|
|
667
|
+
// the opcode ineffective.
|
|
668
|
+
//
|
|
669
|
+
// Testing if this vin is not final is sufficient to
|
|
670
|
+
// prevent this condition. Alternatively we could test all
|
|
671
|
+
// inputs, but testing just this input minimizes the data
|
|
672
|
+
// required to prove correct CHECKLOCKTIMEVERIFY execution.
|
|
673
|
+
if (this.tx.inputs[this.nin].isFinal()) {
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
return true;
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Checks a sequence parameter with the transaction's sequence.
|
|
682
|
+
* @param {BN} nSequence the sequence read from the script
|
|
683
|
+
* @return {boolean} true if the transaction's sequence is less than or equal to
|
|
684
|
+
* the transaction's sequence
|
|
685
|
+
*/
|
|
686
|
+
Interpreter.prototype.checkSequence = function (nSequence) {
|
|
687
|
+
// Relative lock times are supported by comparing the passed in operand to
|
|
688
|
+
// the sequence number of the input.
|
|
689
|
+
var txToSequence = this.tx.inputs[this.nin].sequenceNumber;
|
|
690
|
+
|
|
691
|
+
// Fail if the transaction's version number is not set high enough to
|
|
692
|
+
// trigger BIP 68 rules.
|
|
693
|
+
if (this.tx.version < 2) {
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Sequence numbers with their most significant bit set are not consensus
|
|
698
|
+
// constrained. Testing that the transaction's sequence number do not have
|
|
699
|
+
// this bit set prevents using this property to get around a
|
|
700
|
+
// CHECKSEQUENCEVERIFY check.
|
|
701
|
+
if (txToSequence & Interpreter.SEQUENCE_LOCKTIME_DISABLE_FLAG) {
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Mask off any bits that do not have consensus-enforced meaning before
|
|
706
|
+
// doing the integer comparisons
|
|
707
|
+
var nLockTimeMask = Interpreter.SEQUENCE_LOCKTIME_TYPE_FLAG | Interpreter.SEQUENCE_LOCKTIME_MASK;
|
|
708
|
+
var txToSequenceMasked = new BN(txToSequence & nLockTimeMask);
|
|
709
|
+
var nSequenceMasked = nSequence.and(nLockTimeMask);
|
|
710
|
+
|
|
711
|
+
// There are two kinds of nSequence: lock-by-blockheight and
|
|
712
|
+
// lock-by-blocktime, distinguished by whether nSequenceMasked <
|
|
713
|
+
// CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG.
|
|
714
|
+
//
|
|
715
|
+
// We want to compare apples to apples, so fail the script unless the type
|
|
716
|
+
// of nSequenceMasked being tested is the same as the nSequenceMasked in the
|
|
717
|
+
// transaction.
|
|
718
|
+
var SEQUENCE_LOCKTIME_TYPE_FLAG_BN = new BN(Interpreter.SEQUENCE_LOCKTIME_TYPE_FLAG);
|
|
719
|
+
|
|
720
|
+
if (
|
|
721
|
+
!(
|
|
722
|
+
(txToSequenceMasked.lt(SEQUENCE_LOCKTIME_TYPE_FLAG_BN) &&
|
|
723
|
+
nSequenceMasked.lt(SEQUENCE_LOCKTIME_TYPE_FLAG_BN)) ||
|
|
724
|
+
(txToSequenceMasked.gte(SEQUENCE_LOCKTIME_TYPE_FLAG_BN) &&
|
|
725
|
+
nSequenceMasked.gte(SEQUENCE_LOCKTIME_TYPE_FLAG_BN))
|
|
726
|
+
)
|
|
727
|
+
) {
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Now that we know we're comparing apples-to-apples, the comparison is a
|
|
732
|
+
// simple numeric one.
|
|
733
|
+
if (nSequenceMasked.gt(txToSequenceMasked)) {
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
return true;
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
function padBufferToSize(buf, len) {
|
|
740
|
+
let b = buf;
|
|
741
|
+
while (b.length < len) {
|
|
742
|
+
b = Buffer.concat([Buffer.from([0x00]), b]);
|
|
743
|
+
}
|
|
744
|
+
return b;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Executes a single step in the script interpreter.
|
|
750
|
+
*
|
|
751
|
+
* This method processes the current opcode in the script, performs the corresponding operation,
|
|
752
|
+
* and updates the stack or interpreter state accordingly. It handles various opcode types including
|
|
753
|
+
* stack operations, arithmetic, bitwise logic, cryptographic operations, and control flow.
|
|
754
|
+
*
|
|
755
|
+
* Based on the inner loop of bitcoind's EvalScript function
|
|
756
|
+
* bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104
|
|
757
|
+
* @param {string} scriptType - The type of script being executed (e.g., scriptPubkey, scriptSig).
|
|
758
|
+
* @returns {boolean} Returns `true` if the step executed successfully, or `false` if an error occurred.
|
|
759
|
+
* Errors are stored in `this.errstr`.
|
|
760
|
+
*/
|
|
761
|
+
Interpreter.prototype.step = function (scriptType) {
|
|
762
|
+
var self = this;
|
|
763
|
+
|
|
764
|
+
function stacktop(i) {
|
|
765
|
+
return self.stack.stacktop(i);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function vartop(i) {
|
|
769
|
+
return self.stack.vartop(i);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
function isOpcodeDisabled(opcode) {
|
|
773
|
+
switch (opcode) {
|
|
774
|
+
case Opcode.OP_2MUL:
|
|
775
|
+
case Opcode.OP_2DIV:
|
|
776
|
+
// Disabled opcodes.
|
|
777
|
+
return true;
|
|
778
|
+
|
|
779
|
+
case Opcode.OP_INVERT:
|
|
780
|
+
case Opcode.OP_MUL:
|
|
781
|
+
case Opcode.OP_LSHIFT:
|
|
782
|
+
case Opcode.OP_RSHIFT:
|
|
783
|
+
// Opcodes that have been reenabled.
|
|
784
|
+
if ((self.flags & Interpreter.SCRIPT_ENABLE_MAGNETIC_OPCODES) === 0) {
|
|
785
|
+
return true;
|
|
786
|
+
}
|
|
787
|
+
break;
|
|
788
|
+
case Opcode.OP_DIV:
|
|
789
|
+
case Opcode.OP_MOD:
|
|
790
|
+
case Opcode.OP_SPLIT:
|
|
791
|
+
case Opcode.OP_CAT:
|
|
792
|
+
case Opcode.OP_AND:
|
|
793
|
+
case Opcode.OP_OR:
|
|
794
|
+
case Opcode.OP_XOR:
|
|
795
|
+
case Opcode.OP_BIN2NUM:
|
|
796
|
+
case Opcode.OP_NUM2BIN:
|
|
797
|
+
// Opcodes that have been reenabled.
|
|
798
|
+
if ((self.flags & Interpreter.SCRIPT_ENABLE_MONOLITH_OPCODES) === 0) {
|
|
799
|
+
return true;
|
|
800
|
+
}
|
|
801
|
+
break;
|
|
802
|
+
default:
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
return false;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
var fRequireMinimal = (this.flags & Interpreter.SCRIPT_VERIFY_MINIMALDATA) !== 0;
|
|
810
|
+
|
|
811
|
+
// bool fExec = !count(vfExec.begin(), vfExec.end(), false);
|
|
812
|
+
|
|
813
|
+
var buf, buf1, buf2, spliced, n, x1, x2, bn, bn1, bn2, bufSig, bufPubkey, subscript;
|
|
814
|
+
var sig, pubkey;
|
|
815
|
+
var fValue, fSuccess;
|
|
816
|
+
var var1, var2, var3;
|
|
817
|
+
|
|
818
|
+
// Read instruction
|
|
819
|
+
var chunk = this.script.chunks[this.pc];
|
|
820
|
+
this.pc++;
|
|
821
|
+
var opcodenum = chunk.opcodenum;
|
|
822
|
+
if (_.isUndefined(opcodenum)) {
|
|
823
|
+
this.errstr = 'SCRIPT_ERR_UNDEFINED_OPCODE';
|
|
824
|
+
return false;
|
|
825
|
+
}
|
|
826
|
+
if (chunk.buf && chunk.buf.length > Interpreter.MAX_SCRIPT_ELEMENT_SIZE) {
|
|
827
|
+
this.errstr = 'SCRIPT_ERR_PUSH_SIZE';
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Do not execute instructions if Genesis OP_RETURN was found in executed branches.
|
|
832
|
+
var fExec =
|
|
833
|
+
this.vfExec.indexOf(false) === -1 &&
|
|
834
|
+
(!this.nonTopLevelReturnAfterGenesis || opcodenum === Opcode.OP_RETURN);
|
|
835
|
+
|
|
836
|
+
// Note how Opcode.OP_RESERVED does not count towards the opcode limit.
|
|
837
|
+
if (opcodenum > Opcode.OP_16 && ++this.nOpCount > Interpreter.MAX_OPCODE_COUNT) {
|
|
838
|
+
this.errstr = 'SCRIPT_ERR_OP_COUNT';
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
if (isOpcodeDisabled(opcodenum)) {
|
|
843
|
+
this.errstr = 'SCRIPT_ERR_DISABLED_OPCODE';
|
|
844
|
+
return false;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if (fExec && opcodenum >= 0 && opcodenum <= Opcode.OP_PUSHDATA4) {
|
|
848
|
+
if (fRequireMinimal && !this.script.checkMinimalPush(this.pc - 1)) {
|
|
849
|
+
this.errstr = 'SCRIPT_ERR_MINIMALDATA';
|
|
850
|
+
return false;
|
|
851
|
+
}
|
|
852
|
+
if (!chunk.buf) {
|
|
853
|
+
this.stack.push(Interpreter.getFalse());
|
|
854
|
+
} else if (chunk.len !== chunk.buf.length) {
|
|
855
|
+
throw new Error(
|
|
856
|
+
`Length of push value not equal to length of data (${chunk.len},${chunk.buf.length})`,
|
|
857
|
+
);
|
|
858
|
+
} else {
|
|
859
|
+
this.stack.push(chunk.buf);
|
|
860
|
+
}
|
|
861
|
+
} else if (fExec || (Opcode.OP_IF <= opcodenum && opcodenum <= Opcode.OP_ENDIF)) {
|
|
862
|
+
switch (opcodenum) {
|
|
863
|
+
// Push value
|
|
864
|
+
case Opcode.OP_1NEGATE:
|
|
865
|
+
case Opcode.OP_1:
|
|
866
|
+
case Opcode.OP_2:
|
|
867
|
+
case Opcode.OP_3:
|
|
868
|
+
case Opcode.OP_4:
|
|
869
|
+
case Opcode.OP_5:
|
|
870
|
+
case Opcode.OP_6:
|
|
871
|
+
case Opcode.OP_7:
|
|
872
|
+
case Opcode.OP_8:
|
|
873
|
+
case Opcode.OP_9:
|
|
874
|
+
case Opcode.OP_10:
|
|
875
|
+
case Opcode.OP_11:
|
|
876
|
+
case Opcode.OP_12:
|
|
877
|
+
case Opcode.OP_13:
|
|
878
|
+
case Opcode.OP_14:
|
|
879
|
+
case Opcode.OP_15:
|
|
880
|
+
case Opcode.OP_16:
|
|
881
|
+
// ( -- value)
|
|
882
|
+
// ScriptNum bn((int)opcode - (int)(Opcode.OP_1 - 1));
|
|
883
|
+
n = opcodenum - (Opcode.OP_1 - 1);
|
|
884
|
+
buf = new BN(n).toScriptNumBuffer();
|
|
885
|
+
this.stack.push(buf);
|
|
886
|
+
// The result of these opcodes should always be the minimal way to push the data
|
|
887
|
+
// they push, so no need for a CheckMinimalPush here.
|
|
888
|
+
break;
|
|
889
|
+
|
|
890
|
+
//
|
|
891
|
+
// Control
|
|
892
|
+
//
|
|
893
|
+
case Opcode.OP_NOP:
|
|
894
|
+
break;
|
|
895
|
+
|
|
896
|
+
case Opcode.OP_NOP2:
|
|
897
|
+
case Opcode.OP_CHECKLOCKTIMEVERIFY:
|
|
898
|
+
if (!(this.flags & Interpreter.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) {
|
|
899
|
+
// not enabled; treat as a NOP2
|
|
900
|
+
if (this.flags & Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
|
|
901
|
+
this.errstr = 'SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS';
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
break;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
if (this.stack.length < 1) {
|
|
908
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
909
|
+
return false;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Note that elsewhere numeric opcodes are limited to
|
|
913
|
+
// operands in the range -2**31+1 to 2**31-1, however it is
|
|
914
|
+
// legal for opcodes to produce results exceeding that
|
|
915
|
+
// range. This limitation is implemented by CScriptNum's
|
|
916
|
+
// default 4-byte limit.
|
|
917
|
+
//
|
|
918
|
+
// If we kept to that limit we'd have a year 2038 problem,
|
|
919
|
+
// even though the nLockTime field in transactions
|
|
920
|
+
// themselves is uint32 which only becomes meaningless
|
|
921
|
+
// after the year 2106.
|
|
922
|
+
//
|
|
923
|
+
// Thus as a special case we tell CScriptNum to accept up
|
|
924
|
+
// to 5-byte bignums, which are good until 2**39-1, well
|
|
925
|
+
// beyond the 2**32-1 limit of the nLockTime field itself.
|
|
926
|
+
var nLockTime = BN.fromScriptNumBuffer(this.stack.stacktop(-1), fRequireMinimal, 5);
|
|
927
|
+
|
|
928
|
+
// In the rare event that the argument may be < 0 due to
|
|
929
|
+
// some arithmetic being done first, you can always use
|
|
930
|
+
// 0 MAX CHECKLOCKTIMEVERIFY.
|
|
931
|
+
if (nLockTime.lt(new BN(0))) {
|
|
932
|
+
this.errstr = 'SCRIPT_ERR_NEGATIVE_LOCKTIME';
|
|
933
|
+
return false;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// Actually compare the specified lock time with the transaction.
|
|
937
|
+
if (!this.checkLockTime(nLockTime)) {
|
|
938
|
+
this.errstr = 'SCRIPT_ERR_UNSATISFIED_LOCKTIME';
|
|
939
|
+
return false;
|
|
940
|
+
}
|
|
941
|
+
break;
|
|
942
|
+
|
|
943
|
+
case Opcode.OP_NOP3:
|
|
944
|
+
case Opcode.OP_CHECKSEQUENCEVERIFY:
|
|
945
|
+
if (!(this.flags & Interpreter.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) {
|
|
946
|
+
// not enabled; treat as a NOP3
|
|
947
|
+
if (this.flags & Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
|
|
948
|
+
this.errstr = 'SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS';
|
|
949
|
+
return false;
|
|
950
|
+
}
|
|
951
|
+
break;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
if (this.stack.length < 1) {
|
|
955
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
956
|
+
return false;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// nSequence, like nLockTime, is a 32-bit unsigned
|
|
960
|
+
// integer field. See the comment in CHECKLOCKTIMEVERIFY
|
|
961
|
+
// regarding 5-byte numeric operands.
|
|
962
|
+
|
|
963
|
+
var nSequence = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal, 5);
|
|
964
|
+
|
|
965
|
+
// In the rare event that the argument may be < 0 due to
|
|
966
|
+
// some arithmetic being done first, you can always use
|
|
967
|
+
// 0 MAX CHECKSEQUENCEVERIFY.
|
|
968
|
+
if (nSequence.lt(new BN(0))) {
|
|
969
|
+
this.errstr = 'SCRIPT_ERR_NEGATIVE_LOCKTIME';
|
|
970
|
+
return false;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// To provide for future soft-fork extensibility, if the
|
|
974
|
+
// operand has the disabled lock-time flag set,
|
|
975
|
+
// CHECKSEQUENCEVERIFY behaves as a NOP.
|
|
976
|
+
if ((nSequence & Interpreter.SEQUENCE_LOCKTIME_DISABLE_FLAG) !== 0) {
|
|
977
|
+
break;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// Actually compare the specified lock time with the transaction.
|
|
981
|
+
if (!this.checkSequence(nSequence)) {
|
|
982
|
+
this.errstr = 'SCRIPT_ERR_UNSATISFIED_LOCKTIME';
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
break;
|
|
986
|
+
|
|
987
|
+
case Opcode.OP_NOP1:
|
|
988
|
+
case Opcode.OP_NOP4:
|
|
989
|
+
case Opcode.OP_NOP5:
|
|
990
|
+
case Opcode.OP_NOP6:
|
|
991
|
+
case Opcode.OP_NOP7:
|
|
992
|
+
case Opcode.OP_NOP8:
|
|
993
|
+
case Opcode.OP_NOP9:
|
|
994
|
+
case Opcode.OP_NOP10:
|
|
995
|
+
if (this.flags & Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
|
|
996
|
+
this.errstr = 'SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS';
|
|
997
|
+
return false;
|
|
998
|
+
}
|
|
999
|
+
break;
|
|
1000
|
+
|
|
1001
|
+
case Opcode.OP_IF:
|
|
1002
|
+
case Opcode.OP_NOTIF:
|
|
1003
|
+
// <expression> if [statements] [else [statements]] endif
|
|
1004
|
+
// bool fValue = false;
|
|
1005
|
+
fValue = false;
|
|
1006
|
+
if (fExec) {
|
|
1007
|
+
if (this.stack.length < 1) {
|
|
1008
|
+
this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL';
|
|
1009
|
+
return false;
|
|
1010
|
+
}
|
|
1011
|
+
buf = stacktop(-1);
|
|
1012
|
+
|
|
1013
|
+
fValue = Interpreter.castToBool(buf);
|
|
1014
|
+
if (opcodenum === Opcode.OP_NOTIF) {
|
|
1015
|
+
fValue = !fValue;
|
|
1016
|
+
}
|
|
1017
|
+
this.stack.pop();
|
|
1018
|
+
}
|
|
1019
|
+
this.vfExec.push(fValue);
|
|
1020
|
+
this.vfElse.push(false);
|
|
1021
|
+
break;
|
|
1022
|
+
|
|
1023
|
+
case Opcode.OP_ELSE:
|
|
1024
|
+
if (this.vfExec.length === 0 || this.vfElse[this.vfElse.length - 1] === true) {
|
|
1025
|
+
this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL';
|
|
1026
|
+
return false;
|
|
1027
|
+
}
|
|
1028
|
+
this.vfExec[this.vfExec.length - 1] = !this.vfExec[this.vfExec.length - 1];
|
|
1029
|
+
this.vfElse[this.vfElse.length - 1] = true;
|
|
1030
|
+
break;
|
|
1031
|
+
|
|
1032
|
+
case Opcode.OP_ENDIF:
|
|
1033
|
+
if (this.vfExec.length === 0) {
|
|
1034
|
+
this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL';
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
this.vfExec.pop();
|
|
1038
|
+
this.vfElse.pop();
|
|
1039
|
+
break;
|
|
1040
|
+
|
|
1041
|
+
case Opcode.OP_VERIFY:
|
|
1042
|
+
// (true -- ) or
|
|
1043
|
+
// (false -- false) and return
|
|
1044
|
+
if (this.stack.length < 1) {
|
|
1045
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
1048
|
+
buf = stacktop(-1);
|
|
1049
|
+
fValue = Interpreter.castToBool(buf);
|
|
1050
|
+
if (fValue) {
|
|
1051
|
+
this.stack.pop();
|
|
1052
|
+
} else {
|
|
1053
|
+
this.errstr = 'SCRIPT_ERR_VERIFY';
|
|
1054
|
+
return false;
|
|
1055
|
+
}
|
|
1056
|
+
break;
|
|
1057
|
+
|
|
1058
|
+
case Opcode.OP_RETURN:
|
|
1059
|
+
// utxo_after_genesis
|
|
1060
|
+
if (this.vfExec.length === 0) {
|
|
1061
|
+
// Terminate the execution as successful. The remaining of the script does not affect the validity (even in
|
|
1062
|
+
// presence of unbalanced IFs, invalid opcodes etc)
|
|
1063
|
+
this.returned = true;
|
|
1064
|
+
return true;
|
|
1065
|
+
}
|
|
1066
|
+
// op_return encountered inside if statement after genesis --> check for invalid grammar
|
|
1067
|
+
this.nonTopLevelReturnAfterGenesis = true;
|
|
1068
|
+
break;
|
|
1069
|
+
|
|
1070
|
+
//
|
|
1071
|
+
// Stack ops
|
|
1072
|
+
//
|
|
1073
|
+
case Opcode.OP_TOALTSTACK:
|
|
1074
|
+
if (this.stack.length < 1) {
|
|
1075
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1076
|
+
return false;
|
|
1077
|
+
}
|
|
1078
|
+
var1 = vartop(-1);
|
|
1079
|
+
this.altstack.push(this.stack.pop(), var1);
|
|
1080
|
+
break;
|
|
1081
|
+
|
|
1082
|
+
case Opcode.OP_FROMALTSTACK:
|
|
1083
|
+
if (this.altstack.length < 1) {
|
|
1084
|
+
this.errstr = 'SCRIPT_ERR_INVALID_ALTSTACK_OPERATION';
|
|
1085
|
+
return false;
|
|
1086
|
+
}
|
|
1087
|
+
const varAlt = this.altstack.vartop(-1);
|
|
1088
|
+
this.stack.push(this.altstack.pop(), varAlt);
|
|
1089
|
+
break;
|
|
1090
|
+
|
|
1091
|
+
case Opcode.OP_2DROP:
|
|
1092
|
+
// (x1 x2 -- )
|
|
1093
|
+
if (this.stack.length < 2) {
|
|
1094
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1095
|
+
return false;
|
|
1096
|
+
}
|
|
1097
|
+
this.stack.pop();
|
|
1098
|
+
this.stack.pop();
|
|
1099
|
+
break;
|
|
1100
|
+
|
|
1101
|
+
case Opcode.OP_2DUP:
|
|
1102
|
+
// (x1 x2 -- x1 x2 x1 x2)
|
|
1103
|
+
if (this.stack.length < 2) {
|
|
1104
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1105
|
+
return false;
|
|
1106
|
+
}
|
|
1107
|
+
buf1 = stacktop(-2);
|
|
1108
|
+
buf2 = stacktop(-1);
|
|
1109
|
+
var1 = vartop(-2);
|
|
1110
|
+
var2 = vartop(-1);
|
|
1111
|
+
this.stack.push(Buffer.from(buf1), `$${var1}`);
|
|
1112
|
+
this.stack.push(Buffer.from(buf2), `$${var2}`);
|
|
1113
|
+
break;
|
|
1114
|
+
|
|
1115
|
+
case Opcode.OP_3DUP:
|
|
1116
|
+
// (x1 x2 x3 -- x1 x2 x3 x1 x2 x3)
|
|
1117
|
+
if (this.stack.length < 3) {
|
|
1118
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1119
|
+
return false;
|
|
1120
|
+
}
|
|
1121
|
+
buf1 = stacktop(-3);
|
|
1122
|
+
buf2 = stacktop(-2);
|
|
1123
|
+
var buf3 = stacktop(-1);
|
|
1124
|
+
var1 = vartop(-3);
|
|
1125
|
+
var2 = vartop(-2);
|
|
1126
|
+
var3 = vartop(-1);
|
|
1127
|
+
this.stack.push(Buffer.from(buf1), `$${var1}`);
|
|
1128
|
+
this.stack.push(Buffer.from(buf2), `$${var2}`);
|
|
1129
|
+
this.stack.push(Buffer.from(buf3), `$${var3}`);
|
|
1130
|
+
break;
|
|
1131
|
+
|
|
1132
|
+
case Opcode.OP_2OVER:
|
|
1133
|
+
// (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
|
|
1134
|
+
if (this.stack.length < 4) {
|
|
1135
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1136
|
+
return false;
|
|
1137
|
+
}
|
|
1138
|
+
buf1 = stacktop(-4);
|
|
1139
|
+
buf2 = stacktop(-3);
|
|
1140
|
+
var1 = vartop(-4);
|
|
1141
|
+
var2 = vartop(-3);
|
|
1142
|
+
this.stack.push(Buffer.from(buf1), `$${var1}`);
|
|
1143
|
+
this.stack.push(Buffer.from(buf2), `$${var2}`);
|
|
1144
|
+
break;
|
|
1145
|
+
|
|
1146
|
+
case Opcode.OP_2ROT:
|
|
1147
|
+
// (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
|
|
1148
|
+
if (this.stack.length < 6) {
|
|
1149
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1150
|
+
return false;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
var1 = vartop(-6);
|
|
1154
|
+
var2 = vartop(-5);
|
|
1155
|
+
|
|
1156
|
+
spliced = this.stack.splice(this.stack.length - 6, 2);
|
|
1157
|
+
this.stack.push(spliced[0], var1);
|
|
1158
|
+
this.stack.push(spliced[1], var2);
|
|
1159
|
+
break;
|
|
1160
|
+
|
|
1161
|
+
case Opcode.OP_2SWAP:
|
|
1162
|
+
// (x1 x2 x3 x4 -- x3 x4 x1 x2)
|
|
1163
|
+
if (this.stack.length < 4) {
|
|
1164
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1165
|
+
return false;
|
|
1166
|
+
}
|
|
1167
|
+
var1 = vartop(-4);
|
|
1168
|
+
var2 = vartop(-3);
|
|
1169
|
+
spliced = this.stack.splice(this.stack.length - 4, 2);
|
|
1170
|
+
this.stack.push(spliced[0], var1);
|
|
1171
|
+
this.stack.push(spliced[1], var2);
|
|
1172
|
+
break;
|
|
1173
|
+
|
|
1174
|
+
case Opcode.OP_IFDUP:
|
|
1175
|
+
// (x - 0 | x x)
|
|
1176
|
+
if (this.stack.length < 1) {
|
|
1177
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1178
|
+
return false;
|
|
1179
|
+
}
|
|
1180
|
+
buf = stacktop(-1);
|
|
1181
|
+
fValue = Interpreter.castToBool(buf);
|
|
1182
|
+
if (fValue) {
|
|
1183
|
+
var1 = vartop(-1);
|
|
1184
|
+
this.stack.push(Buffer.from(buf), `$${var1}`);
|
|
1185
|
+
}
|
|
1186
|
+
break;
|
|
1187
|
+
|
|
1188
|
+
case Opcode.OP_DEPTH:
|
|
1189
|
+
// -- stacksize
|
|
1190
|
+
buf = new BN(this.stack.length).toScriptNumBuffer();
|
|
1191
|
+
this.stack.push(buf, '$depth');
|
|
1192
|
+
break;
|
|
1193
|
+
|
|
1194
|
+
case Opcode.OP_DROP:
|
|
1195
|
+
// (x -- )
|
|
1196
|
+
if (this.stack.length < 1) {
|
|
1197
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1198
|
+
return false;
|
|
1199
|
+
}
|
|
1200
|
+
this.stack.pop();
|
|
1201
|
+
break;
|
|
1202
|
+
|
|
1203
|
+
case Opcode.OP_DUP:
|
|
1204
|
+
// (x -- x x)
|
|
1205
|
+
if (this.stack.length < 1) {
|
|
1206
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1207
|
+
return false;
|
|
1208
|
+
}
|
|
1209
|
+
var1 = vartop(-1);
|
|
1210
|
+
this.stack.push(Buffer.from(stacktop(-1)), `$${var1}`);
|
|
1211
|
+
break;
|
|
1212
|
+
|
|
1213
|
+
case Opcode.OP_NIP:
|
|
1214
|
+
// (x1 x2 -- x2)
|
|
1215
|
+
if (this.stack.length < 2) {
|
|
1216
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1217
|
+
return false;
|
|
1218
|
+
}
|
|
1219
|
+
this.stack.splice(this.stack.length - 2, 1);
|
|
1220
|
+
break;
|
|
1221
|
+
|
|
1222
|
+
case Opcode.OP_OVER:
|
|
1223
|
+
// (x1 x2 -- x1 x2 x1)
|
|
1224
|
+
if (this.stack.length < 2) {
|
|
1225
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
var2 = vartop(-2);
|
|
1229
|
+
this.stack.push(Buffer.from(stacktop(-2)), `$${var2}`);
|
|
1230
|
+
break;
|
|
1231
|
+
|
|
1232
|
+
case Opcode.OP_PICK:
|
|
1233
|
+
case Opcode.OP_ROLL:
|
|
1234
|
+
// (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
|
|
1235
|
+
// (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
|
|
1236
|
+
if (this.stack.length < 2) {
|
|
1237
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1238
|
+
return false;
|
|
1239
|
+
}
|
|
1240
|
+
buf = stacktop(-1);
|
|
1241
|
+
bn = BN.fromScriptNumBuffer(buf, fRequireMinimal, 4);
|
|
1242
|
+
n = bn.toNumber();
|
|
1243
|
+
this.stack.pop();
|
|
1244
|
+
if (n < 0 || n >= this.stack.length) {
|
|
1245
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1246
|
+
return false;
|
|
1247
|
+
}
|
|
1248
|
+
buf = stacktop(-n - 1);
|
|
1249
|
+
var1 = vartop(-n - 1);
|
|
1250
|
+
if (opcodenum === Opcode.OP_ROLL) {
|
|
1251
|
+
this.stack.splice(this.stack.length - n - 1, 1);
|
|
1252
|
+
this.stack.push(Buffer.from(buf), var1);
|
|
1253
|
+
} else {
|
|
1254
|
+
this.stack.push(Buffer.from(buf), `$${var1}`);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
break;
|
|
1258
|
+
|
|
1259
|
+
case Opcode.OP_ROT:
|
|
1260
|
+
// (x1 x2 x3 -- x2 x3 x1)
|
|
1261
|
+
// x2 x1 x3 after first swap
|
|
1262
|
+
// x2 x3 x1 after second swap
|
|
1263
|
+
if (this.stack.length < 3) {
|
|
1264
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1265
|
+
return false;
|
|
1266
|
+
}
|
|
1267
|
+
x1 = stacktop(-3);
|
|
1268
|
+
x2 = stacktop(-2);
|
|
1269
|
+
var x3 = stacktop(-1);
|
|
1270
|
+
var1 = vartop(-3);
|
|
1271
|
+
var2 = vartop(-2);
|
|
1272
|
+
var3 = vartop(-1);
|
|
1273
|
+
this.stack.write(-3, x2);
|
|
1274
|
+
this.stack.write(-2, x3);
|
|
1275
|
+
this.stack.write(-1, x1);
|
|
1276
|
+
this.stack.updateTopVars([var1, var3, var2]);
|
|
1277
|
+
break;
|
|
1278
|
+
|
|
1279
|
+
case Opcode.OP_SWAP:
|
|
1280
|
+
// (x1 x2 -- x2 x1)
|
|
1281
|
+
if (this.stack.length < 2) {
|
|
1282
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
x1 = stacktop(-2);
|
|
1286
|
+
x2 = stacktop(-1);
|
|
1287
|
+
var1 = vartop(-2);
|
|
1288
|
+
var2 = vartop(-1);
|
|
1289
|
+
this.stack.write(-2, x2);
|
|
1290
|
+
this.stack.write(-1, x1);
|
|
1291
|
+
this.stack.updateTopVars([var1, var2]);
|
|
1292
|
+
break;
|
|
1293
|
+
|
|
1294
|
+
case Opcode.OP_TUCK:
|
|
1295
|
+
// (x1 x2 -- x2 x1 x2)
|
|
1296
|
+
if (this.stack.length < 2) {
|
|
1297
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1298
|
+
return false;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
var1 = vartop(-2);
|
|
1302
|
+
var2 = vartop(-1);
|
|
1303
|
+
|
|
1304
|
+
this.stack.splice(this.stack.length - 2, 0, Buffer.from(stacktop(-1)));
|
|
1305
|
+
this.stack.updateTopVars([var2, var1, `$${var2}`]);
|
|
1306
|
+
break;
|
|
1307
|
+
|
|
1308
|
+
case Opcode.OP_SIZE:
|
|
1309
|
+
// (in -- in size)
|
|
1310
|
+
if (this.stack.length < 1) {
|
|
1311
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1312
|
+
return false;
|
|
1313
|
+
}
|
|
1314
|
+
bn = new BN(stacktop(-1).length);
|
|
1315
|
+
this.stack.push(bn.toScriptNumBuffer(), `$size`);
|
|
1316
|
+
break;
|
|
1317
|
+
|
|
1318
|
+
//
|
|
1319
|
+
// Bitwise logic
|
|
1320
|
+
//
|
|
1321
|
+
case Opcode.OP_AND:
|
|
1322
|
+
case Opcode.OP_OR:
|
|
1323
|
+
case Opcode.OP_XOR:
|
|
1324
|
+
// (x1 x2 - out)
|
|
1325
|
+
if (this.stack.length < 2) {
|
|
1326
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
buf1 = stacktop(-2);
|
|
1330
|
+
buf2 = stacktop(-1);
|
|
1331
|
+
|
|
1332
|
+
// Inputs must be the same size
|
|
1333
|
+
if (buf1.length !== buf2.length) {
|
|
1334
|
+
this.errstr = 'SCRIPT_ERR_INVALID_OPERAND_SIZE';
|
|
1335
|
+
return false;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
// To avoid allocating, we modify vch1 in place.
|
|
1339
|
+
switch (opcodenum) {
|
|
1340
|
+
case Opcode.OP_AND:
|
|
1341
|
+
for (let i = 0; i < buf1.length; i++) {
|
|
1342
|
+
buf1[i] &= buf2[i];
|
|
1343
|
+
}
|
|
1344
|
+
break;
|
|
1345
|
+
case Opcode.OP_OR:
|
|
1346
|
+
for (let i = 0; i < buf1.length; i++) {
|
|
1347
|
+
buf1[i] |= buf2[i];
|
|
1348
|
+
}
|
|
1349
|
+
break;
|
|
1350
|
+
case Opcode.OP_XOR:
|
|
1351
|
+
for (let i = 0; i < buf1.length; i++) {
|
|
1352
|
+
buf1[i] ^= buf2[i];
|
|
1353
|
+
}
|
|
1354
|
+
break;
|
|
1355
|
+
default:
|
|
1356
|
+
break;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// And pop vch2.
|
|
1360
|
+
this.stack.pop();
|
|
1361
|
+
break;
|
|
1362
|
+
|
|
1363
|
+
case Opcode.OP_INVERT:
|
|
1364
|
+
// (x -- out)
|
|
1365
|
+
if (this.stack.length < 1) {
|
|
1366
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1367
|
+
}
|
|
1368
|
+
buf = stacktop(-1);
|
|
1369
|
+
for (let i = 0; i < buf.length; i++) {
|
|
1370
|
+
buf[i] = ~buf[i];
|
|
1371
|
+
}
|
|
1372
|
+
break;
|
|
1373
|
+
|
|
1374
|
+
case Opcode.OP_LSHIFT:
|
|
1375
|
+
case Opcode.OP_RSHIFT:
|
|
1376
|
+
// (x n -- out)
|
|
1377
|
+
if (this.stack.length < 2) {
|
|
1378
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1379
|
+
return false;
|
|
1380
|
+
}
|
|
1381
|
+
buf1 = stacktop(-2);
|
|
1382
|
+
if (buf1.length === 0) {
|
|
1383
|
+
this.stack.pop();
|
|
1384
|
+
} else {
|
|
1385
|
+
bn1 = new BN(buf1);
|
|
1386
|
+
bn2 = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal, 4);
|
|
1387
|
+
n = bn2.toNumber();
|
|
1388
|
+
if (n < 0) {
|
|
1389
|
+
this.errstr = 'SCRIPT_ERR_INVALID_NUMBER_RANGE';
|
|
1390
|
+
return false;
|
|
1391
|
+
}
|
|
1392
|
+
this.stack.pop();
|
|
1393
|
+
this.stack.pop();
|
|
1394
|
+
let shifted;
|
|
1395
|
+
if (opcodenum === Opcode.OP_LSHIFT) {
|
|
1396
|
+
shifted = bn1.ushln(n);
|
|
1397
|
+
}
|
|
1398
|
+
if (opcodenum === Opcode.OP_RSHIFT) {
|
|
1399
|
+
shifted = bn1.ushrn(n);
|
|
1400
|
+
}
|
|
1401
|
+
// bitcoin client implementation of l/rshift is unconventional, therefore this implementation is a bit unconventional
|
|
1402
|
+
// bn library has shift functions however it expands the carried bits into a new byte
|
|
1403
|
+
// in contrast to the bitcoin client implementation which drops off the carried bits
|
|
1404
|
+
// in other words, if operand was 1 byte then we put 1 byte back on the stack instead of expanding to more shifted bytes
|
|
1405
|
+
let bufShifted = padBufferToSize(
|
|
1406
|
+
Buffer.from(shifted.toArray().slice(buf1.length * -1)),
|
|
1407
|
+
buf1.length,
|
|
1408
|
+
);
|
|
1409
|
+
this.stack.push(bufShifted);
|
|
1410
|
+
}
|
|
1411
|
+
break;
|
|
1412
|
+
|
|
1413
|
+
case Opcode.OP_EQUAL:
|
|
1414
|
+
case Opcode.OP_EQUALVERIFY:
|
|
1415
|
+
// case Opcode.OP_NOTEQUAL: // use Opcode.OP_NUMNOTEQUAL
|
|
1416
|
+
// (x1 x2 - bool)
|
|
1417
|
+
if (this.stack.length < 2) {
|
|
1418
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1419
|
+
return false;
|
|
1420
|
+
}
|
|
1421
|
+
buf1 = stacktop(-2);
|
|
1422
|
+
buf2 = stacktop(-1);
|
|
1423
|
+
var fEqual = buf1.toString('hex') === buf2.toString('hex');
|
|
1424
|
+
this.stack.pop();
|
|
1425
|
+
this.stack.pop();
|
|
1426
|
+
this.stack.push(fEqual ? Interpreter.getTrue() : Interpreter.getFalse());
|
|
1427
|
+
if (opcodenum === Opcode.OP_EQUALVERIFY) {
|
|
1428
|
+
if (fEqual) {
|
|
1429
|
+
this.stack.pop();
|
|
1430
|
+
} else {
|
|
1431
|
+
this.errstr = 'SCRIPT_ERR_EQUALVERIFY';
|
|
1432
|
+
return false;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
break;
|
|
1436
|
+
|
|
1437
|
+
//
|
|
1438
|
+
// Numeric
|
|
1439
|
+
//
|
|
1440
|
+
case Opcode.OP_1ADD:
|
|
1441
|
+
case Opcode.OP_1SUB:
|
|
1442
|
+
case Opcode.OP_NEGATE:
|
|
1443
|
+
case Opcode.OP_ABS:
|
|
1444
|
+
case Opcode.OP_NOT:
|
|
1445
|
+
case Opcode.OP_0NOTEQUAL:
|
|
1446
|
+
// (in -- out)
|
|
1447
|
+
if (this.stack.length < 1) {
|
|
1448
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1449
|
+
return false;
|
|
1450
|
+
}
|
|
1451
|
+
buf = stacktop(-1);
|
|
1452
|
+
bn = BN.fromScriptNumBuffer(buf, fRequireMinimal);
|
|
1453
|
+
switch (opcodenum) {
|
|
1454
|
+
case Opcode.OP_1ADD:
|
|
1455
|
+
bn = bn.add(BN.One);
|
|
1456
|
+
break;
|
|
1457
|
+
case Opcode.OP_1SUB:
|
|
1458
|
+
bn = bn.sub(BN.One);
|
|
1459
|
+
break;
|
|
1460
|
+
case Opcode.OP_NEGATE:
|
|
1461
|
+
bn = bn.neg();
|
|
1462
|
+
break;
|
|
1463
|
+
case Opcode.OP_ABS:
|
|
1464
|
+
if (bn.cmp(BN.Zero) < 0) {
|
|
1465
|
+
bn = bn.neg();
|
|
1466
|
+
}
|
|
1467
|
+
break;
|
|
1468
|
+
case Opcode.OP_NOT:
|
|
1469
|
+
bn = new BN((bn.cmp(BN.Zero) === 0) + 0);
|
|
1470
|
+
break;
|
|
1471
|
+
case Opcode.OP_0NOTEQUAL:
|
|
1472
|
+
bn = new BN((bn.cmp(BN.Zero) !== 0) + 0);
|
|
1473
|
+
break;
|
|
1474
|
+
// default: assert(!'invalid opcode'); break; // TODO: does this ever occur?
|
|
1475
|
+
}
|
|
1476
|
+
this.stack.pop();
|
|
1477
|
+
this.stack.push(bn.toScriptNumBuffer());
|
|
1478
|
+
break;
|
|
1479
|
+
|
|
1480
|
+
case Opcode.OP_ADD:
|
|
1481
|
+
case Opcode.OP_SUB:
|
|
1482
|
+
case Opcode.OP_MUL:
|
|
1483
|
+
case Opcode.OP_MOD:
|
|
1484
|
+
case Opcode.OP_DIV:
|
|
1485
|
+
case Opcode.OP_BOOLAND:
|
|
1486
|
+
case Opcode.OP_BOOLOR:
|
|
1487
|
+
case Opcode.OP_NUMEQUAL:
|
|
1488
|
+
case Opcode.OP_NUMEQUALVERIFY:
|
|
1489
|
+
case Opcode.OP_NUMNOTEQUAL:
|
|
1490
|
+
case Opcode.OP_LESSTHAN:
|
|
1491
|
+
case Opcode.OP_GREATERTHAN:
|
|
1492
|
+
case Opcode.OP_LESSTHANOREQUAL:
|
|
1493
|
+
case Opcode.OP_GREATERTHANOREQUAL:
|
|
1494
|
+
case Opcode.OP_MIN:
|
|
1495
|
+
case Opcode.OP_MAX:
|
|
1496
|
+
// (x1 x2 -- out)
|
|
1497
|
+
if (this.stack.length < 2) {
|
|
1498
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1499
|
+
return false;
|
|
1500
|
+
}
|
|
1501
|
+
bn1 = BN.fromScriptNumBuffer(stacktop(-2), fRequireMinimal);
|
|
1502
|
+
bn2 = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal);
|
|
1503
|
+
bn = new BN(0);
|
|
1504
|
+
|
|
1505
|
+
switch (opcodenum) {
|
|
1506
|
+
case Opcode.OP_ADD:
|
|
1507
|
+
bn = bn1.add(bn2);
|
|
1508
|
+
break;
|
|
1509
|
+
|
|
1510
|
+
case Opcode.OP_SUB:
|
|
1511
|
+
bn = bn1.sub(bn2);
|
|
1512
|
+
break;
|
|
1513
|
+
|
|
1514
|
+
case Opcode.OP_MUL:
|
|
1515
|
+
bn = bn1.mul(bn2);
|
|
1516
|
+
break;
|
|
1517
|
+
|
|
1518
|
+
case Opcode.OP_DIV:
|
|
1519
|
+
// denominator must not be 0
|
|
1520
|
+
if (bn2 === 0) {
|
|
1521
|
+
this.errstr = 'SCRIPT_ERR_DIV_BY_ZERO';
|
|
1522
|
+
return false;
|
|
1523
|
+
}
|
|
1524
|
+
bn = bn1.div(bn2);
|
|
1525
|
+
break;
|
|
1526
|
+
|
|
1527
|
+
case Opcode.OP_MOD:
|
|
1528
|
+
// divisor must not be 0
|
|
1529
|
+
if (bn2 === 0) {
|
|
1530
|
+
this.errstr = 'SCRIPT_ERR_DIV_BY_ZERO';
|
|
1531
|
+
return false;
|
|
1532
|
+
}
|
|
1533
|
+
bn = bn1.mod(bn2);
|
|
1534
|
+
break;
|
|
1535
|
+
|
|
1536
|
+
case Opcode.OP_BOOLAND:
|
|
1537
|
+
bn = new BN((bn1.cmp(BN.Zero) !== 0 && bn2.cmp(BN.Zero) !== 0) + 0);
|
|
1538
|
+
break;
|
|
1539
|
+
// case Opcode.OP_BOOLOR: bn = (bn1 !== bnZero || bn2 !== bnZero); break;
|
|
1540
|
+
case Opcode.OP_BOOLOR:
|
|
1541
|
+
bn = new BN((bn1.cmp(BN.Zero) !== 0 || bn2.cmp(BN.Zero) !== 0) + 0);
|
|
1542
|
+
break;
|
|
1543
|
+
// case Opcode.OP_NUMEQUAL: bn = (bn1 === bn2); break;
|
|
1544
|
+
case Opcode.OP_NUMEQUAL:
|
|
1545
|
+
bn = new BN((bn1.cmp(bn2) === 0) + 0);
|
|
1546
|
+
break;
|
|
1547
|
+
// case Opcode.OP_NUMEQUALVERIFY: bn = (bn1 === bn2); break;
|
|
1548
|
+
case Opcode.OP_NUMEQUALVERIFY:
|
|
1549
|
+
bn = new BN((bn1.cmp(bn2) === 0) + 0);
|
|
1550
|
+
break;
|
|
1551
|
+
// case Opcode.OP_NUMNOTEQUAL: bn = (bn1 !== bn2); break;
|
|
1552
|
+
case Opcode.OP_NUMNOTEQUAL:
|
|
1553
|
+
bn = new BN((bn1.cmp(bn2) !== 0) + 0);
|
|
1554
|
+
break;
|
|
1555
|
+
// case Opcode.OP_LESSTHAN: bn = (bn1 < bn2); break;
|
|
1556
|
+
case Opcode.OP_LESSTHAN:
|
|
1557
|
+
bn = new BN((bn1.cmp(bn2) < 0) + 0);
|
|
1558
|
+
break;
|
|
1559
|
+
// case Opcode.OP_GREATERTHAN: bn = (bn1 > bn2); break;
|
|
1560
|
+
case Opcode.OP_GREATERTHAN:
|
|
1561
|
+
bn = new BN((bn1.cmp(bn2) > 0) + 0);
|
|
1562
|
+
break;
|
|
1563
|
+
// case Opcode.OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break;
|
|
1564
|
+
case Opcode.OP_LESSTHANOREQUAL:
|
|
1565
|
+
bn = new BN((bn1.cmp(bn2) <= 0) + 0);
|
|
1566
|
+
break;
|
|
1567
|
+
// case Opcode.OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break;
|
|
1568
|
+
case Opcode.OP_GREATERTHANOREQUAL:
|
|
1569
|
+
bn = new BN((bn1.cmp(bn2) >= 0) + 0);
|
|
1570
|
+
break;
|
|
1571
|
+
case Opcode.OP_MIN:
|
|
1572
|
+
bn = bn1.cmp(bn2) < 0 ? bn1 : bn2;
|
|
1573
|
+
break;
|
|
1574
|
+
case Opcode.OP_MAX:
|
|
1575
|
+
bn = bn1.cmp(bn2) > 0 ? bn1 : bn2;
|
|
1576
|
+
break;
|
|
1577
|
+
// default: assert(!'invalid opcode'); break; //TODO: does this ever occur?
|
|
1578
|
+
}
|
|
1579
|
+
this.stack.pop();
|
|
1580
|
+
this.stack.pop();
|
|
1581
|
+
this.stack.push(bn.toScriptNumBuffer());
|
|
1582
|
+
|
|
1583
|
+
if (opcodenum === Opcode.OP_NUMEQUALVERIFY) {
|
|
1584
|
+
// if (CastToBool(stacktop(-1)))
|
|
1585
|
+
if (Interpreter.castToBool(stacktop(-1))) {
|
|
1586
|
+
this.stack.pop();
|
|
1587
|
+
} else {
|
|
1588
|
+
this.errstr = 'SCRIPT_ERR_NUMEQUALVERIFY';
|
|
1589
|
+
return false;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
break;
|
|
1593
|
+
|
|
1594
|
+
case Opcode.OP_WITHIN:
|
|
1595
|
+
// (x min max -- out)
|
|
1596
|
+
if (this.stack.length < 3) {
|
|
1597
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1598
|
+
return false;
|
|
1599
|
+
}
|
|
1600
|
+
bn1 = BN.fromScriptNumBuffer(stacktop(-3), fRequireMinimal);
|
|
1601
|
+
bn2 = BN.fromScriptNumBuffer(stacktop(-2), fRequireMinimal);
|
|
1602
|
+
var bn3 = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal);
|
|
1603
|
+
// bool fValue = (bn2 <= bn1 && bn1 < bn3);
|
|
1604
|
+
fValue = bn2.cmp(bn1) <= 0 && bn1.cmp(bn3) < 0;
|
|
1605
|
+
this.stack.pop();
|
|
1606
|
+
this.stack.pop();
|
|
1607
|
+
this.stack.pop();
|
|
1608
|
+
this.stack.push(fValue ? Interpreter.getTrue() : Interpreter.getFalse());
|
|
1609
|
+
break;
|
|
1610
|
+
|
|
1611
|
+
//
|
|
1612
|
+
// Crypto
|
|
1613
|
+
//
|
|
1614
|
+
case Opcode.OP_RIPEMD160:
|
|
1615
|
+
case Opcode.OP_SHA1:
|
|
1616
|
+
case Opcode.OP_SHA256:
|
|
1617
|
+
case Opcode.OP_HASH160:
|
|
1618
|
+
case Opcode.OP_HASH256:
|
|
1619
|
+
// (in -- hash)
|
|
1620
|
+
if (this.stack.length < 1) {
|
|
1621
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1622
|
+
return false;
|
|
1623
|
+
}
|
|
1624
|
+
buf = stacktop(-1);
|
|
1625
|
+
// valtype vchHash((opcode === Opcode.OP_RIPEMD160 ||
|
|
1626
|
+
// opcode === Opcode.OP_SHA1 || opcode === Opcode.OP_HASH160) ? 20 : 32);
|
|
1627
|
+
var bufHash;
|
|
1628
|
+
if (opcodenum === Opcode.OP_RIPEMD160) {
|
|
1629
|
+
bufHash = Hash.ripemd160(buf);
|
|
1630
|
+
} else if (opcodenum === Opcode.OP_SHA1) {
|
|
1631
|
+
bufHash = Hash.sha1(buf);
|
|
1632
|
+
} else if (opcodenum === Opcode.OP_SHA256) {
|
|
1633
|
+
bufHash = Hash.sha256(buf);
|
|
1634
|
+
} else if (opcodenum === Opcode.OP_HASH160) {
|
|
1635
|
+
bufHash = Hash.sha256ripemd160(buf);
|
|
1636
|
+
} else if (opcodenum === Opcode.OP_HASH256) {
|
|
1637
|
+
bufHash = Hash.sha256sha256(buf);
|
|
1638
|
+
}
|
|
1639
|
+
this.stack.pop();
|
|
1640
|
+
this.stack.push(bufHash);
|
|
1641
|
+
break;
|
|
1642
|
+
|
|
1643
|
+
case Opcode.OP_CODESEPARATOR:
|
|
1644
|
+
// Hash starts after the code separator
|
|
1645
|
+
this.pbegincodehash = this.pc;
|
|
1646
|
+
break;
|
|
1647
|
+
|
|
1648
|
+
case Opcode.OP_CHECKSIG:
|
|
1649
|
+
case Opcode.OP_CHECKSIGVERIFY:
|
|
1650
|
+
// (sig pubkey -- bool)
|
|
1651
|
+
if (this.stack.length < 2) {
|
|
1652
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1653
|
+
return false;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
bufSig = stacktop(-2);
|
|
1657
|
+
bufPubkey = stacktop(-1);
|
|
1658
|
+
|
|
1659
|
+
if (!this.checkSignatureEncoding(bufSig) || !this.checkPubkeyEncoding(bufPubkey)) {
|
|
1660
|
+
return false;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
// Subset of script starting at the most recent codeseparator
|
|
1664
|
+
// CScript scriptCode(pbegincodehash, pend);
|
|
1665
|
+
subscript = this.subscript();
|
|
1666
|
+
|
|
1667
|
+
// Drop the signature, since there's no way for a signature to sign itself
|
|
1668
|
+
var tmpScript = new Script().add(bufSig);
|
|
1669
|
+
subscript.findAndDelete(tmpScript);
|
|
1670
|
+
|
|
1671
|
+
try {
|
|
1672
|
+
sig = Signature.fromTxFormat(bufSig);
|
|
1673
|
+
pubkey = PublicKey.fromBuffer(bufPubkey, false);
|
|
1674
|
+
|
|
1675
|
+
fSuccess = this.tx.verifySignature(
|
|
1676
|
+
sig,
|
|
1677
|
+
pubkey,
|
|
1678
|
+
this.nin,
|
|
1679
|
+
subscript,
|
|
1680
|
+
this.satoshisBN,
|
|
1681
|
+
this.flags,
|
|
1682
|
+
);
|
|
1683
|
+
} catch (e) {
|
|
1684
|
+
console.error('invalid sig or pubkey', e);
|
|
1685
|
+
// invalid sig or pubkey
|
|
1686
|
+
fSuccess = false;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
if (!fSuccess && this.flags & Interpreter.SCRIPT_VERIFY_NULLFAIL && bufSig.length) {
|
|
1690
|
+
this.errstr = 'SCRIPT_ERR_NULLFAIL';
|
|
1691
|
+
return false;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
this.stack.pop();
|
|
1695
|
+
this.stack.pop();
|
|
1696
|
+
|
|
1697
|
+
// stack.push_back(fSuccess ? vchTrue : vchFalse);
|
|
1698
|
+
this.stack.push(fSuccess ? Interpreter.getTrue() : Interpreter.getFalse());
|
|
1699
|
+
if (opcodenum === Opcode.OP_CHECKSIGVERIFY) {
|
|
1700
|
+
if (fSuccess) {
|
|
1701
|
+
this.stack.pop();
|
|
1702
|
+
} else {
|
|
1703
|
+
this.errstr = 'SCRIPT_ERR_CHECKSIGVERIFY';
|
|
1704
|
+
return false;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
break;
|
|
1708
|
+
|
|
1709
|
+
case Opcode.OP_CHECKMULTISIG:
|
|
1710
|
+
case Opcode.OP_CHECKMULTISIGVERIFY:
|
|
1711
|
+
// ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)
|
|
1712
|
+
|
|
1713
|
+
var i = 1;
|
|
1714
|
+
if (this.stack.length < i) {
|
|
1715
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1716
|
+
return false;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
var nKeysCount = BN.fromScriptNumBuffer(stacktop(-i), fRequireMinimal).toNumber();
|
|
1720
|
+
// TODO: Keys and opcount are parameterized in client. No magic numbers!
|
|
1721
|
+
if (nKeysCount < 0 || nKeysCount > 20) {
|
|
1722
|
+
this.errstr = 'SCRIPT_ERR_PUBKEY_COUNT';
|
|
1723
|
+
return false;
|
|
1724
|
+
}
|
|
1725
|
+
this.nOpCount += nKeysCount;
|
|
1726
|
+
if (this.nOpCount > Interpreter.MAX_OPCODE_COUNT) {
|
|
1727
|
+
this.errstr = 'SCRIPT_ERR_OP_COUNT';
|
|
1728
|
+
return false;
|
|
1729
|
+
}
|
|
1730
|
+
// int ikey = ++i;
|
|
1731
|
+
var ikey = ++i;
|
|
1732
|
+
i += nKeysCount;
|
|
1733
|
+
|
|
1734
|
+
// ikey2 is the position of last non-signature item in
|
|
1735
|
+
// the stack. Top stack item = 1. With
|
|
1736
|
+
// SCRIPT_VERIFY_NULLFAIL, this is used for cleanup if
|
|
1737
|
+
// operation fails.
|
|
1738
|
+
var ikey2 = nKeysCount + 2;
|
|
1739
|
+
|
|
1740
|
+
if (this.stack.length < i) {
|
|
1741
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1742
|
+
return false;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
var nSigsCount = BN.fromScriptNumBuffer(stacktop(-i), fRequireMinimal).toNumber();
|
|
1746
|
+
if (nSigsCount < 0 || nSigsCount > nKeysCount) {
|
|
1747
|
+
this.errstr = 'SCRIPT_ERR_SIG_COUNT';
|
|
1748
|
+
return false;
|
|
1749
|
+
}
|
|
1750
|
+
// int isig = ++i;
|
|
1751
|
+
var isig = ++i;
|
|
1752
|
+
i += nSigsCount;
|
|
1753
|
+
if (this.stack.length < i) {
|
|
1754
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1755
|
+
return false;
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
// Subset of script starting at the most recent codeseparator
|
|
1759
|
+
subscript = this.subscript();
|
|
1760
|
+
|
|
1761
|
+
// Drop the signatures, since there's no way for a signature to sign itself
|
|
1762
|
+
for (var k = 0; k < nSigsCount; k++) {
|
|
1763
|
+
bufSig = stacktop(-isig - k);
|
|
1764
|
+
subscript.findAndDelete(new Script().add(bufSig));
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
fSuccess = true;
|
|
1768
|
+
while (fSuccess && nSigsCount > 0) {
|
|
1769
|
+
// valtype& vchSig = stacktop(-isig);
|
|
1770
|
+
bufSig = stacktop(-isig);
|
|
1771
|
+
// valtype& vchPubKey = stacktop(-ikey);
|
|
1772
|
+
bufPubkey = stacktop(-ikey);
|
|
1773
|
+
|
|
1774
|
+
if (!this.checkSignatureEncoding(bufSig) || !this.checkPubkeyEncoding(bufPubkey)) {
|
|
1775
|
+
return false;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
var fOk;
|
|
1779
|
+
try {
|
|
1780
|
+
sig = Signature.fromTxFormat(bufSig);
|
|
1781
|
+
pubkey = PublicKey.fromBuffer(bufPubkey, false);
|
|
1782
|
+
fOk = this.tx.verifySignature(
|
|
1783
|
+
sig,
|
|
1784
|
+
pubkey,
|
|
1785
|
+
this.nin,
|
|
1786
|
+
subscript,
|
|
1787
|
+
this.satoshisBN,
|
|
1788
|
+
this.flags,
|
|
1789
|
+
);
|
|
1790
|
+
} catch (e) {
|
|
1791
|
+
// invalid sig or pubkey
|
|
1792
|
+
fOk = false;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
if (fOk) {
|
|
1796
|
+
isig++;
|
|
1797
|
+
nSigsCount--;
|
|
1798
|
+
}
|
|
1799
|
+
ikey++;
|
|
1800
|
+
nKeysCount--;
|
|
1801
|
+
|
|
1802
|
+
// If there are more signatures left than keys left,
|
|
1803
|
+
// then too many signatures have failed
|
|
1804
|
+
if (nSigsCount > nKeysCount) {
|
|
1805
|
+
fSuccess = false;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
// Clean up stack of actual arguments
|
|
1810
|
+
while (i-- > 1) {
|
|
1811
|
+
if (
|
|
1812
|
+
!fSuccess &&
|
|
1813
|
+
this.flags & Interpreter.SCRIPT_VERIFY_NULLFAIL &&
|
|
1814
|
+
!ikey2 &&
|
|
1815
|
+
stacktop(-1).length
|
|
1816
|
+
) {
|
|
1817
|
+
this.errstr = 'SCRIPT_ERR_NULLFAIL';
|
|
1818
|
+
return false;
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
if (ikey2 > 0) {
|
|
1822
|
+
ikey2--;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
this.stack.pop();
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
// A bug causes CHECKMULTISIG to consume one extra argument
|
|
1829
|
+
// whose contents were not checked in any way.
|
|
1830
|
+
//
|
|
1831
|
+
// Unfortunately this is a potential source of mutability,
|
|
1832
|
+
// so optionally verify it is exactly equal to zero prior
|
|
1833
|
+
// to removing it from the stack.
|
|
1834
|
+
if (this.stack.length < 1) {
|
|
1835
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1836
|
+
return false;
|
|
1837
|
+
}
|
|
1838
|
+
if (this.flags & Interpreter.SCRIPT_VERIFY_NULLDUMMY && stacktop(-1).length) {
|
|
1839
|
+
this.errstr = 'SCRIPT_ERR_SIG_NULLDUMMY';
|
|
1840
|
+
return false;
|
|
1841
|
+
}
|
|
1842
|
+
this.stack.pop();
|
|
1843
|
+
|
|
1844
|
+
this.stack.push(fSuccess ? Interpreter.getTrue() : Interpreter.getFalse());
|
|
1845
|
+
|
|
1846
|
+
if (opcodenum === Opcode.OP_CHECKMULTISIGVERIFY) {
|
|
1847
|
+
if (fSuccess) {
|
|
1848
|
+
this.stack.pop();
|
|
1849
|
+
} else {
|
|
1850
|
+
this.errstr = 'SCRIPT_ERR_CHECKMULTISIGVERIFY';
|
|
1851
|
+
return false;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
break;
|
|
1855
|
+
|
|
1856
|
+
//
|
|
1857
|
+
// Byte string operations
|
|
1858
|
+
//
|
|
1859
|
+
case Opcode.OP_CAT:
|
|
1860
|
+
if (this.stack.length < 2) {
|
|
1861
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1862
|
+
return false;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
buf1 = stacktop(-2);
|
|
1866
|
+
buf2 = stacktop(-1);
|
|
1867
|
+
if (buf1.length + buf2.length > Interpreter.MAX_SCRIPT_ELEMENT_SIZE) {
|
|
1868
|
+
this.errstr = 'SCRIPT_ERR_PUSH_SIZE';
|
|
1869
|
+
return false;
|
|
1870
|
+
}
|
|
1871
|
+
this.stack.write(-2, Buffer.concat([buf1, buf2]));
|
|
1872
|
+
this.stack.pop();
|
|
1873
|
+
break;
|
|
1874
|
+
|
|
1875
|
+
case Opcode.OP_SPLIT:
|
|
1876
|
+
if (this.stack.length < 2) {
|
|
1877
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1878
|
+
return false;
|
|
1879
|
+
}
|
|
1880
|
+
buf1 = stacktop(-2);
|
|
1881
|
+
|
|
1882
|
+
// Make sure the split point is apropriate.
|
|
1883
|
+
var position = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal).toNumber();
|
|
1884
|
+
if (position < 0 || position > buf1.length) {
|
|
1885
|
+
this.errstr = 'SCRIPT_ERR_INVALID_SPLIT_RANGE';
|
|
1886
|
+
return false;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
// Prepare the results in their own buffer as `data`
|
|
1890
|
+
// will be invalidated.
|
|
1891
|
+
// Copy buffer data, to slice it before
|
|
1892
|
+
var n1 = Buffer.from(buf1);
|
|
1893
|
+
|
|
1894
|
+
// Replace existing stack values by the new values.
|
|
1895
|
+
this.stack.write(-2, n1.slice(0, position));
|
|
1896
|
+
this.stack.write(-1, n1.slice(position));
|
|
1897
|
+
break;
|
|
1898
|
+
|
|
1899
|
+
//
|
|
1900
|
+
// Conversion operations
|
|
1901
|
+
//
|
|
1902
|
+
case Opcode.OP_NUM2BIN:
|
|
1903
|
+
// (in -- out)
|
|
1904
|
+
if (this.stack.length < 2) {
|
|
1905
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1906
|
+
return false;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
var size = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal).toNumber();
|
|
1910
|
+
if (size > Interpreter.MAX_SCRIPT_ELEMENT_SIZE) {
|
|
1911
|
+
this.errstr = 'SCRIPT_ERR_PUSH_SIZE';
|
|
1912
|
+
return false;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
this.stack.pop();
|
|
1916
|
+
var rawnum = stacktop(-1);
|
|
1917
|
+
|
|
1918
|
+
// Try to see if we can fit that number in the number of
|
|
1919
|
+
// byte requested.
|
|
1920
|
+
rawnum = Interpreter._minimallyEncode(rawnum);
|
|
1921
|
+
|
|
1922
|
+
if (rawnum.length > size) {
|
|
1923
|
+
// We definitively cannot.
|
|
1924
|
+
this.errstr = 'SCRIPT_ERR_IMPOSSIBLE_ENCODING';
|
|
1925
|
+
return false;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
// We already have an element of the right size, we
|
|
1929
|
+
// don't need to do anything.
|
|
1930
|
+
if (rawnum.length === size) {
|
|
1931
|
+
this.stack.write(-1, rawnum);
|
|
1932
|
+
break;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
var signbit = 0x00;
|
|
1936
|
+
if (rawnum.length > 0) {
|
|
1937
|
+
signbit = rawnum[rawnum.length - 1] & 0x80;
|
|
1938
|
+
rawnum[rawnum.length - 1] &= 0x7f;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
var num = Buffer.alloc(size);
|
|
1942
|
+
rawnum.copy(num, 0);
|
|
1943
|
+
|
|
1944
|
+
var l = rawnum.length - 1;
|
|
1945
|
+
while (l++ < size - 2) {
|
|
1946
|
+
num[l] = 0x00;
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
num[l] = signbit;
|
|
1950
|
+
|
|
1951
|
+
this.stack.write(-1, num);
|
|
1952
|
+
break;
|
|
1953
|
+
|
|
1954
|
+
case Opcode.OP_BIN2NUM:
|
|
1955
|
+
// (in -- out)
|
|
1956
|
+
if (this.stack.length < 1) {
|
|
1957
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
|
|
1958
|
+
return false;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
buf1 = stacktop(-1);
|
|
1962
|
+
buf2 = Interpreter._minimallyEncode(buf1);
|
|
1963
|
+
|
|
1964
|
+
this.stack.write(-1, buf2);
|
|
1965
|
+
|
|
1966
|
+
// The resulting number must be a valid number.
|
|
1967
|
+
if (!Interpreter._isMinimallyEncoded(buf2)) {
|
|
1968
|
+
this.errstr = 'SCRIPT_ERR_INVALID_NUMBER_RANGE';
|
|
1969
|
+
return false;
|
|
1970
|
+
}
|
|
1971
|
+
break;
|
|
1972
|
+
|
|
1973
|
+
default:
|
|
1974
|
+
this.errstr = 'SCRIPT_ERR_BAD_OPCODE';
|
|
1975
|
+
return false;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
// only when next opcode is evaluate opcode, we update stack
|
|
1980
|
+
if (this.vfExec.indexOf(false) === -1) {
|
|
1981
|
+
this._callbackStack(this.stack, this.pc, scriptType);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
return true;
|
|
1985
|
+
};
|
|
1986
|
+
|
|
1987
|
+
|
|
1988
|
+
module.exports = Interpreter;
|