diff --git a/index.js b/index.js index 0a4069c11..2ad6ab2f2 100644 --- a/index.js +++ b/index.js @@ -45,7 +45,6 @@ bitcore.PaymentProtocol = require('./lib/paymentprotocol'); bitcore.PrivateKey = require('./lib/privatekey'); bitcore.PublicKey = require('./lib/publickey'); bitcore.Script = require('./lib/script'); -bitcore.ScriptInterpreter = require('./lib/script_interpreter'); bitcore.Transaction = require('./lib/transaction'); bitcore.URI = require('./lib/uri'); bitcore.Unit = require('./lib/unit'); diff --git a/lib/script_interpreter.js b/lib/script_interpreter.js deleted file mode 100644 index a7de7d603..000000000 --- a/lib/script_interpreter.js +++ /dev/null @@ -1,1139 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -var Script = require('./script'); -var Opcode = require('./opcode'); -var BN = require('./crypto/bn'); -var Hash = require('./crypto/hash'); -var BufferReader = require('./encoding/bufferreader'); -var BufferWriter = require('./encoding/bufferwriter'); -var Signature = require('./crypto/signature'); -var PublicKey = require('./publickey'); -var Transaction = require('./transaction'); - -/** - * 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., ScriptInterpreter().verify( ... ); - */ -var ScriptInterpreter = function ScriptInterpreter(obj) { - if (!(this instanceof ScriptInterpreter)) { - return new ScriptInterpreter(obj); - } - if (obj) { - this.initialize(); - this.set(obj); - } else { - this.initialize(); - } -}; - -module.exports = ScriptInterpreter; - -ScriptInterpreter.prototype.initialize = function(obj) { - this.stack = []; - this.altstack = []; - this.pc = 0; - this.pbegincodehash = 0; - this.nOpCount = 0; - this.vfExec = []; - this.errstr = ''; - this.flags = 0; -}; - -ScriptInterpreter.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.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; -}; - -ScriptInterpreter.true = new Buffer([1]); -ScriptInterpreter.false = new Buffer([]); - -ScriptInterpreter.MAX_SCRIPT_ELEMENT_SIZE = 520; - -// flags taken from bitcoind -// bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104 -ScriptInterpreter.SCRIPT_VERIFY_NONE = 0; - -// Evaluate P2SH subscripts (softfork safe, BIP16). -ScriptInterpreter.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). -ScriptInterpreter.SCRIPT_VERIFY_STRICTENC = (1 << 1); - -// Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1) -ScriptInterpreter.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). -ScriptInterpreter.SCRIPT_VERIFY_LOW_S = (1 << 3); - -// verify dummy stack item consumed by CHECKMULTISIG is of zero-length (softfork safe, BIP62 rule 7). -ScriptInterpreter.SCRIPT_VERIFY_NULLDUMMY = (1 << 4); - -// Using a non-push operator in the scriptSig causes script failure (softfork safe, BIP62 rule 2). -ScriptInterpreter.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) -ScriptInterpreter.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. -ScriptInterpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1 << 7); - -ScriptInterpreter.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; -}; - -/** - * Translated from bitcoind's CheckSignatureEncoding - */ -ScriptInterpreter.prototype.checkSignatureEncoding = function(buf) { - var sig; - if ((this.flags & (ScriptInterpreter.SCRIPT_VERIFY_DERSIG | ScriptInterpreter.SCRIPT_VERIFY_LOW_S | ScriptInterpreter.SCRIPT_VERIFY_STRICTENC)) !== 0 && !Signature.isTxDER(buf)) { - this.errstr = 'SCRIPT_ERR_SIG_DER'; - return false; - } else if ((this.flags & ScriptInterpreter.SCRIPT_VERIFY_LOW_S) !== 0) { - sig = Signature().fromTxFormat(buf); - if (!sig.hasLowS()) { - this.errstr = 'SCRIPT_ERR_SIG_DER'; - return false; - } - } else if ((this.flags & ScriptInterpreter.SCRIPT_VERIFY_STRICTENC) !== 0) { - sig = Signature().fromTxFormat(buf); - if (!sig.hasDefinedHashtype()) { - this.errstr = 'SCRIPT_ERR_SIG_HASHTYPE'; - return false; - } - } - return true; -}; - -/** - * Translated from bitcoind's CheckPubKeyEncoding - */ -ScriptInterpreter.prototype.checkPubkeyEncoding = function(buf) { - if ((this.flags & ScriptInterpreter.SCRIPT_VERIFY_STRICTENC) !== 0 && !Pubkey.isCompressedOrUncompressed(buf)) { - this.errstr = 'SCRIPT_ERR_PUBKEYTYPE'; - return false; - } - return true; -}; - -/** - * Based on bitcoind's EvalScript function, with the inner loop moved to - * ScriptInterpreter.prototype.step() - * bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104 - */ -ScriptInterpreter.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; -} - -/** - * Based on the inner loop of bitcoind's EvalScript function - * bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104 - */ -ScriptInterpreter.prototype.step = function() { - - var fRequireMinimal = (this.flags & ScriptInterpreter.SCRIPT_VERIFY_MINIMALDATA) != 0; - - //bool fExec = !count(vfExec.begin(), vfExec.end(), false); - var fExec = (this.vfExec.indexOf(false) !== -1); - - // - // Read instruction - // - var chunk = this.script.chunks[this.pc]; - this.pc++; - var opcodenum = chunk.opcodenum; - if (_.isUndefined(opcodenum)) { - opcodenum = chunk; - } - if (chunk.buf && chunk.buf.length > ScriptInterpreter.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 (opcodenum === Opcode.OP_CAT || - opcodenum === Opcode.OP_SUBSTR || - opcodenum === Opcode.OP_LEFT || - opcodenum === Opcode.OP_RIGHT || - opcodenum === Opcode.OP_INVERT || - opcodenum === Opcode.OP_AND || - opcodenum === Opcode.OP_OR || - opcodenum === Opcode.OP_XOR || - opcodenum === Opcode.OP_2MUL || - opcodenum === Opcode.OP_2DIV || - opcodenum === Opcode.OP_MUL || - opcodenum === Opcode.OP_DIV || - opcodenum === Opcode.OP_MOD || - opcodenum === Opcode.OP_LSHIFT || - opcodenum === Opcode.OP_RSHIFT) { - 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(ScriptInterpreter.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)); - var n = opcodenum - (Opcode.OP_1 - 1); - var buf = 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_NOP1: - case Opcode.OP_NOP2: - case Opcode.OP_NOP3: - 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 & ScriptInterpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { - this.errstr = 'SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS'; - return false; - } - } - break; - - case Opcode.OP_IF: - case Opcode.OP_NOTIF: - { - // if [statements] [else [statements]] endif - // bool fValue = false; - var fValue = false; - if (fExec) { - if (this.stack.length < 1) { - this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL'; - return false; - } - var buf = this.stack.pop(); - fValue = ScriptInterpreter.castToBool(buf); - if (opcodenum === Opcode.OP_NOTIF) - fValue = !fValue; - } - 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; - } - var buf = this.stack[this.stack.length - 1]; - var fValue = ScriptInterpreter.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; - } - var buf1 = this.stack[this.stack.length - 2]; - var buf2 = this.stack[this.stack.length - 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; - } - var buf1 = this.stack[this.stack.length - 3]; - var buf2 = this.stack[this.stack.length - 2]; - var buf3 = this.stack[this.stack.length - 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; - } - var buf1 = this.stack[this.stack.length - 4]; - var buf2 = this.stack[this.stack.length - 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; - } - var 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; - } - var 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; - } - var buf = this.stack[this.stack.length - 1]; - var fValue = ScriptInterpreter.castToBool(buf); - if (fValue) - this.stack.push(buf); - } - break; - - case Opcode.OP_DEPTH: - { - // -- stacksize - var buf = 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(this.stack[this.stack.length - 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(this.stack[this.stack.length - 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; - } - var buf = this.stack[this.stack.length - 1]; - var bn = BN().fromScriptNumBuffer(buf, fRequireMinimal); - var n = bn.toNumber(); - this.stack.pop(); - if (n < 0 || n >= this.stack.length) { - this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; - return false; - } - var buf = this.stack[this.stack.length - 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; - } - var x1 = this.stack[this.stack.length - 3]; - var x2 = this.stack[this.stack.length - 2]; - var x3 = this.stack[this.stack.length - 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; - } - var x1 = this.stack[this.stack.length - 2]; - var x2 = this.stack[this.stack.length - 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, this.stack[this.stack.length - 1]); - } - break; - - - case Opcode.OP_SIZE: - { - // (in -- in size) - if (this.stack.length < 1) { - this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; - return false; - } - var bn = BN(this.stack[this.stack.length - 1].length); - this.stack.push(bn.toScriptNumBuffer()); - } - break; - - - // - // Bitwise logic - // - 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; - } - var buf1 = this.stack[this.stack.length - 2]; - var buf2 = this.stack[this.stack.length - 1]; - var fEqual = buf1.toString('hex') === buf2.toString('hex'); - // Opcode.OP_NOTEQUAL is disabled because it would be too easy to say - // something like n != 1 and have some wiseguy pass in 1 with extra - // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001) - //if (opcode == Opcode.OP_NOTEQUAL) - // fEqual = !fEqual; - this.stack.pop(); - this.stack.pop(); - this.stack.push(fEqual ? ScriptInterpreter.true : ScriptInterpreter.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; - } - var buf = this.stack[this.stack.length - 1]; - var bn = BN().fromScriptNumBuffer(buf, fRequireMinimal); - switch (opcodenum) { - case Opcode.OP_1ADD: - bn = bn.add(1); - break; - case Opcode.OP_1SUB: - bn = bn.sub(1); - break; - case Opcode.OP_NEGATE: - bn = bn.neg(); - break; - case Opcode.OP_ABS: - if (bn.cmp(0) < 0) bn = bn.neg(); - break; - case Opcode.OP_NOT: - bn = BN((bn.cmp(0) === 0) + 0); - break; - case Opcode.OP_0NOTEQUAL: - bn = BN((bn.cmp(0) !== 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_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; - } - var bn1 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 2], fRequireMinimal); - var bn2 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 1], fRequireMinimal); - var bn = BN(0); - - switch (opcodenum) { - case Opcode.OP_ADD: - bn = bn1.add(bn2); - break; - - case Opcode.OP_SUB: - bn = bn1.sub(bn2); - break; - - // case Opcode.OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break; - case Opcode.OP_BOOLAND: - bn = BN(((bn1.cmp(0) !== 0) && (bn2.cmp(0) !== 0)) + 0); - break; - // case Opcode.OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break; - case Opcode.OP_BOOLOR: - bn = BN(((bn1.cmp(0) !== 0) || (bn2.cmp(0) !== 0)) + 0); - break; - // case Opcode.OP_NUMEQUAL: bn = (bn1 == bn2); break; - case Opcode.OP_NUMEQUAL: - bn = BN((bn1.cmp(bn2) === 0) + 0); - break; - // case Opcode.OP_NUMEQUALVERIFY: bn = (bn1 == bn2); break; - case Opcode.OP_NUMEQUALVERIFY: - bn = BN((bn1.cmp(bn2) === 0) + 0); - break; - // case Opcode.OP_NUMNOTEQUAL: bn = (bn1 != bn2); break; - case Opcode.OP_NUMNOTEQUAL: - bn = BN((bn1.cmp(bn2) !== 0) + 0); - break; - // case Opcode.OP_LESSTHAN: bn = (bn1 < bn2); break; - case Opcode.OP_LESSTHAN: - bn = BN((bn1.cmp(bn2) < 0) + 0); - break; - // case Opcode.OP_GREATERTHAN: bn = (bn1 > bn2); break; - case Opcode.OP_GREATERTHAN: - bn = BN((bn1.cmp(bn2) > 0) + 0); - break; - // case Opcode.OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break; - case Opcode.OP_LESSTHANOREQUAL: - bn = BN((bn1.cmp(bn2) <= 0) + 0); - break; - // case Opcode.OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break; - case Opcode.OP_GREATERTHANOREQUAL: - bn = BN((bn1.cmp(bn2) >= 0) + 0); - break; - case Opcode.OP_MIN: - bn = (bn1.cmp(bn2) < 0 ? bn1 : bn2); - break; - case Opcode.OP_MAX: - bn = (bn1.cmp(bn2) > 0 ? bn1 : bn2); - break; - // default: assert(!'invalid opcode'); break; //TODO: does this ever occur? - } - this.stack.pop(); - this.stack.pop(); - this.stack.push(bn.toScriptNumBuffer()); - - if (opcodenum === Opcode.OP_NUMEQUALVERIFY) { - // if (CastToBool(stacktop(-1))) - if (ScriptInterpreter.castToBool(this.stack[this.stack.length - 1])) - this.stack.pop(); - else { - this.errstr = 'SCRIPT_ERR_NUMEQUALVERIFY'; - return false; - } - } - } - break; - - case Opcode.OP_WITHIN: - { - // (x min max -- out) - if (this.stack.length < 3) { - this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; - return false; - } - var bn1 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 3], fRequireMinimal); - var bn2 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 2], fRequireMinimal); - var bn3 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 1], fRequireMinimal); - //bool fValue = (bn2 <= bn1 && bn1 < bn3); - var fValue = (bn2.cmp(bn1) <= 0) && (bn1.cmp(bn3) < 0); - this.stack.pop(); - this.stack.pop(); - this.stack.pop(); - this.stack.push(fValue ? ScriptInterpreter.true : ScriptInterpreter.false); - } - break; - - - // - // Crypto - // - case Opcode.OP_RIPEMD160: - case Opcode.OP_SHA1: - case Opcode.OP_SHA256: - case Opcode.OP_HASH160: - case Opcode.OP_HASH256: - { - // (in -- hash) - if (this.stack.length < 1) { - this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; - return false; - } - var buf = this.stack[this.stack.length - 1]; - //valtype vchHash((opcode == Opcode.OP_RIPEMD160 || opcode == Opcode.OP_SHA1 || opcode == Opcode.OP_HASH160) ? 20 : 32); - var bufHash; - if (opcodenum === Opcode.OP_RIPEMD160) - bufHash = Hash.ripemd160(buf); - else if (opcodenum === Opcode.OP_SHA1) - bufHash = Hash.sha1(buf); - else if (opcodenum === Opcode.OP_SHA256) - bufHash = Hash.sha256(buf); - else if (opcodenum === Opcode.OP_HASH160) - bufHash = Hash.sha256ripemd160(buf); - else if (opcodenum === Opcode.OP_HASH256) - bufHash = Hash.sha256sha256(buf); - this.stack.pop(); - this.stack.push(bufHash); - } - break; - - case Opcode.OP_CODESEPARATOR: - { - // Hash starts after the code separator - this.pbegincodehash = this.pc; - } - break; - - case Opcode.OP_CHECKSIG: - case Opcode.OP_CHECKSIGVERIFY: - { - // (sig pubkey -- bool) - if (this.stack.length < 2) { - this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; - return false; - } - - var bufSig = this.stack[this.stack.length - 2]; - var bufPubkey = this.stack[this.stack.length - 1]; - - // Subset of script starting at the most recent codeseparator - // CScript scriptCode(pbegincodehash, pend); - var subscript = Script().set({ - chunks: this.script.chunks.slice(this.pbegincodehash) - }); - - // Drop the signature, since there's no way for a signature to sign itself - subscript.findAndDelete(Script().writeBuffer(bufSig)); - - if (!this.checkSignatureEncoding(bufSig) || !this.checkPubkeyEncoding(bufPubkey)) { - // serror is set - return false; - } - - var fSuccess; - try { - var sig = Signature().fromTxFormat(bufSig); - var pubkey = Pubkey().fromBuffer(bufPubkey, false); - fSuccess = this.tx.verify(sig, pubkey, this.nin, subscript); - } catch (e) { - //invalid sig or pubkey - fSuccess = false; - } - - this.stack.pop(); - this.stack.pop(); - // stack.push_back(fSuccess ? vchTrue : vchFalse); - this.stack.push(fSuccess ? ScriptInterpreter.true : ScriptInterpreter.false); - if (opcodenum === Opcode.OP_CHECKSIGVERIFY) { - if (fSuccess) - this.stack.pop(); - else { - this.errstr = 'SCRIPT_ERR_CHECKSIGVERIFY'; - return false; - } - } - } - break; - - case Opcode.OP_CHECKMULTISIG: - case Opcode.OP_CHECKMULTISIGVERIFY: - { - // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) - - var i = 1; - if (this.stack.length < i) { - this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; - return false; - } - - var nKeysCount = BN().fromScriptNumBuffer(this.stack[this.stack.length - i], fRequireMinimal).toNumber(); - if (nKeysCount < 0 || nKeysCount > 20) { - this.errstr = 'SCRIPT_ERR_PUBKEY_COUNT'; - return false; - } - this.nOpCount += nKeysCount; - if (this.nOpCount > 201) { - this.errstr = 'SCRIPT_ERR_OP_COUNT'; - return false; - } - // int ikey = ++i; - var ikey = ++i; - i += nKeysCount; - if (this.stack.length < i) { - this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; - return false; - } - - var nSigsCount = BN().fromScriptNumBuffer(this.stack[this.stack.length - i], fRequireMinimal).toNumber(); - if (nSigsCount < 0 || nSigsCount > nKeysCount) { - this.errstr = 'SCRIPT_ERR_SIG_COUNT'; - return false; - } - // int isig = ++i; - var isig = ++i; - i += nSigsCount; - if (this.stack.length < i) { - this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; - return false; - } - - // Subset of script starting at the most recent codeseparator - var subscript = Script().set({ - chunks: this.script.chunks.slice(this.pbegincodehash) - }); - - // Drop the signatures, since there's no way for a signature to sign itself - for (var k = 0; k < nSigsCount; k++) { - var bufSig = this.stack[this.stack.length - isig - k]; - subscript.findAndDelete(Script().writeBuffer(bufSig)); - } - - var fSuccess = true; - while (fSuccess && nSigsCount > 0) { - // valtype& vchSig = stacktop(-isig); - var bufSig = this.stack[this.stack.length - isig]; - // valtype& vchPubKey = stacktop(-ikey); - var bufPubkey = this.stack[this.stack.length - ikey]; - - if (!this.checkSignatureEncoding(bufSig) || !this.checkPubkeyEncoding(bufPubkey)) { - // serror is set - return false; - } - - var fOk; - try { - var sig = Signature().fromTxFormat(bufSig); - var pubkey = Pubkey().fromBuffer(bufPubkey, false); - fOk = this.tx.verify(sig, pubkey, this.nin, subscript); - } catch (e) { - //invalid sig or pubkey - fOk = false; - } - - if (fOk) { - isig++; - nSigsCount--; - } - ikey++; - nKeysCount--; - - // If there are more signatures left than keys left, - // then too many signatures have failed - if (nSigsCount > nKeysCount) - fSuccess = false; - } - - // Clean up stack of actual arguments - while (i-- > 1) - this.stack.pop(); - - // A bug causes CHECKMULTISIG to consume one extra argument - // whose contents were not checked in any way. - // - // Unfortunately this is a potential source of mutability, - // so optionally verify it is exactly equal to zero prior - // to removing it from the stack. - if (this.stack.length < 1) { - this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; - return false; - } - if ((this.flags & ScriptInterpreter.SCRIPT_VERIFY_NULLDUMMY) && this.stack[this.stack.length - 1].length) { - this.errstr = 'SCRIPT_ERR_SIG_NULLDUMMY'; - return false; - } - this.stack.pop(); - - this.stack.push(fSuccess ? ScriptInterpreter.true : ScriptInterpreter.false); - - if (opcodenum === Opcode.OP_CHECKMULTISIGVERIFY) { - if (fSuccess) - this.stack.pop(); - else { - this.errstr = 'SCRIPT_ERR_CHECKMULTISIGVERIFY'; - return false; - } - } - } - break; - - default: - this.errstr = 'SCRIPT_ERR_BAD_OPCODE'; - return false; - } - - return true; -} - -/** - * Translated from bitcoind's VerifyScript - */ -ScriptInterpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags) { - if (_.isUndefined(tx)) { - tx = new Transaction(); - } - if (_.isUndefined(nin)) { - nin = 0; - } - this.set({ - script: scriptSig, - tx: tx, - nin: nin, - flags: flags - }); - - if ((flags & ScriptInterpreter.SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.isPushOnly()) { - this.errstr = 'SCRIPT_ERR_SIG_PUSHONLY'; - return false; - } - - if (!this.evaluate()) - return false; - - if (flags & ScriptInterpreter.SCRIPT_VERIFY_P2SH) - var stackCopy = this.stack.slice(); - - var stack = this.stack; - this.initialize(); - this.set({ - script: scriptPubkey, - stack: stack, - tx: tx, - nin: nin, - flags: flags - }); - - if (!this.evaluate()) - return false; - - if (this.stack.length === 0) { - this.errstr = 'SCRIPT_ERR_EVAL_FALSE'; - return false; - } - - var buf = this.stack[this.stack.length - 1]; - if (!ScriptInterpreter.castToBool(buf)) { - this.errstr = 'SCRIPT_ERR_EVAL_FALSE'; - return false; - } - - // Additional validation for spend-to-script-hash transactions: - if ((flags & ScriptInterpreter.SCRIPT_VERIFY_P2SH) && scriptPubkey.isScripthashOut()) { - // 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 pubkeySerialized = stackCopy[stackCopy.length - 1]; - var scriptPubkey2 = Script().fromBuffer(pubkeySerialized); - stackCopy.pop(); - - this.initialize(); - this.set({ - script: scriptPubkey2, - stack: stackCopy, - tx: tx, - nin: nin, - flags: flags - }); - - if (!this.evaluate()) - // serror is set - return false; - - if (stackCopy.length === 0) { - this.errstr = 'SCRIPT_ERR_EVAL_FALSE'; - return false; - } - - if (!ScriptInterpreter.castToBool(stackCopy[stackCopy.length - 1])) { - this.errstr = 'SCRIPT_ERR_EVAL_FALSE'; - return false; - } else { - return true; - } - } - - return true; -}; diff --git a/test/script_interpreter.js b/test/script_interpreter.js deleted file mode 100644 index 802b5db4c..000000000 --- a/test/script_interpreter.js +++ /dev/null @@ -1,305 +0,0 @@ -'use strict'; - -var should = require('chai').should(); -var bitcore = require('..'); -var ScriptInterpreter = bitcore.ScriptInterpreter; -var Transaction = bitcore.Transaction; -var Script = bitcore.Script; -var BN = bitcore.crypto.BN; -var Sig = bitcore.crypto.Signature; -var BufferReader = bitcore.encoding.BufferReader; - -var script_valid = require('./data/bitcoind/script_valid'); -var script_invalid = require('./data/bitcoind/script_invalid'); -var tx_valid = require('./transaction/tx_valid'); -var tx_invalid = require('./transaction/tx_invalid'); - -describe('ScriptInterpreter', function() { - - it('should make a new interp', function() { - var interp = new ScriptInterpreter(); - (interp instanceof ScriptInterpreter).should.equal(true); - interp.stack.length.should.equal(0); - interp.altstack.length.should.equal(0); - interp.pc.should.equal(0); - interp.pbegincodehash.should.equal(0); - interp.nOpCount.should.equal(0); - interp.vfExec.length.should.equal(0); - interp.errstr.should.equal(''); - interp.flags.should.equal(0); - }); - - describe('@castToBool', function() { - - it('should cast these bufs to bool correctly', function() { - ScriptInterpreter.castToBool(BN(0).toSM({ - endian: 'little' - })).should.equal(false); - ScriptInterpreter.castToBool(new Buffer('0080', 'hex')).should.equal(false); //negative 0 - ScriptInterpreter.castToBool(BN(1).toSM({ - endian: 'little' - })).should.equal(true); - ScriptInterpreter.castToBool(BN(-1).toSM({ - endian: 'little' - })).should.equal(true); - - var buf = new Buffer('00', 'hex'); - var bool = BN().fromSM(buf, { - endian: 'little' - }).cmp(0) !== 0; - ScriptInterpreter.castToBool(buf).should.equal(bool); - }); - - }); - - describe('#verify', function() { - - it('should verify these trivial scripts', function() { - var verified; - var si = ScriptInterpreter(); - verified = si.verify(Script('OP_1'), Script('OP_1')); - verified.should.equal(true); - verified = ScriptInterpreter().verify(Script('OP_1'), Script('OP_0')); - verified.should.equal(false); - verified = ScriptInterpreter().verify(Script('OP_0'), Script('OP_1')); - verified.should.equal(true); - verified = ScriptInterpreter().verify(Script('OP_CODESEPARATOR'), Script('OP_1')); - verified.should.equal(true); - verified = ScriptInterpreter().verify(Script(''), Script('OP_DEPTH OP_0 OP_EQUAL')); - verified.should.equal(true); - verified = ScriptInterpreter().verify(Script('OP_1 OP_2'), Script('OP_2 OP_EQUALVERIFY OP_1 OP_EQUAL')); - verified.should.equal(true); - verified = ScriptInterpreter().verify(Script('9 0x000000000000000010'), Script('')); - verified.should.equal(true); - verified = ScriptInterpreter().verify(Script('OP_1'), Script('OP_15 OP_ADD OP_16 OP_EQUAL')); - verified.should.equal(true); - verified = ScriptInterpreter().verify(Script('OP_0'), Script('OP_IF OP_VER OP_ELSE OP_1 OP_ENDIF')); - verified.should.equal(true); - }); - - it('should verify this new pay-to-pubkey script', function() { - var keypair = Keypair().fromRandom(); - var scriptPubkey = Script().writeBuffer(keypair.pubkey.toDER(true)).writeOp('OP_CHECKSIG'); - - var hashbuf = new Buffer(32); - hashbuf.fill(0); - var credtx = Transaction(); - credtx.addTxin(hashbuf, 0xffffffff, Script('OP_0 OP_0'), 0xffffffff); - credtx.addTxout(BN(0), scriptPubkey); - - var idbuf = credtx.hash(); - var spendtx = Transaction(); - spendtx.addTxin(idbuf, 0, Script(), 0xffffffff); - spendtx.addTxout(BN(0), Script()); - - var sig = spendtx.sign(keypair, Sig.SIGHASH_ALL, 0, scriptPubkey); - var scriptSig = Script().writeBuffer(sig.toTxFormat()); - spendtx.txins[0].setScript(scriptSig); - - var interp = ScriptInterpreter(); - var verified = interp.verify(scriptSig, scriptPubkey, spendtx, 0); - verified.should.equal(true); - }); - - it('should verify this pay-to-pubkey script from script_valid.json', function() { - var scriptSig = Script().fromBitcoindString('0x47 0x3044022007415aa37ce7eaa6146001ac8bdefca0ddcba0e37c5dc08c4ac99392124ebac802207d382307fd53f65778b07b9c63b6e196edeadf0be719130c5db21ff1e700d67501'); - var scriptPubkey = Script().fromBitcoindString('0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG'); - - var hashbuf = new Buffer(32); - hashbuf.fill(0); - var credtx = Transaction(); - credtx.addTxin(hashbuf, 0xffffffff, Script('OP_0 OP_0'), 0xffffffff); - credtx.addTxout(BN(0), scriptPubkey); - - var idbuf = credtx.hash(); - var spendtx = Transaction(); - spendtx.addTxin(idbuf, 0, scriptSig, 0xffffffff); - spendtx.addTxout(BN(0), Script()); - - var interp = ScriptInterpreter(); - var verified = interp.verify(scriptSig, scriptPubkey, spendtx, 0, 0); - verified.should.equal(true); - }); - - }); - - describe('vectors', function() { - - var getFlags = function getFlags(flagstr) { - var flags = 0; - if (flagstr.indexOf('NONE') !== -1) { - flags = flags | ScriptInterpreter.SCRIPT_VERIFY_NONE; - } - if (flagstr.indexOf('P2SH') !== -1) { - flags = flags | ScriptInterpreter.SCRIPT_VERIFY_P2SH; - } - if (flagstr.indexOf('STRICTENC') !== -1) { - flags = flags | ScriptInterpreter.SCRIPT_VERIFY_STRICTENC; - } - if (flagstr.indexOf('DERSIG') !== -1) { - flags = flags | ScriptInterpreter.SCRIPT_VERIFY_DERSIG; - } - if (flagstr.indexOf('LOW_S') !== -1) { - flags = flags | ScriptInterpreter.SCRIPT_VERIFY_LOW_S; - } - if (flagstr.indexOf('NULLDUMMY') !== -1) { - flags = flags | ScriptInterpreter.SCRIPT_VERIFY_NULLDUMMY; - } - if (flagstr.indexOf('SIGPUSHONLY') !== -1) { - flags = flags | ScriptInterpreter.SCRIPT_VERIFY_SIGPUSHONLY; - } - if (flagstr.indexOf('MINIMALDATA') !== -1) { - flags = flags | ScriptInterpreter.SCRIPT_VERIFY_MINIMALDATA; - } - if (flagstr.indexOf('DISCOURAGE_UPGRADABLE_NOPS') !== -1) { - flags = flags | ScriptInterpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS; - } - return flags; - }; - - var c = 0; - script_valid.forEach(function(vector) { - if (vector.length === 1) { - return; - } - c++; - var descstr = vector[3]; - it('should pass script_valid vector ' + c + '(' + descstr + ')', function() { - var scriptSig = Script().fromBitcoindString(vector[0]); - var scriptPubkey = Script().fromBitcoindString(vector[1]); - var flags = getFlags(vector[2]); - - var hashbuf = new Buffer(32); - hashbuf.fill(0); - var credtx = Transaction(); - credtx.addTxin(hashbuf, 0xffffffff, Script('OP_0 OP_0'), 0xffffffff); - credtx.addTxout(BN(0), scriptPubkey); - - var idbuf = credtx.hash(); - var spendtx = Transaction(); - spendtx.addTxin(idbuf, 0, scriptSig, 0xffffffff); - spendtx.addTxout(BN(0), Script()); - - var interp = ScriptInterpreter(); - var verified = interp.verify(scriptSig, scriptPubkey, spendtx, 0, flags); - verified.should.equal(true); - }); - }); - - c = 0; - script_invalid.forEach(function(vector) { - if (vector.length === 1) { - return; - } - c++; - var descstr = vector[3]; - it('should pass script_invalid vector ' + c + '(' + descstr + ')', function() { - var scriptSig = Script().fromBitcoindString(vector[0]); - var scriptPubkey = Script().fromBitcoindString(vector[1]); - var flags = getFlags(vector[2]); - - var hashbuf = new Buffer(32); - hashbuf.fill(0); - var credtx = Transaction(); - credtx.addTxin(hashbuf, 0xffffffff, Script('OP_0 OP_0'), 0xffffffff); - credtx.addTxout(BN(0), scriptPubkey); - - var idbuf = credtx.hash(); - var spendtx = Transaction(); - spendtx.addTxin(idbuf, 0, scriptSig, 0xffffffff); - spendtx.addTxout(BN(0), Script()); - - var interp = ScriptInterpreter(); - var verified = interp.verify(scriptSig, scriptPubkey, spendtx, 0, flags); - verified.should.equal(false); - }); - }); - - c = 0; - tx_valid.forEach(function(vector) { - if (vector.length === 1) { - return; - } - c++; - it('should pass tx_valid vector ' + c, function() { - var inputs = vector[0]; - var txhex = vector[1]; - var flags = getFlags(vector[2]); - - var map = {}; - inputs.forEach(function(input) { - var txoutnum = input[1]; - if (txoutnum === -1) { - txoutnum = 0xffffffff; //bitcoind casts -1 to an unsigned int - } - map[input[0] + ':' + txoutnum] = Script().fromBitcoindString(input[2]); - }); - - var tx = Transaction().fromBuffer(new Buffer(txhex, 'hex')); - tx.txins.forEach(function(txin, j) { - var scriptSig = txin.script; - var txidhex = BufR(txin.txidbuf).readReverse().toString('hex'); - var txoutnum = txin.txoutnum; - var scriptPubkey = map[txidhex + ':' + txoutnum]; - should.exist(scriptPubkey); - var interp = ScriptInterpreter(); - var verified = interp.verify(scriptSig, scriptPubkey, tx, j, flags); - verified.should.equal(true); - }); - }); - }); - - c = 0; - tx_invalid.forEach(function(vector) { - if (vector.length === 1) { - return; - } - c++; - - // tests intentionally not performed by the script interpreter: - // TODO: check this? - /* - if (c === 7 || // tests if valuebn is negative - c === 8 || // tests if valuebn is greater than MAX_MONEY - c === 10 || // tests if two inputs are equal - c === 11 || // coinbase - c === 12 || // coinbase - c === 13 // null input - ) { - return; - } - */ - - it('should pass tx_invalid vector ' + c, function() { - var inputs = vector[0]; - var txhex = vector[1]; - var flags = getFlags(vector[2]); - - var map = {}; - inputs.forEach(function(input) { - var txoutnum = input[1]; - if (txoutnum === -1) { - txoutnum = 0xffffffff; //bitcoind casts -1 to an unsigned int - } - map[input[0] + ':' + txoutnum] = Script().fromBitcoindString(input[2]); - }); - - var tx = Transaction().fromBuffer(new Buffer(txhex, 'hex')); - if (tx.txins.length > 0) { - tx.txins.some(function(txin, j) { - var scriptSig = txin.script; - var txidhex = BufR(txin.txidbuf).readReverse().toString('hex'); - var txoutnum = txin.txoutnum; - var scriptPubkey = map[txidhex + ':' + txoutnum]; - should.exist(scriptPubkey); - var interp = ScriptInterpreter(); - var verified = interp.verify(scriptSig, scriptPubkey, tx, j, flags); - return verified === false; - }).should.equal(true); - } - }); - }); - - }); - -});