script: refactor verification and execution.
This commit is contained in:
parent
c13852f81e
commit
c43df089f6
@ -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!
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user