|
|
@ -48,6 +48,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
executeStep.call(this, callback); |
|
|
|
|
|
|
|
function executeStep(cb) { |
|
|
|
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
|
|
|
@ -668,8 +669,10 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
} |
|
|
|
var keys = []; |
|
|
|
for (var i = 0, l = keysCount; i < l; i++) { |
|
|
|
keys.push(this.stackPop()); |
|
|
|
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"); |
|
|
@ -690,10 +693,11 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
// Convert to binary
|
|
|
|
var scriptCode = Script.fromChunks(scriptChunks); |
|
|
|
|
|
|
|
// Drop the signatures, since a signature can't sign itself
|
|
|
|
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); |
|
|
|
}); |
|
|
|
|
|
|
@ -705,9 +709,9 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
function checkMultiSigStep() { |
|
|
|
if (success && sigsCount > 0) { |
|
|
|
var sig = sigs[isig]; |
|
|
|
var key = keys[ikey]; |
|
|
|
var pubkey = keys[ikey]; |
|
|
|
|
|
|
|
checkSig(sig, key, scriptCode, tx, inIndex, hashType, function(e, result) { |
|
|
|
checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) { |
|
|
|
if (!e && result) { |
|
|
|
isig++; |
|
|
|
sigsCount--; |
|
|
@ -760,6 +764,9 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
} else { |
|
|
|
executeStep.call(this, cb); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
cb(e); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -866,9 +873,9 @@ ScriptInterpreter.prototype.getResult = function getResult() { |
|
|
|
return castBool(this.stack[this.stack.length - 1]); |
|
|
|
}; |
|
|
|
|
|
|
|
// Use ScriptInterpreter.verifyFull instead
|
|
|
|
// WARN: Use ScriptInterpreter.verifyFull instead
|
|
|
|
ScriptInterpreter.verify = |
|
|
|
function verify(scriptSig, scriptPubKey, txTo, n, hashType, callback) { |
|
|
|
function verify(scriptSig, scriptPubKey, tx, n, hashType, callback) { |
|
|
|
if ("function" !== typeof callback) { |
|
|
|
throw new Error("ScriptInterpreter.verify() requires a callback"); |
|
|
|
} |
|
|
@ -877,7 +884,7 @@ ScriptInterpreter.verify = |
|
|
|
var si = new ScriptInterpreter(); |
|
|
|
|
|
|
|
// Evaluate scripts
|
|
|
|
si.evalTwo(scriptSig, scriptPubKey, txTo, n, hashType, function(err) { |
|
|
|
si.evalTwo(scriptSig, scriptPubKey, tx, n, hashType, function(err) { |
|
|
|
if (err) { |
|
|
|
callback(err); |
|
|
|
return; |
|
|
@ -892,8 +899,8 @@ ScriptInterpreter.verify = |
|
|
|
return si; |
|
|
|
}; |
|
|
|
|
|
|
|
ScriptInterpreter.prototype.verifyStep4 = function(scriptSig, scriptPubKey, |
|
|
|
txTo, nIn, hashType, callback, siCopy) { |
|
|
|
ScriptInterpreter.prototype.verifyStep4 = function(callback, siCopy) { |
|
|
|
// 4th step, check P2SH subscript evaluated to true
|
|
|
|
if (siCopy.stack.length == 0) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
@ -903,91 +910,105 @@ ScriptInterpreter.prototype.verifyStep4 = function(scriptSig, scriptPubKey, |
|
|
|
} |
|
|
|
|
|
|
|
ScriptInterpreter.prototype.verifyStep3 = function(scriptSig, |
|
|
|
scriptPubKey, txTo, nIn, hashType, callback, siCopy) { |
|
|
|
if (this.stack.length == 0) { |
|
|
|
scriptPubKey, tx, nIn, hashType, callback, siCopy) { |
|
|
|
|
|
|
|
// 3rd step, check result (stack should contain true)
|
|
|
|
|
|
|
|
// if stack is empty, script considered invalid
|
|
|
|
if (this.stack.length === 0) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// if top of stack contains false, script evaluated to false
|
|
|
|
if (castBool(this.stackBack()) == false) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// if not P2SH, we're done
|
|
|
|
// if not P2SH, script evaluated to true
|
|
|
|
if (!this.opts.verifyP2SH || !scriptPubKey.isP2SH()) { |
|
|
|
callback(null, true); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// if P2SH, scriptSig should be push-only
|
|
|
|
if (!scriptSig.isPushOnly()) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (siCopy.length === 0) |
|
|
|
// P2SH script should exist
|
|
|
|
if (siCopy.length === 0) { |
|
|
|
throw new Error('siCopy should have length != 0'); |
|
|
|
} |
|
|
|
|
|
|
|
var subscript = new Script(siCopy.stackPop()); |
|
|
|
|
|
|
|
var that = this; |
|
|
|
siCopy.eval(subscript, txTo, nIn, hashType, function(err) { |
|
|
|
if (err) callback(err); |
|
|
|
else that.verifyStep4(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, callback, siCopy); |
|
|
|
// evaluate the P2SH subscript
|
|
|
|
siCopy.eval(subscript, tx, nIn, hashType, function(err) { |
|
|
|
if (err) return callback(err); |
|
|
|
that.verifyStep4(callback, siCopy); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
ScriptInterpreter.prototype.verifyStep2 = function(scriptSig, scriptPubKey, |
|
|
|
txTo, nIn, hashType, callback, siCopy) { |
|
|
|
tx, nIn, hashType, callback, siCopy) { |
|
|
|
var siCopy; |
|
|
|
if (this.opts.verifyP2SH) { |
|
|
|
siCopy = new ScriptInterpreter(this.opts); |
|
|
|
this.stack.forEach(function(item) { |
|
|
|
siCopy.stack.push(item); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
var that = this; |
|
|
|
this.eval(scriptPubKey, txTo, nIn, hashType, function(err) { |
|
|
|
if (err) callback(err); |
|
|
|
else that.verifyStep3(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
// 2nd step, evaluate scriptPubKey
|
|
|
|
this.eval(scriptPubKey, tx, nIn, hashType, function(err) { |
|
|
|
if (err) return callback(err); |
|
|
|
that.verifyStep3(scriptSig, scriptPubKey, tx, nIn, |
|
|
|
hashType, callback, siCopy); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
ScriptInterpreter.prototype.verifyFull = function(scriptSig, scriptPubKey, |
|
|
|
tx, nIn, hashType, callback) { |
|
|
|
var that = this; |
|
|
|
|
|
|
|
// 1st step, evaluate scriptSig
|
|
|
|
this.eval(scriptSig, tx, nIn, hashType, function(err) { |
|
|
|
if (err) return callback(err); |
|
|
|
that.verifyStep2(scriptSig, scriptPubKey, tx, nIn, |
|
|
|
hashType, callback); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
ScriptInterpreter.verifyFull = |
|
|
|
function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType, |
|
|
|
function verifyFull(scriptSig, scriptPubKey, tx, nIn, hashType, |
|
|
|
opts, callback) { |
|
|
|
var si = new ScriptInterpreter(opts); |
|
|
|
si.verifyFull(scriptSig, scriptPubKey, |
|
|
|
txTo, nIn, hashType, callback); |
|
|
|
tx, nIn, hashType, callback); |
|
|
|
}; |
|
|
|
|
|
|
|
ScriptInterpreter.prototype.verifyFull = function(scriptSig, scriptPubKey, |
|
|
|
txTo, nIn, hashType, callback) { |
|
|
|
var siCopy = new ScriptInterpreter(this.opts); |
|
|
|
var that = this; |
|
|
|
this.eval(scriptSig, txTo, nIn, hashType, function(err) { |
|
|
|
if (err) callback(err); |
|
|
|
else { |
|
|
|
that.verifyStep2(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, callback, siCopy); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
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) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (hashType == 0) { |
|
|
|
// If the hash-type value is 0, then it is replaced by the last_byte of the signature.
|
|
|
|
if (hashType === 0) { |
|
|
|
hashType = sig[sig.length - 1]; |
|
|
|
} else if (hashType != sig[sig.length - 1]) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Then the last byte of the signature is always deleted. (hashType removed)
|
|
|
|
sig = sig.slice(0, sig.length - 1); |
|
|
|
|
|
|
|
// Signature verification requires a special hash procedure
|
|
|
@ -995,7 +1016,9 @@ var checkSig = ScriptInterpreter.checkSig = |
|
|
|
|
|
|
|
// Verify signature
|
|
|
|
var key = new Key(); |
|
|
|
if (pubkey.length === 0) pubkey = new Buffer('00', 'hex'); |
|
|
|
key.public = pubkey; |
|
|
|
|
|
|
|
key.verifySignature(hash, sig, callback); |
|
|
|
}; |
|
|
|
|
|
|
|