script: refactor branch/disabled opcodes.
This commit is contained in:
parent
85ec9ae2c3
commit
be457ee581
@ -35,7 +35,7 @@ function Opcode(value, data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check to see if a pushdata abides by minimaldata.
|
* Test whether a pushdata abides by minimaldata.
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -64,6 +64,42 @@ Opcode.prototype.isMinimal = function isMinimal() {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether opcode is a disabled opcode.
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Opcode.prototype.isDisabled = function isDisabled() {
|
||||||
|
switch (this.value) {
|
||||||
|
case opcodes.OP_CAT:
|
||||||
|
case opcodes.OP_SUBSTR:
|
||||||
|
case opcodes.OP_LEFT:
|
||||||
|
case opcodes.OP_RIGHT:
|
||||||
|
case opcodes.OP_INVERT:
|
||||||
|
case opcodes.OP_AND:
|
||||||
|
case opcodes.OP_OR:
|
||||||
|
case opcodes.OP_XOR:
|
||||||
|
case opcodes.OP_2MUL:
|
||||||
|
case opcodes.OP_2DIV:
|
||||||
|
case opcodes.OP_MUL:
|
||||||
|
case opcodes.OP_DIV:
|
||||||
|
case opcodes.OP_MOD:
|
||||||
|
case opcodes.OP_LSHIFT:
|
||||||
|
case opcodes.OP_RSHIFT:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether opcode is a branch (if/else/endif).
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Opcode.prototype.isBranch = function isBranch() {
|
||||||
|
return this.value >= opcodes.OP_IF && this.value <= opcodes.OP_ENDIF;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode the opcode to a buffer writer.
|
* Encode the opcode to a buffer writer.
|
||||||
* @param {BufferWriter} bw
|
* @param {BufferWriter} bw
|
||||||
|
|||||||
@ -501,7 +501,6 @@ Script.prototype.removeSeparators = function removeSeparators() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Script.prototype.execute = function execute(stack, flags, tx, index, value, version) {
|
Script.prototype.execute = function execute(stack, flags, tx, index, value, version) {
|
||||||
var ip = 0;
|
|
||||||
var lastSep = 0;
|
var lastSep = 0;
|
||||||
var opCount = 0;
|
var opCount = 0;
|
||||||
var alt = [];
|
var alt = [];
|
||||||
@ -510,7 +509,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
var minimal = false;
|
var minimal = false;
|
||||||
var val, v1, v2, v3;
|
var val, v1, v2, v3;
|
||||||
var num, n1, n2, n3;
|
var num, n1, n2, n3;
|
||||||
var op, m, n, key, sig;
|
var op, ip, m, n, key, sig;
|
||||||
var type, subscript, hash;
|
var type, subscript, hash;
|
||||||
var ikey, isig, ikey2;
|
var ikey, isig, ikey2;
|
||||||
var i, j, res, locktime;
|
var i, j, res, locktime;
|
||||||
@ -537,8 +536,6 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
if (op.data.length > consensus.MAX_SCRIPT_PUSH)
|
if (op.data.length > consensus.MAX_SCRIPT_PUSH)
|
||||||
throw new ScriptError('PUSH_SIZE', op, ip);
|
throw new ScriptError('PUSH_SIZE', op, ip);
|
||||||
|
|
||||||
// Note that minimaldata is not checked
|
|
||||||
// on unexecuted branches of code.
|
|
||||||
if (negate === 0) {
|
if (negate === 0) {
|
||||||
if (minimal && !op.isMinimal())
|
if (minimal && !op.isMinimal())
|
||||||
throw new ScriptError('MINIMALDATA', op, ip);
|
throw new ScriptError('MINIMALDATA', op, ip);
|
||||||
@ -551,99 +548,10 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
if (op.value > opcodes.OP_16 && ++opCount > consensus.MAX_SCRIPT_OPS)
|
if (op.value > opcodes.OP_16 && ++opCount > consensus.MAX_SCRIPT_OPS)
|
||||||
throw new ScriptError('OP_COUNT', op, ip);
|
throw new ScriptError('OP_COUNT', op, ip);
|
||||||
|
|
||||||
// It's very important to make a distiction
|
if (op.isDisabled())
|
||||||
// here: these opcodes will fail _even if they
|
throw new ScriptError('DISABLED_OPCODE', op, ip);
|
||||||
// are in unexecuted branches of code_. Whereas
|
|
||||||
// a totally unknown opcode is fine as long as it
|
|
||||||
// is unexecuted.
|
|
||||||
switch (op.value) {
|
|
||||||
case opcodes.OP_CAT:
|
|
||||||
case opcodes.OP_SUBSTR:
|
|
||||||
case opcodes.OP_LEFT:
|
|
||||||
case opcodes.OP_RIGHT:
|
|
||||||
case opcodes.OP_INVERT:
|
|
||||||
case opcodes.OP_AND:
|
|
||||||
case opcodes.OP_OR:
|
|
||||||
case opcodes.OP_XOR:
|
|
||||||
case opcodes.OP_2MUL:
|
|
||||||
case opcodes.OP_2DIV:
|
|
||||||
case opcodes.OP_MUL:
|
|
||||||
case opcodes.OP_DIV:
|
|
||||||
case opcodes.OP_MOD:
|
|
||||||
case opcodes.OP_LSHIFT:
|
|
||||||
case opcodes.OP_RSHIFT:
|
|
||||||
throw new ScriptError('DISABLED_OPCODE', op, ip);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op.value >= opcodes.OP_IF && op.value <= opcodes.OP_ENDIF) {
|
if (negate !== 0 && !op.isBranch())
|
||||||
switch (op.value) {
|
|
||||||
case opcodes.OP_IF:
|
|
||||||
case opcodes.OP_NOTIF: {
|
|
||||||
val = false;
|
|
||||||
|
|
||||||
if (negate === 0) {
|
|
||||||
if (stack.length < 1)
|
|
||||||
throw new ScriptError('UNBALANCED_CONDITIONAL', op, ip);
|
|
||||||
|
|
||||||
val = stack.top(-1);
|
|
||||||
|
|
||||||
if (version === 1 && (flags & Script.flags.VERIFY_MINIMALIF)) {
|
|
||||||
if (val.length > 1)
|
|
||||||
throw new ScriptError('MINIMALIF');
|
|
||||||
|
|
||||||
if (val.length === 1 && val[0] !== 1)
|
|
||||||
throw new ScriptError('MINIMALIF');
|
|
||||||
}
|
|
||||||
|
|
||||||
val = Script.bool(val);
|
|
||||||
|
|
||||||
if (op.value === opcodes.OP_NOTIF)
|
|
||||||
val = !val;
|
|
||||||
|
|
||||||
stack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
state.push(val);
|
|
||||||
|
|
||||||
if (!val)
|
|
||||||
negate++;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case opcodes.OP_ELSE: {
|
|
||||||
if (state.length === 0)
|
|
||||||
throw new ScriptError('UNBALANCED_CONDITIONAL', op, ip);
|
|
||||||
|
|
||||||
state[state.length - 1] = !state[state.length - 1];
|
|
||||||
|
|
||||||
if (!state[state.length - 1])
|
|
||||||
negate++;
|
|
||||||
else
|
|
||||||
negate--;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case opcodes.OP_ENDIF: {
|
|
||||||
if (state.length === 0)
|
|
||||||
throw new ScriptError('UNBALANCED_CONDITIONAL', op, ip);
|
|
||||||
|
|
||||||
if (!state.pop())
|
|
||||||
negate--;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case opcodes.OP_VERIF:
|
|
||||||
case opcodes.OP_VERNOTIF: {
|
|
||||||
throw new ScriptError('BAD_OPCODE', op, ip);
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
assert(false, 'Fatal script error.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (negate !== 0)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (op.value) {
|
switch (op.value) {
|
||||||
@ -744,6 +652,61 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
throw new ScriptError('DISCOURAGE_UPGRADABLE_NOPS', op, ip);
|
throw new ScriptError('DISCOURAGE_UPGRADABLE_NOPS', op, ip);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case opcodes.OP_IF:
|
||||||
|
case opcodes.OP_NOTIF: {
|
||||||
|
val = false;
|
||||||
|
|
||||||
|
if (negate === 0) {
|
||||||
|
if (stack.length < 1)
|
||||||
|
throw new ScriptError('UNBALANCED_CONDITIONAL', op, ip);
|
||||||
|
|
||||||
|
val = stack.top(-1);
|
||||||
|
|
||||||
|
if (version === 1 && (flags & Script.flags.VERIFY_MINIMALIF)) {
|
||||||
|
if (val.length > 1)
|
||||||
|
throw new ScriptError('MINIMALIF');
|
||||||
|
|
||||||
|
if (val.length === 1 && val[0] !== 1)
|
||||||
|
throw new ScriptError('MINIMALIF');
|
||||||
|
}
|
||||||
|
|
||||||
|
val = Script.bool(val);
|
||||||
|
|
||||||
|
if (op.value === opcodes.OP_NOTIF)
|
||||||
|
val = !val;
|
||||||
|
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
state.push(val);
|
||||||
|
|
||||||
|
if (!val)
|
||||||
|
negate++;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case opcodes.OP_ELSE: {
|
||||||
|
if (state.length === 0)
|
||||||
|
throw new ScriptError('UNBALANCED_CONDITIONAL', op, ip);
|
||||||
|
|
||||||
|
state[state.length - 1] = !state[state.length - 1];
|
||||||
|
|
||||||
|
if (!state[state.length - 1])
|
||||||
|
negate++;
|
||||||
|
else
|
||||||
|
negate--;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case opcodes.OP_ENDIF: {
|
||||||
|
if (state.length === 0)
|
||||||
|
throw new ScriptError('UNBALANCED_CONDITIONAL', op, ip);
|
||||||
|
|
||||||
|
if (!state.pop())
|
||||||
|
negate--;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
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);
|
||||||
@ -3282,6 +3245,7 @@ function sortKeys(keys) {
|
|||||||
function validateKey(key, flags, version) {
|
function validateKey(key, flags, version) {
|
||||||
assert(Buffer.isBuffer(key));
|
assert(Buffer.isBuffer(key));
|
||||||
assert(typeof flags === 'number');
|
assert(typeof flags === 'number');
|
||||||
|
assert(typeof version === 'number');
|
||||||
|
|
||||||
if (flags & Script.flags.VERIFY_STRICTENC) {
|
if (flags & Script.flags.VERIFY_STRICTENC) {
|
||||||
if (!common.isKeyEncoding(key))
|
if (!common.isKeyEncoding(key))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user