diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -58,6 +58,7 @@ test/mempool_tests.cpp \ test/merkle_tests.cpp \ test/miner_tests.cpp \ + test/monolith_opcodes_type.cpp \ test/multisig_tests.cpp \ test/net_tests.cpp \ test/netbase_tests.cpp \ diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -304,10 +304,10 @@ return true; case OP_BIN2NUM: - return true; + return false; case OP_NUM2BIN: - return true; + return false; case OP_AND: return true; @@ -1238,6 +1238,82 @@ } } 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); + valtype res = MinimalizeBigEndianArray(bin); + + if (res.size() > DEFAULT_MAX_NUM_BYTES) { + return set_error( + serror, SCRIPT_ERR_INVALID_BIN2NUM_OPERATION); + } + + // big endian to little endian conversion + std::reverse(res.begin(), res.end()); + + CScriptNum num(res, false); + if (num > (INT_MAX >> 1) || num < (INT_MIN >> 1)) { + return set_error( + serror, SCRIPT_ERR_INVALID_BIN2NUM_OPERATION); + } + popstack(stack); + 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) > + DEFAULT_MAX_NUM_BYTES) { + 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 < static_cast(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; + popstack(stack); + popstack(stack); + stack.push_back(result); + } 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 @@ -20,6 +20,10 @@ SCRIPT_ERR_SIG_COUNT, SCRIPT_ERR_PUBKEY_COUNT, + /* Operands checks */ + SCRIPT_ERR_INVALID_BIN2NUM_OPERATION, + SCRIPT_ERR_INVALID_NUM2BIN_OPERATION, + /* Failed verify operations */ SCRIPT_ERR_VERIFY, SCRIPT_ERR_EQUALVERIFY, 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 @@ -34,6 +34,10 @@ return "Signature count negative or greater than pubkey count"; case SCRIPT_ERR_PUBKEY_COUNT: return "Pubkey count negative or limit exceeded"; + 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_BAD_OPCODE: return "Opcode missing or not understood"; case SCRIPT_ERR_DISABLED_OPCODE: diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -35,6 +35,7 @@ mempool_tests.cpp merkle_tests.cpp miner_tests.cpp + monolith_opcodes_type.cpp multisig_tests.cpp net_tests.cpp netbase_tests.cpp 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 @@ -826,9 +826,21 @@ ["'abc' 1 1 0", "IF SPLIT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "SPLIT disabled"], ["'abc' 1 1 0", "IF SPLIT ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "SPLIT disabled"], ["'abc' 2 0", "IF NUM2BIN ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "NUM2BIN disabled"], -["'abc' 2 0", "IF NUM2BIN ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "NUM2BIN disabled"], ["'abc' 2 0", "IF BIN2NUM ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "BIN2NUM disabled"], -["'abc' 2 0", "IF BIN2NUM ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "BIN2NUM disabled"], + +["0x01 0x00", "BIN2NUM 0 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["0x05 0x0000000000", "BIN2NUM 0 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["0x01 0x01", "BIN2NUM 1 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["0x05 0x0000000001", "BIN2NUM", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["0x05 0x00000000FE", "BIN2NUM 254 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["0x05 0x8000000005", "BIN2NUM 0x01 0x85 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["0x05 0xffffffffff", "BIN2NUM", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_BIN2NUM_OPERATION"], + +["0 1", "NUM2BIN 0x01 0x00 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["1 1", "NUM2BIN 0x01 0x01 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["0x01 0xFE 1", "NUM2BIN 0x01 0xFE EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["0x01 0x02 4", "NUM2BIN 0x04 0x00000002 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["0x01 0x85 4", "NUM2BIN 0x04 0x80000005 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], ["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], diff --git a/src/test/monolith_opcodes_type.cpp b/src/test/monolith_opcodes_type.cpp new file mode 100644 diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -62,6 +62,8 @@ {SCRIPT_ERR_STACK_SIZE, "STACK_SIZE"}, {SCRIPT_ERR_SIG_COUNT, "SIG_COUNT"}, {SCRIPT_ERR_PUBKEY_COUNT, "PUBKEY_COUNT"}, + {SCRIPT_ERR_INVALID_BIN2NUM_OPERATION, "INVALID_BIN2NUM_OPERATION"}, + {SCRIPT_ERR_INVALID_NUM2BIN_OPERATION, "INVALID_NUM2BIN_OPERATION"}, {SCRIPT_ERR_VERIFY, "VERIFY"}, {SCRIPT_ERR_EQUALVERIFY, "EQUALVERIFY"}, {SCRIPT_ERR_CHECKMULTISIGVERIFY, "CHECKMULTISIGVERIFY"},