diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -334,19 +334,18 @@ return set_error(serror, SCRIPT_ERR_OP_COUNT); } - if (opcode == OP_NUM2BIN || opcode == OP_BIN2NUM || - opcode == OP_INVERT || opcode == OP_2MUL || opcode == OP_2DIV || - opcode == OP_MUL || opcode == OP_LSHIFT || - opcode == OP_RSHIFT) { + if (opcode == OP_NUM2BIN || opcode == OP_INVERT || + opcode == OP_2MUL || opcode == OP_2DIV || opcode == OP_MUL || + 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_CAT || opcode == OP_SPLIT || opcode == OP_AND || - opcode == OP_XOR || opcode == OP_OR || opcode == OP_DIV || - opcode == OP_MOD)) { + (opcode == OP_CAT || opcode == OP_SPLIT || + opcode == OP_BIN2NUM || opcode == OP_AND || opcode == OP_XOR || + opcode == OP_OR || opcode == OP_DIV || opcode == OP_MOD)) { // Disabled opcodes. return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); } @@ -1309,6 +1308,28 @@ } } break; + // + // Conversion operations + // + case OP_BIN2NUM: { + // (in -- out) + if (stack.size() < 1) { + return set_error( + serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + } + valtype bin = stacktop(-1); + std::reverse(bin.begin(), bin.end()); // big endian to + // little endian + // conversion + CScriptNum num(bin, false); + if (num > (INT_MAX >> 1) || num < (INT_MIN >> 1)) { + return set_error( + serror, SCRIPT_ERR_INVALID_BIN2NUM_OPERATION); + } + stack.pop_back(); + stack.push_back(num.getvch()); + } break; + default: return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } 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 @@ -37,6 +37,7 @@ SCRIPT_ERR_DIV_BY_ZERO, SCRIPT_ERR_MOD_BY_ZERO, SCRIPT_ERR_INVALID_SPLIT_RANGE, + SCRIPT_ERR_INVALID_BIN2NUM_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 @@ -54,6 +54,8 @@ return "Invalid modulo operation"; case SCRIPT_ERR_INVALID_SPLIT_RANGE: return "Invalid OP_SPLIT range"; + case SCRIPT_ERR_INVALID_BIN2NUM_OPERATION: + return "Invalid OP_BIN2NUM operation (check operand values)"; case SCRIPT_ERR_NEGATIVE_LOCKTIME: return "Negative locktime"; case SCRIPT_ERR_UNSATISFIED_LOCKTIME: diff --git a/src/test/op_code.cpp b/src/test/op_code.cpp --- a/src/test/op_code.cpp +++ b/src/test/op_code.cpp @@ -496,6 +496,70 @@ test_cat_split({0x01, 0x02}, flags); test_cat_split({0x01, 0x02, 0x03}, flags); } + +/// OP_BIN2NUM tests + +/// make bin - helper function +/// input: a number +/// output: BIN representation Big Endian (BE) +/// removes the sign, constructs a BE array of bytes with the positive +/// number, +/// the adds the sign. +item mk_bin(int64_t numBinary) { + if (numBinary == 0) return item{0x00}; + bool neg = numBinary < 0; + uint64_t v = htobe64(neg ? -numBinary : numBinary); + item result; + result.reserve(sizeof(uint64_t)); + uint8_t *p = reinterpret_cast(&v); + for (size_t i = 0; i < sizeof(uint64_t); ++i, ++p) { + if (result.empty()) { + if (!*p) continue; + if (*p & 0x80) + result.push_back(0x00); // first bit looks like a sign but it is + // not, add a leading 0 + } + result.push_back(*p); + } + if (neg) *result.begin() |= 0x80; // add the sign + return move(result); +} + +void test_bin2num(uint32_t flags) { + CScript script; + script << OP_BIN2NUM; + { + item i{0x00, 0x80, 0x00, 0x05}; + BOOST_CHECK_EQUAL(mk_bin(0x800005) == i, true); + } + { + item i{0x05}; + BOOST_CHECK_EQUAL(mk_bin(0x000005) == i, true); + } + { + item i{0x01, 0x05}; + BOOST_CHECK_EQUAL(mk_bin(0x000105) == i, true); + } + { + item i{0x81, 0x05}; + BOOST_CHECK_EQUAL(mk_bin(-0x000105) == i, true); + } + test(script, stack_t(), flags, SCRIPT_ERR_INVALID_STACK_OPERATION); + test(script, stack_t{mk_bin(0)}, flags, stack_t{{}}); + test(script, stack_t{mk_bin((int64_t)INT_MAX >> 1)}, flags, + stack_t{CScriptNum(INT_MAX >> 1).getvch()}); + test(script, stack_t{mk_bin((int64_t)INT_MIN >> 1)}, flags, + stack_t{CScriptNum(INT_MIN >> 1).getvch()}); + test(script, stack_t{mk_bin((int64_t)(INT_MAX >> 1) + 1)}, flags, + SCRIPT_ERR_INVALID_BIN2NUM_OPERATION); + test(script, stack_t{mk_bin((int64_t)(INT_MIN >> 1) - 1)}, flags, + SCRIPT_ERR_INVALID_BIN2NUM_OPERATION); + test(script, stack_t{mk_bin(106894)}, flags, + stack_t{CScriptNum(106894).getvch()}); + test(script, stack_t{mk_bin(-106894)}, flags, + stack_t{CScriptNum(-106894).getvch()}); + test(script, stack_t{mk_bin(0)}, flags, stack_t{CScriptNum(0).getvch()}); +} } /// Entry points @@ -568,4 +632,13 @@ SCRIPT_ENABLE_OPCODES_MONOLITH); } +BOOST_AUTO_TEST_CASE(op_bin2num) { + test_bin2num(SCRIPT_ENABLE_OPCODES_MONOLITH); + test_bin2num(STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); + test_bin2num(STANDARD_NOT_MANDATORY_VERIFY_FLAGS | + SCRIPT_ENABLE_OPCODES_MONOLITH); + test_bin2num(STANDARD_LOCKTIME_VERIFY_FLAGS | + SCRIPT_ENABLE_OPCODES_MONOLITH); +} + BOOST_AUTO_TEST_SUITE_END()