Index: src/script/interpreter.cpp =================================================================== --- src/script/interpreter.cpp +++ src/script/interpreter.cpp @@ -335,9 +335,8 @@ return set_error(serror, SCRIPT_ERR_OP_COUNT); } - if (opcode == OP_RIGHT || opcode == OP_INVERT || opcode == OP_2MUL || - opcode == OP_2DIV || opcode == OP_MUL || opcode == OP_LSHIFT || - opcode == OP_RSHIFT) { + if (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); } @@ -1311,6 +1310,44 @@ stack.push_back(num.getvch()); } break; + case OP_NUM2BIN: { + // (in size -- out) + if (stack.size() < 2) { + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + } + CScriptNum num(stacktop(-2), fRequireMinimal); + int64_t size = CScriptNum(stacktop(-1), fRequireMinimal).getint(); + if (size < 1 || static_cast(size) > CScriptNum::nDefaultMaxNumSize) { + return set_error(serror, SCRIPT_ERR_INVALID_NUM2BIN_OPERATION); + } + // Produces a byte vector of num and check if input size is valid + valtype vchNum = num.getvch(); + if (size < vchNum.size()) { + return set_error(serror, SCRIPT_ERR_INVALID_NUM2BIN_OPERATION); + } + // Initialize byte vector for result + valtype result; + result.reserve(size); + bool neg{false}; + // Avoid negative zero + if (!vchNum.empty()) { + neg = *vchNum.rbegin()&0x80; + *vchNum.rbegin() &= ~0x80; + } + // Pad result to declared input size + size_t pad = size - vchNum.size(); + for (uint8_t i = 0; i < pad; ++i) { + result.push_back(0); + } + for (auto i = vchNum.rbegin(); i != vchNum.rend(); ++i) { + result.push_back(*i); + } + if (neg) *result.begin()|=0x80; + stack.pop_back(); + stack.pop_back(); + stack.push_back(result); + } break; + default: return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } Index: src/script/script.h =================================================================== --- src/script/script.h +++ src/script/script.h @@ -103,7 +103,6 @@ // splice ops OP_CAT = 0x7e, OP_SPLIT = 0x7f, - OP_RIGHT = 0x81, OP_SIZE = 0x82, // bit logic @@ -162,6 +161,7 @@ // expansion OP_BIN2NUM = 0x80, + OP_NUM2BIN = 0x81, OP_NOP1 = 0xb0, OP_CHECKLOCKTIMEVERIFY = 0xb1, OP_NOP2 = OP_CHECKLOCKTIMEVERIFY, Index: src/script/script.cpp =================================================================== --- src/script/script.cpp +++ src/script/script.cpp @@ -123,8 +123,6 @@ return "OP_CAT"; case OP_SPLIT: return "OP_SPLIT"; - case OP_RIGHT: - return "OP_RIGHT"; case OP_SIZE: return "OP_SIZE"; Index: src/script/script_error.h =================================================================== --- src/script/script_error.h +++ src/script/script_error.h @@ -38,6 +38,7 @@ SCRIPT_ERR_MOD_BY_ZERO, SCRIPT_ERR_INVALID_SPLIT_RANGE, SCRIPT_ERR_INVALID_BIN2NUM_OPERATION, + SCRIPT_ERR_INVALID_NUM2BIN_OPERATION, /* CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY */ SCRIPT_ERR_NEGATIVE_LOCKTIME, Index: src/script/script_error.cpp =================================================================== --- src/script/script_error.cpp +++ src/script/script_error.cpp @@ -56,6 +56,8 @@ return "Invalid OP_SPLIT range"; case SCRIPT_ERR_INVALID_BIN2NUM_OPERATION: return "Invalid OP_BIN2NUM operation (check operand values)"; + case SCRIPT_ERR_INVALID_NUM2BIN_OPERATION: + return "Invalid OP_NUM2BIN operation (check operand values)"; case SCRIPT_ERR_NEGATIVE_LOCKTIME: return "Negative locktime"; case SCRIPT_ERR_UNSATISFIED_LOCKTIME: Index: src/test/op_code.cpp =================================================================== --- src/test/op_code.cpp +++ src/test/op_code.cpp @@ -466,6 +466,142 @@ test(script,stack_t{mk_bin(0)},flags,stack_t{CScriptNum(0).getvch()}); } + /// OP_NUM2BIN tests + + /// make expected value - helper function + /// input: number in LE byte order, desired output byte length + /// output: BIN representation as expected to be generated by the + /// interpreter. + /// removes the sign, constructs a BE array of bytes with the positive + /// number, + /// the adds the sign. + /// + vector make_ev(vector vchInput, size_t size) { + if (vchInput.empty()) return vector(size, 0); + vector result; + assert(size >= vchInput.size()); + result.reserve(size); + bool neg = *vchInput.rbegin() & 0x80; + *vchInput.rbegin() &= ~0x80; + size_t pad = size - vchInput.size(); + for (uint8_t i = 0; i < pad; ++i) { + result.push_back(0); + } + for (auto i = vchInput.rbegin(); i != vchInput.rend(); ++i) { + result.push_back(*i); + } + if (neg) *result.begin() |= 0x80; + return result; + } + + void test_num2bin(const CScript &script, vector vchInput, uint32_t flags) { + if (vchInput.empty()) return; + for (uint8_t i = 0; i < vchInput.size(); ++i) { + if (i == 0) test(script, stack_t{vchInput, {}}, flags, SCRIPT_ERR_INVALID_NUM2BIN_OPERATION); + else test(script, stack_t{vchInput, {i}}, flags, SCRIPT_ERR_INVALID_NUM2BIN_OPERATION); + } + for (uint8_t i = vchInput.size(); i <= CScriptNum::nDefaultMaxNumSize; ++i) { + if (i == 0) test(script, stack_t{vchInput, {}}, flags, stack_t{make_ev(vchInput, i)}); + else test(script, stack_t{vchInput, {i}}, flags, stack_t{make_ev(vchInput, i)}); + } + } + + void test_num2bin(uint32_t flags) { + CScript script; + script << OP_NUM2BIN; + test(script, stack_t(), flags, SCRIPT_ERR_INVALID_STACK_OPERATION); + test(script, stack_t{{4}}, flags, SCRIPT_ERR_INVALID_STACK_OPERATION); + test(script, stack_t{{0x02}, {CScriptNum::nDefaultMaxNumSize + 1}}, flags, + SCRIPT_ERR_INVALID_NUM2BIN_OPERATION); + test(script, stack_t{{0x85}, {CScriptNum::nDefaultMaxNumSize + 1}}, flags, + SCRIPT_ERR_INVALID_NUM2BIN_OPERATION); + test(script, stack_t{{0x02}, {}}, flags, + SCRIPT_ERR_INVALID_NUM2BIN_OPERATION); + test(script, stack_t{{0x85}, {0x85}}, flags, + SCRIPT_ERR_INVALID_NUM2BIN_OPERATION); + test(script, stack_t{{0x85}, {}}, flags, + SCRIPT_ERR_INVALID_NUM2BIN_OPERATION); + test_num2bin(script, {}, flags); + test_num2bin(script, {0x7f}, flags); + test_num2bin(script, {0xff, 0x7f}, flags); // LE for 0x7FFF + test_num2bin(script, {0x02, 0x71}, flags); + test_num2bin(script, {0xff, 0xff, 0x7f}, flags); + test_num2bin(script, {0x03, 0x02, 0x71}, flags); + test_num2bin(script, {0xff, 0xff, 0xff, 0x7f}, flags); + test_num2bin(script, {0x04, 0x03, 0x02, 0x71}, flags); + test_num2bin(script, {0x81}, flags); + test_num2bin(script, {0xff, 0x80}, flags); + test_num2bin(script, {0xaf, 0x81}, flags); + test_num2bin(script, {0xed, 0x60, 0x83}, flags); + test_num2bin(script, {0xb6, 0xe3, 0x81}, flags); + test_num2bin(script, {0x81, 0x9a, 0x6e, 0x84}, flags); + test_num2bin(script, {0xe4, 0xc3, 0x92, 0x91}, flags); + } + + /// OP_BIN2NUM + OP_NUM2BIN tests + + void test_bin2num_num2bin(const CScript &script, size_t size, int64_t numBinary, uint32_t flags) { + auto x = mk_bin(numBinary); + test(script, stack_t{x}, flags, stack_t{make_ev(CScriptNum(numBinary).getvch(), size)}); + } + + void test_num2bin_bin2num(const CScript &script, int64_t numBinary, uint32_t flags) { + test(script, stack_t{CScriptNum(numBinary).getvch()}, flags, stack_t{CScriptNum(numBinary).getvch()}); + } + + void test_bin2num_num2bin(int size, uint32_t flags) { + CScript script; + script << OP_BIN2NUM << size << OP_NUM2BIN; + test_bin2num_num2bin(script, size, 0, flags); + test_bin2num_num2bin(script, size, 1, flags); + test_bin2num_num2bin(script, size, -1, flags); + if (size >= 2) { + test_bin2num_num2bin(script, size, 321, flags); + test_bin2num_num2bin(script, size, -321, flags); + if (size >= 3) { + test_bin2num_num2bin(script, size, 106894, flags); + test_bin2num_num2bin(script, size, -106894, flags); + if (size >= 4) { + test_bin2num_num2bin(script, size, INT_MAX >> 1, flags); + test_bin2num_num2bin(script, size, INT_MIN >> 1, flags); + } + } + } + } + + void test_num2bin_bin2num(int size, uint32_t flags) { + CScript script; + script << size << OP_NUM2BIN << OP_BIN2NUM; + test_num2bin_bin2num(script, 0, flags); + test_num2bin_bin2num(script, 1, flags); + test_num2bin_bin2num(script, -1, flags); + if (size >= 2) { + test_num2bin_bin2num(script, 321, flags); + test_num2bin_bin2num(script, -321, flags); + if (size >= 3) { + test_num2bin_bin2num(script, 106894, flags); + test_num2bin_bin2num(script, -106894, flags); + if (size >= 4) { + test_num2bin_bin2num(script, INT_MAX >> 1, flags); + test_num2bin_bin2num(script, INT_MIN >> 1, flags); + } + } + } + } + + void test_bin2num_num2bin(uint32_t flags) { + test_bin2num_num2bin(4,flags); //expect 4 byte output + test_bin2num_num2bin(3,flags); //expect 3 byte output + test_bin2num_num2bin(2,flags); //expect 2 byte output + test_bin2num_num2bin(1,flags); //expect 1 byte output + } + + void test_num2bin_bin2num(uint32_t flags) { + test_num2bin_bin2num(4,flags); //4 byte num2bin output + test_num2bin_bin2num(3,flags); //3 byte num2bin output + test_num2bin_bin2num(2,flags); //2 byte num2bin output + test_num2bin_bin2num(1,flags); //1 byte num2bin output + } } /// Entry points @@ -535,5 +671,27 @@ test_bin2num(STANDARD_LOCKTIME_VERIFY_FLAGS); } +BOOST_AUTO_TEST_CASE(op_num2bin) { + test_num2bin(0); + test_num2bin(STANDARD_SCRIPT_VERIFY_FLAGS); + test_num2bin(STANDARD_NOT_MANDATORY_VERIFY_FLAGS); + test_num2bin(STANDARD_LOCKTIME_VERIFY_FLAGS); + +} + +BOOST_AUTO_TEST_CASE(bin2num_num2bin) { + test_bin2num_num2bin(0); + test_bin2num_num2bin(STANDARD_SCRIPT_VERIFY_FLAGS); + test_bin2num_num2bin(STANDARD_NOT_MANDATORY_VERIFY_FLAGS); + test_bin2num_num2bin(STANDARD_LOCKTIME_VERIFY_FLAGS); +} + +BOOST_AUTO_TEST_CASE(num2bin_bin2num) { + test_num2bin_bin2num(0); + test_num2bin_bin2num(STANDARD_SCRIPT_VERIFY_FLAGS); + test_num2bin_bin2num(STANDARD_NOT_MANDATORY_VERIFY_FLAGS); + test_num2bin_bin2num(STANDARD_LOCKTIME_VERIFY_FLAGS); +} + BOOST_AUTO_TEST_SUITE_END()