diff --git a/Script.js b/Script.js index 82559c640..2b8bad536 100644 --- a/Script.js +++ b/Script.js @@ -280,6 +280,30 @@ function spec(b) { return this.buffer; }; + Script.fromStringContent = function(s) { + var chunks = []; + var split = s.split(' '); + console.log(split); + for (var i=0; i 2 && word.substring(0,2) === '0x') { + chunks.push(new Buffer(word.substring(2,word.length), 'hex')); + } else { + var integer = parseInt(word); + if (isNaN(integer)) { + chunks.push(Opcode.map['OP_'+word]); + } else { + var hexi = integer.toString(16); + if (hexi.length %2 === 1) hexi = '0'+hexi; + console.log(hexi); + chunks.push(new Buffer(hexi,'hex')); + } + } + + } + return Script.fromChunks(chunks); + }; + Script.prototype.getStringContent = function (truncate, maxEl) { if (truncate === null) { @@ -290,26 +314,26 @@ function spec(b) { maxEl = 15; } - var script = ''; + var s = ''; for (var i = 0, l = this.chunks.length; i < l; i++) { var chunk = this.chunks[i]; if (i > 0) { - script += " "; + s += ' '; } if (Buffer.isBuffer(chunk)) { - script += "0x"+util.formatBuffer(chunk, truncate ? null : 0); + s += '0x'+util.formatBuffer(chunk, truncate ? null : 0); } else { - script += Opcode.reverseMap[chunk]; + s += Opcode.reverseMap[chunk]; } if (maxEl && i > maxEl) { - script += " ..."; + s += ' ...'; break; } } - return script; + return s; }; Script.prototype.toString = function (truncate, maxEl) diff --git a/ScriptInterpreter.js b/ScriptInterpreter.js index 35ec40019..ae4221439 100644 --- a/ScriptInterpreter.js +++ b/ScriptInterpreter.js @@ -22,8 +22,7 @@ function spec(b) { this.disableUnsafeOpcodes = true; }; - ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, callback) - { + ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, callback) { if ("function" !== typeof callback) { throw new Error("ScriptInterpreter.eval() requires a callback"); } @@ -75,637 +74,679 @@ function spec(b) { } 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)) { + "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; - - 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: - this.stack.push(bigintToBuffer(opcode - OP_1 + 1)); - 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; - } - } - 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(bigintToBuffer(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(bigintToBuffer(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_EQUALVERIFY negative"); - } - } - 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 = castBigint(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] = bigintToBuffer(num); - break; + case OP_0: + this.stack.push(new Buffer([])); + break; - 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 = castBigint(this.stackTop(2)); - var v2 = castBigint(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_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: + this.stack.push(bigintToBuffer(opcode - OP_1 + 1)); + 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_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_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(bigintToBuffer(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 = castBigint(this.stackTop(3)); - var v2 = castBigint(this.stackTop(2)); - var v3 = castBigint(this.stackTop(1)); - this.stackPop(); - this.stackPop(); - this.stackPop(); - var value = v1.cmp(v2) >= 0 && v1.cmp(v3) < 0; - this.stack.push(bigintToBuffer(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); - - // Verify signature - checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function (e, result) { - try { - 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_CHECKSIGVERIFY) { - if (success) { - this.stackPop(); - } else { - throw new Error("OP_CHECKSIGVERIFY negative"); + 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; - // Run next step - executeStep.call(this, cb); - } catch(e) { - cb(e); - } - }.bind(this)); + case OP_ELSE: + if (execStack.length < 1) { + throw new Error("Unmatched OP_ELSE"); + } + execStack[execStack.length - 1] = !execStack[execStack.length - 1]; + break; - // Note that for asynchronous opcodes we have to return here to prevent - // the next opcode from being executed. - return; + case OP_ENDIF: + if (execStack.length < 1) { + throw new Error("Unmatched OP_ENDIF"); + } + execStack.pop(); + 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++) { - keys.push(this.stackPop()); - } - 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()); - } + case OP_VERIFY: + var value = castBool(this.stackTop()); + if (value) { + this.stackPop(); + } else { + throw new Error("OP_VERIFY negative"); + } + break; - // 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(); + case OP_RETURN: + throw new Error("OP_RETURN"); - // Get the part of this script since the last OP_CODESEPARATOR - var scriptChunks = script.chunks.slice(hashStart); + case OP_TOALTSTACK: + altStack.push(this.stackPop()); + break; - // Convert to binary - var scriptCode = Script.fromChunks(scriptChunks); + case OP_FROMALTSTACK: + if (altStack.length < 1) { + throw new Error("OP_FROMALTSTACK with alt stack empty"); + } + this.stack.push(altStack.pop()); + break; - // Drop the signatures, since a signature can't sign itself - sigs.forEach(function (sig) { - scriptCode.findAndDelete(sig); - }); + case OP_2DROP: + // (x1 x2 -- ) + this.stackPop(); + this.stackPop(); + break; - var success = true, isig = 0, ikey = 0; - checkMultiSigStep.call(this); + 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; - function checkMultiSigStep() { - try { - if (success && sigsCount > 0) { - var sig = sigs[isig]; - var key = keys[ikey]; + 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; - checkSig(sig, key, scriptCode, tx, inIndex, hashType, function (e, result) { - try { - if (!e && result) { - isig++; - sigsCount--; + 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(bigintToBuffer(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(bigintToBuffer(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_EQUALVERIFY negative"); + } + } + 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 = castBigint(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] = bigintToBuffer(num); + break; + + 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 = castBigint(this.stackTop(2)); + var v2 = castBigint(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_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(bigintToBuffer(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 = castBigint(this.stackTop(3)); + var v2 = castBigint(this.stackTop(2)); + var v3 = castBigint(this.stackTop(1)); + this.stackPop(); + this.stackPop(); + this.stackPop(); + var value = v1.cmp(v2) >= 0 && v1.cmp(v3) < 0; + this.stack.push(bigintToBuffer(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); + + // Verify signature + checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) { + try { + 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_CHECKSIGVERIFY) { + if (success) { + this.stackPop(); } else { - ikey++; - keysCount--; + throw new Error("OP_CHECKSIGVERIFY negative"); + } + } - // If there are more signatures than keys left, then too many - // signatures have failed - if (sigsCount > keysCount) { - success = false; + // Run next step + executeStep.call(this, cb); + } catch (e) { + cb(e); + } + }.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++) { + keys.push(this.stackPop()); + } + 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); + + // Drop the signatures, since a signature can't sign itself + sigs.forEach(function(sig) { + scriptCode.findAndDelete(sig); + }); + + var success = true, + isig = 0, + ikey = 0; + checkMultiSigStep.call(this); + + function checkMultiSigStep() { + try { + if (success && sigsCount > 0) { + var sig = sigs[isig]; + var key = keys[ikey]; + + checkSig(sig, key, scriptCode, tx, inIndex, hashType, function(e, result) { + try { + 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); + } catch (e) { + cb(e); + } + }.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"); } } - checkMultiSigStep.call(this); - } catch (e) { - cb(e); - } - }.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); } + } catch (e) { + cb(e); } + }; - // Run next step - executeStep.call(this, cb); - } - } catch(e) { - cb(e); - } - }; + // Note that for asynchronous opcodes we have to return here to prevent + // the next opcode from being executed. + return; - // 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"); - } + default: + console.log('opcode '+opcode); + throw new Error("Unknown opcode encountered"); + } // Size limits if ((this.stack.length + altStack.length) > 1000) { @@ -721,26 +762,25 @@ function spec(b) { executeStep.call(this, cb); } } catch (e) { - log.debug("Script aborted: "+ - (e.message ? e : e)); + log.debug("Script aborted: " + + (e.message ? e : e)); cb(e); } } }; ScriptInterpreter.prototype.evalTwo = - function evalTwo(scriptSig, scriptPubkey, tx, n, hashType, callback) - { - var self = this; + function evalTwo(scriptSig, scriptPubkey, tx, n, hashType, callback) { + var self = this; - self.eval(scriptSig, tx, n, hashType, function (e) { - if (e) { - callback(e) - return; - } + self.eval(scriptSig, tx, n, hashType, function(e) { + if (e) { + callback(e) + return; + } - self.eval(scriptPubkey, tx, n, hashType, callback); - }); + self.eval(scriptPubkey, tx, n, hashType, callback); + }); }; /** @@ -757,11 +797,10 @@ function spec(b) { throw new Error('ScriptInterpreter.stackTop(): Stack underrun'); } - return this.stack[this.stack.length-offset]; + return this.stack[this.stack.length - offset]; }; - ScriptInterpreter.prototype.stackBack = function stackBack() - { + ScriptInterpreter.prototype.stackBack = function stackBack() { return this.stack[-1]; }; @@ -782,7 +821,7 @@ function spec(b) { } var s = this.stack, - l = s.length; + l = s.length; var tmp = s[l - a]; s[l - a] = s[l - b]; @@ -796,7 +835,7 @@ function spec(b) { * integer. Any longer Buffer is converted to a hex string. */ ScriptInterpreter.prototype.getPrimitiveStack = function getPrimitiveStack() { - return this.stack.map(function (entry) { + return this.stack.map(function(entry) { if (entry.length > 2) { return buffertools.toHex(entry.slice(0)); } @@ -813,7 +852,7 @@ function spec(b) { for (var i = 0, l = v.length; i < l; i++) { if (v[i] != 0) { // Negative zero is still zero - if (i == (l-1) && v[i] == 0x80) { + if (i == (l - 1) && v[i] == 0x80) { return false; } return true; @@ -850,7 +889,7 @@ function spec(b) { v = bignum(v); } - var b,c; + var b, c; var cmp = v.cmp(0); if (cmp > 0) { @@ -884,148 +923,143 @@ function spec(b) { throw new Error("Empty stack after script evaluation"); } - return castBool(this.stack[this.stack.length-1]); + return castBool(this.stack[this.stack.length - 1]); }; ScriptInterpreter.verify = - function verify(scriptSig, scriptPubKey, txTo, n, hashType, callback) - { - if ("function" !== typeof callback) { - throw new Error("ScriptInterpreter.verify() requires a callback"); - } - - // Create execution environment - var si = new ScriptInterpreter(); - - // Evaluate scripts - si.evalTwo(scriptSig, scriptPubKey, txTo, n, hashType, function (err) { - if (err) { - callback(err); - return; + function verify(scriptSig, scriptPubKey, txTo, n, hashType, callback) { + if ("function" !== typeof callback) { + throw new Error("ScriptInterpreter.verify() requires a callback"); } - // Cast result to bool - try { - var result = si.getResult(); - } catch (err) { - callback(err); - return; - } + // Create execution environment + var si = new ScriptInterpreter(); - callback(null, result); - }); + // Evaluate scripts + si.evalTwo(scriptSig, scriptPubKey, txTo, n, hashType, function(err) { + if (err) { + callback(err); + return; + } - return si; + // Cast result to bool + try { + var result = si.getResult(); + } catch (err) { + callback(err); + return; + } + + callback(null, result); + }); + + return si; }; function verifyStep4(scriptSig, scriptPubKey, txTo, nIn, - hashType, opts, callback, si, siCopy) - { - if (siCopy.stack.length == 0) { - callback(null, false); - return; - } - - callback(null, castBool(siCopy.stackBack())); + hashType, opts, callback, si, siCopy) { + if (siCopy.stack.length == 0) { + callback(null, false); + return; + } + + callback(null, castBool(siCopy.stackBack())); } function verifyStep3(scriptSig, scriptPubKey, txTo, nIn, - hashType, opts, callback, si, siCopy) - { - if (si.stack.length == 0) { - callback(null, false); - return; - } + hashType, opts, callback, si, siCopy) { + if (si.stack.length == 0) { + callback(null, false); + return; + } if (castBool(si.stackBack()) == false) { - callback(null, false); - return; - } + callback(null, false); + return; + } - // if not P2SH, we're done - if (!opts.verifyP2SH || !scriptPubKey.isP2SH()) { - callback(null, true); - return; - } + // if not P2SH, we're done + if (!opts.verifyP2SH || !scriptPubKey.isP2SH()) { + callback(null, true); + return; + } - if (!scriptSig.isPushOnly()) { - callback(null, false); - return; - } + if (!scriptSig.isPushOnly()) { + callback(null, false); + return; + } - assert.notEqual(siCopy.length, 0); + assert.notEqual(siCopy.length, 0); - var subscript = new Script(siCopy.stackPop()); + var subscript = new Script(siCopy.stackPop()); - ok = true; - siCopy.eval(subscript, txTo, nIn, hashType, function (err) { - if (err) - callback(err); - else - verifyStep4(scriptSig, scriptPubKey, txTo, nIn, - hashType, opts, callback, si, siCopy); - }); - } - - function verifyStep2(scriptSig, scriptPubKey, txTo, nIn, - hashType, opts, callback, si, siCopy) - { - if (opts.verifyP2SH) { - si.stack.forEach(function(item) { - siCopy.stack.push(item); + ok = true; + siCopy.eval(subscript, txTo, nIn, hashType, function(err) { + if (err) + callback(err); + else + verifyStep4(scriptSig, scriptPubKey, txTo, nIn, + hashType, opts, callback, si, siCopy); }); } - si.eval(scriptPubKey, txTo, nIn, hashType, function (err) { - if (err) - callback(err); - else - verifyStep3(scriptSig, scriptPubKey, txTo, nIn, - hashType, opts, callback, si, siCopy); - }); + function verifyStep2(scriptSig, scriptPubKey, txTo, nIn, + hashType, opts, callback, si, siCopy) { + if (opts.verifyP2SH) { + si.stack.forEach(function(item) { + siCopy.stack.push(item); + }); + } + + si.eval(scriptPubKey, txTo, nIn, hashType, function(err) { + if (err) + callback(err); + else + verifyStep3(scriptSig, scriptPubKey, txTo, nIn, + hashType, opts, callback, si, siCopy); + }); } ScriptInterpreter.verifyFull = - function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType, - opts, callback) - { - var si = new ScriptInterpreter(); - var siCopy = new ScriptInterpreter(); + function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType, + opts, callback) { + var si = new ScriptInterpreter(); + var siCopy = new ScriptInterpreter(); - si.eval(scriptSig, txTo, nIn, hashType, function (err) { - if (err) - callback(err); - else - verifyStep2(scriptSig, scriptPubKey, txTo, nIn, + si.eval(scriptSig, txTo, nIn, hashType, function(err) { + if (err) + callback(err); + else + verifyStep2(scriptSig, scriptPubKey, txTo, nIn, hashType, opts, callback, si, siCopy); - }); + }); }; var checkSig = ScriptInterpreter.checkSig = - function (sig, pubkey, scriptCode, tx, n, hashType, callback) { - if (!sig.length) { - callback(null, false); - return; - } + function(sig, pubkey, scriptCode, tx, n, hashType, callback) { + if (!sig.length) { + callback(null, false); + return; + } - if (hashType == 0) { - hashType = sig[sig.length -1]; - } else if (hashType != sig[sig.length -1]) { - callback(null, false); - return; - } - sig = sig.slice(0, sig.length-1); + if (hashType == 0) { + hashType = sig[sig.length - 1]; + } else if (hashType != sig[sig.length - 1]) { + callback(null, false); + return; + } + sig = sig.slice(0, sig.length - 1); - try { - // Signature verification requires a special hash procedure - var hash = tx.hashForSignature(scriptCode, n, hashType); + try { + // Signature verification requires a special hash procedure + var hash = tx.hashForSignature(scriptCode, n, hashType); - // Verify signature - var key = new Util.BitcoinKey(); - key.public = pubkey; - key.verifySignature(hash, sig, callback); - } catch (err) { - callback(null, false); - } + // Verify signature + var key = new Util.BitcoinKey(); + key.public = pubkey; + key.verifySignature(hash, sig, callback); + } catch (err) { + callback(null, false); + } }; return ScriptInterpreter; diff --git a/package.json b/package.json index ad522b463..68fb6e22e 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,8 @@ "browserify-bignum": "git://github.com/maraoz/browserify-bignum.git", "browserify-buffertools": "~1.0.2", "chai": "~1.9.0", - "brfs": "~1.0.0" + "brfs": "~1.0.0", + "async": "~0.2.10" }, "license": "MIT" } diff --git a/test/data/script_valid.json b/test/data/script_valid.json index e4c181cae..ae3390f3a 100644 --- a/test/data/script_valid.json +++ b/test/data/script_valid.json @@ -1,8 +1,8 @@ [ ["0x01 0x0b", "11 EQUAL", "push 1 byte"], -["0x02 0x417a", "'Az' EQUAL"], +["0x02 0x417a", "0x417a EQUAL"], ["0x4b 0x417a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a", - "'Azzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' EQUAL", "push 75 bytes"], + "0x417a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a EQUAL", "push 75 bytes"], ["0x4c 0x01 0x07","7 EQUAL", "0x4c is OP_PUSHDATA1"], ["0x4d 0x0100 0x08","8 EQUAL", "0x4d is OP_PUSHDATA2"], @@ -11,7 +11,7 @@ ["0x4c 0x00","0 EQUAL"], ["0x4d 0x0000","0 EQUAL"], ["0x4e 0x00000000","0 EQUAL"], -["0x4f 1000 ADD","999 EQUAL"], +["0x4f 0x03e8 ADD","0x03e7 EQUAL"], ["0", "IF 0x50 ENDIF 1", "0x50 is reserved (ok if not executed)"], ["0x51", "0x5f ADD 0x60 EQUAL", "0x51 through 0x60 push 1 through 16 onto stack"], ["1","NOP"], @@ -53,8 +53,8 @@ ["1 1", "VERIFY"], -["10 0 11 TOALTSTACK DROP FROMALTSTACK", "ADD 21 EQUAL"], -["'gavin_was_here' TOALTSTACK 11 FROMALTSTACK", "'gavin_was_here' EQUALVERIFY 11 EQUAL"], +["10 0 11 TOALTSTACK DROP FROMALTSTACK", "ADD 0x15 EQUAL"], +["0x676176696e5f7761735f68657265 TOALTSTACK 11 FROMALTSTACK", "0x676176696e5f7761735f68657265 EQUALVERIFY 11 EQUAL"], ["0 IFDUP", "DEPTH 1 EQUALVERIFY 0 EQUAL"], ["1 IFDUP", "DEPTH 2 EQUALVERIFY 1 EQUALVERIFY 1 EQUAL"], diff --git a/test/test.Script.js b/test/test.Script.js index bf2a71cb9..9b04dae16 100644 --- a/test/test.Script.js +++ b/test/test.Script.js @@ -9,6 +9,7 @@ var ScriptModule = bitcore.Script; var Address = bitcore.Address.class(); var networks = bitcore.networks; var Script; +var test_data = require('./testdata'); describe('Script', function() { it('should initialze the main object', function() { @@ -82,4 +83,17 @@ describe('Script', function() { script.chunks[0].should.equal(0); }); }); + + + + test_data.dataScriptValid.forEach(function(datum) { + if (datum.length < 2) throw new Error('Invalid test data'); + var human = datum[1]; + it('should parse script from human readable ' + human, function() { + Script.fromStringContent(human).getStringContent(false, null).should.equal(human); + }); + + + }); + }); diff --git a/test/test.Transaction.js b/test/test.Transaction.js index 989a6349f..fbc3a9e2b 100644 --- a/test/test.Transaction.js +++ b/test/test.Transaction.js @@ -12,6 +12,7 @@ var Out; var Script = bitcore.Script.class(); var buffertools = require('buffertools'); var test_data = require('./testdata'); +var async = require('async'); describe('Transaction', function() { it('should initialze the main object', function() { @@ -37,28 +38,38 @@ describe('Transaction', function() { // ... where all scripts are stringified scripts. test_data.dataTxValid.forEach(function(datum) { if (datum.length === 3) { - it('valid tx=' + datum[1], function() { + it.skip('valid tx=' + datum[1], function(done) { var inputs = datum[0]; - var mapprevOutScriptPubKeys = {}; - var ins = []; + var map = {}; inputs.forEach(function(vin) { var hash = vin[0]; var index = vin[1]; - var scriptPubKey = vin[2]; - var input = new In({ - s: scriptPubKey, - q: 0xffffffff, - oTxHash: hash, - oIndex: index - }); - //mapprevOutScriptPubKeys[input] = new Script(scriptPubKey); - ins.push(input); + var scriptPubKey = new Script(new Buffer(vin[2])); + map[[hash, index]] = scriptPubKey;//Script.fromStringContent(scriptPubKey); + console.log(scriptPubKey.getStringContent()); + console.log('********************************'); }); var raw = new Buffer(datum[1], 'hex'); var tx = new Transaction(); tx.parse(raw); + buffertools.toHex(tx.serialize()).should.equal(buffertools.toHex(raw)); + + var i = 0; + var stx = tx.getStandardizedObject(); + async.eachSeries(tx.ins, + function(txin, next) { + var scriptPubKey = map[[stx.in[i].prev_out.hash, stx.in[i].prev_out.n]]; + i += 1; + next(); + + }, + function(err) { + should.not.exist(err); + done(); + } + ); }); } }); diff --git a/test/testdata.js b/test/testdata.js index 3d587fc2f..9d62e9d85 100644 --- a/test/testdata.js +++ b/test/testdata.js @@ -7,9 +7,13 @@ var dataInvalid = JSON.parse(fs.readFileSync('test/data/base58_keys_invalid.json var dataEncodeDecode = JSON.parse(fs.readFileSync('test/data/base58_encode_decode.json')); var dataTxValid = JSON.parse(fs.readFileSync('test/data/tx_valid.json')); var dataTxInvalid = JSON.parse(fs.readFileSync('test/data/tx_invalid.json')); +var dataScriptValid = JSON.parse(fs.readFileSync('test/data/script_valid.json')); +var dataScriptInvalid = JSON.parse(fs.readFileSync('test/data/script_invalid.json')); module.exports.dataValid = dataValid; module.exports.dataInvalid = dataInvalid; module.exports.dataEncodeDecode = dataEncodeDecode; module.exports.dataTxValid = dataTxValid; module.exports.dataTxInvalid = dataTxInvalid; +module.exports.dataScriptValid = dataScriptValid; +module.exports.dataScriptInvalid = dataScriptInvalid;