script: refactor verification and execution.

This commit is contained in:
Christopher Jeffrey 2016-10-02 07:06:55 -07:00
parent c13852f81e
commit c43df089f6
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 442 additions and 543 deletions

View File

@ -301,11 +301,13 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
var alt = []; var alt = [];
var state = []; var state = [];
var negate = 0; var negate = 0;
var op, code, data, val, v1, v2, v3; var op, code, data
var n, n1, n2, n3; var val, v1, v2, v3;
var res, key, sig, type, subscript, hash; var num, n1, n2, n3;
var i, j, m, ikey, ikey2, isig; var m, n, key, sig;
var locktime; var type, subscript, hash;
var ikey, isig, ikey2;
var i, j, res, locktime;
if (flags == null) if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS; flags = constants.flags.STANDARD_VERIFY_FLAGS;
@ -324,6 +326,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
if (data) { if (data) {
if (data.length > constants.script.MAX_PUSH) if (data.length > constants.script.MAX_PUSH)
throw new ScriptError('PUSH_SIZE', op, ip); throw new ScriptError('PUSH_SIZE', op, ip);
// Note that minimaldata is not checked // Note that minimaldata is not checked
// on unexecuted branches of code. // on unexecuted branches of code.
if (negate === 0) { if (negate === 0) {
@ -331,6 +334,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
throw new ScriptError('MINIMALDATA', op, ip); throw new ScriptError('MINIMALDATA', op, ip);
stack.push(data); stack.push(data);
} }
continue; continue;
} }
@ -371,7 +375,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
if (stack.length < 1) if (stack.length < 1)
throw new ScriptError('UNBALANCED_CONDITIONAL', op, ip); throw new ScriptError('UNBALANCED_CONDITIONAL', op, ip);
val = stack.pop(); val = stack.top(-1);
if (version === 1 && (flags & constants.flags.VERIFY_MINIMALIF)) { if (version === 1 && (flags & constants.flags.VERIFY_MINIMALIF)) {
if (val.length > 1) if (val.length > 1)
@ -385,6 +389,8 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
if (op === opcodes.OP_NOTIF) if (op === opcodes.OP_NOTIF)
val = !val; val = !val;
stack.pop();
} }
state.push(val); state.push(val);
@ -531,104 +537,209 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
case opcodes.OP_VERIFY: { case opcodes.OP_VERIFY: {
if (stack.length === 0) if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
if (!Script.bool(stack.pop()))
if (!Script.bool(stack.top(-1)))
throw new ScriptError('VERIFY', op, ip); throw new ScriptError('VERIFY', op, ip);
stack.pop();
break; break;
} }
case opcodes.OP_RETURN: { case opcodes.OP_RETURN: {
throw new ScriptError('OP_RETURN', op, ip); throw new ScriptError('OP_RETURN', op, ip);
} }
case opcodes.OP_TOALTSTACK: { case opcodes.OP_TOALTSTACK: {
stack.toalt(alt); if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
alt.push(stack.pop());
break; break;
} }
case opcodes.OP_FROMALTSTACK: { case opcodes.OP_FROMALTSTACK: {
stack.fromalt(alt); if (alt.length === 0)
throw new ScriptError('INVALID_ALTSTACK_OPERATION', op, ip);
stack.push(alt.pop());
break; break;
} }
case opcodes.OP_2DROP: { case opcodes.OP_2DROP: {
stack.drop2(); if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.pop();
stack.pop();
break; break;
} }
case opcodes.OP_2DUP: { case opcodes.OP_2DUP: {
stack.dup2(); if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
v1 = stack.top(-2);
v2 = stack.top(-1);
stack.push(v1);
stack.push(v2);
break; break;
} }
case opcodes.OP_3DUP: { case opcodes.OP_3DUP: {
stack.dup3(); if (stack.length < 3)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
v1 = stack.top(-3);
v2 = stack.top(-2);
v3 = stack.top(-1);
stack.push(v1);
stack.push(v2);
stack.push(v3);
break; break;
} }
case opcodes.OP_2OVER: { case opcodes.OP_2OVER: {
stack.over2(); if (stack.length < 4)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
v1 = stack.top(-4);
v2 = stack.top(-3);
stack.push(v1);
stack.push(v2);
break; break;
} }
case opcodes.OP_2ROT: { case opcodes.OP_2ROT: {
stack.rot2(); if (stack.length < 6)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
v1 = stack.top(-6);
v2 = stack.top(-5);
stack.erase(-6, -4);
stack.push(v1);
stack.push(v2);
break; break;
} }
case opcodes.OP_2SWAP: { case opcodes.OP_2SWAP: {
stack.swap2(); if (stack.length < 4)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.swap(-4, -2);
stack.swap(-3, -1);
break; break;
} }
case opcodes.OP_IFDUP: { case opcodes.OP_IFDUP: {
stack.ifdup(); if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
val = stack.top(-1);
if (Script.bool(val))
stack.push(val);
break; break;
} }
case opcodes.OP_DEPTH: { case opcodes.OP_DEPTH: {
stack.depth(); stack.push(Script.array(stack.length));
break; break;
} }
case opcodes.OP_DROP: { case opcodes.OP_DROP: {
stack.drop(); if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.pop();
break; break;
} }
case opcodes.OP_DUP: { case opcodes.OP_DUP: {
stack.dup(); if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.push(stack.top(-1));
break; break;
} }
case opcodes.OP_NIP: { case opcodes.OP_NIP: {
stack.nip(); if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.remove(-2);
break; break;
} }
case opcodes.OP_OVER: { case opcodes.OP_OVER: {
stack.over(); if (stack.length < 2)
break; throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
}
case opcodes.OP_PICK: { stack.push(stack.top(-2));
stack.pick(flags);
break; break;
} }
case opcodes.OP_PICK:
case opcodes.OP_ROLL: { case opcodes.OP_ROLL: {
stack.roll(flags); if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op);
num = Script.num(stack.top(-1), flags).toNumber();
stack.pop();
if (num < 0 || num >= stack.length)
throw new ScriptError('INVALID_STACK_OPERATION', op);
val = stack.top(-num - 1);
if (op === opcodes.OP_ROLL)
stack.remove(-num - 1);
stack.push(val);
break; break;
} }
case opcodes.OP_ROT: { case opcodes.OP_ROT: {
stack.rot(); if (stack.length < 3)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.swap(-3, -2);
stack.swap(-2, -1);
break; break;
} }
case opcodes.OP_SWAP: { case opcodes.OP_SWAP: {
stack.swap(); if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.swap(-2, -1);
break; break;
} }
case opcodes.OP_TUCK: { case opcodes.OP_TUCK: {
stack.tuck(); if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.insert(-2, stack.top(-1));
break; break;
} }
case opcodes.OP_SIZE: { case opcodes.OP_SIZE: {
stack.size(); if (stack.length < 1)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.push(Script.array(stack.top(-1).length));
break; break;
} }
case opcodes.OP_EQUAL: case opcodes.OP_EQUAL:
case opcodes.OP_EQUALVERIFY: { case opcodes.OP_EQUALVERIFY: {
// case opcodes.OP_NOTEQUAL: // use OP_NUMNOTEQUAL
if (stack.length < 2) if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
res = utils.equal(stack.pop(), stack.pop());
v1 = stack.top(-2);
v2 = stack.top(-1);
res = utils.equal(v1, v2);
// if (op == opcodes.OP_NOTEQUAL)
// res = !res;
stack.pop();
stack.pop();
stack.push(res ? STACK_TRUE : STACK_FALSE);
if (op === opcodes.OP_EQUALVERIFY) { if (op === opcodes.OP_EQUALVERIFY) {
if (!res) if (!res)
throw new ScriptError('EQUALVERIFY', op, ip); throw new ScriptError('EQUALVERIFY', op, ip);
} else { stack.pop();
stack.push(res ? STACK_TRUE : STACK_FALSE);
} }
break; break;
} }
case opcodes.OP_1ADD: case opcodes.OP_1ADD:
@ -641,39 +752,45 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
case opcodes.OP_0NOTEQUAL: { case opcodes.OP_0NOTEQUAL: {
if (stack.length < 1) if (stack.length < 1)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
n = Script.num(stack.pop(), flags);
num = Script.num(stack.top(-1), flags);
switch (op) { switch (op) {
case opcodes.OP_1ADD: case opcodes.OP_1ADD:
n.iaddn(1); num.iaddn(1);
break; break;
case opcodes.OP_1SUB: case opcodes.OP_1SUB:
n.isubn(1); num.isubn(1);
break; break;
case opcodes.OP_2MUL: case opcodes.OP_2MUL:
n.iushln(1); num.iushln(1);
break; break;
case opcodes.OP_2DIV: case opcodes.OP_2DIV:
n.iushrn(1); num.iushrn(1);
break; break;
case opcodes.OP_NEGATE: case opcodes.OP_NEGATE:
n.ineg(); num.ineg();
break; break;
case opcodes.OP_ABS: case opcodes.OP_ABS:
if (n.cmpn(0) < 0) if (num.cmpn(0) < 0)
n.ineg(); num.ineg();
break; break;
case opcodes.OP_NOT: case opcodes.OP_NOT:
n = n.cmpn(0) === 0; num = num.cmpn(0) === 0;
num = new bn(num ? 1 : 0);
break; break;
case opcodes.OP_0NOTEQUAL: case opcodes.OP_0NOTEQUAL:
n = n.cmpn(0) !== 0; num = num.cmpn(0) !== 0;
num = new bn(num ? 1 : 0);
break; break;
default: default:
assert(false, 'Fatal script error.'); assert(false, 'Fatal script error.');
break;
} }
if (typeof n === 'boolean')
n = new bn(n ? 1 : 0); stack.pop();
stack.push(Script.array(n)); stack.push(Script.array(num));
break; break;
} }
case opcodes.OP_ADD: case opcodes.OP_ADD:
@ -694,142 +811,148 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
case opcodes.OP_GREATERTHANOREQUAL: case opcodes.OP_GREATERTHANOREQUAL:
case opcodes.OP_MIN: case opcodes.OP_MIN:
case opcodes.OP_MAX: { case opcodes.OP_MAX: {
switch (op) {
case opcodes.OP_ADD:
case opcodes.OP_SUB:
case opcodes.OP_MUL:
case opcodes.OP_DIV:
case opcodes.OP_MOD:
case opcodes.OP_LSHIFT:
case opcodes.OP_RSHIFT:
case opcodes.OP_BOOLAND:
case opcodes.OP_BOOLOR:
case opcodes.OP_NUMEQUAL:
case opcodes.OP_NUMEQUALVERIFY:
case opcodes.OP_NUMNOTEQUAL:
case opcodes.OP_LESSTHAN:
case opcodes.OP_GREATERTHAN:
case opcodes.OP_LESSTHANOREQUAL:
case opcodes.OP_GREATERTHANOREQUAL:
case opcodes.OP_MIN:
case opcodes.OP_MAX:
if (stack.length < 2) if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
n2 = Script.num(stack.pop(), flags);
n1 = Script.num(stack.pop(), flags); n1 = Script.num(stack.top(-2), flags);
n = new bn(0); n2 = Script.num(stack.top(-1), flags);
switch (op) { switch (op) {
case opcodes.OP_ADD: case opcodes.OP_ADD:
n = n1.add(n2); num = n1.add(n2);
break; break;
case opcodes.OP_SUB: case opcodes.OP_SUB:
n = n1.sub(n2); num = n1.sub(n2);
break; break;
case opcodes.OP_MUL: case opcodes.OP_MUL:
n = n1.mul(n2); num = n1.mul(n2);
break; break;
case opcodes.OP_DIV: case opcodes.OP_DIV:
n = n1.div(n2); num = n1.div(n2);
break; break;
case opcodes.OP_MOD: case opcodes.OP_MOD:
n = n1.mod(n2); num = n1.mod(n2);
break; break;
case opcodes.OP_LSHIFT: case opcodes.OP_LSHIFT:
if (n2.cmpn(0) < 0 || n2.cmpn(2048) > 0) if (n2.cmpn(0) < 0 || n2.cmpn(2048) > 0)
throw new ScriptError('UNKNOWN_ERROR', 'Bad shift.'); throw new ScriptError('UNKNOWN_ERROR', 'Bad shift.');
n = n1.ushln(n2.toNumber());
num = n1.ushln(n2.toNumber());
break; break;
case opcodes.OP_RSHIFT: case opcodes.OP_RSHIFT:
if (n2.cmpn(0) < 0 || n2.cmpn(2048) > 0) if (n2.cmpn(0) < 0 || n2.cmpn(2048) > 0)
throw new ScriptError('UNKNOWN_ERROR', 'Bad shift.'); throw new ScriptError('UNKNOWN_ERROR', 'Bad shift.');
n = n1.ushrn(n2.toNumber());
num = n1.ushrn(n2.toNumber());
break; break;
case opcodes.OP_BOOLAND: case opcodes.OP_BOOLAND:
n = n1.cmpn(0) !== 0 && n2.cmpn(0) !== 0; num = n1.cmpn(0) !== 0 && n2.cmpn(0) !== 0;
num = new bn(num ? 1 : 0);
break; break;
case opcodes.OP_BOOLOR: case opcodes.OP_BOOLOR:
n = n1.cmpn(0) !== 0 || n2.cmpn(0) !== 0; num = n1.cmpn(0) !== 0 || n2.cmpn(0) !== 0;
num = new bn(num ? 1 : 0);
break; break;
case opcodes.OP_NUMEQUAL: case opcodes.OP_NUMEQUAL:
n = n1.cmp(n2) === 0; num = n1.cmp(n2) === 0;
num = new bn(num ? 1 : 0);
break; break;
case opcodes.OP_NUMEQUALVERIFY: case opcodes.OP_NUMEQUALVERIFY:
n = n1.cmp(n2) === 0; num = n1.cmp(n2) === 0;
num = new bn(num ? 1 : 0);
break; break;
case opcodes.OP_NUMNOTEQUAL: case opcodes.OP_NUMNOTEQUAL:
n = n1.cmp(n2) !== 0; num = n1.cmp(n2) !== 0;
num = new bn(num ? 1 : 0);
break; break;
case opcodes.OP_LESSTHAN: case opcodes.OP_LESSTHAN:
n = n1.cmp(n2) < 0; num = n1.cmp(n2) < 0;
num = new bn(num ? 1 : 0);
break; break;
case opcodes.OP_GREATERTHAN: case opcodes.OP_GREATERTHAN:
n = n1.cmp(n2) > 0; num = n1.cmp(n2) > 0;
num = new bn(num ? 1 : 0);
break; break;
case opcodes.OP_LESSTHANOREQUAL: case opcodes.OP_LESSTHANOREQUAL:
n = n1.cmp(n2) <= 0; num = n1.cmp(n2) <= 0;
num = new bn(num ? 1 : 0);
break; break;
case opcodes.OP_GREATERTHANOREQUAL: case opcodes.OP_GREATERTHANOREQUAL:
n = n1.cmp(n2) >= 0; num = n1.cmp(n2) >= 0;
num = new bn(num ? 1 : 0);
break; break;
case opcodes.OP_MIN: case opcodes.OP_MIN:
n = n1.cmp(n2) < 0 ? n1 : n2; num = n1.cmp(n2) < 0 ? n1 : n2;
break; break;
case opcodes.OP_MAX: case opcodes.OP_MAX:
n = n1.cmp(n2) > 0 ? n1 : n2; num = n1.cmp(n2) > 0 ? n1 : n2;
break; break;
default: default:
assert(false, 'Fatal script error.'); assert(false, 'Fatal script error.');
}
if (typeof n === 'boolean')
n = new bn(n ? 1 : 0);
if (op === opcodes.OP_NUMEQUALVERIFY) {
if (!Script.bool(n))
throw new ScriptError('NUMEQUALVERIFY', op, ip);
} else {
stack.push(Script.array(n));
}
break; break;
} }
stack.pop();
stack.pop();
stack.push(Script.array(num));
if (op === opcodes.OP_NUMEQUALVERIFY) {
if (!Script.bool(stack.top(-1)))
throw new ScriptError('NUMEQUALVERIFY', op, ip);
stack.pop();
}
break; break;
} }
case opcodes.OP_WITHIN: { case opcodes.OP_WITHIN: {
if (stack.length < 3) if (stack.length < 3)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
n3 = Script.num(stack.pop(), flags);
n2 = Script.num(stack.pop(), flags); n1 = Script.num(stack.top(-3), flags);
n1 = Script.num(stack.pop(), flags); n2 = Script.num(stack.top(-2), flags);
n3 = Script.num(stack.top(-1), flags);
val = n2.cmp(n1) <= 0 && n1.cmp(n3) < 0; val = n2.cmp(n1) <= 0 && n1.cmp(n3) < 0;
stack.pop();
stack.pop();
stack.pop();
stack.push(val ? STACK_TRUE : STACK_FALSE); stack.push(val ? STACK_TRUE : STACK_FALSE);
break; break;
} }
case opcodes.OP_RIPEMD160: { case opcodes.OP_RIPEMD160: {
if (stack.length === 0) if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.push(crypto.ripemd160(stack.pop())); stack.push(crypto.ripemd160(stack.pop()));
break; break;
} }
case opcodes.OP_SHA1: { case opcodes.OP_SHA1: {
if (stack.length === 0) if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.push(crypto.sha1(stack.pop())); stack.push(crypto.sha1(stack.pop()));
break; break;
} }
case opcodes.OP_SHA256: { case opcodes.OP_SHA256: {
if (stack.length === 0) if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.push(crypto.sha256(stack.pop())); stack.push(crypto.sha256(stack.pop()));
break; break;
} }
case opcodes.OP_HASH160: { case opcodes.OP_HASH160: {
if (stack.length === 0) if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.push(crypto.hash160(stack.pop())); stack.push(crypto.hash160(stack.pop()));
break; break;
} }
case opcodes.OP_HASH256: { case opcodes.OP_HASH256: {
if (stack.length === 0) if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.push(crypto.hash256(stack.pop())); stack.push(crypto.hash256(stack.pop()));
break; break;
} }
@ -845,32 +968,38 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
if (stack.length < 2) if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
key = stack.pop(); sig = stack.top(-2);
sig = stack.pop(); key = stack.top(-1);
res = false;
subscript = this.getSubscript(lastSep); subscript = this.getSubscript(lastSep);
if (version === 0) if (version === 0)
subscript.removeData(sig); subscript.removeData(sig);
Script.validateSignature(sig, flags); Script.validateSignature(sig, flags);
Script.validateKey(key, flags); Script.validateKey(key, flags);
if (sig.length > 0) {
type = sig[sig.length - 1]; type = sig[sig.length - 1];
hash = tx.signatureHash(index, subscript, type, version); hash = tx.signatureHash(index, subscript, type, version);
res = Script.checksig(hash, sig, key, flags); res = Script.checksig(hash, sig, key, flags);
}
stack.pop();
stack.pop();
if (!res && (flags & constants.flags.VERIFY_NULLFAIL)) { if (!res && (flags & constants.flags.VERIFY_NULLFAIL)) {
if (sig.length !== 0) if (sig.length !== 0)
throw new ScriptError('NULLFAIL', op, ip); throw new ScriptError('NULLFAIL', op, ip);
} }
stack.push(res ? STACK_TRUE : STACK_FALSE);
if (op === opcodes.OP_CHECKSIGVERIFY) { if (op === opcodes.OP_CHECKSIGVERIFY) {
if (!res) if (!res)
throw new ScriptError('CHECKSIGVERIFY', op, ip); throw new ScriptError('CHECKSIGVERIFY', op, ip);
} else { stack.pop();
stack.push(res ? STACK_TRUE : STACK_FALSE);
} }
break; break;
@ -930,6 +1059,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
Script.validateSignature(sig, flags); Script.validateSignature(sig, flags);
Script.validateKey(key, flags); Script.validateKey(key, flags);
if (sig.length > 0) {
type = sig[sig.length - 1]; type = sig[sig.length - 1];
hash = tx.signatureHash(index, subscript, type, version); hash = tx.signatureHash(index, subscript, type, version);
@ -937,6 +1067,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
isig++; isig++;
m--; m--;
} }
}
ikey++; ikey++;
n--; n--;
@ -965,11 +1096,12 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
stack.pop(); stack.pop();
stack.push(res ? STACK_TRUE : STACK_FALSE);
if (op === opcodes.OP_CHECKMULTISIGVERIFY) { if (op === opcodes.OP_CHECKMULTISIGVERIFY) {
if (!res) if (!res)
throw new ScriptError('CHECKMULTISIGVERIFY', op, ip); throw new ScriptError('CHECKMULTISIGVERIFY', op, ip);
} else { stack.pop();
stack.push(res ? STACK_TRUE : STACK_FALSE);
} }
break; break;
@ -977,52 +1109,73 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
case opcodes.OP_CAT: { case opcodes.OP_CAT: {
if (stack.length < 2) if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
v2 = stack.pop();
v1 = stack.pop(); v1 = stack.top(-2);
stack.push(utils.concat(v1, v2)); v2 = stack.top(-1);
if (stack.top(-1).length > constants.script.MAX_PUSH)
throw new ScriptError('PUSH_SIZE', op, ip); stack.set(-2, utils.concat(v1, v2));
stack.pop();
break; break;
} }
case opcodes.OP_SUBSTR: { case opcodes.OP_SUBSTR: {
if (stack.length < 3) if (stack.length < 3)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
v3 = Script.num(stack.pop(), flags).toNumber(); // end
v2 = Script.num(stack.pop(), flags).toNumber(); // begin v1 = stack.top(-3);
v1 = stack.pop(); // string v2 = Script.num(stack.top(-2), flags).toNumber();
v3 = Script.num(stack.top(-1), flags).toNumber();
if (v2 < 0 || v3 < v2) if (v2 < 0 || v3 < v2)
throw new ScriptError('UNKNOWN_ERROR', 'String out of range.'); throw new ScriptError('UNKNOWN_ERROR', 'String out of range.');
if (v2 > v1.length) if (v2 > v1.length)
v2 = v1.length; v2 = v1.length;
if (v3 > v1.length) if (v3 > v1.length)
v3 = v1.length; v3 = v1.length;
stack.push(v1.slice(v2, v3));
stack.set(-3, v1.slice(v2, v3));
stack.pop();
stack.pop();
break; break;
} }
case opcodes.OP_LEFT: case opcodes.OP_LEFT:
case opcodes.OP_RIGHT: { case opcodes.OP_RIGHT: {
if (stack.length < 2) if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
v2 = Script.num(stack.pop(), flags).toNumber(); // size
v1 = stack.pop(); // string v1 = stack.top(-2);
v2 = Script.num(stack.top(-1), flags).toNumber();
if (v2 < 0) if (v2 < 0)
throw new ScriptError('UNKNOWN_ERROR', 'String size out of range.'); throw new ScriptError('UNKNOWN_ERROR', 'String size out of range.');
if (v2 > v1.length) if (v2 > v1.length)
v2 = v1.length; v2 = v1.length;
if (op === opcodes.OP_LEFT) if (op === opcodes.OP_LEFT)
v1 = v1.slice(0, v2); v1 = v1.slice(0, v2);
else else
v1 = v1.slice(v1.length - v2); v1 = v1.slice(v1.length - v2);
stack.push(v1);
stack.set(-2, v1);
stack.pop();
break; break;
} }
case opcodes.OP_INVERT: { case opcodes.OP_INVERT: {
if (stack.length < 1) if (stack.length < 1)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
val = utils.copy(stack.pop());
val = utils.copy(stack.top(-1));
stack.set(-1, val);
for (i = 0; i < val.length; i++) for (i = 0; i < val.length; i++)
val[i] = ~val[i] & 0xff; val[i] = ~val[i] & 0xff;
stack.push(val);
break; break;
} }
case opcodes.OP_AND: case opcodes.OP_AND:
@ -1030,18 +1183,26 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
case opcodes.OP_XOR: { case opcodes.OP_XOR: {
if (stack.length < 2) if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
v2 = stack.pop();
v1 = utils.copy(stack.pop()); v1 = utils.copy(stack.top(-2));
v2 = stack.top(-1);
if (v1.length < v2.length) { if (v1.length < v2.length) {
v3 = new Buffer(v2.length - v1.length); v3 = new Buffer(v2.length);
v3.fill(0); v3.fill(0);
v1 = utils.concat(v1, v3); v1.copy(v3, 0);
v1 = v3;
} }
if (v2.length < v1.length) { if (v2.length < v1.length) {
v3 = new Buffer(v1.length - v2.length); v3 = new Buffer(v1.length);
v3.fill(0); v3.fill(0);
v2 = utils.concat(v2, v3); v2.copy(v3, 0);
v2 = v3;
} }
stack.set(-2, v1);
if (op === opcodes.OP_AND) { if (op === opcodes.OP_AND) {
for (i = 0; i < v1.length; i++) for (i = 0; i < v1.length; i++)
v1[i] &= v2[i]; v1[i] &= v2[i];
@ -1052,7 +1213,9 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
for (i = 0; i < v1.length; i++) for (i = 0; i < v1.length; i++)
v1[i] ^= v2[i]; v1[i] ^= v2[i];
} }
stack.push(v1);
stack.pop();
break; break;
} }
default: { default: {
@ -1061,7 +1224,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
} }
} }
if (stack.getSize(alt) > constants.script.MAX_STACK) if (stack.length + alt.length > constants.script.MAX_STACK)
throw new ScriptError('STACK_SIZE', op, ip); throw new ScriptError('STACK_SIZE', op, ip);
if (state.length !== 0) if (state.length !== 0)
@ -1145,9 +1308,6 @@ Script.checkSequence = function checkSequence(sequence, tx, i) {
Script.bool = function bool(value) { Script.bool = function bool(value) {
var i; var i;
if (bn.isBN(value))
return value.cmpn(0) !== 0;
assert(Buffer.isBuffer(value)); assert(Buffer.isBuffer(value));
for (i = 0; i < value.length; i++) { for (i = 0; i < value.length; i++) {
@ -1235,9 +1395,6 @@ Script.num = function num(value, flags, size) {
Script.array = function(value) { Script.array = function(value) {
var neg, result; var neg, result;
if (Buffer.isBuffer(value))
return value;
if (utils.isNumber(value)) if (utils.isNumber(value))
value = new bn(value); value = new bn(value);
@ -1299,6 +1456,8 @@ Script.prototype.removeData = function removeData(data) {
op = this.code[i]; op = this.code[i];
if (op.value === -1) { if (op.value === -1) {
// Can't reserialize
// a parse error.
if (index.length > 0) if (index.length > 0)
index.push(i); index.push(i);
break; break;
@ -1400,11 +1559,7 @@ Script.isMinimal = function isMinimal(data, opcode, flags) {
Script.isCode = function isCode(raw) { Script.isCode = function isCode(raw) {
var i, op, code; var i, op, code;
if (!raw) assert(Buffer.isBuffer(raw));
return false;
if (!Buffer.isBuffer(raw))
return false;
code = Script.decode(raw); code = Script.decode(raw);
@ -1606,8 +1761,7 @@ Script.prototype.fromAddress = function fromAddress(address) {
if (typeof address === 'string') if (typeof address === 'string')
address = Address.fromBase58(address); address = Address.fromBase58(address);
if (!address) assert(address instanceof Address, 'Not an address.');
throw new Error('Unknown address type.');
if (address.type === scriptTypes.PUBKEYHASH) if (address.type === scriptTypes.PUBKEYHASH)
return this.fromPubkeyhash(address.hash); return this.fromPubkeyhash(address.hash);
@ -1618,7 +1772,7 @@ Script.prototype.fromAddress = function fromAddress(address) {
if (address.version !== -1) if (address.version !== -1)
return this.fromProgram(address.version, address.hash); return this.fromProgram(address.version, address.hash);
assert(false, 'Bad address type.'); throw new Error('Unknown address type.');
}; };
/** /**
@ -2564,8 +2718,7 @@ Script.validateKey = function validateKey(key, flags) {
if (flags == null) if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS; flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (!Buffer.isBuffer(key)) assert(Buffer.isBuffer(key));
throw new ScriptError('BAD_OPCODE');
if (flags & constants.flags.VERIFY_STRICTENC) { if (flags & constants.flags.VERIFY_STRICTENC) {
if (!Script.isKeyEncoding(key)) if (!Script.isKeyEncoding(key))
@ -2582,8 +2735,7 @@ Script.validateKey = function validateKey(key, flags) {
*/ */
Script.isKeyEncoding = function isKeyEncoding(key) { Script.isKeyEncoding = function isKeyEncoding(key) {
if (!Buffer.isBuffer(key)) assert(Buffer.isBuffer(key));
return false;
if (key.length < 33) if (key.length < 33)
return false; return false;
@ -2617,8 +2769,7 @@ Script.validateSignature = function validateSignature(sig, flags) {
if (flags == null) if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS; flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (!Buffer.isBuffer(sig)) assert(Buffer.isBuffer(sig));
throw new ScriptError('BAD_OPCODE');
// Allow empty sigs // Allow empty sigs
if (sig.length === 0) if (sig.length === 0)
@ -2654,8 +2805,7 @@ Script.validateSignature = function validateSignature(sig, flags) {
Script.isSignatureEncoding = function isSignatureEncoding(sig) { Script.isSignatureEncoding = function isSignatureEncoding(sig) {
var lenR, lenS; var lenR, lenS;
if (!Buffer.isBuffer(sig)) assert(Buffer.isBuffer(sig));
return false;
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash] // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
// * total-length: 1-byte length descriptor of everything that follows, // * total-length: 1-byte length descriptor of everything that follows,
@ -2745,8 +2895,7 @@ Script.isSignatureEncoding = function isSignatureEncoding(sig) {
Script.isHashType = function isHashType(sig) { Script.isHashType = function isHashType(sig) {
var type; var type;
if (!Buffer.isBuffer(sig)) assert(Buffer.isBuffer(sig));
return false;
if (sig.length === 0) if (sig.length === 0)
return false; return false;
@ -3149,8 +3298,7 @@ Script.getSmall = function getSmall(op) {
*/ */
Script.verify = function verify(input, witness, output, tx, i, flags) { Script.verify = function verify(input, witness, output, tx, i, flags) {
var copy, raw, redeem, hadWitness; var stack, copy, raw, redeem, hadWitness;
var stack = new Stack();
if (flags == null) if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS; flags = constants.flags.STANDARD_VERIFY_FLAGS;
@ -3160,6 +3308,9 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
throw new ScriptError('SIG_PUSHONLY'); throw new ScriptError('SIG_PUSHONLY');
} }
// Setup a stack.
stack = new Stack();
// Execute the input script // Execute the input script
input.execute(stack, flags, tx, i, 0); input.execute(stack, flags, tx, i, 0);
@ -3171,7 +3322,7 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
output.execute(stack, flags, tx, i, 0); output.execute(stack, flags, tx, i, 0);
// Verify the stack values. // Verify the stack values.
if (stack.length === 0 || !Script.bool(stack.pop())) if (stack.length === 0 || !Script.bool(stack.top(-1)))
throw new ScriptError('EVAL_FALSE'); throw new ScriptError('EVAL_FALSE');
if ((flags & constants.flags.VERIFY_WITNESS) && output.isProgram()) { if ((flags & constants.flags.VERIFY_WITNESS) && output.isProgram()) {
@ -3185,7 +3336,7 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
Script.verifyProgram(witness, output, flags, tx, i); Script.verifyProgram(witness, output, flags, tx, i);
// Force a cleanstack // Force a cleanstack
stack.length = 0; stack.length = 1;
} }
// If the script is P2SH, execute the real output script // If the script is P2SH, execute the real output script
@ -3209,7 +3360,7 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
redeem.execute(stack, flags, tx, i, 0); redeem.execute(stack, flags, tx, i, 0);
// Verify the the stack values. // Verify the the stack values.
if (stack.length === 0 || !Script.bool(stack.pop())) if (stack.length === 0 || !Script.bool(stack.top(-1)))
throw new ScriptError('EVAL_FALSE'); throw new ScriptError('EVAL_FALSE');
if ((flags & constants.flags.VERIFY_WITNESS) && redeem.isProgram()) { if ((flags & constants.flags.VERIFY_WITNESS) && redeem.isProgram()) {
@ -3223,15 +3374,14 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
Script.verifyProgram(witness, redeem, flags, tx, i); Script.verifyProgram(witness, redeem, flags, tx, i);
// Force a cleanstack. // Force a cleanstack.
stack.length = 0; stack.length = 1;
} }
} }
// Ensure there is nothing left on the stack. // Ensure there is nothing left on the stack.
if (flags & constants.flags.VERIFY_CLEANSTACK) { if (flags & constants.flags.VERIFY_CLEANSTACK) {
assert((flags & constants.flags.VERIFY_P2SH) !== 0); assert((flags & constants.flags.VERIFY_P2SH) !== 0);
// assert((flags & constants.flags.VERIFY_WITNESS) !== 0); if (stack.length !== 1)
if (stack.length !== 0)
throw new ScriptError('CLEANSTACK'); throw new ScriptError('CLEANSTACK');
} }
@ -3311,7 +3461,7 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i) {
redeem.execute(stack, flags, tx, i, 1); redeem.execute(stack, flags, tx, i, 1);
// Verify the stack values. // Verify the stack values.
if (stack.length !== 1 || !Script.bool(stack.pop())) if (stack.length !== 1 || !Script.bool(stack.top(-1)))
throw new ScriptError('EVAL_FALSE'); throw new ScriptError('EVAL_FALSE');
return true; return true;
@ -3344,12 +3494,12 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i) {
if (stack.length < 4) if (stack.length < 4)
throw new ScriptError('INVALID_MAST_STACK'); throw new ScriptError('INVALID_MAST_STACK');
metadata = stack.pop(); metadata = stack.top(-1);
if (metadata.length < 1 || metadata.length > 5) if (metadata.length < 1 || metadata.length > 5)
throw new ScriptError('INVALID_MAST_STACK'); throw new ScriptError('INVALID_MAST_STACK');
subscripts = metadata[0]; subscripts = metadata[0];
if (subscripts === 0 || stack.length < subscripts + 2) if (subscripts === 0 || stack.length < subscripts + 3)
throw new ScriptError('INVALID_MAST_STACK'); throw new ScriptError('INVALID_MAST_STACK');
ops = subscripts; ops = subscripts;
@ -3371,7 +3521,7 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i) {
mastRoot.writeU32(version); mastRoot.writeU32(version);
pathdata = stack.pop(); pathdata = stack.top(-2);
if (pathdata.length & 0x1f) if (pathdata.length & 0x1f)
throw new ScriptError('INVALID_MAST_STACK'); throw new ScriptError('INVALID_MAST_STACK');
@ -3392,7 +3542,7 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i) {
for (j = 0; j < depth; j++) for (j = 0; j < depth; j++)
path.push(pathdata.slice(j * 32, j * 32 + 32)); path.push(pathdata.slice(j * 32, j * 32 + 32));
posdata = stack.pop(); posdata = stack.top(-3);
if (posdata.length > 4) if (posdata.length > 4)
throw new ScriptError('INVALID_MAST_STACK'); throw new ScriptError('INVALID_MAST_STACK');
@ -3417,7 +3567,7 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i) {
scripts.writeBytes(output.raw); scripts.writeBytes(output.raw);
for (j = 0; j < subscripts; j++) { for (j = 0; j < subscripts; j++) {
script = stack.pop(); script = stack.top(-(4 + j));
if (version === 0) { if (version === 0) {
if ((scripts.written + script.length) > constants.script.MAX_SIZE) if ((scripts.written + script.length) > constants.script.MAX_SIZE)
throw new ScriptError('SCRIPT_SIZE'); throw new ScriptError('SCRIPT_SIZE');
@ -3436,17 +3586,19 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i) {
throw new ScriptError('WITNESS_PROGRAM_MISMATCH'); throw new ScriptError('WITNESS_PROGRAM_MISMATCH');
if (version === 0) { if (version === 0) {
stack.length -= 3 + subscripts;
for (j = 0; j < stack.length; j++) { for (j = 0; j < stack.length; j++) {
if (stack.get(j).length > constants.script.MAX_PUSH) if (stack.get(j).length > constants.script.MAX_PUSH)
throw new ScriptError('PUSH_SIZE'); throw new ScriptError('PUSH_SIZE');
} }
}
output = new Script(scripts.render()); output = new Script(scripts.render());
output.execute(stack, flags, tx, i, 1); output.execute(stack, flags, tx, i, 1);
if (stack.length !== 0) if (stack.length !== 0)
throw new ScriptError('EVAL_FALSE'); throw new ScriptError('EVAL_FALSE');
}
return true; return true;
}; };
@ -3471,9 +3623,6 @@ Script.checksig = function checksig(msg, sig, key, flags) {
if (flags == null) if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS; flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (!Buffer.isBuffer(sig))
return false;
// Attempt to normalize the signature // Attempt to normalize the signature
// length before passing to elliptic. // length before passing to elliptic.
// Note: We only do this for historical data! // Note: We only do this for historical data!

View File

@ -9,9 +9,6 @@
module.exports = Stack; module.exports = Stack;
var constants = require('../protocol/constants');
var opcodes = constants.opcodes;
var ScriptError = require('../utils/errors').ScriptError;
var Script = require('./script'); var Script = require('./script');
var Witness = require('./witness'); var Witness = require('./witness');
@ -88,16 +85,6 @@ Stack.prototype.clone = function clone() {
return new Stack(this.items.slice()); return new Stack(this.items.slice());
}; };
/**
* Get total size of the stack, including the alt stack.
* @param {Array} alt - Alt stack.
* @returns {Number}
*/
Stack.prototype.getSize = function getSize(alt) {
return this.items.length + alt.length;
};
/** /**
* Push item onto stack. * Push item onto stack.
* @see Array#push * @see Array#push
@ -125,11 +112,12 @@ Stack.prototype.unshift = function unshift(item) {
* @param {Number} start * @param {Number} start
* @param {Number} end * @param {Number} end
* @see Array#slice * @see Array#slice
* @returns {Buffer[]} * @returns {Stack}
*/ */
Stack.prototype.slice = function slice(start, end) { Stack.prototype.slice = function slice(start, end) {
return this.items.slice(start, end); this.items = this.items.slice(start, end);
return this;
}; };
/** /**
@ -142,11 +130,62 @@ Stack.prototype.slice = function slice(start, end) {
*/ */
Stack.prototype.splice = function splice(i, remove, insert) { Stack.prototype.splice = function splice(i, remove, insert) {
if (i < 0)
i = this.items.length + i;
if (insert === undefined) if (insert === undefined)
return this.items.splice(i, remove); return this.items.splice(i, remove);
return this.items.splice(i, remove, insert); return this.items.splice(i, remove, insert);
}; };
/**
* Erase stack items.
* @param {Number} start
* @param {Number} end
* @returns {Buffer[]}
*/
Stack.prototype.erase = function erase(start, end) {
if (start < 0)
start = this.items.length + start;
if (end < 0)
end = this.items.length + end;
this.items.splice(start, end - start);
};
/**
* Insert an item.
* @param {Number} index
* @param {Buffer} item
* @returns {Buffer}
*/
Stack.prototype.insert = function insert(i, item) {
if (i < 0)
i = this.items.length + i;
this.items.splice(i, 0, item);
};
/**
* Remove an item.
* @param {Number} index
* @returns {Buffer}
*/
Stack.prototype.remove = function remove(i) {
if (i < 0)
i = this.items.length + i;
if (i >= this.items.length)
return;
return this.items.splice(i, 1)[0];
};
/** /**
* Pop a stack item. * Pop a stack item.
* @see Array#pop * @see Array#pop
@ -206,20 +245,25 @@ Stack.prototype.clear = function clear() {
*/ */
Stack.prototype.set = function set(i, value) { Stack.prototype.set = function set(i, value) {
if (i < 0)
i = this.items.length + i;
return this.items[i] = value; return this.items[i] = value;
}; };
/** /**
* Swap stack values. * Swap stack values.
* @private
* @param {Number} i1 - Index 1. * @param {Number} i1 - Index 1.
* @param {Number} i2 - Index 2. * @param {Number} i2 - Index 2.
*/ */
Stack.prototype._swap = function _swap(i1, i2) { Stack.prototype.swap = function swap(i1, i2) {
var v1, v2; var v1, v2;
if (i1 < 0)
i1 = this.items.length + i1; i1 = this.items.length + i1;
if (i2 < 0)
i2 = this.items.length + i2; i2 = this.items.length + i2;
v1 = this.items[i1]; v1 = this.items[i1];
@ -229,300 +273,6 @@ Stack.prototype._swap = function _swap(i1, i2) {
this.items[i2] = v1; this.items[i2] = v1;
}; };
/**
* Perform the OP_TOALTSTACK operation.
* @param {Array} alt - Alt stack.
* @throws {ScriptError}
*/
Stack.prototype.toalt = function toalt(alt) {
if (this.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_TOALTSTACK);
alt.push(this.pop());
};
/**
* Perform the OP_FROMALTSTACK operation.
* @param {Array} alt - Alt stack.
* @throws {ScriptError}
*/
Stack.prototype.fromalt = function fromalt(alt) {
if (alt.length === 0)
throw new ScriptError('INVALID_ALTSTACK_OPERATION', opcodes.OP_FROMALTSTACK);
this.push(alt.pop());
};
/**
* Perform the OP_IFDUP operation.
* @throws {ScriptError}
*/
Stack.prototype.ifdup = function ifdup() {
if (this.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_IFDUP);
if (Script.bool(this.top(-1)))
this.push(this.top(-1));
};
/**
* Perform the OP_DEPTH operation.
* @throws {ScriptError}
*/
Stack.prototype.depth = function depth() {
this.push(Script.array(this.length));
};
/**
* Perform the OP_DROP operation.
* @throws {ScriptError}
*/
Stack.prototype.drop = function drop() {
if (this.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_DROP);
this.pop();
};
/**
* Perform the OP_DUP operation.
* @throws {ScriptError}
*/
Stack.prototype.dup = function dup() {
if (this.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_DUP);
this.push(this.top(-1));
};
/**
* Perform the OP_NIP operation.
* @throws {ScriptError}
*/
Stack.prototype.nip = function nip() {
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_NIP);
this.splice(this.length - 2, 1);
};
/**
* Perform the OP_OVER operation.
* @throws {ScriptError}
*/
Stack.prototype.over = function over() {
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_OVER);
this.push(this.top(-2));
};
/**
* Perform the OP_PICK operation.
* @param {VerifyFlags} flags
* @throws {ScriptError}
*/
Stack.prototype.pick = function pick(flags) {
return this._pickroll(opcodes.OP_PICK, flags);
};
/**
* Perform the OP_ROLL operation.
* @param {VerifyFlags} flags
* @throws {ScriptError}
*/
Stack.prototype.roll = function roll(flags) {
return this._pickroll(opcodes.OP_ROLL, flags);
};
/**
* Perform a pick or roll.
* @private
* @param {Number} op
* @param {VerifyFlags} flags
* @throws {ScriptError}
*/
Stack.prototype._pickroll = function pickroll(op, flags) {
var val, n;
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op);
val = this.pop();
n = Script.num(val, flags).toNumber();
if (n < 0 || n >= this.length)
throw new ScriptError('INVALID_STACK_OPERATION', op);
val = this.top(-n - 1);
if (op === opcodes.OP_ROLL)
this.splice(this.length - n - 1, 1);
this.push(val);
};
/**
* Perform the OP_ROT operation.
* @throws {ScriptError}
*/
Stack.prototype.rot = function rot() {
if (this.length < 3)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_ROT);
this._swap(-3, -2);
this._swap(-2, -1);
};
/**
* Perform the OP_SWAP operation.
* @throws {ScriptError}
*/
Stack.prototype.swap = function swap() {
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_SWAP);
this._swap(-2, -1);
};
/**
* Perform the OP_TUCK operation.
* @throws {ScriptError}
*/
Stack.prototype.tuck = function tuck() {
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_TUCK);
this.splice(this.length - 2, 0, this.top(-1));
};
/**
* Perform the OP_2DROP operation.
* @throws {ScriptError}
*/
Stack.prototype.drop2 = function drop2() {
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_2DROP);
this.pop();
this.pop();
};
/**
* Perform the OP_2DUP operation.
* @throws {ScriptError}
*/
Stack.prototype.dup2 = function dup2() {
var v1, v2;
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_2DUP);
v1 = this.top(-2);
v2 = this.top(-1);
this.push(v1);
this.push(v2);
};
/**
* Perform the OP_3DUP operation.
* @throws {ScriptError}
*/
Stack.prototype.dup3 = function dup3() {
var v1, v2, v3;
if (this.length < 3)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_3DUP);
v1 = this.top(-3);
v2 = this.top(-2);
v3 = this.top(-1);
this.push(v1);
this.push(v2);
this.push(v3);
};
/**
* Perform the OP_2OVER operation.
* @throws {ScriptError}
*/
Stack.prototype.over2 = function over2() {
var v1, v2;
if (this.length < 4)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_2OVER);
v1 = this.top(-4);
v2 = this.top(-3);
this.push(v1);
this.push(v2);
};
/**
* Perform the OP_2ROT operation.
* @throws {ScriptError}
*/
Stack.prototype.rot2 = function rot2() {
var v1, v2;
if (this.length < 6)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_2ROT);
v1 = this.top(-6);
v2 = this.top(-5);
this.splice(this.length - 6, 2);
this.push(v1);
this.push(v2);
};
/**
* Perform the OP_2SWAP operation.
* @throws {ScriptError}
*/
Stack.prototype.swap2 = function swap2() {
if (this.length < 4)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_2SWAP);
this._swap(-4, -2);
this._swap(-3, -1);
};
/**
* Perform the OP_SIZE operation.
* @throws {ScriptError}
*/
Stack.prototype.size = function size() {
if (this.length < 1)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_SIZE);
this.push(Script.array(this.top(-1).length));
};
/** /**
* Test an object to see if it is a Stack. * Test an object to see if it is a Stack.
* @param {Object} obj * @param {Object} obj

View File

@ -67,7 +67,7 @@ describe('Script', function() {
inputScript.execute(stack); inputScript.execute(stack);
var res = prevOutScript.execute(stack); var res = prevOutScript.execute(stack);
assert(res); assert(res);
assert.deepEqual(stack.slice(), [[1], [3], [5]]); assert.deepEqual(stack.items, [[1], [3], [5]]);
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]); var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
var prevOutScript = new Script([ var prevOutScript = new Script([
@ -84,7 +84,7 @@ describe('Script', function() {
inputScript.execute(stack); inputScript.execute(stack);
var res = prevOutScript.execute(stack); var res = prevOutScript.execute(stack);
assert(res); assert(res);
assert.deepEqual(stack.slice(), [[1], [4], [5]]); assert.deepEqual(stack.items, [[1], [4], [5]]);
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]); var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
var prevOutScript = new Script([ var prevOutScript = new Script([
@ -99,7 +99,7 @@ describe('Script', function() {
inputScript.execute(stack); inputScript.execute(stack);
var res = prevOutScript.execute(stack); var res = prevOutScript.execute(stack);
assert(res); assert(res);
assert.deepEqual(stack.slice(), [[1], [3], [5]]); assert.deepEqual(stack.items, [[1], [3], [5]]);
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]); var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
var prevOutScript = new Script([ var prevOutScript = new Script([
@ -114,7 +114,7 @@ describe('Script', function() {
inputScript.execute(stack); inputScript.execute(stack);
var res = prevOutScript.execute(stack); var res = prevOutScript.execute(stack);
assert(res); assert(res);
assert.deepEqual(stack.slice(), [[1], [5]]); assert.deepEqual(stack.items, [[1], [5]]);
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]); var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
var prevOutScript = new Script([ var prevOutScript = new Script([
@ -129,7 +129,7 @@ describe('Script', function() {
inputScript.execute(stack); inputScript.execute(stack);
var res = prevOutScript.execute(stack); var res = prevOutScript.execute(stack);
assert(res); assert(res);
assert.deepEqual(stack.slice(), [[1], [3], [5]]); assert.deepEqual(stack.items, [[1], [3], [5]]);
}); });
function success(res, stack) { function success(res, stack) {