|
|
@ -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"); |
|
|
|
} |
|
|
@ -123,8 +122,16 @@ function spec(b) { |
|
|
|
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: |
|
|
|
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: |
|
|
@ -144,7 +151,7 @@ function spec(b) { |
|
|
|
if (execStack.length < 1) { |
|
|
|
throw new Error("Unmatched OP_ELSE"); |
|
|
|
} |
|
|
|
execStack[execStack.length-1] = !execStack[execStack.length-1]; |
|
|
|
execStack[execStack.length - 1] = !execStack[execStack.length - 1]; |
|
|
|
break; |
|
|
|
|
|
|
|
case OP_ENDIF: |
|
|
@ -269,7 +276,7 @@ function spec(b) { |
|
|
|
if (n < 0 || n >= this.stack.length) { |
|
|
|
throw new Error("OP_PICK/OP_ROLL insufficient stack size"); |
|
|
|
} |
|
|
|
var value = this.stackTop(n+1); |
|
|
|
var value = this.stackTop(n + 1); |
|
|
|
if (opcode === OP_ROLL) { |
|
|
|
this.stack.splice(this.stack.length - n - 1, 1); |
|
|
|
} |
|
|
@ -319,7 +326,7 @@ function spec(b) { |
|
|
|
} |
|
|
|
this.stackPop(); |
|
|
|
this.stackPop(); |
|
|
|
this.stack[this.stack.length-1] = buf.slice(start, start + len); |
|
|
|
this.stack[this.stack.length - 1] = buf.slice(start, start + len); |
|
|
|
break; |
|
|
|
|
|
|
|
case OP_LEFT: |
|
|
@ -335,9 +342,9 @@ function spec(b) { |
|
|
|
} |
|
|
|
this.stackPop(); |
|
|
|
if (opcode === OP_LEFT) { |
|
|
|
this.stack[this.stack.length-1] = buf.slice(0, size); |
|
|
|
this.stack[this.stack.length - 1] = buf.slice(0, size); |
|
|
|
} else { |
|
|
|
this.stack[this.stack.length-1] = buf.slice(buf.length - size); |
|
|
|
this.stack[this.stack.length - 1] = buf.slice(buf.length - size); |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
@ -417,16 +424,32 @@ function spec(b) { |
|
|
|
// (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); |
|
|
|
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: |
|
|
@ -452,11 +475,21 @@ function spec(b) { |
|
|
|
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_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) { |
|
|
@ -485,7 +518,8 @@ function spec(b) { |
|
|
|
num = bignum(v1.cmp(v2) == 0 ? 1 : 0); |
|
|
|
break; |
|
|
|
|
|
|
|
case OP_NUMNOTEQUAL:; |
|
|
|
case OP_NUMNOTEQUAL: |
|
|
|
; |
|
|
|
num = bignum(v1.cmp(v2) != 0 ? 1 : 0); |
|
|
|
break; |
|
|
|
|
|
|
@ -505,8 +539,12 @@ function spec(b) { |
|
|
|
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; |
|
|
|
case OP_MIN: |
|
|
|
num = (v1.lt(v2) ? v1 : v2); |
|
|
|
break; |
|
|
|
case OP_MAX: |
|
|
|
num = (v1.gt(v2) ? v1 : v2); |
|
|
|
break; |
|
|
|
} |
|
|
|
this.stackPop(); |
|
|
|
this.stackPop(); |
|
|
@ -576,7 +614,7 @@ function spec(b) { |
|
|
|
scriptCode.findAndDelete(sig); |
|
|
|
|
|
|
|
// Verify signature
|
|
|
|
checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function (e, result) { |
|
|
|
checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) { |
|
|
|
try { |
|
|
|
var success; |
|
|
|
|
|
|
@ -602,7 +640,7 @@ function spec(b) { |
|
|
|
|
|
|
|
// Run next step
|
|
|
|
executeStep.call(this, cb); |
|
|
|
} catch(e) { |
|
|
|
} catch (e) { |
|
|
|
cb(e); |
|
|
|
} |
|
|
|
}.bind(this)); |
|
|
@ -647,11 +685,13 @@ function spec(b) { |
|
|
|
var scriptCode = Script.fromChunks(scriptChunks); |
|
|
|
|
|
|
|
// Drop the signatures, since a signature can't sign itself
|
|
|
|
sigs.forEach(function (sig) { |
|
|
|
sigs.forEach(function(sig) { |
|
|
|
scriptCode.findAndDelete(sig); |
|
|
|
}); |
|
|
|
|
|
|
|
var success = true, isig = 0, ikey = 0; |
|
|
|
var success = true, |
|
|
|
isig = 0, |
|
|
|
ikey = 0; |
|
|
|
checkMultiSigStep.call(this); |
|
|
|
|
|
|
|
function checkMultiSigStep() { |
|
|
@ -660,7 +700,7 @@ function spec(b) { |
|
|
|
var sig = sigs[isig]; |
|
|
|
var key = keys[ikey]; |
|
|
|
|
|
|
|
checkSig(sig, key, scriptCode, tx, inIndex, hashType, function (e, result) { |
|
|
|
checkSig(sig, key, scriptCode, tx, inIndex, hashType, function(e, result) { |
|
|
|
try { |
|
|
|
if (!e && result) { |
|
|
|
isig++; |
|
|
@ -694,7 +734,7 @@ function spec(b) { |
|
|
|
// Run next step
|
|
|
|
executeStep.call(this, cb); |
|
|
|
} |
|
|
|
} catch(e) { |
|
|
|
} catch (e) { |
|
|
|
cb(e); |
|
|
|
} |
|
|
|
}; |
|
|
@ -704,6 +744,7 @@ function spec(b) { |
|
|
|
return; |
|
|
|
|
|
|
|
default: |
|
|
|
console.log('opcode '+opcode); |
|
|
|
throw new Error("Unknown opcode encountered"); |
|
|
|
} |
|
|
|
|
|
|
@ -721,7 +762,7 @@ function spec(b) { |
|
|
|
executeStep.call(this, cb); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
log.debug("Script aborted: "+ |
|
|
|
log.debug("Script aborted: " + |
|
|
|
(e.message ? e : e)); |
|
|
|
cb(e); |
|
|
|
} |
|
|
@ -729,11 +770,10 @@ function spec(b) { |
|
|
|
}; |
|
|
|
|
|
|
|
ScriptInterpreter.prototype.evalTwo = |
|
|
|
function evalTwo(scriptSig, scriptPubkey, tx, n, hashType, callback) |
|
|
|
{ |
|
|
|
function evalTwo(scriptSig, scriptPubkey, tx, n, hashType, callback) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
self.eval(scriptSig, tx, n, hashType, function (e) { |
|
|
|
self.eval(scriptSig, tx, n, hashType, function(e) { |
|
|
|
if (e) { |
|
|
|
callback(e) |
|
|
|
return; |
|
|
@ -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]; |
|
|
|
}; |
|
|
|
|
|
|
@ -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,12 +923,11 @@ 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) |
|
|
|
{ |
|
|
|
function verify(scriptSig, scriptPubKey, txTo, n, hashType, callback) { |
|
|
|
if ("function" !== typeof callback) { |
|
|
|
throw new Error("ScriptInterpreter.verify() requires a callback"); |
|
|
|
} |
|
|
@ -898,7 +936,7 @@ function spec(b) { |
|
|
|
var si = new ScriptInterpreter(); |
|
|
|
|
|
|
|
// Evaluate scripts
|
|
|
|
si.evalTwo(scriptSig, scriptPubKey, txTo, n, hashType, function (err) { |
|
|
|
si.evalTwo(scriptSig, scriptPubKey, txTo, n, hashType, function(err) { |
|
|
|
if (err) { |
|
|
|
callback(err); |
|
|
|
return; |
|
|
@ -919,8 +957,7 @@ function spec(b) { |
|
|
|
}; |
|
|
|
|
|
|
|
function verifyStep4(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, opts, callback, si, siCopy) |
|
|
|
{ |
|
|
|
hashType, opts, callback, si, siCopy) { |
|
|
|
if (siCopy.stack.length == 0) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
@ -930,8 +967,7 @@ function spec(b) { |
|
|
|
} |
|
|
|
|
|
|
|
function verifyStep3(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, opts, callback, si, siCopy) |
|
|
|
{ |
|
|
|
hashType, opts, callback, si, siCopy) { |
|
|
|
if (si.stack.length == 0) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
@ -957,7 +993,7 @@ function spec(b) { |
|
|
|
var subscript = new Script(siCopy.stackPop()); |
|
|
|
|
|
|
|
ok = true; |
|
|
|
siCopy.eval(subscript, txTo, nIn, hashType, function (err) { |
|
|
|
siCopy.eval(subscript, txTo, nIn, hashType, function(err) { |
|
|
|
if (err) |
|
|
|
callback(err); |
|
|
|
else |
|
|
@ -967,15 +1003,14 @@ function spec(b) { |
|
|
|
} |
|
|
|
|
|
|
|
function verifyStep2(scriptSig, scriptPubKey, txTo, nIn, |
|
|
|
hashType, opts, callback, si, siCopy) |
|
|
|
{ |
|
|
|
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) { |
|
|
|
si.eval(scriptPubKey, txTo, nIn, hashType, function(err) { |
|
|
|
if (err) |
|
|
|
callback(err); |
|
|
|
else |
|
|
@ -986,12 +1021,11 @@ function spec(b) { |
|
|
|
|
|
|
|
ScriptInterpreter.verifyFull = |
|
|
|
function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType, |
|
|
|
opts, callback) |
|
|
|
{ |
|
|
|
opts, callback) { |
|
|
|
var si = new ScriptInterpreter(); |
|
|
|
var siCopy = new ScriptInterpreter(); |
|
|
|
|
|
|
|
si.eval(scriptSig, txTo, nIn, hashType, function (err) { |
|
|
|
si.eval(scriptSig, txTo, nIn, hashType, function(err) { |
|
|
|
if (err) |
|
|
|
callback(err); |
|
|
|
else |
|
|
@ -1001,19 +1035,19 @@ function spec(b) { |
|
|
|
}; |
|
|
|
|
|
|
|
var checkSig = ScriptInterpreter.checkSig = |
|
|
|
function (sig, pubkey, scriptCode, tx, n, hashType, callback) { |
|
|
|
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]) { |
|
|
|
hashType = sig[sig.length - 1]; |
|
|
|
} else if (hashType != sig[sig.length - 1]) { |
|
|
|
callback(null, false); |
|
|
|
return; |
|
|
|
} |
|
|
|
sig = sig.slice(0, sig.length-1); |
|
|
|
sig = sig.slice(0, sig.length - 1); |
|
|
|
|
|
|
|
try { |
|
|
|
// Signature verification requires a special hash procedure
|
|
|
|