diff --git a/Script.js b/Script.js index bce3f11..fe59127 100644 --- a/Script.js +++ b/Script.js @@ -66,6 +66,15 @@ function spec(b) { } }; + Script.prototype.isPushOnly = function () + { + for (var i = 0; i < this.chunks.length; i++) + if (!Buffer.isBuffer(this.chunks[i])) + return false; + + return true; + }; + Script.prototype.isP2SH = function () { return (this.chunks.length == 3 && diff --git a/ScriptInterpreter.js b/ScriptInterpreter.js index e90796d..86ffe5c 100644 --- a/ScriptInterpreter.js +++ b/ScriptInterpreter.js @@ -1,6 +1,7 @@ require('classtool'); function spec(b) { + var assert = require('assert'); var config = b.config || require('./config'); var log = b.log || require('./util/log')(config); @@ -758,6 +759,11 @@ function spec(b) { return this.stack[this.stack.length-offset]; }; + ScriptInterpreter.prototype.stackBack = function stackBack() + { + return this.stack[-1]; + }; + /** * Pop the top element off the stack and return it. */ @@ -911,6 +917,88 @@ function spec(b) { 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())); + } + + function verifyStep3(scriptSig, scriptPubKey, txTo, nIn, + hashType, opts, callback, si, siCopy) + { + if (si.stack.length == 0) { + callback(null, false); + return; + } + if (castBool(si.stackBack()) == false) { + callback(null, false); + return; + } + + // if not P2SH, we're done + if (!opts.verifyP2SH || !scriptPubKey.isP2SH()) { + callback(null, true); + return; + } + + if (!scriptSig.isPushOnly()) { + callback(null, false); + return; + } + + assert.notEqual(siCopy.length, 0); + + 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); + }); + } + + 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(); + + 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) {