From b6f05a3111b5aa91a6d56f62e6579577011eaf83 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 21 Aug 2013 08:29:06 -0400 Subject: [PATCH 1/2] Script, ScriptInterpreter: Verify P2SH scripts and signatures --- Script.js | 9 +++++++ ScriptInterpreter.js | 62 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/Script.js b/Script.js index bce3f1116..fe5912785 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 e90796daf..c54bc8b1f 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,62 @@ function spec(b) { return si; }; + ScriptInterpreter.verifyFull = + function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType, opts) + { + var si = new ScriptInterpreter(); + var siCopy = new ScriptInterpreter(); + + var ok = true; + si.eval(scriptSig, txTo, nIn, hashType, function (err) { + if (err) + ok = false; + }); + if (!ok) + return false; + + if (opts.verifyP2SH) { + si.stack.forEach(function(item) { + siCopy.stack.push(item); + }); + } + + ok = true; + si.eval(scriptPubKey, txTo, nIn, hashType, function (err) { + if (err) + ok = false; + }); + if (!ok) + return false; + if (si.stack.length == 0) + return false; + + if (castBool(si.stackBack()) == false) + return false; + + if (opts.verifyP2SH && scriptPubKey.isP2SH()) { + if (!scriptSig.isPushOnly()) + return false; + + assert.notEqual(siCopy.length, 0); + + var subscript = new Script(siCopy.stackPop()); + + ok = true; + siCopy.eval(subscript, txTo, nIn, hashType, function (err) { + if (err) + ok = false; + }); + if (!ok) + return false; + if (siCopy.stack.length == 0) + return false; + return castBool(siCopy.stackBack()); + } + + return true; + }; + var checkSig = ScriptInterpreter.checkSig = function (sig, pubkey, scriptCode, tx, n, hashType, callback) { if (!sig.length) { From 3a29b6e0dd1b2d3e21a825f594d01f9e6fbd9116 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 21 Aug 2013 08:42:31 -0400 Subject: [PATCH 2/2] ScriptInterpreter: convert verifyFull() to use callbacks --- ScriptInterpreter.js | 108 +++++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 41 deletions(-) diff --git a/ScriptInterpreter.js b/ScriptInterpreter.js index c54bc8b1f..86ffe5c3b 100644 --- a/ScriptInterpreter.js +++ b/ScriptInterpreter.js @@ -917,60 +917,86 @@ function spec(b) { return si; }; - ScriptInterpreter.verifyFull = - function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType, opts) + function verifyStep4(scriptSig, scriptPubKey, txTo, nIn, + hashType, opts, callback, si, siCopy) { - var si = new ScriptInterpreter(); - var siCopy = new ScriptInterpreter(); - - var ok = true; - si.eval(scriptSig, txTo, nIn, hashType, function (err) { - if (err) - ok = false; - }); - if (!ok) - return false; + 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); }); } - ok = true; si.eval(scriptPubKey, txTo, nIn, hashType, function (err) { if (err) - ok = false; + callback(err); + else + verifyStep3(scriptSig, scriptPubKey, txTo, nIn, + hashType, opts, callback, si, siCopy); }); - if (!ok) - return false; - if (si.stack.length == 0) - return false; + } - if (castBool(si.stackBack()) == false) - return false; + ScriptInterpreter.verifyFull = + function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType, + opts, callback) + { + var si = new ScriptInterpreter(); + var siCopy = new ScriptInterpreter(); - if (opts.verifyP2SH && scriptPubKey.isP2SH()) { - if (!scriptSig.isPushOnly()) - return false; - - assert.notEqual(siCopy.length, 0); - - var subscript = new Script(siCopy.stackPop()); - - ok = true; - siCopy.eval(subscript, txTo, nIn, hashType, function (err) { - if (err) - ok = false; - }); - if (!ok) - return false; - if (siCopy.stack.length == 0) - return false; - return castBool(siCopy.stackBack()); - } - - return true; + 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 =