Merge pull request #321 from ryanxcharles/feature/remove-eval

Remove eval
This commit is contained in:
Ryan X. Charles 2014-05-05 11:00:49 -04:00
commit b7d41ab246
3 changed files with 201 additions and 215 deletions

View File

@ -4,11 +4,6 @@ var log = imports.log || require('../util/log');
var Opcode = imports.Opcode || require('./Opcode'); var Opcode = imports.Opcode || require('./Opcode');
var buffertools = imports.buffertools || require('buffertools'); var buffertools = imports.buffertools || require('buffertools');
// Make opcodes available as pseudo-constants
for (var i in Opcode.map) {
eval(i + " = " + Opcode.map[i] + ";");
}
var util = imports.util || require('../util/util'); var util = imports.util || require('../util/util');
var Parser = imports.Parser || require('../util/BinaryParser'); var Parser = imports.Parser || require('../util/BinaryParser');
var Put = imports.Put || require('bufferput'); var Put = imports.Put || require('bufferput');
@ -51,18 +46,18 @@ Script.prototype.parse = function() {
var opcode = parser.word8(); var opcode = parser.word8();
var len, chunk; var len, chunk;
if (opcode > 0 && opcode < OP_PUSHDATA1) { if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) {
// Read some bytes of data, opcode value is the length of data // Read some bytes of data, opcode value is the length of data
this.chunks.push(parser.buffer(opcode)); this.chunks.push(parser.buffer(opcode));
} else if (opcode === OP_PUSHDATA1) { } else if (opcode === Opcode.map.OP_PUSHDATA1) {
len = parser.word8(); len = parser.word8();
chunk = parser.buffer(len); chunk = parser.buffer(len);
this.chunks.push(chunk); this.chunks.push(chunk);
} else if (opcode === OP_PUSHDATA2) { } else if (opcode === Opcode.map.OP_PUSHDATA2) {
len = parser.word16le(); len = parser.word16le();
chunk = parser.buffer(len); chunk = parser.buffer(len);
this.chunks.push(chunk); this.chunks.push(chunk);
} else if (opcode === OP_PUSHDATA4) { } else if (opcode === Opcode.map.OP_PUSHDATA4) {
len = parser.word32le(); len = parser.word32le();
chunk = parser.buffer(len); chunk = parser.buffer(len);
this.chunks.push(chunk); this.chunks.push(chunk);
@ -75,7 +70,7 @@ Script.prototype.parse = function() {
Script.prototype.isPushOnly = function() { Script.prototype.isPushOnly = function() {
for (var i = 0; i < this.chunks.length; i++) { for (var i = 0; i < this.chunks.length; i++) {
var op = this.chunks[i]; var op = this.chunks[i];
if (!Buffer.isBuffer(op) && op > OP_16) { if (!Buffer.isBuffer(op) && op > Opcode.map.OP_16) {
return false; return false;
} }
} }
@ -85,38 +80,38 @@ Script.prototype.isPushOnly = function() {
Script.prototype.isP2SH = function() { Script.prototype.isP2SH = function() {
return (this.chunks.length == 3 && return (this.chunks.length == 3 &&
this.chunks[0] == OP_HASH160 && this.chunks[0] == Opcode.map.OP_HASH160 &&
Buffer.isBuffer(this.chunks[1]) && Buffer.isBuffer(this.chunks[1]) &&
this.chunks[1].length == 20 && this.chunks[1].length == 20 &&
this.chunks[2] == OP_EQUAL); this.chunks[2] == Opcode.map.OP_EQUAL);
}; };
Script.prototype.isPubkey = function() { Script.prototype.isPubkey = function() {
return (this.chunks.length == 2 && return (this.chunks.length == 2 &&
Buffer.isBuffer(this.chunks[0]) && Buffer.isBuffer(this.chunks[0]) &&
this.chunks[1] == OP_CHECKSIG); this.chunks[1] == Opcode.map.OP_CHECKSIG);
}; };
Script.prototype.isPubkeyHash = function() { Script.prototype.isPubkeyHash = function() {
return (this.chunks.length == 5 && return (this.chunks.length == 5 &&
this.chunks[0] == OP_DUP && this.chunks[0] == Opcode.map.OP_DUP &&
this.chunks[1] == OP_HASH160 && this.chunks[1] == Opcode.map.OP_HASH160 &&
Buffer.isBuffer(this.chunks[2]) && Buffer.isBuffer(this.chunks[2]) &&
this.chunks[2].length == 20 && this.chunks[2].length == 20 &&
this.chunks[3] == OP_EQUALVERIFY && this.chunks[3] == Opcode.map.OP_EQUALVERIFY &&
this.chunks[4] == OP_CHECKSIG); this.chunks[4] == Opcode.map.OP_CHECKSIG);
}; };
function isSmallIntOp(opcode) { function isSmallIntOp(opcode) {
return ((opcode == OP_0) || return ((opcode == Opcode.map.OP_0) ||
((opcode >= OP_1) && (opcode <= OP_16))); ((opcode >= Opcode.map.OP_1) && (opcode <= Opcode.map.OP_16)));
}; };
Script.prototype.isMultiSig = function() { Script.prototype.isMultiSig = function() {
return (this.chunks.length > 3 && return (this.chunks.length > 3 &&
isSmallIntOp(this.chunks[0]) && isSmallIntOp(this.chunks[0]) &&
isSmallIntOp(this.chunks[this.chunks.length - 2]) && isSmallIntOp(this.chunks[this.chunks.length - 2]) &&
this.chunks[this.chunks.length - 1] == OP_CHECKMULTISIG); this.chunks[this.chunks.length - 1] == Opcode.map.OP_CHECKMULTISIG);
}; };
Script.prototype.isP2shScriptSig = function() { Script.prototype.isP2shScriptSig = function() {
@ -384,13 +379,13 @@ Script.prototype.writeN = function(n) {
throw new Error("writeN: out of range value " + n); throw new Error("writeN: out of range value " + n);
if (n == 0) if (n == 0)
this.writeOp(OP_0); this.writeOp(Opcode.map.OP_0);
else else
this.writeOp(OP_1 + n - 1); this.writeOp(Opcode.map.OP_1 + n - 1);
}; };
function prefixSize(data_length) { function prefixSize(data_length) {
if (data_length < OP_PUSHDATA1) { if (data_length < Opcode.map.OP_PUSHDATA1) {
return 1; return 1;
} else if (data_length <= 0xff) { } else if (data_length <= 0xff) {
return 1 + 1; return 1 + 1;
@ -403,20 +398,20 @@ function prefixSize(data_length) {
function encodeLen(data_length) { function encodeLen(data_length) {
var buf = undefined; var buf = undefined;
if (data_length < OP_PUSHDATA1) { if (data_length < Opcode.map.OP_PUSHDATA1) {
buf = new Buffer(1); buf = new Buffer(1);
buf.writeUInt8(data_length, 0); buf.writeUInt8(data_length, 0);
} else if (data_length <= 0xff) { } else if (data_length <= 0xff) {
buf = new Buffer(1 + 1); buf = new Buffer(1 + 1);
buf.writeUInt8(OP_PUSHDATA1, 0); buf.writeUInt8(Opcode.map.OP_PUSHDATA1, 0);
buf.writeUInt8(data_length, 1); buf.writeUInt8(data_length, 1);
} else if (data_length <= 0xffff) { } else if (data_length <= 0xffff) {
buf = new Buffer(1 + 2); buf = new Buffer(1 + 2);
buf.writeUInt8(OP_PUSHDATA2, 0); buf.writeUInt8(Opcode.map.OP_PUSHDATA2, 0);
buf.writeUInt16LE(data_length, 1); buf.writeUInt16LE(data_length, 1);
} else { } else {
buf = new Buffer(1 + 4); buf = new Buffer(1 + 4);
buf.writeUInt8(OP_PUSHDATA4, 0); buf.writeUInt8(Opcode.map.OP_PUSHDATA4, 0);
buf.writeUInt32LE(data_length, 1); buf.writeUInt32LE(data_length, 1);
} }
@ -469,7 +464,7 @@ Script.prototype.findAndDelete = function(chunk) {
Script.createPubKeyOut = function(pubkey) { Script.createPubKeyOut = function(pubkey) {
var script = new Script(); var script = new Script();
script.writeBytes(pubkey); script.writeBytes(pubkey);
script.writeOp(OP_CHECKSIG); script.writeOp(Opcode.map.OP_CHECKSIG);
return script; return script;
}; };
@ -478,11 +473,11 @@ Script.createPubKeyOut = function(pubkey) {
*/ */
Script.createPubKeyHashOut = function(pubKeyHash) { Script.createPubKeyHashOut = function(pubKeyHash) {
var script = new Script(); var script = new Script();
script.writeOp(OP_DUP); script.writeOp(Opcode.map.OP_DUP);
script.writeOp(OP_HASH160); script.writeOp(Opcode.map.OP_HASH160);
script.writeBytes(pubKeyHash); script.writeBytes(pubKeyHash);
script.writeOp(OP_EQUALVERIFY); script.writeOp(Opcode.map.OP_EQUALVERIFY);
script.writeOp(OP_CHECKSIG); script.writeOp(Opcode.map.OP_CHECKSIG);
return script; return script;
}; };
@ -514,15 +509,15 @@ Script.createMultisig = function(n_required, inKeys, opts) {
script.writeBytes(key); script.writeBytes(key);
}); });
script.writeN(keys.length); script.writeN(keys.length);
script.writeOp(OP_CHECKMULTISIG); script.writeOp(Opcode.map.OP_CHECKMULTISIG);
return script; return script;
}; };
Script.createP2SH = function(scriptHash) { Script.createP2SH = function(scriptHash) {
var script = new Script(); var script = new Script();
script.writeOp(OP_HASH160); script.writeOp(Opcode.map.OP_HASH160);
script.writeBytes(scriptHash); script.writeBytes(scriptHash);
script.writeOp(OP_EQUAL); script.writeOp(Opcode.map.OP_EQUAL);
return script; return script;
}; };
@ -626,16 +621,16 @@ Script.chunksToBuffer = function(chunks) {
for (var i = 0, l = chunks.length; i < l; i++) { for (var i = 0, l = chunks.length; i < l; i++) {
var data = chunks[i]; var data = chunks[i];
if (Buffer.isBuffer(data)) { if (Buffer.isBuffer(data)) {
if (data.length < OP_PUSHDATA1) { if (data.length < Opcode.map.OP_PUSHDATA1) {
buf.word8(data.length); buf.word8(data.length);
} else if (data.length <= 0xff) { } else if (data.length <= 0xff) {
buf.word8(OP_PUSHDATA1); buf.word8(Opcode.map.OP_PUSHDATA1);
buf.word8(data.length); buf.word8(data.length);
} else if (data.length <= 0xffff) { } else if (data.length <= 0xffff) {
buf.word8(OP_PUSHDATA2); buf.word8(Opcode.map.OP_PUSHDATA2);
buf.word16le(data.length); buf.word16le(data.length);
} else { } else {
buf.word8(OP_PUSHDATA4); buf.word8(Opcode.map.OP_PUSHDATA4);
buf.word32le(data.length); buf.word32le(data.length);
} }
buf.put(data); buf.put(data);

View File

@ -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 {

View File

@ -21,16 +21,12 @@ describe('Opcode', function() {
should.exist(oc); should.exist(oc);
}); });
it('should be able to create some constants', function() { it('should be able to create some constants', function() {
// TODO: test works in node but not in browser should.exist(Opcode.map.OP_VER);
for (var i in Opcode.map) { should.exist(Opcode.map.OP_HASH160);
eval('var ' + i + ' = ' + Opcode.map[i] + ';'); should.exist(Opcode.map.OP_RETURN);
} should.exist(Opcode.map.OP_EQUALVERIFY);
should.exist(OP_VER); should.exist(Opcode.map.OP_CHECKSIG);
should.exist(OP_HASH160); should.exist(Opcode.map.OP_CHECKMULTISIG);
should.exist(OP_RETURN);
should.exist(OP_EQUALVERIFY);
should.exist(OP_CHECKSIG);
should.exist(OP_CHECKMULTISIG);
}); });
it('#asList should work', function() { it('#asList should work', function() {
var list = Opcode.asList(); var list = Opcode.asList();