script: refactor branch/disabled opcodes.

This commit is contained in:
Christopher Jeffrey 2017-06-12 00:23:18 -07:00
parent 85ec9ae2c3
commit be457ee581
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 97 additions and 97 deletions

View File

@ -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}
*/
@ -64,6 +64,42 @@ Opcode.prototype.isMinimal = function isMinimal() {
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.
* @param {BufferWriter} bw

View File

@ -501,7 +501,6 @@ Script.prototype.removeSeparators = function removeSeparators() {
*/
Script.prototype.execute = function execute(stack, flags, tx, index, value, version) {
var ip = 0;
var lastSep = 0;
var opCount = 0;
var alt = [];
@ -510,7 +509,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
var minimal = false;
var val, v1, v2, v3;
var num, n1, n2, n3;
var op, m, n, key, sig;
var op, ip, m, n, key, sig;
var type, subscript, hash;
var ikey, isig, ikey2;
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)
throw new ScriptError('PUSH_SIZE', op, ip);
// Note that minimaldata is not checked
// on unexecuted branches of code.
if (negate === 0) {
if (minimal && !op.isMinimal())
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)
throw new ScriptError('OP_COUNT', op, ip);
// It's very important to make a distiction
// here: these opcodes will fail _even if they
// 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.isDisabled())
throw new ScriptError('DISABLED_OPCODE', op, ip);
if (op.value >= opcodes.OP_IF && op.value <= opcodes.OP_ENDIF) {
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)
if (negate !== 0 && !op.isBranch())
continue;
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);
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: {
if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
@ -3282,6 +3245,7 @@ function sortKeys(keys) {
function validateKey(key, flags, version) {
assert(Buffer.isBuffer(key));
assert(typeof flags === 'number');
assert(typeof version === 'number');
if (flags & Script.flags.VERIFY_STRICTENC) {
if (!common.isKeyEncoding(key))