From 13e1103abe3e328c5a4e2039b51a546f8be6c60a Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Thu, 10 Jan 2019 15:40:40 +0000 Subject: [PATCH] Re-enable several disabled opcodes Re-enabled opcodes are: - OP_CAT - OP_SUBSTR - OP_LEFT - OP_RIGHT - OP_INVERT - OP_AND - OP_OR - OP_XOR - OP_LSHIFT - OP_RSHIFT --- src/script/interpreter.cpp | 269 ++++++++++++++++++++++++++++++-- src/test/data/script_tests.json | 15 -- 2 files changed, 257 insertions(+), 27 deletions(-) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index d0e1bc8317..e78c86731d 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -293,7 +293,7 @@ bool EvalScript(std::vector >& stack, const CScript& // static const CScriptNum bnFalse(0); // static const CScriptNum bnTrue(1); static const valtype vchFalse(0); - // static const valtype vchZero(0); + static const valtype vchZero(0); static const valtype vchTrue(1, 1); CScript::const_iterator pc = script.begin(); @@ -327,22 +327,26 @@ bool EvalScript(std::vector >& stack, const CScript& if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) return set_error(serror, SCRIPT_ERR_OP_COUNT); - if (opcode == OP_CAT || - opcode == OP_SUBSTR || - opcode == OP_LEFT || - opcode == OP_RIGHT || - opcode == OP_INVERT || - opcode == OP_AND || - opcode == OP_OR || - opcode == OP_XOR || + // ELEMENTS: + // commented out opcodes are re-enabled in Elements + if (//opcode == OP_CAT || + //opcode == OP_SUBSTR || + //opcode == OP_LEFT || + //opcode == OP_RIGHT || + //opcode == OP_INVERT || + //opcode == OP_AND || + //opcode == OP_OR || + //opcode == OP_XOR || + //opcode == OP_LSHIFT || + //opcode == OP_RSHIFT || opcode == OP_2MUL || opcode == OP_2DIV || opcode == OP_MUL || opcode == OP_DIV || - opcode == OP_MOD || - opcode == OP_LSHIFT || - opcode == OP_RSHIFT) + opcode == OP_MOD + ) { return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes. + } // With SCRIPT_VERIFY_CONST_SCRIPTCODE, OP_CODESEPARATOR in non-segwit script is rejected even in an unexecuted branch if (opcode == OP_CODESEPARATOR && sigversion == SigVersion::BASE && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) @@ -733,6 +737,26 @@ bool EvalScript(std::vector >& stack, const CScript& } break; + case OP_CAT: + { + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + valtype vch1 = stacktop(-2); + valtype vch2 = stacktop(-1); + + if (vch1.size() + vch2.size() > MAX_SCRIPT_ELEMENT_SIZE) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype vch3; + vch3.reserve(vch1.size() + vch2.size()); + vch3.insert(vch3.end(), vch1.begin(), vch1.end()); + vch3.insert(vch3.end(), vch2.begin(), vch2.end()); + + popstack(stack); + popstack(stack); + stack.push_back(vch3); + } + break; case OP_SIZE: { @@ -745,9 +769,230 @@ bool EvalScript(std::vector >& stack, const CScript& break; + // + // String operators + // + case OP_LEFT: + case OP_RIGHT: + { + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype vch1 = stacktop(-2); + CScriptNum start(stacktop(-1), fRequireMinimal); + + if (start < 0) + return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); + + valtype vch2; + switch (opcode) { + case OP_RIGHT: + { + if (start >= vch1.size()) + vch2 = vchZero; + else + vch2.insert(vch2.begin(), vch1.begin() + start.getint(), vch1.end()); + break; + } + case OP_LEFT: + { + if (start >= vch1.size()) + vch2 = vch1; + else + vch2.insert(vch2.begin(), vch1.begin(), vch1.begin() + start.getint()); + break; + } + default: + { + assert(!"invalid opcode"); + break; + } + } + popstack(stack); + popstack(stack); + stack.push_back(vch2); + } + break; + + case OP_SUBSTR: + { + if (stack.size() < 3) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype vch1 = stacktop(-3); + CScriptNum start(stacktop(-2), fRequireMinimal); + CScriptNum length(stacktop(-1), fRequireMinimal); + + if (length < 0 || start < 0) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + if (start >= vch1.size()) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + if (length > vch1.size()) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + if ((start + length) > vch1.size()) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype vch2; + vch2.insert(vch2.begin(), vch1.begin() + start.getint(), vch1.begin() + (start + length).getint()); + + popstack(stack); + popstack(stack); + popstack(stack); + stack.push_back(vch2); + } + break; + + // // Bitwise logic // + case OP_RSHIFT: + { + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + valtype vch1 = stacktop(-2); + CScriptNum bn(stacktop(-1), fRequireMinimal); + + if (bn < 0) + return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); + + unsigned int full_bytes = bn.getint() / 8; + unsigned int bits = bn.getint() % 8; + + if (full_bytes >= vch1.size()) { + popstack(stack); + popstack(stack); + stack.push_back(vchZero); + break; + } + + valtype vch2; + vch2.insert(vch2.begin(), vch1.begin() + full_bytes, vch1.end()); + + uint16_t temp = 0; + for (int i=(vch2.size()-1);i>=0;--i) { + temp = (vch2[i] << (8 - bits)) | ((temp << 8) & 0xff00); + vch2[i] = (temp & 0xff00) >> 8; + } + + // 0x0fff >> 4 == 0x00ff or 0xff, reduce to minimal representation + while (!vch2.empty() && vch2.back() == 0) + vch2.pop_back(); + + popstack(stack); + popstack(stack); + stack.push_back(vch2); + } + break; + + case OP_LSHIFT: + { + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + valtype vch1 = stacktop(-2); + CScriptNum bn(stacktop(-1), fRequireMinimal); + + if (bn < 0) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + unsigned int full_bytes = bn.getint() / 8; + unsigned int bits = bn.getint() % 8; + + if (vch1.size() + full_bytes + (bits ? 1 : 0) > MAX_SCRIPT_ELEMENT_SIZE) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype vch2; + vch2.reserve(vch1.size() + full_bytes + 1); + vch2.insert(vch2.end(), full_bytes, 0); + vch2.insert(vch2.end(), vch1.begin(), vch1.end()); + vch2.insert(vch2.end(), 1, 0); + + uint16_t temp = 0; + for (size_t i=0;i> 8); + vch2[i] = temp & 0xff; + } + + // reduce to minimal representation + while (!vch2.empty() && vch2.back() == 0) + vch2.pop_back(); + + popstack(stack); + popstack(stack); + stack.push_back(vch2); + } + break; + + case OP_INVERT: + { + if (stack.size() < 1) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + valtype& vch1 = stacktop(-1); + for (size_t i = 0; i < vch1.size(); ++i) + vch1[i] = ~vch1[i]; + } + break; + + case OP_AND: + { + // (x1 x2 -- x1 & x2) + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + valtype& vch1 = stacktop(-1); + valtype& vch2 = stacktop(-2); + if (vch1.size() != vch2.size()) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype vch3(vch1); + for (size_t i = 0; i < vch1.size(); i++) + vch3[i] &= vch2[i]; + popstack(stack); + popstack(stack); + stack.push_back(vch3); + } + break; + + case OP_OR: + { + // (x1 x2 -- x1 | x2) + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + valtype& vch1 = stacktop(-1); + valtype& vch2 = stacktop(-2); + if (vch1.size() != vch2.size()) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype vch3(vch1); + for (size_t i = 0; i < vch1.size(); i++) + vch3[i] |= vch2[i]; + popstack(stack); + popstack(stack); + stack.push_back(vch3); + } + break; + + case OP_XOR: + { + // (x1 x2 -- x1 ^ x2) + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + valtype& vch1 = stacktop(-1); + valtype& vch2 = stacktop(-2); + if (vch1.size() != vch2.size()) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype vch3(vch1); + for (size_t i = 0; i < vch1.size(); i++) + vch3[i] ^= vch2[i]; + popstack(stack); + popstack(stack); + stack.push_back(vch3); + } + break; + case OP_EQUAL: case OP_EQUALVERIFY: //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json index 286a19ed8e..cb8f82d1a0 100644 --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -826,26 +826,13 @@ ["NOP", "2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], ["1", "2 3 2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], -["'a' 'b'", "CAT", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], -["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], -["'abc' 1 1", "SUBSTR", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"], -["'abc' 1 1 0", "IF SUBSTR ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"], -["'abc' 2 0", "IF LEFT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "LEFT disabled"], -["'abc' 2 0", "IF RIGHT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "RIGHT disabled"], - ["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], -["'abc'", "IF INVERT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "INVERT disabled"], -["1 2 0 IF AND ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "AND disabled"], -["1 2 0 IF OR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "OR disabled"], -["1 2 0 IF XOR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "XOR disabled"], ["2 0 IF 2MUL ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "2MUL disabled"], ["2 0 IF 2DIV ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "2DIV disabled"], ["2 2 0 IF MUL ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "MUL disabled"], ["2 2 0 IF DIV ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "DIV disabled"], ["2 2 0 IF MOD ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "MOD disabled"], -["2 2 0 IF LSHIFT ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "LSHIFT disabled"], -["2 2 0 IF RSHIFT ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "RSHIFT disabled"], ["", "EQUAL NOT", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "EQUAL must error when there are no stack items"], ["0", "EQUAL NOT", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "EQUAL must error when there are not 2 stack items"], @@ -863,8 +850,6 @@ ["2 2MUL", "4 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], ["2 2DIV", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], ["7 3 MOD", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], -["2 2 LSHIFT", "8 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], -["2 1 RSHIFT", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], ["1", "NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 2 EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"], ["'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_11' EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"],