|
|
@ -7,6 +7,7 @@ var buffertools = imports.buffertools || require('buffertools'); |
|
|
|
var bignum = imports.bignum || require('bignum'); |
|
|
|
var Util = imports.Util || require('./util/util'); |
|
|
|
var Script = require('./Script'); |
|
|
|
var Key = require('./Key'); |
|
|
|
|
|
|
|
var SIGHASH_ALL = 1; |
|
|
|
var SIGHASH_NONE = 2; |
|
|
@ -21,7 +22,8 @@ for (var i in Opcode.map) { |
|
|
|
var intToBufferSM = Util.intToBufferSM |
|
|
|
var bufferSMToInt = Util.bufferSMToInt; |
|
|
|
|
|
|
|
function ScriptInterpreter() { |
|
|
|
function ScriptInterpreter(opts) { |
|
|
|
this.opts = opts || {}; |
|
|
|
this.stack = []; |
|
|
|
this.disableUnsafeOpcodes = true; |
|
|
|
}; |
|
|
@ -60,7 +62,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
// 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.)
|
|
|
@ -98,8 +99,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
|
|
|
|
if (exec && Buffer.isBuffer(opcode)) { |
|
|
|
this.stack.push(opcode); |
|
|
|
} |
|
|
|
else if (exec || (OP_IF <= opcode && opcode <= OP_ENDIF)) |
|
|
|
} else if (exec || (OP_IF <= opcode && opcode <= OP_ENDIF)) |
|
|
|
switch (opcode) { |
|
|
|
case OP_0: |
|
|
|
this.stack.push(new Buffer([])); |
|
|
@ -620,12 +620,11 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
// Remove signature if present (a signature can't sign itself)
|
|
|
|
scriptCode.findAndDelete(sig); |
|
|
|
|
|
|
|
//
|
|
|
|
isCanonicalSignature(new Buffer(sig)); |
|
|
|
// check canonical signature
|
|
|
|
this.isCanonicalSignature(new Buffer(sig)); |
|
|
|
|
|
|
|
// Verify signature
|
|
|
|
checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) { |
|
|
|
try { |
|
|
|
var success; |
|
|
|
|
|
|
|
if (e) { |
|
|
@ -650,9 +649,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
|
|
|
|
// 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
|
|
|
@ -695,8 +691,9 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
var scriptCode = Script.fromChunks(scriptChunks); |
|
|
|
|
|
|
|
// Drop the signatures, since a signature can't sign itself
|
|
|
|
var that = this; |
|
|
|
sigs.forEach(function(sig) { |
|
|
|
isCanonicalSignature(new Buffer(sig)); |
|
|
|
that.isCanonicalSignature(new Buffer(sig)); |
|
|
|
scriptCode.findAndDelete(sig); |
|
|
|
}); |
|
|
|
|
|
|
@ -706,13 +703,11 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
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--; |
|
|
@ -728,9 +723,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
} |
|
|
|
|
|
|
|
checkMultiSigStep.call(this); |
|
|
|
} catch (e) { |
|
|
|
cb(e); |
|
|
|
} |
|
|
|
}.bind(this)); |
|
|
|
} else { |
|
|
|
this.stack.push(new Buffer([success ? 1 : 0])); |
|
|
@ -745,9 +737,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
// Run next step
|
|
|
|
executeStep.call(this, cb); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
cb(e); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// Note that for asynchronous opcodes we have to return here to prevent
|
|
|
@ -764,18 +753,13 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
|
} |
|
|
|
|
|
|
|
// Run next step
|
|
|
|
if (pc % 100) { |
|
|
|
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) { |
|
|
|
log.debug("Script aborted: " + |
|
|
|
(e.message ? e.message : e)); |
|
|
|
cb(e); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -845,15 +829,15 @@ ScriptInterpreter.prototype.stackSwap = function stackSwap(a, b) { |
|
|
|
* integer. Any longer Buffer is converted to a hex string. |
|
|
|
*/ |
|
|
|
ScriptInterpreter.prototype.getPrimitiveStack = function getPrimitiveStack() { |
|
|
|
return this.stack.map(function(entry) { |
|
|
|
if (entry.length > 2) { |
|
|
|
return buffertools.toHex(entry.slice(0)); |
|
|
|
return this.stack.map(function(chunk) { |
|
|
|
if (chunk.length > 2) { |
|
|
|
return buffertools.toHex(chunk.slice(0)); |
|
|
|
} |
|
|
|
var num = bufferSMToInt(entry); |
|
|
|
var num = bufferSMToInt(chunk); |
|
|
|
if (num.cmp(-128) >= 0 && num.cmp(127) <= 0) { |
|
|
|
return num.toNumber(); |
|
|
|
} else { |
|
|
|
return buffertools.toHex(entry.slice(0)); |
|
|
|
return buffertools.toHex(chunk.slice(0)); |
|
|
|
} |
|
|
|
}); |
|
|
|
}; |
|
|
@ -882,6 +866,7 @@ ScriptInterpreter.prototype.getResult = function getResult() { |
|
|
|
return castBool(this.stack[this.stack.length - 1]); |
|
|
|
}; |
|
|
|
|
|
|
|
// Use ScriptInterpreter.verifyFull instead
|
|
|
|
ScriptInterpreter.verify = |
|
|
|
function verify(scriptSig, scriptPubKey, txTo, n, hashType, callback) { |
|
|
|
if ("function" !== typeof callback) { |
|
|
@ -899,12 +884,7 @@ ScriptInterpreter.verify = |
|
|
|
} |
|
|
|
|
|
|
|
// Cast result to bool
|
|
|
|
try { |
|
|
|
var result = si.getResult(); |
|
|
|
} catch (err) { |
|
|
|
callback(err); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
callback(null, result); |
|
|
|
}); |
|
|
@ -912,8 +892,8 @@ ScriptInterpreter.verify = |
|
|
|
return si; |
|
|
|
}; |
|
|
|
|
|
|
|
function verifyStep4(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, opts, callback, si, siCopy) { |
|
|
|
ScriptInterpreter.prototype.verifyStep4 = function(scriptSig, scriptPubKey, |
|
|
|
txTo, nIn, hashType, callback, siCopy) { |
|
|
|
if (siCopy.stack.length == 0) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
@ -922,19 +902,19 @@ function verifyStep4(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
callback(null, castBool(siCopy.stackBack())); |
|
|
|
} |
|
|
|
|
|
|
|
function verifyStep3(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, opts, callback, si, siCopy) { |
|
|
|
if (si.stack.length == 0) { |
|
|
|
ScriptInterpreter.prototype.verifyStep3 = function(scriptSig, |
|
|
|
scriptPubKey, txTo, nIn, hashType, callback, siCopy) { |
|
|
|
if (this.stack.length == 0) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (castBool(si.stackBack()) == false) { |
|
|
|
if (castBool(this.stackBack()) == false) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// if not P2SH, we're done
|
|
|
|
if (!opts.verifyP2SH || !scriptPubKey.isP2SH()) { |
|
|
|
if (!this.opts.verifyP2SH || !scriptPubKey.isP2SH()) { |
|
|
|
callback(null, true); |
|
|
|
return; |
|
|
|
} |
|
|
@ -949,46 +929,50 @@ function verifyStep3(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
|
|
|
|
var subscript = new Script(siCopy.stackPop()); |
|
|
|
|
|
|
|
ok = true; |
|
|
|
var that = this; |
|
|
|
siCopy.eval(subscript, txTo, nIn, hashType, function(err) { |
|
|
|
if (err) |
|
|
|
callback(err); |
|
|
|
else |
|
|
|
verifyStep4(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, opts, callback, si, siCopy); |
|
|
|
if (err) callback(err); |
|
|
|
else that.verifyStep4(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, callback, siCopy); |
|
|
|
}); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
function verifyStep2(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, opts, callback, si, siCopy) { |
|
|
|
if (opts.verifyP2SH) { |
|
|
|
si.stack.forEach(function(item) { |
|
|
|
ScriptInterpreter.prototype.verifyStep2 = function(scriptSig, scriptPubKey, |
|
|
|
txTo, nIn, hashType, callback, siCopy) { |
|
|
|
if (this.opts.verifyP2SH) { |
|
|
|
this.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); |
|
|
|
var that = this; |
|
|
|
this.eval(scriptPubKey, txTo, nIn, hashType, function(err) { |
|
|
|
if (err) callback(err); |
|
|
|
else that.verifyStep3(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, callback, siCopy); |
|
|
|
}); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
ScriptInterpreter.verifyFull = |
|
|
|
function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType, |
|
|
|
opts, callback) { |
|
|
|
var si = new ScriptInterpreter(); |
|
|
|
var siCopy = new ScriptInterpreter(); |
|
|
|
var si = new ScriptInterpreter(opts); |
|
|
|
si.verifyFull(scriptSig, scriptPubKey, |
|
|
|
txTo, nIn, hashType, callback); |
|
|
|
}; |
|
|
|
|
|
|
|
si.eval(scriptSig, txTo, nIn, hashType, function(err) { |
|
|
|
if (err) |
|
|
|
callback(err); |
|
|
|
else |
|
|
|
verifyStep2(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, opts, callback, si, siCopy); |
|
|
|
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 = |
|
|
@ -1006,20 +990,16 @@ var checkSig = ScriptInterpreter.checkSig = |
|
|
|
} |
|
|
|
sig = sig.slice(0, sig.length - 1); |
|
|
|
|
|
|
|
try { |
|
|
|
// Signature verification requires a special hash procedure
|
|
|
|
var hash = tx.hashForSignature(scriptCode, n, hashType); |
|
|
|
|
|
|
|
// Verify signature
|
|
|
|
var key = new Util.BitcoinKey(); |
|
|
|
var key = new Key(); |
|
|
|
key.public = pubkey; |
|
|
|
key.verifySignature(hash, sig, callback); |
|
|
|
} catch (err) { |
|
|
|
callback(null, false); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
var isCanonicalSignature = ScriptInterpreter.isCanonicalSignature = function(sig, opts) { |
|
|
|
ScriptInterpreter.prototype.isCanonicalSignature = function(sig) { |
|
|
|
// See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
|
|
|
|
// A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
|
|
|
|
// Where R and S are not negative (their first byte has its highest bit not set), and not
|
|
|
@ -1029,7 +1009,9 @@ var isCanonicalSignature = ScriptInterpreter.isCanonicalSignature = function(sig |
|
|
|
if (!Buffer.isBuffer(sig)) |
|
|
|
throw new Error("arg should be a Buffer"); |
|
|
|
|
|
|
|
opts = opts || {}; |
|
|
|
// TODO: change to opts.verifyStrictEnc to make the default
|
|
|
|
// behavior not verify, as in bitcoin core
|
|
|
|
if (this.opts.dontVerifyStrictEnc) return true; |
|
|
|
|
|
|
|
var l = sig.length; |
|
|
|
if (l < 9) throw new Error("Non-canonical signature: too short"); |
|
|
@ -1076,7 +1058,7 @@ var isCanonicalSignature = ScriptInterpreter.isCanonicalSignature = function(sig |
|
|
|
if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) |
|
|
|
throw new Error("Non-canonical signature: S value excessively padded"); |
|
|
|
|
|
|
|
if (opts.verifyEvenS) { |
|
|
|
if (this.opts.verifyEvenS) { |
|
|
|
if (S[nLenS - 1] & 1) |
|
|
|
throw new Error("Non-canonical signature: S value odd"); |
|
|
|
} |
|
|
|