From be457ee581b65369f7aa5c1c1d23c5f894340b6d Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 12 Jun 2017 00:23:18 -0700 Subject: [PATCH] script: refactor branch/disabled opcodes. --- lib/script/opcode.js | 38 ++++++++++- lib/script/script.js | 156 +++++++++++++++++-------------------------- 2 files changed, 97 insertions(+), 97 deletions(-) diff --git a/lib/script/opcode.js b/lib/script/opcode.js index bdbcfd98..0add94ae 100644 --- a/lib/script/opcode.js +++ b/lib/script/opcode.js @@ -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 diff --git a/lib/script/script.js b/lib/script/script.js index e3013169..4f786445 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -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))