|
@ -14,11 +14,6 @@ var SIGHASH_NONE = 2; |
|
|
var SIGHASH_SINGLE = 3; |
|
|
var SIGHASH_SINGLE = 3; |
|
|
var SIGHASH_ANYONECANPAY = 80; |
|
|
var SIGHASH_ANYONECANPAY = 80; |
|
|
|
|
|
|
|
|
// Make opcodes available as pseudo-constants
|
|
|
|
|
|
for (var i in Opcode.map) { |
|
|
|
|
|
eval(i + " = " + Opcode.map[i] + ";"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var intToBufferSM = Util.intToBufferSM |
|
|
var intToBufferSM = Util.intToBufferSM |
|
|
var bufferSMToInt = Util.bufferSMToInt; |
|
|
var bufferSMToInt = Util.bufferSMToInt; |
|
|
|
|
|
|
|
@ -74,101 +69,101 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
throw new Error("Max push value size exceeded (>520)"); |
|
|
throw new Error("Max push value size exceeded (>520)"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (opcode > OP_16 && ++opCount > 201) { |
|
|
if (opcode > Opcode.map.OP_16 && ++opCount > 201) { |
|
|
throw new Error("Opcode limit exceeded (>200)"); |
|
|
throw new Error("Opcode limit exceeded (>200)"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.disableUnsafeOpcodes && |
|
|
if (this.disableUnsafeOpcodes && |
|
|
"number" === typeof opcode && |
|
|
"number" === typeof opcode && |
|
|
(opcode === OP_CAT || |
|
|
(opcode === Opcode.map.OP_CAT || |
|
|
opcode === OP_SUBSTR || |
|
|
opcode === Opcode.map.OP_SUBSTR || |
|
|
opcode === OP_LEFT || |
|
|
opcode === Opcode.map.OP_LEFT || |
|
|
opcode === OP_RIGHT || |
|
|
opcode === Opcode.map.OP_RIGHT || |
|
|
opcode === OP_INVERT || |
|
|
opcode === Opcode.map.OP_INVERT || |
|
|
opcode === OP_AND || |
|
|
opcode === Opcode.map.OP_AND || |
|
|
opcode === OP_OR || |
|
|
opcode === Opcode.map.OP_OR || |
|
|
opcode === OP_XOR || |
|
|
opcode === Opcode.map.OP_XOR || |
|
|
opcode === OP_2MUL || |
|
|
opcode === Opcode.map.OP_2MUL || |
|
|
opcode === OP_2DIV || |
|
|
opcode === Opcode.map.OP_2DIV || |
|
|
opcode === OP_MUL || |
|
|
opcode === Opcode.map.OP_MUL || |
|
|
opcode === OP_DIV || |
|
|
opcode === Opcode.map.OP_DIV || |
|
|
opcode === OP_MOD || |
|
|
opcode === Opcode.map.OP_MOD || |
|
|
opcode === OP_LSHIFT || |
|
|
opcode === Opcode.map.OP_LSHIFT || |
|
|
opcode === OP_RSHIFT)) { |
|
|
opcode === Opcode.map.OP_RSHIFT)) { |
|
|
throw new Error("Encountered a disabled opcode"); |
|
|
throw new Error("Encountered a disabled opcode"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (exec && Buffer.isBuffer(opcode)) { |
|
|
if (exec && Buffer.isBuffer(opcode)) { |
|
|
this.stack.push(opcode); |
|
|
this.stack.push(opcode); |
|
|
} else if (exec || (OP_IF <= opcode && opcode <= OP_ENDIF)) |
|
|
} else if (exec || (Opcode.map.OP_IF <= opcode && opcode <= Opcode.map.OP_ENDIF)) |
|
|
switch (opcode) { |
|
|
switch (opcode) { |
|
|
case OP_0: |
|
|
case Opcode.map.OP_0: |
|
|
this.stack.push(new Buffer([])); |
|
|
this.stack.push(new Buffer([])); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_1NEGATE: |
|
|
case Opcode.map.OP_1NEGATE: |
|
|
case OP_1: |
|
|
case Opcode.map.OP_1: |
|
|
case OP_2: |
|
|
case Opcode.map.OP_2: |
|
|
case OP_3: |
|
|
case Opcode.map.OP_3: |
|
|
case OP_4: |
|
|
case Opcode.map.OP_4: |
|
|
case OP_5: |
|
|
case Opcode.map.OP_5: |
|
|
case OP_6: |
|
|
case Opcode.map.OP_6: |
|
|
case OP_7: |
|
|
case Opcode.map.OP_7: |
|
|
case OP_8: |
|
|
case Opcode.map.OP_8: |
|
|
case OP_9: |
|
|
case Opcode.map.OP_9: |
|
|
case OP_10: |
|
|
case Opcode.map.OP_10: |
|
|
case OP_11: |
|
|
case Opcode.map.OP_11: |
|
|
case OP_12: |
|
|
case Opcode.map.OP_12: |
|
|
case OP_13: |
|
|
case Opcode.map.OP_13: |
|
|
case OP_14: |
|
|
case Opcode.map.OP_14: |
|
|
case OP_15: |
|
|
case Opcode.map.OP_15: |
|
|
case OP_16: |
|
|
case Opcode.map.OP_16: |
|
|
var opint = opcode - OP_1 + 1; |
|
|
var opint = opcode - Opcode.map.OP_1 + 1; |
|
|
var opbuf = intToBufferSM(opint); |
|
|
var opbuf = intToBufferSM(opint); |
|
|
this.stack.push(opbuf); |
|
|
this.stack.push(opbuf); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_NOP: |
|
|
case Opcode.map.OP_NOP: |
|
|
case OP_NOP1: |
|
|
case Opcode.map.OP_NOP1: |
|
|
case OP_NOP2: |
|
|
case Opcode.map.OP_NOP2: |
|
|
case OP_NOP3: |
|
|
case Opcode.map.OP_NOP3: |
|
|
case OP_NOP4: |
|
|
case Opcode.map.OP_NOP4: |
|
|
case OP_NOP5: |
|
|
case Opcode.map.OP_NOP5: |
|
|
case OP_NOP6: |
|
|
case Opcode.map.OP_NOP6: |
|
|
case OP_NOP7: |
|
|
case Opcode.map.OP_NOP7: |
|
|
case OP_NOP8: |
|
|
case Opcode.map.OP_NOP8: |
|
|
case OP_NOP9: |
|
|
case Opcode.map.OP_NOP9: |
|
|
case OP_NOP10: |
|
|
case Opcode.map.OP_NOP10: |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_IF: |
|
|
case Opcode.map.OP_IF: |
|
|
case OP_NOTIF: |
|
|
case Opcode.map.OP_NOTIF: |
|
|
// <expression> if [statements] [else [statements]] endif
|
|
|
// <expression> if [statements] [else [statements]] endif
|
|
|
var value = false; |
|
|
var value = false; |
|
|
if (exec) { |
|
|
if (exec) { |
|
|
value = castBool(this.stackPop()); |
|
|
value = castBool(this.stackPop()); |
|
|
if (opcode === OP_NOTIF) { |
|
|
if (opcode === Opcode.map.OP_NOTIF) { |
|
|
value = !value; |
|
|
value = !value; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
execStack.push(value); |
|
|
execStack.push(value); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_ELSE: |
|
|
case Opcode.map.OP_ELSE: |
|
|
if (execStack.length < 1) { |
|
|
if (execStack.length < 1) { |
|
|
throw new Error("Unmatched OP_ELSE"); |
|
|
throw new Error("Unmatched OP_ELSE"); |
|
|
} |
|
|
} |
|
|
execStack[execStack.length - 1] = !execStack[execStack.length - 1]; |
|
|
execStack[execStack.length - 1] = !execStack[execStack.length - 1]; |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_ENDIF: |
|
|
case Opcode.map.OP_ENDIF: |
|
|
if (execStack.length < 1) { |
|
|
if (execStack.length < 1) { |
|
|
throw new Error("Unmatched OP_ENDIF"); |
|
|
throw new Error("Unmatched OP_ENDIF"); |
|
|
} |
|
|
} |
|
|
execStack.pop(); |
|
|
execStack.pop(); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_VERIFY: |
|
|
case Opcode.map.OP_VERIFY: |
|
|
var value = castBool(this.stackTop()); |
|
|
var value = castBool(this.stackTop()); |
|
|
if (value) { |
|
|
if (value) { |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
@ -177,27 +172,27 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_RETURN: |
|
|
case Opcode.map.OP_RETURN: |
|
|
throw new Error("OP_RETURN"); |
|
|
throw new Error("OP_RETURN"); |
|
|
|
|
|
|
|
|
case OP_TOALTSTACK: |
|
|
case Opcode.map.OP_TOALTSTACK: |
|
|
altStack.push(this.stackPop()); |
|
|
altStack.push(this.stackPop()); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_FROMALTSTACK: |
|
|
case Opcode.map.OP_FROMALTSTACK: |
|
|
if (altStack.length < 1) { |
|
|
if (altStack.length < 1) { |
|
|
throw new Error("OP_FROMALTSTACK with alt stack empty"); |
|
|
throw new Error("OP_FROMALTSTACK with alt stack empty"); |
|
|
} |
|
|
} |
|
|
this.stack.push(altStack.pop()); |
|
|
this.stack.push(altStack.pop()); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_2DROP: |
|
|
case Opcode.map.OP_2DROP: |
|
|
// (x1 x2 -- )
|
|
|
// (x1 x2 -- )
|
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_2DUP: |
|
|
case Opcode.map.OP_2DUP: |
|
|
// (x1 x2 -- x1 x2 x1 x2)
|
|
|
// (x1 x2 -- x1 x2 x1 x2)
|
|
|
var v1 = this.stackTop(2); |
|
|
var v1 = this.stackTop(2); |
|
|
var v2 = this.stackTop(1); |
|
|
var v2 = this.stackTop(1); |
|
@ -205,7 +200,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stack.push(v2); |
|
|
this.stack.push(v2); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_3DUP: |
|
|
case Opcode.map.OP_3DUP: |
|
|
// (x1 x2 -- x1 x2 x1 x2)
|
|
|
// (x1 x2 -- x1 x2 x1 x2)
|
|
|
var v1 = this.stackTop(3); |
|
|
var v1 = this.stackTop(3); |
|
|
var v2 = this.stackTop(2); |
|
|
var v2 = this.stackTop(2); |
|
@ -215,7 +210,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stack.push(v3); |
|
|
this.stack.push(v3); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_2OVER: |
|
|
case Opcode.map.OP_2OVER: |
|
|
// (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
|
|
|
// (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
|
|
|
var v1 = this.stackTop(4); |
|
|
var v1 = this.stackTop(4); |
|
|
var v2 = this.stackTop(3); |
|
|
var v2 = this.stackTop(3); |
|
@ -223,7 +218,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stack.push(v2); |
|
|
this.stack.push(v2); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_2ROT: |
|
|
case Opcode.map.OP_2ROT: |
|
|
// (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
|
|
|
// (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
|
|
|
var v1 = this.stackTop(6); |
|
|
var v1 = this.stackTop(6); |
|
|
var v2 = this.stackTop(5); |
|
|
var v2 = this.stackTop(5); |
|
@ -232,13 +227,13 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stack.push(v2); |
|
|
this.stack.push(v2); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_2SWAP: |
|
|
case Opcode.map.OP_2SWAP: |
|
|
// (x1 x2 x3 x4 -- x3 x4 x1 x2)
|
|
|
// (x1 x2 x3 x4 -- x3 x4 x1 x2)
|
|
|
this.stackSwap(4, 2); |
|
|
this.stackSwap(4, 2); |
|
|
this.stackSwap(3, 1); |
|
|
this.stackSwap(3, 1); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_IFDUP: |
|
|
case Opcode.map.OP_IFDUP: |
|
|
// (x - 0 | x x)
|
|
|
// (x - 0 | x x)
|
|
|
var value = this.stackTop(); |
|
|
var value = this.stackTop(); |
|
|
if (castBool(value)) { |
|
|
if (castBool(value)) { |
|
@ -246,23 +241,23 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_DEPTH: |
|
|
case Opcode.map.OP_DEPTH: |
|
|
// -- stacksize
|
|
|
// -- stacksize
|
|
|
var value = bignum(this.stack.length); |
|
|
var value = bignum(this.stack.length); |
|
|
this.stack.push(intToBufferSM(value)); |
|
|
this.stack.push(intToBufferSM(value)); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_DROP: |
|
|
case Opcode.map.OP_DROP: |
|
|
// (x -- )
|
|
|
// (x -- )
|
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_DUP: |
|
|
case Opcode.map.OP_DUP: |
|
|
// (x -- x x)
|
|
|
// (x -- x x)
|
|
|
this.stack.push(this.stackTop()); |
|
|
this.stack.push(this.stackTop()); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_NIP: |
|
|
case Opcode.map.OP_NIP: |
|
|
// (x1 x2 -- x2)
|
|
|
// (x1 x2 -- x2)
|
|
|
if (this.stack.length < 2) { |
|
|
if (this.stack.length < 2) { |
|
|
throw new Error("OP_NIP insufficient stack size"); |
|
|
throw new Error("OP_NIP insufficient stack size"); |
|
@ -270,13 +265,13 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stack.splice(this.stack.length - 2, 1); |
|
|
this.stack.splice(this.stack.length - 2, 1); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_OVER: |
|
|
case Opcode.map.OP_OVER: |
|
|
// (x1 x2 -- x1 x2 x1)
|
|
|
// (x1 x2 -- x1 x2 x1)
|
|
|
this.stack.push(this.stackTop(2)); |
|
|
this.stack.push(this.stackTop(2)); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_PICK: |
|
|
case Opcode.map.OP_PICK: |
|
|
case OP_ROLL: |
|
|
case Opcode.map.OP_ROLL: |
|
|
// (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
|
|
|
// (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
|
|
|
// (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
|
|
|
// (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
|
|
|
var n = castInt(this.stackPop()); |
|
|
var n = castInt(this.stackPop()); |
|
@ -284,13 +279,13 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
throw new Error("OP_PICK/OP_ROLL insufficient stack size"); |
|
|
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) { |
|
|
if (opcode === Opcode.map.OP_ROLL) { |
|
|
this.stack.splice(this.stack.length - n - 1, 1); |
|
|
this.stack.splice(this.stack.length - n - 1, 1); |
|
|
} |
|
|
} |
|
|
this.stack.push(value); |
|
|
this.stack.push(value); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_ROT: |
|
|
case Opcode.map.OP_ROT: |
|
|
// (x1 x2 x3 -- x2 x3 x1)
|
|
|
// (x1 x2 x3 -- x2 x3 x1)
|
|
|
// x2 x1 x3 after first swap
|
|
|
// x2 x1 x3 after first swap
|
|
|
// x2 x3 x1 after second swap
|
|
|
// x2 x3 x1 after second swap
|
|
@ -298,12 +293,12 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stackSwap(2, 1); |
|
|
this.stackSwap(2, 1); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_SWAP: |
|
|
case Opcode.map.OP_SWAP: |
|
|
// (x1 x2 -- x2 x1)
|
|
|
// (x1 x2 -- x2 x1)
|
|
|
this.stackSwap(2, 1); |
|
|
this.stackSwap(2, 1); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_TUCK: |
|
|
case Opcode.map.OP_TUCK: |
|
|
// (x1 x2 -- x2 x1 x2)
|
|
|
// (x1 x2 -- x2 x1 x2)
|
|
|
if (this.stack.length < 2) { |
|
|
if (this.stack.length < 2) { |
|
|
throw new Error("OP_TUCK insufficient stack size"); |
|
|
throw new Error("OP_TUCK insufficient stack size"); |
|
@ -311,7 +306,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stack.splice(this.stack.length - 2, 0, this.stackTop()); |
|
|
this.stack.splice(this.stack.length - 2, 0, this.stackTop()); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_CAT: |
|
|
case Opcode.map.OP_CAT: |
|
|
// (x1 x2 -- out)
|
|
|
// (x1 x2 -- out)
|
|
|
var v1 = this.stackTop(2); |
|
|
var v1 = this.stackTop(2); |
|
|
var v2 = this.stackTop(1); |
|
|
var v2 = this.stackTop(1); |
|
@ -320,7 +315,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stack.push(Buffer.concat([v1, v2])); |
|
|
this.stack.push(Buffer.concat([v1, v2])); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_SUBSTR: |
|
|
case Opcode.map.OP_SUBSTR: |
|
|
// (in begin size -- out)
|
|
|
// (in begin size -- out)
|
|
|
var buf = this.stackTop(3); |
|
|
var buf = this.stackTop(3); |
|
|
var start = castInt(this.stackTop(2)); |
|
|
var start = castInt(this.stackTop(2)); |
|
@ -336,8 +331,8 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stack[this.stack.length - 1] = buf.slice(start, start + len); |
|
|
this.stack[this.stack.length - 1] = buf.slice(start, start + len); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_LEFT: |
|
|
case Opcode.map.OP_LEFT: |
|
|
case OP_RIGHT: |
|
|
case Opcode.map.OP_RIGHT: |
|
|
// (in size -- out)
|
|
|
// (in size -- out)
|
|
|
var buf = this.stackTop(2); |
|
|
var buf = this.stackTop(2); |
|
|
var size = castInt(this.stackTop(1)); |
|
|
var size = castInt(this.stackTop(1)); |
|
@ -348,20 +343,20 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
size = buf.length; |
|
|
size = buf.length; |
|
|
} |
|
|
} |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
if (opcode === OP_LEFT) { |
|
|
if (opcode === Opcode.map.OP_LEFT) { |
|
|
this.stack[this.stack.length - 1] = buf.slice(0, size); |
|
|
this.stack[this.stack.length - 1] = buf.slice(0, size); |
|
|
} else { |
|
|
} else { |
|
|
this.stack[this.stack.length - 1] = buf.slice(buf.length - size); |
|
|
this.stack[this.stack.length - 1] = buf.slice(buf.length - size); |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_SIZE: |
|
|
case Opcode.map.OP_SIZE: |
|
|
// (in -- in size)
|
|
|
// (in -- in size)
|
|
|
var value = bignum(this.stackTop().length); |
|
|
var value = bignum(this.stackTop().length); |
|
|
this.stack.push(intToBufferSM(value)); |
|
|
this.stack.push(intToBufferSM(value)); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_INVERT: |
|
|
case Opcode.map.OP_INVERT: |
|
|
// (in - out)
|
|
|
// (in - out)
|
|
|
var buf = this.stackTop(); |
|
|
var buf = this.stackTop(); |
|
|
for (var i = 0, l = buf.length; i < l; i++) { |
|
|
for (var i = 0, l = buf.length; i < l; i++) { |
|
@ -369,24 +364,24 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_AND: |
|
|
case Opcode.map.OP_AND: |
|
|
case OP_OR: |
|
|
case Opcode.map.OP_OR: |
|
|
case OP_XOR: |
|
|
case Opcode.map.OP_XOR: |
|
|
// (x1 x2 - out)
|
|
|
// (x1 x2 - out)
|
|
|
var v1 = this.stackTop(2); |
|
|
var v1 = this.stackTop(2); |
|
|
var v2 = this.stackTop(1); |
|
|
var v2 = this.stackTop(1); |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
var out = new Buffer(Math.max(v1.length, v2.length)); |
|
|
var out = new Buffer(Math.max(v1.length, v2.length)); |
|
|
if (opcode === OP_AND) { |
|
|
if (opcode === Opcode.map.OP_AND) { |
|
|
for (var i = 0, l = out.length; i < l; i++) { |
|
|
for (var i = 0, l = out.length; i < l; i++) { |
|
|
out[i] = v1[i] & v2[i]; |
|
|
out[i] = v1[i] & v2[i]; |
|
|
} |
|
|
} |
|
|
} else if (opcode === OP_OR) { |
|
|
} else if (opcode === Opcode.map.OP_OR) { |
|
|
for (var i = 0, l = out.length; i < l; i++) { |
|
|
for (var i = 0, l = out.length; i < l; i++) { |
|
|
out[i] = v1[i] | v2[i]; |
|
|
out[i] = v1[i] | v2[i]; |
|
|
} |
|
|
} |
|
|
} else if (opcode === OP_XOR) { |
|
|
} else if (opcode === Opcode.map.OP_XOR) { |
|
|
for (var i = 0, l = out.length; i < l; i++) { |
|
|
for (var i = 0, l = out.length; i < l; i++) { |
|
|
out[i] = v1[i] ^ v2[i]; |
|
|
out[i] = v1[i] ^ v2[i]; |
|
|
} |
|
|
} |
|
@ -394,8 +389,8 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stack.push(out); |
|
|
this.stack.push(out); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_EQUAL: |
|
|
case Opcode.map.OP_EQUAL: |
|
|
case OP_EQUALVERIFY: |
|
|
case Opcode.map.OP_EQUALVERIFY: |
|
|
//case OP_NOTEQUAL: // use OP_NUMNOTEQUAL
|
|
|
//case OP_NOTEQUAL: // use OP_NUMNOTEQUAL
|
|
|
// (x1 x2 - bool)
|
|
|
// (x1 x2 - bool)
|
|
|
var v1 = this.stackTop(2); |
|
|
var v1 = this.stackTop(2); |
|
@ -412,7 +407,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
this.stack.push(new Buffer([value ? 1 : 0])); |
|
|
this.stack.push(new Buffer([value ? 1 : 0])); |
|
|
if (opcode === OP_EQUALVERIFY) { |
|
|
if (opcode === Opcode.map.OP_EQUALVERIFY) { |
|
|
if (value) { |
|
|
if (value) { |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
} else { |
|
|
} else { |
|
@ -421,136 +416,136 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_1ADD: |
|
|
case Opcode.map.OP_1ADD: |
|
|
case OP_1SUB: |
|
|
case Opcode.map.OP_1SUB: |
|
|
case OP_2MUL: |
|
|
case Opcode.map.OP_2MUL: |
|
|
case OP_2DIV: |
|
|
case Opcode.map.OP_2DIV: |
|
|
case OP_NEGATE: |
|
|
case Opcode.map.OP_NEGATE: |
|
|
case OP_ABS: |
|
|
case Opcode.map.OP_ABS: |
|
|
case OP_NOT: |
|
|
case Opcode.map.OP_NOT: |
|
|
case OP_0NOTEQUAL: |
|
|
case Opcode.map.OP_0NOTEQUAL: |
|
|
// (in -- out)
|
|
|
// (in -- out)
|
|
|
var num = bufferSMToInt(this.stackTop()); |
|
|
var num = bufferSMToInt(this.stackTop()); |
|
|
switch (opcode) { |
|
|
switch (opcode) { |
|
|
case OP_1ADD: |
|
|
case Opcode.map.OP_1ADD: |
|
|
num = num.add(bignum(1)); |
|
|
num = num.add(bignum(1)); |
|
|
break; |
|
|
break; |
|
|
case OP_1SUB: |
|
|
case Opcode.map.OP_1SUB: |
|
|
num = num.sub(bignum(1)); |
|
|
num = num.sub(bignum(1)); |
|
|
break; |
|
|
break; |
|
|
case OP_2MUL: |
|
|
case Opcode.map.OP_2MUL: |
|
|
num = num.mul(bignum(2)); |
|
|
num = num.mul(bignum(2)); |
|
|
break; |
|
|
break; |
|
|
case OP_2DIV: |
|
|
case Opcode.map.OP_2DIV: |
|
|
num = num.div(bignum(2)); |
|
|
num = num.div(bignum(2)); |
|
|
break; |
|
|
break; |
|
|
case OP_NEGATE: |
|
|
case Opcode.map.OP_NEGATE: |
|
|
num = num.neg(); |
|
|
num = num.neg(); |
|
|
break; |
|
|
break; |
|
|
case OP_ABS: |
|
|
case Opcode.map.OP_ABS: |
|
|
num = num.abs(); |
|
|
num = num.abs(); |
|
|
break; |
|
|
break; |
|
|
case OP_NOT: |
|
|
case Opcode.map.OP_NOT: |
|
|
num = bignum(num.cmp(0) == 0 ? 1 : 0); |
|
|
num = bignum(num.cmp(0) == 0 ? 1 : 0); |
|
|
break; |
|
|
break; |
|
|
case OP_0NOTEQUAL: |
|
|
case Opcode.map.OP_0NOTEQUAL: |
|
|
num = bignum(num.cmp(0) == 0 ? 0 : 1); |
|
|
num = bignum(num.cmp(0) == 0 ? 0 : 1); |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
this.stack[this.stack.length - 1] = intToBufferSM(num); |
|
|
this.stack[this.stack.length - 1] = intToBufferSM(num); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_ADD: |
|
|
case Opcode.map.OP_ADD: |
|
|
case OP_SUB: |
|
|
case Opcode.map.OP_SUB: |
|
|
case OP_MUL: |
|
|
case Opcode.map.OP_MUL: |
|
|
case OP_DIV: |
|
|
case Opcode.map.OP_DIV: |
|
|
case OP_MOD: |
|
|
case Opcode.map.OP_MOD: |
|
|
case OP_LSHIFT: |
|
|
case Opcode.map.OP_LSHIFT: |
|
|
case OP_RSHIFT: |
|
|
case Opcode.map.OP_RSHIFT: |
|
|
case OP_BOOLAND: |
|
|
case Opcode.map.OP_BOOLAND: |
|
|
case OP_BOOLOR: |
|
|
case Opcode.map.OP_BOOLOR: |
|
|
case OP_NUMEQUAL: |
|
|
case Opcode.map.OP_NUMEQUAL: |
|
|
case OP_NUMEQUALVERIFY: |
|
|
case Opcode.map.OP_NUMEQUALVERIFY: |
|
|
case OP_NUMNOTEQUAL: |
|
|
case Opcode.map.OP_NUMNOTEQUAL: |
|
|
case OP_LESSTHAN: |
|
|
case Opcode.map.OP_LESSTHAN: |
|
|
case OP_GREATERTHAN: |
|
|
case Opcode.map.OP_GREATERTHAN: |
|
|
case OP_LESSTHANOREQUAL: |
|
|
case Opcode.map.OP_LESSTHANOREQUAL: |
|
|
case OP_GREATERTHANOREQUAL: |
|
|
case Opcode.map.OP_GREATERTHANOREQUAL: |
|
|
case OP_MIN: |
|
|
case Opcode.map.OP_MIN: |
|
|
case OP_MAX: |
|
|
case Opcode.map.OP_MAX: |
|
|
// (x1 x2 -- out)
|
|
|
// (x1 x2 -- out)
|
|
|
var v1 = bufferSMToInt(this.stackTop(2)); |
|
|
var v1 = bufferSMToInt(this.stackTop(2)); |
|
|
var v2 = bufferSMToInt(this.stackTop(1)); |
|
|
var v2 = bufferSMToInt(this.stackTop(1)); |
|
|
var num; |
|
|
var num; |
|
|
switch (opcode) { |
|
|
switch (opcode) { |
|
|
case OP_ADD: |
|
|
case Opcode.map.OP_ADD: |
|
|
num = v1.add(v2); |
|
|
num = v1.add(v2); |
|
|
break; |
|
|
break; |
|
|
case OP_SUB: |
|
|
case Opcode.map.OP_SUB: |
|
|
num = v1.sub(v2); |
|
|
num = v1.sub(v2); |
|
|
break; |
|
|
break; |
|
|
case OP_MUL: |
|
|
case Opcode.map.OP_MUL: |
|
|
num = v1.mul(v2); |
|
|
num = v1.mul(v2); |
|
|
break; |
|
|
break; |
|
|
case OP_DIV: |
|
|
case Opcode.map.OP_DIV: |
|
|
num = v1.div(v2); |
|
|
num = v1.div(v2); |
|
|
break; |
|
|
break; |
|
|
case OP_MOD: |
|
|
case Opcode.map.OP_MOD: |
|
|
num = v1.mod(v2); |
|
|
num = v1.mod(v2); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_LSHIFT: |
|
|
case Opcode.map.OP_LSHIFT: |
|
|
if (v2.cmp(0) < 0 || v2.cmp(2048) > 0) { |
|
|
if (v2.cmp(0) < 0 || v2.cmp(2048) > 0) { |
|
|
throw new Error("OP_LSHIFT parameter out of bounds"); |
|
|
throw new Error("OP_LSHIFT parameter out of bounds"); |
|
|
} |
|
|
} |
|
|
num = v1.shiftLeft(v2); |
|
|
num = v1.shiftLeft(v2); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_RSHIFT: |
|
|
case Opcode.map.OP_RSHIFT: |
|
|
if (v2.cmp(0) < 0 || v2.cmp(2048) > 0) { |
|
|
if (v2.cmp(0) < 0 || v2.cmp(2048) > 0) { |
|
|
throw new Error("OP_RSHIFT parameter out of bounds"); |
|
|
throw new Error("OP_RSHIFT parameter out of bounds"); |
|
|
} |
|
|
} |
|
|
num = v1.shiftRight(v2); |
|
|
num = v1.shiftRight(v2); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_BOOLAND: |
|
|
case Opcode.map.OP_BOOLAND: |
|
|
num = bignum((v1.cmp(0) != 0 && v2.cmp(0) != 0) ? 1 : 0); |
|
|
num = bignum((v1.cmp(0) != 0 && v2.cmp(0) != 0) ? 1 : 0); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_BOOLOR: |
|
|
case Opcode.map.OP_BOOLOR: |
|
|
num = bignum((v1.cmp(0) != 0 || v2.cmp(0) != 0) ? 1 : 0); |
|
|
num = bignum((v1.cmp(0) != 0 || v2.cmp(0) != 0) ? 1 : 0); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_NUMEQUAL: |
|
|
case Opcode.map.OP_NUMEQUAL: |
|
|
case OP_NUMEQUALVERIFY: |
|
|
case Opcode.map.OP_NUMEQUALVERIFY: |
|
|
num = bignum(v1.cmp(v2) == 0 ? 1 : 0); |
|
|
num = bignum(v1.cmp(v2) == 0 ? 1 : 0); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_NUMNOTEQUAL: |
|
|
case Opcode.map.OP_NUMNOTEQUAL: |
|
|
; |
|
|
; |
|
|
num = bignum(v1.cmp(v2) != 0 ? 1 : 0); |
|
|
num = bignum(v1.cmp(v2) != 0 ? 1 : 0); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_LESSTHAN: |
|
|
case Opcode.map.OP_LESSTHAN: |
|
|
num = bignum(v1.lt(v2) ? 1 : 0); |
|
|
num = bignum(v1.lt(v2) ? 1 : 0); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_GREATERTHAN: |
|
|
case Opcode.map.OP_GREATERTHAN: |
|
|
num = bignum(v1.gt(v2) ? 1 : 0); |
|
|
num = bignum(v1.gt(v2) ? 1 : 0); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_LESSTHANOREQUAL: |
|
|
case Opcode.map.OP_LESSTHANOREQUAL: |
|
|
num = bignum(v1.gt(v2) ? 0 : 1); |
|
|
num = bignum(v1.gt(v2) ? 0 : 1); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_GREATERTHANOREQUAL: |
|
|
case Opcode.map.OP_GREATERTHANOREQUAL: |
|
|
num = bignum(v1.lt(v2) ? 0 : 1); |
|
|
num = bignum(v1.lt(v2) ? 0 : 1); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_MIN: |
|
|
case Opcode.map.OP_MIN: |
|
|
num = (v1.lt(v2) ? v1 : v2); |
|
|
num = (v1.lt(v2) ? v1 : v2); |
|
|
break; |
|
|
break; |
|
|
case OP_MAX: |
|
|
case Opcode.map.OP_MAX: |
|
|
num = (v1.gt(v2) ? v1 : v2); |
|
|
num = (v1.gt(v2) ? v1 : v2); |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
@ -558,7 +553,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
this.stack.push(intToBufferSM(num)); |
|
|
this.stack.push(intToBufferSM(num)); |
|
|
|
|
|
|
|
|
if (opcode === OP_NUMEQUALVERIFY) { |
|
|
if (opcode === Opcode.map.OP_NUMEQUALVERIFY) { |
|
|
if (castBool(this.stackTop())) { |
|
|
if (castBool(this.stackTop())) { |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
} else { |
|
|
} else { |
|
@ -567,7 +562,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_WITHIN: |
|
|
case Opcode.map.OP_WITHIN: |
|
|
// (x min max -- out)
|
|
|
// (x min max -- out)
|
|
|
var v1 = bufferSMToInt(this.stackTop(3)); |
|
|
var v1 = bufferSMToInt(this.stackTop(3)); |
|
|
var v2 = bufferSMToInt(this.stackTop(2)); |
|
|
var v2 = bufferSMToInt(this.stackTop(2)); |
|
@ -579,35 +574,35 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stack.push(intToBufferSM(value ? 1 : 0)); |
|
|
this.stack.push(intToBufferSM(value ? 1 : 0)); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_RIPEMD160: |
|
|
case Opcode.map.OP_RIPEMD160: |
|
|
case OP_SHA1: |
|
|
case Opcode.map.OP_SHA1: |
|
|
case OP_SHA256: |
|
|
case Opcode.map.OP_SHA256: |
|
|
case OP_HASH160: |
|
|
case Opcode.map.OP_HASH160: |
|
|
case OP_HASH256: |
|
|
case Opcode.map.OP_HASH256: |
|
|
// (in -- hash)
|
|
|
// (in -- hash)
|
|
|
var value = this.stackPop(); |
|
|
var value = this.stackPop(); |
|
|
var hash; |
|
|
var hash; |
|
|
if (opcode === OP_RIPEMD160) { |
|
|
if (opcode === Opcode.map.OP_RIPEMD160) { |
|
|
hash = Util.ripe160(value); |
|
|
hash = Util.ripe160(value); |
|
|
} else if (opcode === OP_SHA1) { |
|
|
} else if (opcode === Opcode.map.OP_SHA1) { |
|
|
hash = Util.sha1(value); |
|
|
hash = Util.sha1(value); |
|
|
} else if (opcode === OP_SHA256) { |
|
|
} else if (opcode === Opcode.map.OP_SHA256) { |
|
|
hash = Util.sha256(value); |
|
|
hash = Util.sha256(value); |
|
|
} else if (opcode === OP_HASH160) { |
|
|
} else if (opcode === Opcode.map.OP_HASH160) { |
|
|
hash = Util.sha256ripe160(value); |
|
|
hash = Util.sha256ripe160(value); |
|
|
} else if (opcode === OP_HASH256) { |
|
|
} else if (opcode === Opcode.map.OP_HASH256) { |
|
|
hash = Util.twoSha256(value); |
|
|
hash = Util.twoSha256(value); |
|
|
} |
|
|
} |
|
|
this.stack.push(hash); |
|
|
this.stack.push(hash); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_CODESEPARATOR: |
|
|
case Opcode.map.OP_CODESEPARATOR: |
|
|
// Hash starts after the code separator
|
|
|
// Hash starts after the code separator
|
|
|
hashStart = pc; |
|
|
hashStart = pc; |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case OP_CHECKSIG: |
|
|
case Opcode.map.OP_CHECKSIG: |
|
|
case OP_CHECKSIGVERIFY: |
|
|
case Opcode.map.OP_CHECKSIGVERIFY: |
|
|
// (sig pubkey -- bool)
|
|
|
// (sig pubkey -- bool)
|
|
|
var sig = this.stackTop(2); |
|
|
var sig = this.stackTop(2); |
|
|
var pubkey = this.stackTop(1); |
|
|
var pubkey = this.stackTop(1); |
|
@ -640,7 +635,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
this.stack.push(new Buffer([success ? 1 : 0])); |
|
|
this.stack.push(new Buffer([success ? 1 : 0])); |
|
|
if (opcode === OP_CHECKSIGVERIFY) { |
|
|
if (opcode === Opcode.map.OP_CHECKSIGVERIFY) { |
|
|
if (success) { |
|
|
if (success) { |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
} else { |
|
|
} else { |
|
@ -656,8 +651,8 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
// the next opcode from being executed.
|
|
|
// the next opcode from being executed.
|
|
|
return; |
|
|
return; |
|
|
|
|
|
|
|
|
case OP_CHECKMULTISIG: |
|
|
case Opcode.map.OP_CHECKMULTISIG: |
|
|
case OP_CHECKMULTISIGVERIFY: |
|
|
case Opcode.map.OP_CHECKMULTISIGVERIFY: |
|
|
// ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)
|
|
|
// ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)
|
|
|
var keysCount = castInt(this.stackPop()); |
|
|
var keysCount = castInt(this.stackPop()); |
|
|
if (keysCount < 0 || keysCount > 20) { |
|
|
if (keysCount < 0 || keysCount > 20) { |
|
@ -729,7 +724,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, |
|
|
}.bind(this)); |
|
|
}.bind(this)); |
|
|
} else { |
|
|
} else { |
|
|
this.stack.push(new Buffer([success ? 1 : 0])); |
|
|
this.stack.push(new Buffer([success ? 1 : 0])); |
|
|
if (opcode === OP_CHECKMULTISIGVERIFY) { |
|
|
if (opcode === Opcode.map.OP_CHECKMULTISIGVERIFY) { |
|
|
if (success) { |
|
|
if (success) { |
|
|
this.stackPop(); |
|
|
this.stackPop(); |
|
|
} else { |
|
|
} else { |
|
|