diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -61,6 +61,7 @@ test/multisig_tests.cpp \ test/net_tests.cpp \ test/netbase_tests.cpp \ + test/op_code.cpp \ test/pmt_tests.cpp \ test/policyestimator_tests.cpp \ test/pow_tests.cpp \ diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -335,15 +335,21 @@ } if (opcode == OP_CAT || opcode == OP_SPLIT || opcode == OP_LEFT || - opcode == OP_RIGHT || opcode == OP_INVERT || opcode == OP_AND || - opcode == OP_OR || opcode == OP_XOR || opcode == OP_2MUL || - opcode == OP_2DIV || opcode == OP_MUL || opcode == OP_DIV || - opcode == OP_MOD || opcode == OP_LSHIFT || + opcode == OP_RIGHT || opcode == OP_INVERT || + opcode == OP_2MUL || opcode == OP_2DIV || opcode == OP_MUL || + opcode == OP_DIV || opcode == OP_MOD || opcode == OP_LSHIFT || opcode == OP_RSHIFT) { // Disabled opcodes. return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); } + // if not monolith protocol upgrade (May 2018) then still disabled + if (!fEnabledOpCodesMonolith && + (opcode == OP_AND || opcode == OP_XOR || opcode == OP_OR)) { + // Disabled opcodes. + return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); + } + if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) { if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { @@ -780,6 +786,50 @@ // // Bitwise logic // + case OP_AND: + case OP_OR: + case OP_XOR: { + // (x1 x2 - out) + if (stack.size() < 2) { + return set_error( + serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + } + valtype &vch1 = stacktop(-2); + valtype &vch2 = stacktop(-1); + // throw error if inputs are not the same size + if (vch1.size() != vch2.size()) { + return set_error( + serror, SCRIPT_ERR_INVALID_BITWISE_OPERATION); + } + + switch (opcode) { + case OP_AND: + for (auto it = vch1.begin(); it != vch1.end(); + ++it) { + auto i = std::distance(vch1.begin(), it); + *it &= vch2[i]; + } + break; + case OP_OR: + for (auto it = vch1.begin(); it != vch1.end(); + ++it) { + auto i = std::distance(vch1.begin(), it); + *it |= vch2[i]; + } + break; + case OP_XOR: + for (auto it = vch1.begin(); it != vch1.end(); + ++it) { + auto i = std::distance(vch1.begin(), it); + *it ^= vch2[i]; + } + break; + default: + break; + } + stack.pop_back(); + } break; + case OP_EQUAL: case OP_EQUALVERIFY: // case OP_NOTEQUAL: // use OP_NUMNOTEQUAL diff --git a/src/script/script_error.h b/src/script/script_error.h --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -33,6 +33,7 @@ SCRIPT_ERR_INVALID_STACK_OPERATION, SCRIPT_ERR_INVALID_ALTSTACK_OPERATION, SCRIPT_ERR_UNBALANCED_CONDITIONAL, + SCRIPT_ERR_INVALID_BITWISE_OPERATION, /* CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY */ SCRIPT_ERR_NEGATIVE_LOCKTIME, diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -46,6 +46,8 @@ return "OP_RETURN was encountered"; case SCRIPT_ERR_UNBALANCED_CONDITIONAL: return "Invalid OP_IF construction"; + case SCRIPT_ERR_INVALID_BITWISE_OPERATION: + return "Invalid bitwise operation (check length of inputs)"; case SCRIPT_ERR_NEGATIVE_LOCKTIME: return "Negative locktime"; case SCRIPT_ERR_UNSATISFIED_LOCKTIME: diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -38,6 +38,7 @@ multisig_tests.cpp net_tests.cpp netbase_tests.cpp + op_code.cpp pmt_tests.cpp policyestimator_tests.cpp pow_tests.cpp diff --git a/src/test/op_code.cpp b/src/test/op_code.cpp new file mode 100644 --- /dev/null +++ b/src/test/op_code.cpp @@ -0,0 +1,265 @@ +// Copyright (c) 2011-2018 The Bitcoin Cash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "policy/policy.h" +#include "script/interpreter.h" +#include "script/script.h" +#include +#include + +using namespace std; + +#ifdef VERBOSE +#undef VERBOSE +#endif + +//-------------------------- +// uncomment the following line to see debug output +//#define VERBOSE +//-------------------------- + +#ifdef VERBOSE +#include "core_io.h" +#include +#include +#endif + +namespace { +typedef vector item; +typedef vector stack_t; + +#ifdef VERBOSE +void print(const item &i) { + if (i.empty()) cout << "empty"; + for (auto &s : i) + cout << hex << setw(2) << setfill('0') << (int)s << " "; + cout << endl; +} +void print(const stack_t &i) { + for (auto &s : i) + print(s); + cout << endl; +} +#endif + +/// Deepest sole function for testing expected errors +/// Invokes the interpreter. +void test(const CScript &script, stack_t stack, uint32_t flags, + const ScriptError se) { +#ifdef VERBOSE + cout << "--------------" << endl; + cout << "Checking script \"" << FormatScript(script) << "\" flags " << flags + << endl; + cout << "with input stack: " << endl; + print(stack); + cout << "expected error: " << se << endl; +#endif + ScriptError err = SCRIPT_ERR_OK; + BaseSignatureChecker sigchecker; + bool r = EvalScript(stack, script, flags, sigchecker, &err); + BOOST_CHECK_EQUAL(r, false); +#ifdef VERBOSE + cout << "got error: " << err << " vs " << se << endl; +#endif + BOOST_CHECK_EQUAL(err == se, true); +} + +/// Deepest sole function for testing expected returning stacks +/// Invokes the interpreter. +void test(const CScript &script, stack_t stack, uint32_t flags, + stack_t expected) { +#ifdef VERBOSE + cout << "--------------" << endl; + cout << "Checking script \"" << FormatScript(script) << "\" flags " << flags + << endl; + cout << "with input stack: " << endl; + print(stack); + cout << "expected output stack: " << endl; + print(expected); +#endif + ScriptError err; + BaseSignatureChecker sigchecker; + bool r = EvalScript(stack, script, flags, sigchecker, &err); +#ifdef VERBOSE + cout << "got output stack: " << endl; + print(stack); +#endif + BOOST_CHECK_EQUAL(r, true); + BOOST_CHECK_EQUAL(err, SCRIPT_ERR_OK); + BOOST_CHECK_EQUAL(stack == expected, true); +} + +/// OP_AND, OP_OR, and OP_XOR + +void test_bitwiseop(const CScript &script, uint32_t flags) { + // number of inputs + test(script, stack_t(), flags, SCRIPT_ERR_INVALID_STACK_OPERATION); + test(script, stack_t(), flags, SCRIPT_ERR_INVALID_STACK_OPERATION); + test(script, stack_t{{0x01}}, flags, SCRIPT_ERR_INVALID_STACK_OPERATION); + + // where len(x1) == 0 == len(x2) the output will be an empty array. + test(script, stack_t{{}, {}}, flags, stack_t{{}}); + + // operation fails when length of operands not equal + test(script, stack_t{{0x01}, {}}, flags, + SCRIPT_ERR_INVALID_BITWISE_OPERATION); + test(script, stack_t{{0x01, 0x01}, {}}, flags, + SCRIPT_ERR_INVALID_BITWISE_OPERATION); + test(script, stack_t{{}, {0x01}}, flags, + SCRIPT_ERR_INVALID_BITWISE_OPERATION); + test(script, stack_t{{}, {0x01, 0x01}}, flags, + SCRIPT_ERR_INVALID_BITWISE_OPERATION); + test(script, stack_t{{0x01}, {0x01, 0x01}}, flags, + SCRIPT_ERR_INVALID_BITWISE_OPERATION); + test(script, stack_t{{0x01, 0x01}, {0x01, 0x01, 0x01}}, flags, + SCRIPT_ERR_INVALID_BITWISE_OPERATION); + test(script, stack_t{{0x01, 0x01}, {0x01}}, flags, + SCRIPT_ERR_INVALID_BITWISE_OPERATION); + test(script, stack_t{{0x01, 0x01, 0x01}, {0x01, 0x01}}, flags, + SCRIPT_ERR_INVALID_BITWISE_OPERATION); +} + +/// OP_AND + +void test_and(uint32_t flags) { + CScript script; + script << OP_AND; + test_bitwiseop(script, flags); + test(script, stack_t{{0x00}, {0x00}}, flags, stack_t{{0x00}}); + test(script, stack_t{{0x00}, {0x01}}, flags, stack_t{{0x00}}); + test(script, stack_t{{0x01}, {0x00}}, flags, stack_t{{0x00}}); + test(script, stack_t{{0x01}, {0x01}}, flags, stack_t{{0x01}}); + + test(script, stack_t{{0x00, 0x00}, {0x00, 0x00}}, flags, + stack_t{{0x00, 0x00}}); + test(script, stack_t{{0x00, 0x00}, {0x01, 0x00}}, flags, + stack_t{{0x00, 0x00}}); + test(script, stack_t{{0x01, 0x00}, {0x00, 0x00}}, flags, + stack_t{{0x00, 0x00}}); + test(script, stack_t{{0x01, 0x00}, {0x01, 0x00}}, flags, + stack_t{{0x01, 0x00}}); + + { + item maxlenbin1(MAX_SCRIPT_ELEMENT_SIZE, 0x01); + item maxlenbin2(MAX_SCRIPT_ELEMENT_SIZE, 0xF0); + item maxlenbin3(MAX_SCRIPT_ELEMENT_SIZE, 0x01 & 0xF0); + test(script, stack_t{maxlenbin1, maxlenbin2}, flags, + stack_t{maxlenbin3}); + } + + { + item maxlenbin1(MAX_SCRIPT_ELEMENT_SIZE, 0x3C); + item maxlenbin2(MAX_SCRIPT_ELEMENT_SIZE, 0xDB); + item maxlenbin3(MAX_SCRIPT_ELEMENT_SIZE, 0x3C & 0xDB); + test(script, stack_t{maxlenbin1, maxlenbin2}, flags, + stack_t{maxlenbin3}); + } +} + +/// OP_OR + +void test_or(uint32_t flags) { + CScript script; + script << OP_OR; + test_bitwiseop(script, flags); + + test(script, stack_t{{0x00}, {0x00}}, flags, stack_t{{0x00}}); + test(script, stack_t{{0x00}, {0x01}}, flags, stack_t{{0x01}}); + test(script, stack_t{{0x01}, {0x00}}, flags, stack_t{{0x01}}); + test(script, stack_t{{0x01}, {0x01}}, flags, stack_t{{0x01}}); + + test(script, stack_t{{0x00, 0x00}, {0x00, 0x00}}, flags, + stack_t{{0x00, 0x00}}); + test(script, stack_t{{0x00, 0x00}, {0x01, 0x00}}, flags, + stack_t{{0x01, 0x00}}); + test(script, stack_t{{0x01, 0x00}, {0x00, 0x00}}, flags, + stack_t{{0x01, 0x00}}); + test(script, stack_t{{0x01, 0x00}, {0x01, 0x00}}, flags, + stack_t{{0x01, 0x00}}); + + { + item maxlenbin1(MAX_SCRIPT_ELEMENT_SIZE, 0x01); + item maxlenbin2(MAX_SCRIPT_ELEMENT_SIZE, 0xF0); + item maxlenbin3(MAX_SCRIPT_ELEMENT_SIZE, 0x01 | 0xF0); + test(script, stack_t{maxlenbin1, maxlenbin2}, flags, + stack_t{maxlenbin3}); + } + + { + item maxlenbin1(MAX_SCRIPT_ELEMENT_SIZE, 0x3C); + item maxlenbin2(MAX_SCRIPT_ELEMENT_SIZE, 0xDB); + item maxlenbin3(MAX_SCRIPT_ELEMENT_SIZE, 0x3C | 0xDB); + test(script, stack_t{maxlenbin1, maxlenbin2}, flags, + stack_t{maxlenbin3}); + } +} + +/// OP_XOR tests + +void test_xor(uint32_t flags) { + CScript script; + script << OP_XOR; + test_bitwiseop(script, flags); + + test(script, stack_t{{0x00}, {0x00}}, flags, stack_t{{0x00}}); + test(script, stack_t{{0x00}, {0x01}}, flags, stack_t{{0x01}}); + test(script, stack_t{{0x01}, {0x00}}, flags, stack_t{{0x01}}); + test(script, stack_t{{0x01}, {0x01}}, flags, stack_t{{0x00}}); + + test(script, stack_t{{0x00, 0x00}, {0x00, 0x00}}, flags, + stack_t{{0x00, 0x00}}); + test(script, stack_t{{0x00, 0x00}, {0x01, 0x00}}, flags, + stack_t{{0x01, 0x00}}); + test(script, stack_t{{0x01, 0x00}, {0x00, 0x00}}, flags, + stack_t{{0x01, 0x00}}); + test(script, stack_t{{0x01, 0x00}, {0x01, 0x00}}, flags, + stack_t{{0x00, 0x00}}); + + { + item maxlenbin1(MAX_SCRIPT_ELEMENT_SIZE, 0x01); + item maxlenbin2(MAX_SCRIPT_ELEMENT_SIZE, 0xF0); + item maxlenbin3(MAX_SCRIPT_ELEMENT_SIZE, 0x01 ^ 0xF0); + test(script, stack_t{maxlenbin1, maxlenbin2}, flags, + stack_t{maxlenbin3}); + } + + { + item maxlenbin1(MAX_SCRIPT_ELEMENT_SIZE, 0x3C); + item maxlenbin2(MAX_SCRIPT_ELEMENT_SIZE, 0xDB); + item maxlenbin3(MAX_SCRIPT_ELEMENT_SIZE, 0x3C ^ 0xDB); + test(script, stack_t{maxlenbin1, maxlenbin2}, flags, + stack_t{maxlenbin3}); + } +} +} + +/// Entry points + +BOOST_AUTO_TEST_SUITE(op_code) + +BOOST_AUTO_TEST_CASE(op_and) { + test_and(SCRIPT_ENABLE_OPCODES_MONOLITH); + test_and(STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); + test_and(STANDARD_NOT_MANDATORY_VERIFY_FLAGS | + SCRIPT_ENABLE_OPCODES_MONOLITH); + test_and(STANDARD_LOCKTIME_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); +} + +BOOST_AUTO_TEST_CASE(op_or) { + test_or(SCRIPT_ENABLE_OPCODES_MONOLITH); + test_or(STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); + test_or(STANDARD_NOT_MANDATORY_VERIFY_FLAGS | + SCRIPT_ENABLE_OPCODES_MONOLITH); + test_or(STANDARD_LOCKTIME_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); +} + +BOOST_AUTO_TEST_CASE(op_xor) { + test_xor(SCRIPT_ENABLE_OPCODES_MONOLITH); + test_xor(STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); + test_xor(STANDARD_NOT_MANDATORY_VERIFY_FLAGS | + SCRIPT_ENABLE_OPCODES_MONOLITH); + test_xor(STANDARD_LOCKTIME_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file