UNPKG

bitcore-lib-cash

Version:

A pure and powerful JavaScript Bitcoin Cash library.

1,586 lines (1,374 loc) 79.6 kB
'use strict'; var _ = require('lodash'); var Script = require('./script'); var Opcode = require('../opcode'); var BN = require('../crypto/bn'); var Hash = require('../crypto/hash'); var Signature = require('../crypto/signature'); var PublicKey = require('../publickey'); var ECDSA = require('../crypto/ecdsa'); var Schnorr = require('../crypto/schnorr'); var BufferWriter = require('../encoding/bufferwriter'); /** * Bitcoin transactions contain scripts. Each input has a script called the * scriptSig, and each output has a script called the scriptPubkey. To validate * an input, the input's script is concatenated with the referenced output script, * and the result is executed. If at the end of execution the stack contains a * "true" value, then the transaction is valid. * * The primary way to use this class is via the verify function. * e.g., Interpreter().verify( ... ); */ var Interpreter = function Interpreter(obj) { if (!(this instanceof Interpreter)) { return new Interpreter(obj); } if (obj) { this.initialize(); this.set(obj); } else { this.initialize(); } }; /** * Verifies a Script by executing it and returns true if it is valid. * This function needs to be provided with the scriptSig and the scriptPubkey * separately. * @param {Script} scriptSig - the script's first part (corresponding to the tx input) * @param {Script} scriptPubkey - the script's last part (corresponding to the tx output) * @param {Transaction=} tx - the Transaction containing the scriptSig in one input (used * to check signature validity for some opcodes like OP_CHECKSIG) * @param {number} nin - index of the transaction input containing the scriptSig verified. * @param {number} flags - evaluation flags. See Interpreter.SCRIPT_* constants * @param {number} satoshisBN - amount in satoshis of the input to be verified (when FORKID signhash is used) * * Translated from bitcoind's VerifyScript */ Interpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags, satoshisBN) { var Transaction = require('../transaction'); this.nSigChecks = 0; if (_.isUndefined(tx)) { tx = new Transaction(); } if (_.isUndefined(nin)) { nin = 0; } if (_.isUndefined(flags)) { flags = 0; } // If FORKID is enabled, we also ensure strict encoding. if (flags & Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID) { flags |= Interpreter.SCRIPT_VERIFY_STRICTENC; // If FORKID is enabled, we need the input amount. if (!satoshisBN) { throw new Error('internal error - need satoshisBN to verify FORKID transactions'); } if (tx.toBuffer().length < 65) { throw new Error('bad-txns-undersize'); } } this.set({ script: scriptSig, tx: tx, nin: nin, flags: flags, satoshisBN: satoshisBN, }); var stackCopy; if ((flags & Interpreter.SCRIPT_VERIFY_SIGPUSHONLY) !== 0 && !scriptSig.isPushOnly()) { this.errstr = 'SCRIPT_ERR_SIG_PUSHONLY'; return false; } // evaluate scriptSig if (!this.evaluate()) { return false; } if (flags & Interpreter.SCRIPT_VERIFY_P2SH) { stackCopy = this.stack.slice(); } var stack = this.stack; this.initialize(); this.set({ script: scriptPubkey, stack: stack, tx: tx, nin: nin, flags: flags, satoshisBN: satoshisBN, }); // evaluate scriptPubkey if (!this.evaluate()) { return false; } if (this.stack.length === 0) { this.errstr = 'SCRIPT_ERR_EVAL_FALSE_NO_RESULT'; return false; } var buf = this.stack[this.stack.length - 1]; if (!Interpreter.castToBool(buf)) { this.errstr = 'SCRIPT_ERR_EVAL_FALSE_IN_STACK'; return false; } // Additional validation for spend-to-script-hash transactions: const fEnableP2SH32 = (flags & Interpreter.SCRIPT_ENABLE_P2SH_32) !== 0; if ((flags & Interpreter.SCRIPT_VERIFY_P2SH) && scriptPubkey.isScriptHashOut(fEnableP2SH32)) { // scriptSig must be literals-only or validation fails if (!scriptSig.isPushOnly()) { this.errstr = 'SCRIPT_ERR_SIG_PUSHONLY'; return false; } // stackCopy cannot be empty here, because if it was the // P2SH HASH <> EQUAL scriptPubKey would be evaluated with // an empty stack and the EvalScript above would return false. if (stackCopy.length === 0) { throw new Error('internal error - stack copy empty'); } var redeemScriptSerialized = stackCopy[stackCopy.length - 1]; var redeemScript = Script.fromBuffer(redeemScriptSerialized); stackCopy.pop(); this.initialize(); this.set({ script: redeemScript, stack: stackCopy, tx: tx, nin: nin, flags: flags, satoshisBN: satoshisBN, }); // Bail out early if SCRIPT_DISALLOW_SEGWIT_RECOVERY is not set, the redeem script is a p2sh segwit program, and it was the // only item pushed onto the stack. if ((flags & Interpreter.SCRIPT_DISALLOW_SEGWIT_RECOVERY) == 0 && stackCopy.length === 0 && redeemScript.isWitnessProgram()) { return true; } // evaluate redeemScript if (!this.evaluate()) { return false; } if (stackCopy.length === 0) { this.errstr = 'SCRIPT_ERR_EVAL_FALSE_NO_P2SH_STACK'; return false; } if (!Interpreter.castToBool(stackCopy[stackCopy.length - 1])) { this.errstr = 'SCRIPT_ERR_EVAL_FALSE_IN_P2SH_STACK'; return false; } } // The CLEANSTACK check is only performed after potential P2SH evaluation, // as the non-P2SH evaluation of a P2SH script will obviously not result in // a clean stack (the P2SH inputs remain). The same holds for witness // evaluation. if ((flags & Interpreter.SCRIPT_VERIFY_CLEANSTACK) != 0) { // Disallow CLEANSTACK without P2SH, as otherwise a switch // CLEANSTACK->P2SH+CLEANSTACK would be possible, which is not a // softfork (and P2SH should be one). if ((flags & Interpreter.SCRIPT_VERIFY_P2SH) == 0) { throw new Error('internal error - CLEANSTACK without P2SH'); } if (this.stack.length != 1) { this.errstr = 'SCRIPT_ERR_CLEANSTACK'; return false; } } if (flags & Interpreter.SCRIPT_VERIFY_INPUT_SIGCHECKS) { // This limit is intended for standard use, and is based on an // examination of typical and historical standard uses. // - allowing P2SH ECDSA multisig with compressed keys, which at an // extreme (1-of-15) may have 15 SigChecks in ~590 bytes of scriptSig. // - allowing Bare ECDSA multisig, which at an extreme (1-of-3) may have // 3 sigchecks in ~72 bytes of scriptSig. // - Since the size of an input is 41 bytes + length of scriptSig, then // the most dense possible inputs satisfying this rule would be: // 2 sigchecks and 26 bytes: 1/33.50 sigchecks/byte. // 3 sigchecks and 69 bytes: 1/36.66 sigchecks/byte. // The latter can be readily done with 1-of-3 bare multisignatures, // however the former is not practically doable with standard scripts, // so the practical density limit is 1/36.66. if (scriptSig.toBuffer().length < this.nSigChecks * 43 - 60) { return false; } } return true; }; module.exports = Interpreter; Interpreter.prototype.initialize = function(obj) { this.stack = []; this.altstack = []; this.pc = 0; this.pbegincodehash = 0; this.nOpCount = 0; this.vfExec = []; this.errstr = ''; this.flags = 0; }; Interpreter.prototype.set = function(obj) { this.script = obj.script || this.script; this.tx = obj.tx || this.tx; this.nin = typeof obj.nin !== 'undefined' ? obj.nin : this.nin; this.satoshisBN = obj.satoshisBN || this.satoshisBN; this.stack = obj.stack || this.stack; this.altstack = obj.altack || this.altstack; this.pc = typeof obj.pc !== 'undefined' ? obj.pc : this.pc; this.pbegincodehash = typeof obj.pbegincodehash !== 'undefined' ? obj.pbegincodehash : this.pbegincodehash; this.nOpCount = typeof obj.nOpCount !== 'undefined' ? obj.nOpCount : this.nOpCount; this.vfExec = obj.vfExec || this.vfExec; this.errstr = obj.errstr || this.errstr; this.flags = typeof obj.flags !== 'undefined' ? obj.flags : this.flags; }; Interpreter.true = Buffer.from([1]); Interpreter.false = Buffer.from([]); Interpreter.MAX_SCRIPT_ELEMENT_SIZE = 520; Interpreter.MAXIMUM_ELEMENT_SIZE = 4; Interpreter.LOCKTIME_THRESHOLD = 500000000; Interpreter.LOCKTIME_THRESHOLD_BN = new BN(Interpreter.LOCKTIME_THRESHOLD); // flags taken from bitcoind // bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104 Interpreter.SCRIPT_VERIFY_NONE = 0; // Evaluate P2SH subscripts (softfork safe, BIP16). Interpreter.SCRIPT_VERIFY_P2SH = (1 << 0); // Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure. // Passing a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) to checksig causes that pubkey to be // skipped (not softfork safe: this flag can widen the validity of OP_CHECKSIG OP_NOT). Interpreter.SCRIPT_VERIFY_STRICTENC = (1 << 1); // Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1) Interpreter.SCRIPT_VERIFY_DERSIG = (1 << 2); // Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure // (softfork safe, BIP62 rule 5). Interpreter.SCRIPT_VERIFY_LOW_S = (1 << 3); // verify dummy stack item consumed by CHECKMULTISIG is of zero-length (softfork safe, BIP62 rule 7). Interpreter.SCRIPT_VERIFY_NULLDUMMY = (1 << 4); // Using a non-push operator in the scriptSig causes script failure (softfork safe, BIP62 rule 2). Interpreter.SCRIPT_VERIFY_SIGPUSHONLY = (1 << 5); // Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct // pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating // any other push causes the script to fail (BIP62 rule 3). // In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4). // (softfork safe) Interpreter.SCRIPT_VERIFY_MINIMALDATA = (1 << 6); // Discourage use of NOPs reserved for upgrades (NOP1-10) // // Provided so that nodes can avoid accepting or mining transactions // containing executed NOP's whose meaning may change after a soft-fork, // thus rendering the script invalid; with this flag set executing // discouraged NOPs fails the script. This verification flag will never be // a mandatory flag applied to scripts in a block. NOPs that are not // executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected. Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1 << 7); // Require that only a single stack element remains after evaluation. This // changes the success criterion from "At least one stack element must // remain, and when interpreted as a boolean, it must be true" to "Exactly // one stack element must remain, and when interpreted as a boolean, it must // be true". // (softfork safe, BIP62 rule 6) // Note: CLEANSTACK should never be used without P2SH or WITNESS. Interpreter.SCRIPT_VERIFY_CLEANSTACK = (1 << 8); // CLTV See BIP65 for details. Interpreter.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1 << 9); // support CHECKSEQUENCEVERIFY opcode // // See BIP112 for details Interpreter.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1 << 10); // Segwit script only: Require the argument of OP_IF/NOTIF to be exactly // 0x01 or empty vector // Interpreter.SCRIPT_VERIFY_MINIMALIF = (1 << 13); // Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed // Interpreter.SCRIPT_VERIFY_NULLFAIL = (1 << 14); // Public keys in scripts must be compressed Interpreter.SCRIPT_VERIFY_COMPRESSED_PUBKEYTYPE = (1 << 15); // Do we accept signature using SIGHASH_FORKID // Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID = (1 << 16); // Do we accept activate replay protection using a different fork id. // Interpreter.SCRIPT_ENABLE_REPLAY_PROTECTION = (1 << 17); // Enable new opcodes. // Interpreter.SCRIPT_ENABLE_CHECKDATASIG = (1 << 18); // The exception to CLEANSTACK and P2SH for the recovery of coins sent // to p2sh segwit addresses is not allowed. Interpreter.SCRIPT_DISALLOW_SEGWIT_RECOVERY = (1 << 20); // Whether to allow new OP_CHECKMULTISIG logic to trigger. (new multisig // logic verifies faster, and only allows Schnorr signatures) Interpreter.SCRIPT_ENABLE_SCHNORR_MULTISIG = (1 << 21); /* Below flags apply in the context of BIP 68*/ /** * If this flag set, CTxIn::nSequence is NOT interpreted as a relative * lock-time. */ Interpreter.SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31); /** * If CTxIn::nSequence encodes a relative lock-time and this flag is set, * the relative lock-time has units of 512 seconds, otherwise it specifies * blocks with a granularity of 1. */ Interpreter.SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22); /** * If CTxIn::nSequence encodes a relative lock-time, this mask is applied to * extract that lock-time from the sequence field. */ Interpreter.SEQUENCE_LOCKTIME_MASK = 0x0000ffff; // Require the number of sigchecks in an input to satisfy a specific // bound, defined by scriptSig length. Interpreter.SCRIPT_VERIFY_INPUT_SIGCHECKS = (1 << 22); // A utility flag to decide whether we must enforce the per-tx consensus // sigcheck limit. It does not control the sigcheck limits in // policy/consensus.h . Interpreter.SCRIPT_ENFORCE_SIGCHECKS = (1 << 23); // Flag that allows us to determine if the script interpreter should allow // 64-bit integer arithmetic and the return of OP_MUL or use the previous // semantics. Interpreter.SCRIPT_64_BIT_INTEGERS = (1 << 24); // Native Introspection opcodes. Interpreter.SCRIPT_NATIVE_INTROSPECTION = (1 << 25); // Enable p2sh32 (uses OP_HASH256 rather than OP_HASH160) Interpreter.SCRIPT_ENABLE_P2SH_32 = (1 << 26); // Enable native tokens support, including all consensus rules & native // introspection op-codes related to them. Interpreter.SCRIPT_ENABLE_TOKENS = (1 << 27); Interpreter.castToBool = function(buf) { for (var i = 0; i < buf.length; i++) { if (buf[i] !== 0) { // can be negative zero if (i === buf.length - 1 && buf[i] === 0x80) { return false; } return true; } } return false; }; Interpreter.isSchnorrSig = function(buf) { return (buf.length === 64 || buf.length === 65) && (buf[0] !== 0x30); } /** * Translated from bitcoind's CheckSignatureEncoding */ Interpreter.prototype.checkRawSignatureEncoding = function(buf) { var sig; //TODO update interpreter.js and necessary functions to match bitcoin-abc interpreter.cpp if(Interpreter.isSchnorrSig(buf)) { return true; } if ((this.flags & (Interpreter.SCRIPT_VERIFY_DERSIG | Interpreter.SCRIPT_VERIFY_LOW_S | Interpreter.SCRIPT_VERIFY_STRICTENC)) !== 0 && !Signature.isDER(buf)) { this.errstr = 'SCRIPT_ERR_SIG_DER_INVALID_FORMAT'; return false; } else if ((this.flags & Interpreter.SCRIPT_VERIFY_LOW_S) !== 0) { sig = Signature.fromTxFormat(buf); if (!sig.hasLowS()) { this.errstr = 'SCRIPT_ERR_SIG_DER_HIGH_S'; return false; } } return true; }; // Back compat Interpreter.prototype.checkSignatureEncoding = Interpreter.prototype.checkTxSignatureEncoding = function(buf) { // Empty signature. Not strictly DER encoded, but allowed to provide a // compact way to provide an invalid signature for use with CHECK(MULTI)SIG if (buf.length == 0) { return true; } if (!this.checkRawSignatureEncoding(buf.slice(0,buf.length-1))) { return false; } if ((this.flags & Interpreter.SCRIPT_VERIFY_STRICTENC) !== 0) { var sig = Signature.fromTxFormat(buf); if (!sig.hasDefinedHashtype()) { this.errstr = 'SCRIPT_ERR_SIG_HASHTYPE'; return false; } if (!(this.flags & Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID) && (sig.nhashtype & Signature.SIGHASH_FORKID)) { this.errstr = 'SCRIPT_ERR_ILLEGAL_FORKID'; return false; } if ( (this.flags & Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID) && !(sig.nhashtype & Signature.SIGHASH_FORKID)) { this.errstr = 'SCRIPT_ERR_MUST_USE_FORKID'; return false; } } return true; }; Interpreter.prototype.checkDataSignatureEncoding = function(buf) { // Empty signature. Not strictly DER encoded, but allowed to provide a // compact way to provide an invalid signature for use with CHECK(MULTI)SIG if (buf.length == 0) { return true; } return this.checkRawSignatureEncoding(buf); }; /** * Translated from bitcoind's CheckPubKeyEncoding */ Interpreter.prototype.checkPubkeyEncoding = function(buf) { if ((this.flags & Interpreter.SCRIPT_VERIFY_STRICTENC) !== 0 && !(PublicKey.isValid(buf) || IsCompressedOrUncompressedPubkey(buf))) { this.errstr = 'SCRIPT_ERR_PUBKEYTYPE'; return false; } return true; }; function IsCompressedOrUncompressedPubkey(bufPubkey) { switch(bufPubkey.length) { case 33: return bufPubkey[0] === 0x02 || bufPubkey[0] === 0x03; case 64: return bufPubkey[0] === 0x04; default: return false; } } /** * * Check the buffer is minimally encoded (see https://github.com/bitcoincashorg/spec/blob/master/may-2018-reenabled-opcodes.md#op_bin2num) * * */ Interpreter._isMinimallyEncoded = function(buf, nMaxNumSize) { nMaxNumSize = nMaxNumSize || Interpreter.MAXIMUM_ELEMENT_SIZE; if (buf.length > nMaxNumSize ) { return false; } if (buf.length > 0) { // Check that the number is encoded with the minimum possible number // of bytes. // // If the most-significant-byte - excluding the sign bit - is zero // then we're not minimal. Note how this test also rejects the // negative-zero encoding, 0x80. if ((buf[buf.length-1] & 0x7f) == 0) { // One exception: if there's more than one byte and the most // significant bit of the second-most-significant-byte is set it // would conflict with the sign bit. An example of this case is // +-255, which encode to 0xff00 and 0xff80 respectively. // (big-endian). if (buf.length <= 1 || (buf[buf.length - 2] & 0x80) == 0) { return false; } } } return true; }; /** * * minimally encode the buffer content * * @param {number} nMaxNumSize (max allowed size) */ Interpreter._minimallyEncode = function(buf) { if (buf.length == 0) { return buf; } // If the last byte is not 0x00 or 0x80, we are minimally encoded. var last = buf[buf.length - 1]; if (last & 0x7f) { return buf; } // If the script is one byte long, then we have a zero, which encodes as an // empty array. if (buf.length == 1) { return Buffer.from(''); } // If the next byte has it sign bit set, then we are minimaly encoded. if (buf[buf.length - 2] & 0x80) { return buf; } // We are not minimally encoded, we need to figure out how much to trim. for (var i = buf.length - 1; i > 0; i--) { // We found a non zero byte, time to encode. if (buf[i - 1] != 0) { if (buf[i - 1] & 0x80) { // We found a byte with it sign bit set so we need one more // byte. buf[i++] = last; } else { // the sign bit is clear, we can use it. buf[i - 1] |= last; } return buf.slice(0,i); } } // If we the whole thing is zeros, then we have a zero. return Buffer.from(''); } /** * Based on bitcoind's EvalScript function, with the inner loop moved to * Interpreter.prototype.step() * bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104 */ Interpreter.prototype.evaluate = function() { if (this.script.toBuffer().length > 10000) { this.errstr = 'SCRIPT_ERR_SCRIPT_SIZE'; return false; } try { while (this.pc < this.script.chunks.length) { var fSuccess = this.step(); if (!fSuccess) { return false; } } // Size limits if (this.stack.length + this.altstack.length > 1000) { this.errstr = 'SCRIPT_ERR_STACK_SIZE'; return false; } } catch (e) { this.errstr = 'SCRIPT_ERR_UNKNOWN_ERROR: ' + e; return false; } if (this.vfExec.length > 0) { this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL'; return false; } return true; }; /** * Checks a locktime parameter with the transaction's locktime. * There are two times of nLockTime: lock-by-blockheight and lock-by-blocktime, * distinguished by whether nLockTime < LOCKTIME_THRESHOLD = 500000000 * * See the corresponding code on bitcoin core: * https://github.com/bitcoin/bitcoin/blob/ffd75adce01a78b3461b3ff05bcc2b530a9ce994/src/script/interpreter.cpp#L1129 * * @param {BN} nLockTime the locktime read from the script * @return {boolean} true if the transaction's locktime is less than or equal to * the transaction's locktime */ Interpreter.prototype.checkLockTime = function(nLockTime) { // We want to compare apples to apples, so fail the script // unless the type of nLockTime being tested is the same as // the nLockTime in the transaction. if (!( (this.tx.nLockTime < Interpreter.LOCKTIME_THRESHOLD && nLockTime.lt(Interpreter.LOCKTIME_THRESHOLD_BN)) || (this.tx.nLockTime >= Interpreter.LOCKTIME_THRESHOLD && nLockTime.gte(Interpreter.LOCKTIME_THRESHOLD_BN)) )) { return false; } // Now that we know we're comparing apples-to-apples, the // comparison is a simple numeric one. if (nLockTime.gt(new BN(this.tx.nLockTime))) { return false; } // Finally the nLockTime feature can be disabled and thus // CHECKLOCKTIMEVERIFY bypassed if every txin has been // finalized by setting nSequence to maxint. The // transaction would be allowed into the blockchain, making // the opcode ineffective. // // Testing if this vin is not final is sufficient to // prevent this condition. Alternatively we could test all // inputs, but testing just this input minimizes the data // required to prove correct CHECKLOCKTIMEVERIFY execution. if (!this.tx.inputs[this.nin].isFinal()) { return false; } return true; } /** * Checks a sequence parameter with the transaction's sequence. * @param {BN} nSequence the sequence read from the script * @return {boolean} true if the transaction's sequence is less than or equal to * the transaction's sequence */ Interpreter.prototype.checkSequence = function(nSequence) { // Relative lock times are supported by comparing the passed in operand to // the sequence number of the input. var txToSequence = this.tx.inputs[this.nin].sequenceNumber; // Fail if the transaction's version number is not set high enough to // trigger BIP 68 rules. if (this.tx.version < 2) { return false; } // Sequence numbers with their most significant bit set are not consensus // constrained. Testing that the transaction's sequence number do not have // this bit set prevents using this property to get around a // CHECKSEQUENCEVERIFY check. if (txToSequence & SEQUENCE_LOCKTIME_DISABLE_FLAG) { return false; } // Mask off any bits that do not have consensus-enforced meaning before // doing the integer comparisons var nLockTimeMask = Interpreter.SEQUENCE_LOCKTIME_TYPE_FLAG | Interpreter.SEQUENCE_LOCKTIME_MASK; var txToSequenceMasked = new BN(txToSequence & nLockTimeMask); var nSequenceMasked = nSequence.and(nLockTimeMask); // There are two kinds of nSequence: lock-by-blockheight and // lock-by-blocktime, distinguished by whether nSequenceMasked < // CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG. // // We want to compare apples to apples, so fail the script unless the type // of nSequenceMasked being tested is the same as the nSequenceMasked in the // transaction. var SEQUENCE_LOCKTIME_TYPE_FLAG_BN = new BN(Interpreter.SEQUENCE_LOCKTIME_TYPE_FLAG); if (!((txToSequenceMasked.lt(SEQUENCE_LOCKTIME_TYPE_FLAG_BN) && nSequenceMasked.lt(SEQUENCE_LOCKTIME_TYPE_FLAG_BN)) || (txToSequenceMasked.gte(SEQUENCE_LOCKTIME_TYPE_FLAG_BN) && nSequenceMasked.gte(SEQUENCE_LOCKTIME_TYPE_FLAG_BN)))) { return false; } // Now that we know we're comparing apples-to-apples, the comparison is a // simple numeric one. if (nSequenceMasked.gt(txToSequenceMasked)) { return false; } return true; } /** * Implemented from bitcoin-abc * https://github.com/Bitcoin-ABC/bitcoin-abc/blob/f8cbc0e2b439aa4030430a7b1ecbdafede0dd072/src/script/bitfield.cpp * @param {*} dummy * @param {*} size */ function DecodeBitfield(dummy, size) { if (size > 32) { this.errstr = "INVALID_BITFIELD_SIZE"; return {result: false}; } let bitfieldSize = Math.floor((size + 7) / 8); let dummyBitlength = dummy.length; if (dummyBitlength !== bitfieldSize) { this.errstr = "INVALID_BITFIELD_SIZE"; return {result: false}; } let bitfield = 0; let dummyAs32Bit = Uint32Array.from(dummy); // let one = new Uint8Array([1]); // let oneAs64Bit = BigUint64Array.from(one); for (let i = 0; i < bitfieldSize; i++) { bitfield = bitfield | (dummyAs32Bit[i] << (8*i)); } let mask = (0x01 << size) - 1 if((bitfield & mask) != bitfield) { this.errstr = "INVALID_BIT_RANGE"; return {result: false}; } return {result: true, bitfield: bitfield}; } /** * countBits * Implemented from https://github.com/Bitcoin-ABC/bitcoin-abc/blob/f8cbc0e2b439aa4030430a7b1ecbdafede0dd072/src/util/bitmanip.h * @param {*} v */ function countBits(v) { /** * Computes the number of bits set in each group of 8bits then uses a * multiplication to sum all of them in the 8 most significant bits and * return these. * More detailed explanation can be found at * https://www.playingwithpointers.com/blog/swar.html */ v = v - (((v) >> 1) & 0x55555555); v = (v & 0x33333333) + ((v >> 2) & 0x33333333); return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; } /** * Based on the inner loop of bitcoind's EvalScript function * bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104 */ Interpreter.prototype.step = function() { var self = this; function stacktop(i) { return self.stack[self.stack.length+i]; } function isOpcodeDisabled(opcode, f64BitIntegers) { switch (opcode) { case Opcode.OP_INVERT: case Opcode.OP_2MUL: case Opcode.OP_2DIV: case Opcode.OP_LSHIFT: case Opcode.OP_RSHIFT: // Disabled opcodes. return true; case Opcode.OP_DIV: case Opcode.OP_MOD: case Opcode.OP_SPLIT: case Opcode.OP_CAT: case Opcode.OP_AND: case Opcode.OP_OR: case Opcode.OP_XOR: case Opcode.OP_BIN2NUM: case Opcode.OP_NUM2BIN: // Opcodes that have been reenabled and do not need any flag as for Nov 14,2018 return false; case Opcode.OP_MUL: // OP_MUL was reenabled as part of the Bigger Script Integers upgrade on May 15, 2022 return !f64BitIntegers; default: break; } return false; } const fRequireMinimal = (this.flags & Interpreter.SCRIPT_VERIFY_MINIMALDATA) !== 0; const f64BitIntegers = (this.flags & Interpreter.SCRIPT_64_BIT_INTEGERS) !== 0; const fNativeIntrospection = (this.flags & Interpreter.SCRIPT_NATIVE_INTROSPECTION) !== 0; const fNativeTokens = (this.flags & Interpreter.SCRIPT_ENABLE_TOKENS) !== 0; const maxScriptIntegerSize = f64BitIntegers ? 8 : 4; //bool fExec = !count(vfExec.begin(), vfExec.end(), false); var fExec = (this.vfExec.indexOf(false) === -1); var buf, buf1, buf2, spliced, n, x1, x2, bn, bn1, bn2, bufSig, bufPubkey, bufMessage, subscript; var sig, pubkey; var fValue, fSuccess; // Read instruction var chunk = this.script.chunks[this.pc]; this.pc++; var opcodenum = chunk.opcodenum; if (_.isUndefined(opcodenum)) { this.errstr = 'SCRIPT_ERR_UNDEFINED_OPCODE'; return false; } if (chunk.buf && chunk.buf.length > Interpreter.MAX_SCRIPT_ELEMENT_SIZE) { this.errstr = 'SCRIPT_ERR_PUSH_SIZE'; return false; } // Note how Opcode.OP_RESERVED does not count towards the opcode limit. if (opcodenum > Opcode.OP_16 && ++(this.nOpCount) > 201) { this.errstr = 'SCRIPT_ERR_OP_COUNT'; return false; } if (isOpcodeDisabled(opcodenum, f64BitIntegers)) { this.errstr = 'SCRIPT_ERR_DISABLED_OPCODE'; return false; } if (fExec && 0 <= opcodenum && opcodenum <= Opcode.OP_PUSHDATA4) { if (fRequireMinimal && !this.script.checkMinimalPush(this.pc - 1)) { this.errstr = 'SCRIPT_ERR_MINIMALDATA'; return false; } if (!chunk.buf) { this.stack.push(Interpreter.false); } else if (chunk.len !== chunk.buf.length) { throw new Error('Length of push value not equal to length of data'); } else { this.stack.push(chunk.buf); } } else if (fExec || (Opcode.OP_IF <= opcodenum && opcodenum <= Opcode.OP_ENDIF)) { switch (opcodenum) { // Push value case Opcode.OP_1NEGATE: case Opcode.OP_1: case Opcode.OP_2: case Opcode.OP_3: case Opcode.OP_4: case Opcode.OP_5: case Opcode.OP_6: case Opcode.OP_7: case Opcode.OP_8: case Opcode.OP_9: case Opcode.OP_10: case Opcode.OP_11: case Opcode.OP_12: case Opcode.OP_13: case Opcode.OP_14: case Opcode.OP_15: case Opcode.OP_16: { // ( -- value) // ScriptNum bn((int)opcode - (int)(Opcode.OP_1 - 1)); n = opcodenum - (Opcode.OP_1 - 1); buf = new BN(n).toScriptNumBuffer(); this.stack.push(buf); // The result of these opcodes should always be the minimal way to push the data // they push, so no need for a CheckMinimalPush here. } break; // // Control // case Opcode.OP_NOP: break; case Opcode.OP_NOP2: case Opcode.OP_CHECKLOCKTIMEVERIFY: if (!(this.flags & Interpreter.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) { // not enabled; treat as a NOP2 if (this.flags & Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { this.errstr = 'SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS'; return false; } break; } if (this.stack.length < 1) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } // Note that elsewhere numeric opcodes are limited to // operands in the range -2**31+1 to 2**31-1, however it is // legal for opcodes to produce results exceeding that // range. This limitation is implemented by CScriptNum's // default 4-byte limit. // // If we kept to that limit we'd have a year 2038 problem, // even though the nLockTime field in transactions // themselves is uint32 which only becomes meaningless // after the year 2106. // // Thus as a special case we tell CScriptNum to accept up // to 5-byte bignums, which are good until 2**39-1, well // beyond the 2**32-1 limit of the nLockTime field itself. var nLockTime = BN.fromScriptNumBuffer(this.stack[this.stack.length - 1], fRequireMinimal, 5); // In the rare event that the argument may be < 0 due tod // some arithmetic being done first, you can always use // 0 MAX CHECKLOCKTIMEVERIFY. if (nLockTime.lt(new BN(0))) { this.errstr = 'SCRIPT_ERR_NEGATIVE_LOCKTIME'; return false; } // Actually compare the specified lock time with the transaction. if (!this.checkLockTime(nLockTime)) { this.errstr = 'SCRIPT_ERR_UNSATISFIED_LOCKTIME'; return false; } break; case Opcode.OP_NOP3: case Opcode.OP_CHECKSEQUENCEVERIFY: if (!(this.flags & Interpreter.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) { // not enabled; treat as a NOP3 if (this.flags & Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { this.errstr = 'SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS'; return false; } break; } if (this.stack.length < 1) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } // nSequence, like nLockTime, is a 32-bit unsigned // integer field. See the comment in CHECKLOCKTIMEVERIFY // regarding 5-byte numeric operands. var nSequence = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal, 5); // In the rare event that the argument may be < 0 due to // some arithmetic being done first, you can always use // 0 MAX CHECKSEQUENCEVERIFY. if (nSequence.lt(new BN(0))) { this.errstr = 'SCRIPT_ERR_NEGATIVE_LOCKTIME'; return false; } // To provide for future soft-fork extensibility, if the // operand has the disabled lock-time flag set, // CHECKSEQUENCEVERIFY behaves as a NOP. if ((nSequence & Interpreter.SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0) { break; } // Actually compare the specified lock time with the transaction. if (!this.checkSequence(nSequence)) { this.errstr = 'SCRIPT_ERR_UNSATISFIED_LOCKTIME'; return false; } break; case Opcode.OP_NOP1: case Opcode.OP_NOP4: case Opcode.OP_NOP5: case Opcode.OP_NOP6: case Opcode.OP_NOP7: case Opcode.OP_NOP8: case Opcode.OP_NOP9: case Opcode.OP_NOP10: { if (this.flags & Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { this.errstr = 'SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS'; return false; } } break; case Opcode.OP_IF: case Opcode.OP_NOTIF: { // <expression> if [statements] [else [statements]] endif // bool fValue = false; fValue = false; if (fExec) { if (this.stack.length < 1) { this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL'; return false; } buf = stacktop(-1); if (this.flags & Interpreter.SCRIPT_VERIFY_MINIMALIF) { if (buf.length > 1) { this.errstr = 'SCRIPT_ERR_MINIMALIF'; return false; } if (buf.length == 1 && buf[0]!=1) { this.errstr = 'SCRIPT_ERR_MINIMALIF'; return false; } } fValue = Interpreter.castToBool(buf); if (opcodenum === Opcode.OP_NOTIF) { fValue = !fValue; } this.stack.pop(); } this.vfExec.push(fValue); } break; case Opcode.OP_ELSE: { if (this.vfExec.length === 0) { this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL'; return false; } this.vfExec[this.vfExec.length - 1] = !this.vfExec[this.vfExec.length - 1]; } break; case Opcode.OP_ENDIF: { if (this.vfExec.length === 0) { this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL'; return false; } this.vfExec.pop(); } break; case Opcode.OP_VERIFY: { // (true -- ) or // (false -- false) and return if (this.stack.length < 1) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } buf = stacktop(-1); fValue = Interpreter.castToBool(buf); if (fValue) { this.stack.pop(); } else { this.errstr = 'SCRIPT_ERR_VERIFY'; return false; } } break; case Opcode.OP_RETURN: { this.errstr = 'SCRIPT_ERR_OP_RETURN'; return false; } break; // // Stack ops // case Opcode.OP_TOALTSTACK: { if (this.stack.length < 1) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } this.altstack.push(this.stack.pop()); } break; case Opcode.OP_FROMALTSTACK: { if (this.altstack.length < 1) { this.errstr = 'SCRIPT_ERR_INVALID_ALTSTACK_OPERATION'; return false; } this.stack.push(this.altstack.pop()); } break; case Opcode.OP_2DROP: { // (x1 x2 -- ) if (this.stack.length < 2) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } this.stack.pop(); this.stack.pop(); } break; case Opcode.OP_2DUP: { // (x1 x2 -- x1 x2 x1 x2) if (this.stack.length < 2) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } buf1 = stacktop(-2); buf2 = stacktop(-1); this.stack.push(buf1); this.stack.push(buf2); } break; case Opcode.OP_3DUP: { // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) if (this.stack.length < 3) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } buf1 = stacktop(-3); buf2 = stacktop(-2); var buf3 = stacktop(-1); this.stack.push(buf1); this.stack.push(buf2); this.stack.push(buf3); } break; case Opcode.OP_2OVER: { // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) if (this.stack.length < 4) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } buf1 = stacktop(-4); buf2 = stacktop(-3); this.stack.push(buf1); this.stack.push(buf2); } break; case Opcode.OP_2ROT: { // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) if (this.stack.length < 6) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } spliced = this.stack.splice(this.stack.length - 6, 2); this.stack.push(spliced[0]); this.stack.push(spliced[1]); } break; case Opcode.OP_2SWAP: { // (x1 x2 x3 x4 -- x3 x4 x1 x2) if (this.stack.length < 4) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } spliced = this.stack.splice(this.stack.length - 4, 2); this.stack.push(spliced[0]); this.stack.push(spliced[1]); } break; case Opcode.OP_IFDUP: { // (x - 0 | x x) if (this.stack.length < 1) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } buf = stacktop(-1); fValue = Interpreter.castToBool(buf); if (fValue) { this.stack.push(buf); } } break; case Opcode.OP_DEPTH: { // -- stacksize buf = new BN(this.stack.length).toScriptNumBuffer(); this.stack.push(buf); } break; case Opcode.OP_DROP: { // (x -- ) if (this.stack.length < 1) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } this.stack.pop(); } break; case Opcode.OP_DUP: { // (x -- x x) if (this.stack.length < 1) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } this.stack.push(stacktop(-1)); } break; case Opcode.OP_NIP: { // (x1 x2 -- x2) if (this.stack.length < 2) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } this.stack.splice(this.stack.length - 2, 1); } break; case Opcode.OP_OVER: { // (x1 x2 -- x1 x2 x1) if (this.stack.length < 2) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } this.stack.push(stacktop(-2)); } break; case Opcode.OP_PICK: case Opcode.OP_ROLL: { // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) if (this.stack.length < 2) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } buf = stacktop(-1); bn = BN.fromScriptNumBuffer(buf, fRequireMinimal); n = bn.toNumber(); this.stack.pop(); if (n < 0 || n >= this.stack.length) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } buf = stacktop(-n-1); if (opcodenum === Opcode.OP_ROLL) { this.stack.splice(this.stack.length - n - 1, 1); } this.stack.push(buf); } break; case Opcode.OP_ROT: { // (x1 x2 x3 -- x2 x3 x1) // x2 x1 x3 after first swap // x2 x3 x1 after second swap if (this.stack.length < 3) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } x1 = stacktop(-3); x2 = stacktop(-2); var x3 = stacktop(-1); this.stack[this.stack.length - 3] = x2; this.stack[this.stack.length - 2] = x3; this.stack[this.stack.length - 1] = x1; } break; case Opcode.OP_SWAP: { // (x1 x2 -- x2 x1) if (this.stack.length < 2) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } x1 = stacktop(-2); x2 = stacktop(-1); this.stack[this.stack.length - 2] = x2; this.stack[this.stack.length - 1] = x1; } break; case Opcode.OP_TUCK: { // (x1 x2 -- x2 x1 x2) if (this.stack.length < 2) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } this.stack.splice(this.stack.length - 2, 0, stacktop(-1)); } break; case Opcode.OP_SIZE: { // (in -- in size) if (this.stack.length < 1) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } bn = new BN(stacktop(-1).length); this.stack.push(bn.toScriptNumBuffer()); } break; // // Bitwise logic // case Opcode.OP_AND: case Opcode.OP_OR: case Opcode.OP_XOR: { // (x1 x2 - out) if (this.stack.length < 2) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } buf1 = stacktop(-2); buf2 = stacktop(-1); // Inputs must be the same size if (buf1.length !== buf2.length) { this.errstr = 'SCRIPT_ERR_INVALID_OPERAND_SIZE'; return false; } // To avoid allocating, we modify vch1 in place. switch (opcodenum) { case Opcode.OP_AND: for (var i = 0; i < buf1.length; i++) { buf1[i] &= buf2[i]; } break; case Opcode.OP_OR: for (var i = 0; i < buf1.length; i++) { buf1[i] |= buf2[i]; } break; case Opcode.OP_XOR: for (var i = 0; i < buf1.length; i++) { buf1[i] ^= buf2[i]; } break; default: break; } // And pop vch2. this.stack.pop() } break; case Opcode.OP_EQUAL: case Opcode.OP_EQUALVERIFY: //case Opcode.OP_NOTEQUAL: // use Opcode.OP_NUMNOTEQUAL { // (x1 x2 - bool) if (this.stack.length < 2) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } buf1 = stacktop(-2); buf2 = stacktop(-1); var fEqual = buf1.toString('hex') === buf2.toString('hex'); this.stack.pop(); this.stack.pop(); this.stack.push(fEqual ? Interpreter.true : Interpreter.false); if (opcodenum === Opcode.OP_EQUALVERIFY) { if (fEqual) { this.stack.pop(); } else { this.errstr = 'SCRIPT_ERR_EQUALVERIFY'; return false; } } } break; // // Numeric // case Opcode.OP_1ADD: case Opcode.OP_1SUB: case Opcode.OP_NEGATE: case Opcode.OP_ABS: case Opcode.OP_NOT: case Opcode.OP_0NOTEQUAL: { // (in -- out) if (this.stack.length < 1) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } buf = stacktop(-1); bn = BN.fromScriptNumBuffer(buf, fRequireMinimal, maxScriptIntegerSize); switch (opcodenum) { case Opcode.OP_1ADD: bn = bn.safeAdd(BN.One, maxScriptIntegerSize); break; case Opcode.OP_1SUB: bn = bn.safeSub(BN.One, maxScriptIntegerSize); break; case Opcode.OP_NEGATE: bn = bn.neg(); break; case Opcode.OP_ABS: if (bn.cmp(BN.Zero) < 0) { bn = bn.neg(); } break; case Opcode.OP_NOT: bn = new BN((bn.cmp(BN.Zero) === 0) + 0); break; case Opcode.OP_0NOTEQUAL: bn = new BN((bn.cmp(BN.Zero) !== 0) + 0); break; //default: assert(!'invalid opcode'); break; // TODO: does this ever occur? } this.stack.pop(); this.stack.push(bn.toScriptNumBuffer()); } break; case Opcode.OP_ADD: case Opcode.OP_SUB: case Opcode.OP_MOD: case Opcode.OP_MUL: case Opcode.OP_DIV: case Opcode.OP_BOOLAND: case Opcode.OP_BOOLOR: case Opcode.OP_NUMEQUAL: case Opcode.OP_NUMEQUALVERIFY: case Opcode.OP_NUMNOTEQUAL: case Opcode.OP_LESSTHAN: case Opcode.OP_GREATERTHAN: case Opcode.OP_LESSTHANOREQUAL: case Opcode.OP_GREATERTHANOREQUAL: case Opcode.OP_MIN: case Opcode.OP_MAX: { // (x1 x2 -- out) if (this.stack.length < 2) { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } bn1 = BN.fromScriptNumBuffer(stacktop(-2), fRequireMinimal, maxScriptIntegerSize); bn2 = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal, maxScriptIntegerSize); bn = new BN(0); switch (opcodenum) { case Opcode.OP_ADD: bn = bn1.safeAdd(bn2, maxScriptIntegerSize); break; case Opcode.OP_SUB: bn = bn1.safeSub(bn2, maxScriptIntegerSize); break; case Opcode.OP_DIV: // denominator must not be 0 if (bn2 == 0) { this.errstr = 'SCRIPT_ERR_DIV_BY_ZERO'; return false; } bn = bn1.div(bn2); break; case Opcode.OP_MUL: bn = bn1.safeMul(bn2, maxScriptIntegerSize); break; case Opcode.OP_MOD: // divisor must not be 0 if (bn2 == 0) { this.errstr = 'SCRIPT_ERR_DIV_BY_ZERO'; return false; } bn = bn1.mod(bn2); break; case Opcode.OP_BOOLAND: bn = new BN(((bn1.cmp(BN.Zero) !== 0) && (bn2.cmp(BN.Zero) !== 0)) + 0); break; // case Opcode.OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break; case Opcode.OP_BOOLOR: bn = new BN(((bn1.cmp(BN.Zero) !== 0) || (bn2.cmp(BN.Zero) !== 0)) + 0); break; // case Opcode.OP_NUMEQUAL: bn = (bn1 == bn2); break; case Opcode.OP_NUMEQUAL: bn = new BN((bn1.cmp(bn2) === 0) + 0); break; // case Opcode.OP_NUMEQUALVERIFY: bn = (bn1 == bn2); break; case Opcode.OP_NUMEQUALVERIFY: bn = new BN((bn1.cmp(bn2) === 0) + 0); break; // case Opcode.OP_NUMNOTEQUAL: bn = (bn1 != bn2); break; case Opcode.OP_NUMNOTEQUAL: bn = new BN((bn1.cmp(bn2) !== 0) + 0); break; // case Opcode.OP_LESSTHAN: bn = (bn1 < bn2); break; case Opcode.OP_LESSTHAN: bn = new BN((bn1.cmp(bn2) < 0) + 0); break; // case Opcode.OP_GREATERTHAN: bn = (bn1 > bn2); break; case Opcode.OP_GREATERTHAN: bn = new BN((bn1.cmp(bn2) > 0) + 0); break; // case Opcode.OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break; case Opcode.OP_LESSTHANOREQUAL: bn = new BN((bn1.cmp(bn2) <= 0) + 0);