diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -74,6 +74,7 @@ test/script_antireplay_tests.cpp \ test/script_P2SH_tests.cpp \ test/script_tests.cpp \ + test/op_code.cpp \ test/script_sighashtype_tests.cpp \ test/scriptflags.cpp \ test/scriptflags.h \ diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -336,11 +336,9 @@ } 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_2MUL || + 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) { + opcode == OP_MOD || opcode == OP_LSHIFT || opcode == OP_RSHIFT) { // Disabled opcodes. return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); } @@ -781,6 +779,42 @@ // // 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 (size_t i = 0; i < vch1.size(); i++) { + vch1[i] &= vch2[i]; + } + break; + case OP_OR: + for (size_t i = 0; i < vch1.size(); i++) { + vch1[i] |= vch2[i]; + } + break; + case OP_XOR: + for (size_t i = 0; i < vch1.size(); i++) { + vch1[i] ^= vch2[i]; + } + 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/data/script_tests.json b/src/test/data/script_tests.json --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -819,17 +819,10 @@ ["'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"], 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,262 @@ +// 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 + +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(0); + test_and(STANDARD_SCRIPT_VERIFY_FLAGS); + test_and(STANDARD_NOT_MANDATORY_VERIFY_FLAGS); + test_and(STANDARD_LOCKTIME_VERIFY_FLAGS); +} + +BOOST_AUTO_TEST_CASE(op_or) { + test_or(0); + test_or(STANDARD_SCRIPT_VERIFY_FLAGS); + test_or(STANDARD_NOT_MANDATORY_VERIFY_FLAGS); + test_or(STANDARD_LOCKTIME_VERIFY_FLAGS); +} + +BOOST_AUTO_TEST_CASE(op_xor) { + test_xor(0); + test_xor(STANDARD_SCRIPT_VERIFY_FLAGS); + test_xor(STANDARD_NOT_MANDATORY_VERIFY_FLAGS); + test_xor(STANDARD_LOCKTIME_VERIFY_FLAGS); +} + +BOOST_AUTO_TEST_SUITE_END()