From 0b2dae43c54140a7fbecfa95aa58dfc95e70b080 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Fri, 4 Apr 2014 18:24:34 -0300 Subject: [PATCH] remove console.logs and std::couts --- ScriptInterpreter.js | 1355 +++++++++++++++++++------------------- src/eckey.cc | 1 - test/test.Transaction.js | 20 +- 3 files changed, 681 insertions(+), 695 deletions(-) diff --git a/ScriptInterpreter.js b/ScriptInterpreter.js index 0c54c14..d9a0c21 100644 --- a/ScriptInterpreter.js +++ b/ScriptInterpreter.js @@ -33,7 +33,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, throw new Error("ScriptInterpreter.eval() requires a callback"); } - console.log('eval script '+script.toHumanReadable()); var pc = 0; var execStack = []; var altStack = []; @@ -49,723 +48,724 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, executeStep.call(this, callback); function executeStep(cb) { - // Once all chunks have been processed, execution ends - if (pc >= script.chunks.length) { - // Execution stack must be empty at the end of the script - if (execStack.length) { - cb(new Error("Execution stack ended non-empty")); + try { + // Once all chunks have been processed, execution ends + if (pc >= script.chunks.length) { + // Execution stack must be empty at the end of the script + if (execStack.length) { + cb(new Error("Execution stack ended non-empty")); + return; + } + + // Execution successful (Note that we still have to check whether the + // final stack contains a truthy value.) + cb(null); return; } - // Execution successful (Note that we still have to check whether the - // final stack contains a truthy value.) - cb(null); - return; - } + // The execution bit is true if there are no "false" values in the + // execution stack. (A "false" value indicates that we're in the + // inactive branch of an if statement.) + var exec = !~execStack.indexOf(false); - // The execution bit is true if there are no "false" values in the - // execution stack. (A "false" value indicates that we're in the - // inactive branch of an if statement.) - var exec = !~execStack.indexOf(false); + var opcode = script.chunks[pc++]; - var opcode = script.chunks[pc++]; + if (opcode.length > 520) { + throw new Error("Max push value size exceeded (>520)"); + } - if (opcode.length > 520) { - throw new Error("Max push value size exceeded (>520)"); - } + if (opcode > OP_16 && ++opCount > 201) { + throw new Error("Opcode limit exceeded (>200)"); + } - if (opcode > OP_16 && ++opCount > 201) { - throw new Error("Opcode limit exceeded (>200)"); - } + if (this.disableUnsafeOpcodes && + "number" === typeof opcode && + (opcode === OP_CAT || + opcode === OP_SUBSTR || + opcode === OP_LEFT || + opcode === OP_RIGHT || + opcode === OP_INVERT || + opcode === OP_AND || + opcode === OP_OR || + opcode === OP_XOR || + opcode === OP_2MUL || + opcode === OP_2DIV || + opcode === OP_MUL || + opcode === OP_DIV || + opcode === OP_MOD || + opcode === OP_LSHIFT || + opcode === OP_RSHIFT)) { + throw new Error("Encountered a disabled opcode"); + } - if (this.disableUnsafeOpcodes && - "number" === typeof opcode && - (opcode === OP_CAT || - opcode === OP_SUBSTR || - opcode === OP_LEFT || - opcode === OP_RIGHT || - opcode === OP_INVERT || - opcode === OP_AND || - opcode === OP_OR || - opcode === OP_XOR || - opcode === OP_2MUL || - opcode === OP_2DIV || - opcode === OP_MUL || - opcode === OP_DIV || - opcode === OP_MOD || - opcode === OP_LSHIFT || - opcode === OP_RSHIFT)) { - throw new Error("Encountered a disabled opcode"); - } + if (exec && Buffer.isBuffer(opcode)) { + this.stack.push(opcode); + } else if (exec || (OP_IF <= opcode && opcode <= OP_ENDIF)) + switch (opcode) { + case OP_0: + this.stack.push(new Buffer([])); + break; - if (exec && Buffer.isBuffer(opcode)) { - this.stack.push(opcode); - } else if (exec || (OP_IF <= opcode && opcode <= OP_ENDIF)) - switch (opcode) { - case OP_0: - this.stack.push(new Buffer([])); - break; + case OP_1NEGATE: + case OP_1: + case OP_2: + case OP_3: + case OP_4: + case OP_5: + case OP_6: + case OP_7: + case OP_8: + case OP_9: + case OP_10: + case OP_11: + case OP_12: + case OP_13: + case OP_14: + case OP_15: + case OP_16: + var opint = opcode - OP_1 + 1; + var opbuf = intToBufferSM(opint); + this.stack.push(opbuf); + break; - case OP_1NEGATE: - case OP_1: - case OP_2: - case OP_3: - case OP_4: - case OP_5: - case OP_6: - case OP_7: - case OP_8: - case OP_9: - case OP_10: - case OP_11: - case OP_12: - case OP_13: - case OP_14: - case OP_15: - case OP_16: - var opint = opcode - OP_1 + 1; - var opbuf = intToBufferSM(opint); - this.stack.push(opbuf); - break; + case OP_NOP: + case OP_NOP1: + case OP_NOP2: + case OP_NOP3: + case OP_NOP4: + case OP_NOP5: + case OP_NOP6: + case OP_NOP7: + case OP_NOP8: + case OP_NOP9: + case OP_NOP10: + break; - case OP_NOP: - case OP_NOP1: - case OP_NOP2: - case OP_NOP3: - case OP_NOP4: - case OP_NOP5: - case OP_NOP6: - case OP_NOP7: - case OP_NOP8: - case OP_NOP9: - case OP_NOP10: - break; - - case OP_IF: - case OP_NOTIF: - // if [statements] [else [statements]] endif - var value = false; - if (exec) { - value = castBool(this.stackPop()); - if (opcode === OP_NOTIF) { - value = !value; + case OP_IF: + case OP_NOTIF: + // if [statements] [else [statements]] endif + var value = false; + if (exec) { + value = castBool(this.stackPop()); + if (opcode === OP_NOTIF) { + value = !value; + } } - } - execStack.push(value); - break; + execStack.push(value); + break; - case OP_ELSE: - if (execStack.length < 1) { - throw new Error("Unmatched OP_ELSE"); - } - execStack[execStack.length - 1] = !execStack[execStack.length - 1]; - break; - - case OP_ENDIF: - if (execStack.length < 1) { - throw new Error("Unmatched OP_ENDIF"); - } - execStack.pop(); - break; - - case OP_VERIFY: - var value = castBool(this.stackTop()); - if (value) { - this.stackPop(); - } else { - throw new Error("OP_VERIFY negative"); - } - break; - - case OP_RETURN: - throw new Error("OP_RETURN"); - - case OP_TOALTSTACK: - altStack.push(this.stackPop()); - break; - - case OP_FROMALTSTACK: - if (altStack.length < 1) { - throw new Error("OP_FROMALTSTACK with alt stack empty"); - } - this.stack.push(altStack.pop()); - break; - - case OP_2DROP: - // (x1 x2 -- ) - this.stackPop(); - this.stackPop(); - break; - - case OP_2DUP: - // (x1 x2 -- x1 x2 x1 x2) - var v1 = this.stackTop(2); - var v2 = this.stackTop(1); - this.stack.push(v1); - this.stack.push(v2); - break; - - case OP_3DUP: - // (x1 x2 -- x1 x2 x1 x2) - var v1 = this.stackTop(3); - var v2 = this.stackTop(2); - var v3 = this.stackTop(1); - this.stack.push(v1); - this.stack.push(v2); - this.stack.push(v3); - break; - - case OP_2OVER: - // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) - var v1 = this.stackTop(4); - var v2 = this.stackTop(3); - this.stack.push(v1); - this.stack.push(v2); - break; - - case OP_2ROT: - // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) - var v1 = this.stackTop(6); - var v2 = this.stackTop(5); - this.stack.splice(this.stack.length - 6, 2); - this.stack.push(v1); - this.stack.push(v2); - break; - - case OP_2SWAP: - // (x1 x2 x3 x4 -- x3 x4 x1 x2) - this.stackSwap(4, 2); - this.stackSwap(3, 1); - break; - - case OP_IFDUP: - // (x - 0 | x x) - var value = this.stackTop(); - if (castBool(value)) { - this.stack.push(value); - } - break; - - case OP_DEPTH: - // -- stacksize - var value = bignum(this.stack.length); - this.stack.push(intToBufferSM(value)); - break; - - case OP_DROP: - // (x -- ) - this.stackPop(); - break; - - case OP_DUP: - // (x -- x x) - this.stack.push(this.stackTop()); - break; - - case OP_NIP: - // (x1 x2 -- x2) - if (this.stack.length < 2) { - throw new Error("OP_NIP insufficient stack size"); - } - this.stack.splice(this.stack.length - 2, 1); - break; - - case OP_OVER: - // (x1 x2 -- x1 x2 x1) - this.stack.push(this.stackTop(2)); - break; - - case OP_PICK: - case OP_ROLL: - // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) - // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) - var n = castInt(this.stackPop()); - if (n < 0 || n >= this.stack.length) { - throw new Error("OP_PICK/OP_ROLL insufficient stack size"); - } - var value = this.stackTop(n + 1); - if (opcode === OP_ROLL) { - this.stack.splice(this.stack.length - n - 1, 1); - } - this.stack.push(value); - break; - - case OP_ROT: - // (x1 x2 x3 -- x2 x3 x1) - // x2 x1 x3 after first swap - // x2 x3 x1 after second swap - this.stackSwap(3, 2); - this.stackSwap(2, 1); - break; - - case OP_SWAP: - // (x1 x2 -- x2 x1) - this.stackSwap(2, 1); - break; - - case OP_TUCK: - // (x1 x2 -- x2 x1 x2) - if (this.stack.length < 2) { - throw new Error("OP_TUCK insufficient stack size"); - } - this.stack.splice(this.stack.length - 2, 0, this.stackTop()); - break; - - case OP_CAT: - // (x1 x2 -- out) - var v1 = this.stackTop(2); - var v2 = this.stackTop(1); - this.stackPop(); - this.stackPop(); - this.stack.push(Buffer.concat([v1, v2])); - break; - - case OP_SUBSTR: - // (in begin size -- out) - var buf = this.stackTop(3); - var start = castInt(this.stackTop(2)); - var len = castInt(this.stackTop(1)); - if (start < 0 || len < 0) { - throw new Error("OP_SUBSTR start < 0 or len < 0"); - } - if ((start + len) >= buf.length) { - throw new Error("OP_SUBSTR range out of bounds"); - } - this.stackPop(); - this.stackPop(); - this.stack[this.stack.length - 1] = buf.slice(start, start + len); - break; - - case OP_LEFT: - case OP_RIGHT: - // (in size -- out) - var buf = this.stackTop(2); - var size = castInt(this.stackTop(1)); - if (size < 0) { - throw new Error("OP_LEFT/OP_RIGHT size < 0"); - } - if (size > buf.length) { - size = buf.length; - } - this.stackPop(); - if (opcode === OP_LEFT) { - this.stack[this.stack.length - 1] = buf.slice(0, size); - } else { - this.stack[this.stack.length - 1] = buf.slice(buf.length - size); - } - break; - - case OP_SIZE: - // (in -- in size) - var value = bignum(this.stackTop().length); - this.stack.push(intToBufferSM(value)); - break; - - case OP_INVERT: - // (in - out) - var buf = this.stackTop(); - for (var i = 0, l = buf.length; i < l; i++) { - buf[i] = ~buf[i]; - } - break; - - case OP_AND: - case OP_OR: - case OP_XOR: - // (x1 x2 - out) - var v1 = this.stackTop(2); - var v2 = this.stackTop(1); - this.stackPop(); - this.stackPop(); - var out = new Buffer(Math.max(v1.length, v2.length)); - if (opcode === OP_AND) { - for (var i = 0, l = out.length; i < l; i++) { - out[i] = v1[i] & v2[i]; + case OP_ELSE: + if (execStack.length < 1) { + throw new Error("Unmatched OP_ELSE"); } - } else if (opcode === OP_OR) { - for (var i = 0, l = out.length; i < l; i++) { - out[i] = v1[i] | v2[i]; + execStack[execStack.length - 1] = !execStack[execStack.length - 1]; + break; + + case OP_ENDIF: + if (execStack.length < 1) { + throw new Error("Unmatched OP_ENDIF"); } - } else if (opcode === OP_XOR) { - for (var i = 0, l = out.length; i < l; i++) { - out[i] = v1[i] ^ v2[i]; - } - } - this.stack.push(out); - break; + execStack.pop(); + break; - case OP_EQUAL: - case OP_EQUALVERIFY: - //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL - // (x1 x2 - bool) - var v1 = this.stackTop(2); - var v2 = this.stackTop(1); - - var value = buffertools.compare(v1, v2) === 0; - - // 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 == OP_NOTEQUAL) - // fEqual = !fEqual; - - this.stackPop(); - this.stackPop(); - this.stack.push(new Buffer([value ? 1 : 0])); - if (opcode === OP_EQUALVERIFY) { + case OP_VERIFY: + var value = castBool(this.stackTop()); if (value) { this.stackPop(); } else { - throw new Error("OP_EQUALVERIFY negative"); + throw new Error("OP_VERIFY negative"); } - } - break; + break; - case OP_1ADD: - case OP_1SUB: - case OP_2MUL: - case OP_2DIV: - case OP_NEGATE: - case OP_ABS: - case OP_NOT: - case OP_0NOTEQUAL: - // (in -- out) - var num = bufferSMToInt(this.stackTop()); - switch (opcode) { - case OP_1ADD: - num = num.add(bignum(1)); - break; - case OP_1SUB: - num = num.sub(bignum(1)); - break; - case OP_2MUL: - num = num.mul(bignum(2)); - break; - case OP_2DIV: - num = num.div(bignum(2)); - break; - case OP_NEGATE: - num = num.neg(); - break; - case OP_ABS: - num = num.abs(); - break; - case OP_NOT: - num = bignum(num.cmp(0) == 0 ? 1 : 0); - break; - case OP_0NOTEQUAL: - num = bignum(num.cmp(0) == 0 ? 0 : 1); - break; - } - this.stack[this.stack.length - 1] = intToBufferSM(num); - break; + case OP_RETURN: + throw new Error("OP_RETURN"); - case OP_ADD: - case OP_SUB: - case OP_MUL: - case OP_DIV: - case OP_MOD: - case OP_LSHIFT: - case OP_RSHIFT: - case OP_BOOLAND: - case OP_BOOLOR: - case OP_NUMEQUAL: - case OP_NUMEQUALVERIFY: - case OP_NUMNOTEQUAL: - case OP_LESSTHAN: - case OP_GREATERTHAN: - case OP_LESSTHANOREQUAL: - case OP_GREATERTHANOREQUAL: - case OP_MIN: - case OP_MAX: - // (x1 x2 -- out) - var v1 = bufferSMToInt(this.stackTop(2)); - var v2 = bufferSMToInt(this.stackTop(1)); - var num; - switch (opcode) { - case OP_ADD: - num = v1.add(v2); - break; - case OP_SUB: - num = v1.sub(v2); - break; - case OP_MUL: - num = v1.mul(v2); - break; - case OP_DIV: - num = v1.div(v2); - break; - case OP_MOD: - num = v1.mod(v2); - break; + case OP_TOALTSTACK: + altStack.push(this.stackPop()); + break; - case OP_LSHIFT: - if (v2.cmp(0) < 0 || v2.cmp(2048) > 0) { - throw new Error("OP_LSHIFT parameter out of bounds"); - } - num = v1.shiftLeft(v2); - break; - - case OP_RSHIFT: - if (v2.cmp(0) < 0 || v2.cmp(2048) > 0) { - throw new Error("OP_RSHIFT parameter out of bounds"); - } - num = v1.shiftRight(v2); - break; - - case OP_BOOLAND: - num = bignum((v1.cmp(0) != 0 && v2.cmp(0) != 0) ? 1 : 0); - break; - - case OP_BOOLOR: - num = bignum((v1.cmp(0) != 0 || v2.cmp(0) != 0) ? 1 : 0); - break; - - case OP_NUMEQUAL: - case OP_NUMEQUALVERIFY: - num = bignum(v1.cmp(v2) == 0 ? 1 : 0); - break; - - case OP_NUMNOTEQUAL: - ; - num = bignum(v1.cmp(v2) != 0 ? 1 : 0); - break; - - case OP_LESSTHAN: - num = bignum(v1.lt(v2) ? 1 : 0); - break; - - case OP_GREATERTHAN: - num = bignum(v1.gt(v2) ? 1 : 0); - break; - - case OP_LESSTHANOREQUAL: - num = bignum(v1.gt(v2) ? 0 : 1); - break; - - case OP_GREATERTHANOREQUAL: - num = bignum(v1.lt(v2) ? 0 : 1); - break; - - case OP_MIN: - num = (v1.lt(v2) ? v1 : v2); - break; - case OP_MAX: - num = (v1.gt(v2) ? v1 : v2); - break; - } - this.stackPop(); - this.stackPop(); - this.stack.push(intToBufferSM(num)); - - if (opcode === OP_NUMEQUALVERIFY) { - if (castBool(this.stackTop())) { - this.stackPop(); - } else { - throw new Error("OP_NUMEQUALVERIFY negative"); + case OP_FROMALTSTACK: + if (altStack.length < 1) { + throw new Error("OP_FROMALTSTACK with alt stack empty"); } - } - break; + this.stack.push(altStack.pop()); + break; - case OP_WITHIN: - // (x min max -- out) - var v1 = bufferSMToInt(this.stackTop(3)); - var v2 = bufferSMToInt(this.stackTop(2)); - var v3 = bufferSMToInt(this.stackTop(1)); - this.stackPop(); - this.stackPop(); - this.stackPop(); - var value = v1.cmp(v2) >= 0 && v1.cmp(v3) < 0; - this.stack.push(intToBufferSM(value ? 1 : 0)); - break; - - case OP_RIPEMD160: - case OP_SHA1: - case OP_SHA256: - case OP_HASH160: - case OP_HASH256: - // (in -- hash) - var value = this.stackPop(); - var hash; - if (opcode === OP_RIPEMD160) { - hash = Util.ripe160(value); - } else if (opcode === OP_SHA1) { - hash = Util.sha1(value); - } else if (opcode === OP_SHA256) { - hash = Util.sha256(value); - } else if (opcode === OP_HASH160) { - hash = Util.sha256ripe160(value); - } else if (opcode === OP_HASH256) { - hash = Util.twoSha256(value); - } - this.stack.push(hash); - break; - - case OP_CODESEPARATOR: - // Hash starts after the code separator - hashStart = pc; - break; - - case OP_CHECKSIG: - case OP_CHECKSIGVERIFY: - // (sig pubkey -- bool) - var sig = this.stackTop(2); - var pubkey = this.stackTop(1); - - // Get the part of this script since the last OP_CODESEPARATOR - var scriptChunks = script.chunks.slice(hashStart); - - // Convert to binary - var scriptCode = Script.fromChunks(scriptChunks); - - // Remove signature if present (a signature can't sign itself) - scriptCode.findAndDelete(sig); - - // check canonical signature - this.isCanonicalSignature(new Buffer(sig)); - - // Verify signature - checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) { - var success; - - if (e) { - // We intentionally ignore errors during signature verification and - // treat these cases as an invalid signature. - success = false; - } else { - success = result; - } - - // Update stack + case OP_2DROP: + // (x1 x2 -- ) this.stackPop(); this.stackPop(); - this.stack.push(new Buffer([success ? 1 : 0])); - if (opcode === OP_CHECKSIGVERIFY) { - if (success) { + break; + + case OP_2DUP: + // (x1 x2 -- x1 x2 x1 x2) + var v1 = this.stackTop(2); + var v2 = this.stackTop(1); + this.stack.push(v1); + this.stack.push(v2); + break; + + case OP_3DUP: + // (x1 x2 -- x1 x2 x1 x2) + var v1 = this.stackTop(3); + var v2 = this.stackTop(2); + var v3 = this.stackTop(1); + this.stack.push(v1); + this.stack.push(v2); + this.stack.push(v3); + break; + + case OP_2OVER: + // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) + var v1 = this.stackTop(4); + var v2 = this.stackTop(3); + this.stack.push(v1); + this.stack.push(v2); + break; + + case OP_2ROT: + // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) + var v1 = this.stackTop(6); + var v2 = this.stackTop(5); + this.stack.splice(this.stack.length - 6, 2); + this.stack.push(v1); + this.stack.push(v2); + break; + + case OP_2SWAP: + // (x1 x2 x3 x4 -- x3 x4 x1 x2) + this.stackSwap(4, 2); + this.stackSwap(3, 1); + break; + + case OP_IFDUP: + // (x - 0 | x x) + var value = this.stackTop(); + if (castBool(value)) { + this.stack.push(value); + } + break; + + case OP_DEPTH: + // -- stacksize + var value = bignum(this.stack.length); + this.stack.push(intToBufferSM(value)); + break; + + case OP_DROP: + // (x -- ) + this.stackPop(); + break; + + case OP_DUP: + // (x -- x x) + this.stack.push(this.stackTop()); + break; + + case OP_NIP: + // (x1 x2 -- x2) + if (this.stack.length < 2) { + throw new Error("OP_NIP insufficient stack size"); + } + this.stack.splice(this.stack.length - 2, 1); + break; + + case OP_OVER: + // (x1 x2 -- x1 x2 x1) + this.stack.push(this.stackTop(2)); + break; + + case OP_PICK: + case OP_ROLL: + // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) + // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) + var n = castInt(this.stackPop()); + if (n < 0 || n >= this.stack.length) { + throw new Error("OP_PICK/OP_ROLL insufficient stack size"); + } + var value = this.stackTop(n + 1); + if (opcode === OP_ROLL) { + this.stack.splice(this.stack.length - n - 1, 1); + } + this.stack.push(value); + break; + + case OP_ROT: + // (x1 x2 x3 -- x2 x3 x1) + // x2 x1 x3 after first swap + // x2 x3 x1 after second swap + this.stackSwap(3, 2); + this.stackSwap(2, 1); + break; + + case OP_SWAP: + // (x1 x2 -- x2 x1) + this.stackSwap(2, 1); + break; + + case OP_TUCK: + // (x1 x2 -- x2 x1 x2) + if (this.stack.length < 2) { + throw new Error("OP_TUCK insufficient stack size"); + } + this.stack.splice(this.stack.length - 2, 0, this.stackTop()); + break; + + case OP_CAT: + // (x1 x2 -- out) + var v1 = this.stackTop(2); + var v2 = this.stackTop(1); + this.stackPop(); + this.stackPop(); + this.stack.push(Buffer.concat([v1, v2])); + break; + + case OP_SUBSTR: + // (in begin size -- out) + var buf = this.stackTop(3); + var start = castInt(this.stackTop(2)); + var len = castInt(this.stackTop(1)); + if (start < 0 || len < 0) { + throw new Error("OP_SUBSTR start < 0 or len < 0"); + } + if ((start + len) >= buf.length) { + throw new Error("OP_SUBSTR range out of bounds"); + } + this.stackPop(); + this.stackPop(); + this.stack[this.stack.length - 1] = buf.slice(start, start + len); + break; + + case OP_LEFT: + case OP_RIGHT: + // (in size -- out) + var buf = this.stackTop(2); + var size = castInt(this.stackTop(1)); + if (size < 0) { + throw new Error("OP_LEFT/OP_RIGHT size < 0"); + } + if (size > buf.length) { + size = buf.length; + } + this.stackPop(); + if (opcode === OP_LEFT) { + this.stack[this.stack.length - 1] = buf.slice(0, size); + } else { + this.stack[this.stack.length - 1] = buf.slice(buf.length - size); + } + break; + + case OP_SIZE: + // (in -- in size) + var value = bignum(this.stackTop().length); + this.stack.push(intToBufferSM(value)); + break; + + case OP_INVERT: + // (in - out) + var buf = this.stackTop(); + for (var i = 0, l = buf.length; i < l; i++) { + buf[i] = ~buf[i]; + } + break; + + case OP_AND: + case OP_OR: + case OP_XOR: + // (x1 x2 - out) + var v1 = this.stackTop(2); + var v2 = this.stackTop(1); + this.stackPop(); + this.stackPop(); + var out = new Buffer(Math.max(v1.length, v2.length)); + if (opcode === OP_AND) { + for (var i = 0, l = out.length; i < l; i++) { + out[i] = v1[i] & v2[i]; + } + } else if (opcode === OP_OR) { + for (var i = 0, l = out.length; i < l; i++) { + out[i] = v1[i] | v2[i]; + } + } else if (opcode === OP_XOR) { + for (var i = 0, l = out.length; i < l; i++) { + out[i] = v1[i] ^ v2[i]; + } + } + this.stack.push(out); + break; + + case OP_EQUAL: + case OP_EQUALVERIFY: + //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL + // (x1 x2 - bool) + var v1 = this.stackTop(2); + var v2 = this.stackTop(1); + + var value = buffertools.compare(v1, v2) === 0; + + // 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 == OP_NOTEQUAL) + // fEqual = !fEqual; + + this.stackPop(); + this.stackPop(); + this.stack.push(new Buffer([value ? 1 : 0])); + if (opcode === OP_EQUALVERIFY) { + if (value) { this.stackPop(); } else { - throw new Error("OP_CHECKSIGVERIFY negative"); + throw new Error("OP_EQUALVERIFY negative"); } } + break; - // Run next step - executeStep.call(this, cb); - }.bind(this)); + case OP_1ADD: + case OP_1SUB: + case OP_2MUL: + case OP_2DIV: + case OP_NEGATE: + case OP_ABS: + case OP_NOT: + case OP_0NOTEQUAL: + // (in -- out) + var num = bufferSMToInt(this.stackTop()); + switch (opcode) { + case OP_1ADD: + num = num.add(bignum(1)); + break; + case OP_1SUB: + num = num.sub(bignum(1)); + break; + case OP_2MUL: + num = num.mul(bignum(2)); + break; + case OP_2DIV: + num = num.div(bignum(2)); + break; + case OP_NEGATE: + num = num.neg(); + break; + case OP_ABS: + num = num.abs(); + break; + case OP_NOT: + num = bignum(num.cmp(0) == 0 ? 1 : 0); + break; + case OP_0NOTEQUAL: + num = bignum(num.cmp(0) == 0 ? 0 : 1); + break; + } + this.stack[this.stack.length - 1] = intToBufferSM(num); + break; - // Note that for asynchronous opcodes we have to return here to prevent - // the next opcode from being executed. - return; + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_MOD: + case OP_LSHIFT: + case OP_RSHIFT: + case OP_BOOLAND: + case OP_BOOLOR: + case OP_NUMEQUAL: + case OP_NUMEQUALVERIFY: + case OP_NUMNOTEQUAL: + case OP_LESSTHAN: + case OP_GREATERTHAN: + case OP_LESSTHANOREQUAL: + case OP_GREATERTHANOREQUAL: + case OP_MIN: + case OP_MAX: + // (x1 x2 -- out) + var v1 = bufferSMToInt(this.stackTop(2)); + var v2 = bufferSMToInt(this.stackTop(1)); + var num; + switch (opcode) { + case OP_ADD: + num = v1.add(v2); + break; + case OP_SUB: + num = v1.sub(v2); + break; + case OP_MUL: + num = v1.mul(v2); + break; + case OP_DIV: + num = v1.div(v2); + break; + case OP_MOD: + num = v1.mod(v2); + break; - case OP_CHECKMULTISIG: - case OP_CHECKMULTISIGVERIFY: - // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) - var keysCount = castInt(this.stackPop()); - if (keysCount < 0 || keysCount > 20) { - throw new Error("OP_CHECKMULTISIG keysCount out of bounds"); - } - opCount += keysCount; - if (opCount > 201) { - throw new Error("Opcode limit exceeded (>200)"); - } - var keys = []; - for (var i = 0, l = keysCount; i < l; i++) { - var pubkey = this.stackPop() - keys.push(pubkey); - } - - var sigsCount = castInt(this.stackPop()); - if (sigsCount < 0 || sigsCount > keysCount) { - throw new Error("OP_CHECKMULTISIG sigsCount out of bounds"); - } - var sigs = []; - for (var i = 0, l = sigsCount; i < l; i++) { - sigs.push(this.stackPop()); - } - - // The original client has a bug where it pops an extra element off the - // stack. It can't be fixed without causing a chain split and we need to - // imitate this behavior as well. - this.stackPop(); - - // Get the part of this script since the last OP_CODESEPARATOR - var scriptChunks = script.chunks.slice(hashStart); - - // Convert to binary - var scriptCode = Script.fromChunks(scriptChunks); - - var that = this; - sigs.forEach(function(sig) { - // check each signature is canonical - that.isCanonicalSignature(new Buffer(sig)); - // Drop the signatures for the subscript, since a signature can't sign itself - scriptCode.findAndDelete(sig); - }); - - var success = true, - isig = 0, - ikey = 0; - checkMultiSigStep.call(this); - - function checkMultiSigStep() { - if (success && sigsCount > 0) { - var sig = sigs[isig]; - var pubkey = keys[ikey]; - - checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) { - if (!e && result) { - console.log('sig '+isig+' succeeded with key '+ikey); - isig++; - sigsCount--; - } else { - console.log('key '+ikey+' failed to verify sig '+isig+': '+e +' '+result); - ikey++; - keysCount--; - - // If there are more signatures than keys left, then too many - // signatures have failed - if (sigsCount > keysCount) { - console.log('CHECKMULTISIG sigsCount > keysCount'); - success = false; - } + case OP_LSHIFT: + if (v2.cmp(0) < 0 || v2.cmp(2048) > 0) { + throw new Error("OP_LSHIFT parameter out of bounds"); } + num = v1.shiftLeft(v2); + break; - checkMultiSigStep.call(this); - }.bind(this)); - } else { + case OP_RSHIFT: + if (v2.cmp(0) < 0 || v2.cmp(2048) > 0) { + throw new Error("OP_RSHIFT parameter out of bounds"); + } + num = v1.shiftRight(v2); + break; + + case OP_BOOLAND: + num = bignum((v1.cmp(0) != 0 && v2.cmp(0) != 0) ? 1 : 0); + break; + + case OP_BOOLOR: + num = bignum((v1.cmp(0) != 0 || v2.cmp(0) != 0) ? 1 : 0); + break; + + case OP_NUMEQUAL: + case OP_NUMEQUALVERIFY: + num = bignum(v1.cmp(v2) == 0 ? 1 : 0); + break; + + case OP_NUMNOTEQUAL: + ; + num = bignum(v1.cmp(v2) != 0 ? 1 : 0); + break; + + case OP_LESSTHAN: + num = bignum(v1.lt(v2) ? 1 : 0); + break; + + case OP_GREATERTHAN: + num = bignum(v1.gt(v2) ? 1 : 0); + break; + + case OP_LESSTHANOREQUAL: + num = bignum(v1.gt(v2) ? 0 : 1); + break; + + case OP_GREATERTHANOREQUAL: + num = bignum(v1.lt(v2) ? 0 : 1); + break; + + case OP_MIN: + num = (v1.lt(v2) ? v1 : v2); + break; + case OP_MAX: + num = (v1.gt(v2) ? v1 : v2); + break; + } + this.stackPop(); + this.stackPop(); + this.stack.push(intToBufferSM(num)); + + if (opcode === OP_NUMEQUALVERIFY) { + if (castBool(this.stackTop())) { + this.stackPop(); + } else { + throw new Error("OP_NUMEQUALVERIFY negative"); + } + } + break; + + case OP_WITHIN: + // (x min max -- out) + var v1 = bufferSMToInt(this.stackTop(3)); + var v2 = bufferSMToInt(this.stackTop(2)); + var v3 = bufferSMToInt(this.stackTop(1)); + this.stackPop(); + this.stackPop(); + this.stackPop(); + var value = v1.cmp(v2) >= 0 && v1.cmp(v3) < 0; + this.stack.push(intToBufferSM(value ? 1 : 0)); + break; + + case OP_RIPEMD160: + case OP_SHA1: + case OP_SHA256: + case OP_HASH160: + case OP_HASH256: + // (in -- hash) + var value = this.stackPop(); + var hash; + if (opcode === OP_RIPEMD160) { + hash = Util.ripe160(value); + } else if (opcode === OP_SHA1) { + hash = Util.sha1(value); + } else if (opcode === OP_SHA256) { + hash = Util.sha256(value); + } else if (opcode === OP_HASH160) { + hash = Util.sha256ripe160(value); + } else if (opcode === OP_HASH256) { + hash = Util.twoSha256(value); + } + this.stack.push(hash); + break; + + case OP_CODESEPARATOR: + // Hash starts after the code separator + hashStart = pc; + break; + + case OP_CHECKSIG: + case OP_CHECKSIGVERIFY: + // (sig pubkey -- bool) + var sig = this.stackTop(2); + var pubkey = this.stackTop(1); + + // Get the part of this script since the last OP_CODESEPARATOR + var scriptChunks = script.chunks.slice(hashStart); + + // Convert to binary + var scriptCode = Script.fromChunks(scriptChunks); + + // Remove signature if present (a signature can't sign itself) + scriptCode.findAndDelete(sig); + + // check canonical signature + this.isCanonicalSignature(new Buffer(sig)); + + // Verify signature + checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) { + var success; + + if (e) { + // We intentionally ignore errors during signature verification and + // treat these cases as an invalid signature. + success = false; + } else { + success = result; + } + + // Update stack + this.stackPop(); + this.stackPop(); this.stack.push(new Buffer([success ? 1 : 0])); - if (opcode === OP_CHECKMULTISIGVERIFY) { + if (opcode === OP_CHECKSIGVERIFY) { if (success) { this.stackPop(); } else { - throw new Error("OP_CHECKMULTISIGVERIFY negative"); + throw new Error("OP_CHECKSIGVERIFY negative"); } } // Run next step executeStep.call(this, cb); + }.bind(this)); + + // Note that for asynchronous opcodes we have to return here to prevent + // the next opcode from being executed. + return; + + case OP_CHECKMULTISIG: + case OP_CHECKMULTISIGVERIFY: + // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) + var keysCount = castInt(this.stackPop()); + if (keysCount < 0 || keysCount > 20) { + throw new Error("OP_CHECKMULTISIG keysCount out of bounds"); + } + opCount += keysCount; + if (opCount > 201) { + throw new Error("Opcode limit exceeded (>200)"); + } + var keys = []; + for (var i = 0, l = keysCount; i < l; i++) { + var pubkey = this.stackPop() + keys.push(pubkey); } - }; - // Note that for asynchronous opcodes we have to return here to prevent - // the next opcode from being executed. - return; + var sigsCount = castInt(this.stackPop()); + if (sigsCount < 0 || sigsCount > keysCount) { + throw new Error("OP_CHECKMULTISIG sigsCount out of bounds"); + } + var sigs = []; + for (var i = 0, l = sigsCount; i < l; i++) { + sigs.push(this.stackPop()); + } - default: - throw new Error("Unknown opcode encountered"); + // The original client has a bug where it pops an extra element off the + // stack. It can't be fixed without causing a chain split and we need to + // imitate this behavior as well. + this.stackPop(); + + // Get the part of this script since the last OP_CODESEPARATOR + var scriptChunks = script.chunks.slice(hashStart); + + // Convert to binary + var scriptCode = Script.fromChunks(scriptChunks); + + var that = this; + sigs.forEach(function(sig) { + // check each signature is canonical + that.isCanonicalSignature(new Buffer(sig)); + // Drop the signatures for the subscript, since a signature can't sign itself + scriptCode.findAndDelete(sig); + }); + + var success = true, + isig = 0, + ikey = 0; + checkMultiSigStep.call(this); + + function checkMultiSigStep() { + if (success && sigsCount > 0) { + var sig = sigs[isig]; + var pubkey = keys[ikey]; + + checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) { + if (!e && result) { + isig++; + sigsCount--; + } else { + ikey++; + keysCount--; + + // If there are more signatures than keys left, then too many + // signatures have failed + if (sigsCount > keysCount) { + success = false; + } + } + + checkMultiSigStep.call(this); + }.bind(this)); + } else { + this.stack.push(new Buffer([success ? 1 : 0])); + if (opcode === OP_CHECKMULTISIGVERIFY) { + if (success) { + this.stackPop(); + } else { + throw new Error("OP_CHECKMULTISIGVERIFY negative"); + } + } + + // Run next step + executeStep.call(this, cb); + } + }; + + // Note that for asynchronous opcodes we have to return here to prevent + // the next opcode from being executed. + return; + + default: + throw new Error("Unknown opcode encountered"); + } + + // Size limits + if ((this.stack.length + altStack.length) > 1000) { + throw new Error("Maximum stack size exceeded"); } - // Size limits - if ((this.stack.length + altStack.length) > 1000) { - throw new Error("Maximum stack size exceeded"); - } - - // Run next step - if (false && pc % 100) { - // V8 allows for much deeper stacks than Bitcoin's scripting language, - // but just to be safe, we'll reset the stack every 100 steps - process.nextTick(executeStep.bind(this, cb)); - } else { - executeStep.call(this, cb); + // Run next step + if (false && pc % 100) { + // V8 allows for much deeper stacks than Bitcoin's scripting language, + // but just to be safe, we'll reset the stack every 100 steps + process.nextTick(executeStep.bind(this, cb)); + } else { + executeStep.call(this, cb); + } + } catch (e) { + cb(e); } } }; @@ -916,29 +916,24 @@ ScriptInterpreter.prototype.verifyStep3 = function(scriptSig, // if stack is empty, script considered invalid if (this.stack.length === 0) { - console.log('3rd step: no stack'); callback(null, false); return; } // if top of stack contains false, script evaluated to false if (castBool(this.stackBack()) == false) { - console.log('3rd step: stack contains false'); callback(null, false); return; } // if not P2SH, script evaluated to true if (!this.opts.verifyP2SH || !scriptPubKey.isP2SH()) { - console.log('3rd step: done, true'); callback(null, true); return; } // if P2SH, scriptSig should be push-only if (!scriptSig.isPushOnly()) { - console.log('3rd step: scriptSig should be push only'); - console.log(); callback(null, false); return; } @@ -952,7 +947,6 @@ ScriptInterpreter.prototype.verifyStep3 = function(scriptSig, var that = this; // evaluate the P2SH subscript siCopy.eval(subscript, tx, nIn, hashType, function(err) { - console.log('Err 3nd step: '+err); if (err) return callback(err); that.verifyStep4(callback, siCopy); }); @@ -971,7 +965,6 @@ ScriptInterpreter.prototype.verifyStep2 = function(scriptSig, scriptPubKey, var that = this; // 2nd step, evaluate scriptPubKey this.eval(scriptPubKey, tx, nIn, hashType, function(err) { - console.log('Err 2nd step: '+err); if (err) return callback(err); that.verifyStep3(scriptSig, scriptPubKey, tx, nIn, hashType, callback, siCopy); @@ -984,7 +977,6 @@ ScriptInterpreter.prototype.verifyFull = function(scriptSig, scriptPubKey, // 1st step, evaluate scriptSig this.eval(scriptSig, tx, nIn, hashType, function(err) { - console.log('Err 1st step: '+err); if (err) return callback(err); that.verifyStep2(scriptSig, scriptPubKey, tx, nIn, hashType, callback); @@ -1004,7 +996,6 @@ var checkSig = ScriptInterpreter.checkSig = function(sig, pubkey, scriptCode, tx, n, hashType, callback) { // https://en.bitcoin.it/wiki/OP_CHECKSIG#How_it_works if (!sig.length) { - console.log('sig length 0'); callback(null, false); return; } @@ -1013,7 +1004,6 @@ var checkSig = ScriptInterpreter.checkSig = if (hashType === 0) { hashType = sig[sig.length - 1]; } else if (hashType != sig[sig.length - 1]) { - console.log('wrong hashtype'); callback(null, false); return; } @@ -1022,19 +1012,12 @@ var checkSig = ScriptInterpreter.checkSig = sig = sig.slice(0, sig.length - 1); // Signature verification requires a special hash procedure - console.log('rawtx '+buffertools.toHex(tx.serialize())); var hash = tx.hashForSignature(scriptCode, n, hashType); - console.log('n ='+n+'; hashType='+hashType); - console.log('hash ='+ buffertools.toHex(hash)); // Verify signature var key = new Key(); if (pubkey.length === 0) pubkey = new Buffer('00', 'hex'); - key.public = pubkey; - - console.log('pubkey before verification: '+buffertools.toHex(key.public)); - console.log('sig before verification: '+buffertools.toHex(sig)); - console.log('hash before verification: '+buffertools.toHex(hash)); + key.public = pubkey; key.verifySignature(hash, sig, callback); }; diff --git a/src/eckey.cc b/src/eckey.cc index a41d319..973d989 100644 --- a/src/eckey.cc +++ b/src/eckey.cc @@ -313,7 +313,6 @@ Key::SetPublic(Local property, Local value, const AccessorInfo& i if (!ret) { // TODO: Error - std::cout << ret << "C++++++++++++++++++++++++++++++++++++++\n"; return; } diff --git a/test/test.Transaction.js b/test/test.Transaction.js index f1621e3..70d93dd 100644 --- a/test/test.Transaction.js +++ b/test/test.Transaction.js @@ -48,7 +48,7 @@ function parse_test_transaction(entry) { } describe('Transaction', function() { - it.skip('should initialze the main object', function() { + it('should initialze the main object', function() { should.exist(Transaction); In = Transaction.In; Out = Transaction.Out; @@ -57,7 +57,7 @@ describe('Transaction', function() { }); - it.skip('should be able to create instance', function() { + it('should be able to create instance', function() { var t = new Transaction(); should.exist(t); }); @@ -76,8 +76,8 @@ describe('Transaction', function() { var tx = testTx.transaction; describe((valid ? '' : 'in') + 'valid tx=' + raw, function() { - it.skip('should parse correctly', function() { - buffertools.toHex(tx.serialize()).should.equal(raw); + it('should parse correctly', function() { + buffertools.toHex(tx.serialize()).toLowerCase().should.equal(raw.toLowerCase()); }); var inputs = tx.inputs(); @@ -91,7 +91,6 @@ describe('Transaction', function() { input[0].copy(outpointHash); input[0] = buffertools.reverse(outpointHash); input[0] = buffertools.toHex(input[0]); - buffertools.toHex(tx.serialize()).toLowerCase().should.equal(raw.toLowerCase()); var mapKey = [input]; var scriptPubKey = testTx.inputs[mapKey]; if (!scriptPubKey) throw new Error('Bad test: ' + datum); @@ -102,9 +101,14 @@ describe('Transaction', function() { dontVerifyStrictEnc: true }, function(err, results) { - should.not.exist(err); - should.exist(results); - results.should.equal(valid); + if (valid) { + should.not.exist(err); + should.exist(results); + results.should.equal(valid); + } else { + var invalid = (typeof err !== 'undefined') || results === false; + invalid.should.equal(true); + } done(); } );