diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -337,15 +337,16 @@ if (opcode == OP_CAT || opcode == OP_SPLIT || opcode == OP_NUM2BIN || opcode == OP_BIN2NUM || 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_MUL || 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)) { + (opcode == OP_AND || opcode == OP_XOR || opcode == OP_OR || + opcode == OP_DIV)) { // Disabled opcodes. return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); } @@ -908,6 +909,7 @@ case OP_ADD: case OP_SUB: + case OP_DIV: case OP_BOOLAND: case OP_BOOLOR: case OP_NUMEQUAL: @@ -936,6 +938,15 @@ bn = bn1 - bn2; break; + case OP_DIV: + // 2nd operand must not be 0 + if (bn2 == 0) { + return set_error(serror, + SCRIPT_ERR_DIV_BY_ZERO); + } + bn = bn1 / bn2; + break; + case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break; diff --git a/src/script/script.h b/src/script/script.h --- a/src/script/script.h +++ b/src/script/script.h @@ -275,6 +275,12 @@ inline CScriptNum operator-(const CScriptNum &rhs) const { return operator-(rhs.m_value); } + inline CScriptNum operator/(const int64_t &rhs) const { + return CScriptNum(m_value / rhs); + } + inline CScriptNum operator/(const CScriptNum &rhs) const { + return operator/(rhs.m_value); + } inline CScriptNum &operator+=(const CScriptNum &rhs) { return operator+=(rhs.m_value); 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 @@ -34,6 +34,7 @@ SCRIPT_ERR_INVALID_ALTSTACK_OPERATION, SCRIPT_ERR_UNBALANCED_CONDITIONAL, SCRIPT_ERR_INVALID_BITWISE_OPERATION, + SCRIPT_ERR_DIV_BY_ZERO, /* 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 @@ -48,6 +48,8 @@ return "Invalid OP_IF construction"; case SCRIPT_ERR_INVALID_BITWISE_OPERATION: return "Invalid bitwise operation (check length of inputs)"; + case SCRIPT_ERR_DIV_BY_ZERO: + return "Invalid division operation"; 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 @@ -232,6 +232,91 @@ stack_t{maxlenbin3}); } } + +/// OP_DIV tests + +void test_div(uint32_t flags) { + CScript script; + script << OP_DIV; + + test(script, stack_t(), flags, SCRIPT_ERR_INVALID_STACK_OPERATION); + test(script, stack_t{{}}, flags, SCRIPT_ERR_INVALID_STACK_OPERATION); + + // test not valid numbers + test(script, stack_t{{0x01, 0x02, 0x03, 0x04, 0x05}, + {0x01, 0x02, 0x03, 0x04, 0x05}}, + flags, SCRIPT_ERR_UNKNOWN_ERROR); + test(script, stack_t{{0x01, 0x02, 0x03, 0x04, 0x05}, {0x01}}, flags, + SCRIPT_ERR_UNKNOWN_ERROR); + test(script, stack_t{{0x01, 0x05}, {0x01, 0x02, 0x03, 0x04, 0x05}}, flags, + SCRIPT_ERR_UNKNOWN_ERROR); + // b == 0 ; b is equal to any type of zero + test(script, stack_t{{0x01, 0x05}, {}}, flags, SCRIPT_ERR_DIV_BY_ZERO); + test(script, stack_t{{}, {}}, flags, SCRIPT_ERR_DIV_BY_ZERO); + if (flags & SCRIPT_VERIFY_MINIMALDATA) { + test(script, stack_t{{}, {0x00}}, flags, + SCRIPT_ERR_UNKNOWN_ERROR); // not minimal encoding + test(script, stack_t{{}, {0x00, 0x00}}, flags, + SCRIPT_ERR_UNKNOWN_ERROR); + } else { + test(script, stack_t{{}, {0x00}}, flags, SCRIPT_ERR_DIV_BY_ZERO); + test(script, stack_t{{}, {0x00, 0x00}}, flags, SCRIPT_ERR_DIV_BY_ZERO); + } + // 185377af/85f41b01 =-4 + // 185377af/00001b01 =E69D + test(script, stack_t{{0xaf, 0x77, 0x53, 0x18}, {0x01, 0x1b, 0xf4, 0x85}}, + flags, stack_t{{0x84}}); + test(script, stack_t{{0xaf, 0x77, 0x53, 0x18}, {0x01, 0x1b}}, flags, + stack_t{{0x9D, 0xE6, 0x00}}); + // 15/4 =3 + // 15/-4 =-3 + //-15/4 =-3 + //-15/-4 =3 + test(script, stack_t{{0x0f}, {0x04}}, flags, stack_t{{0x03}}); + test(script, stack_t{{0x0f}, {0x84}}, flags, stack_t{{0x83}}); + test(script, stack_t{{0x8f}, {0x04}}, flags, stack_t{{0x83}}); + test(script, stack_t{{0x8f}, {0x84}}, flags, stack_t{{0x03}}); + // 15000/4 =3750 + // 15000/-4 =-3750 + //-15000/4 =-3750 + //-15000/-4 =3750 + test(script, stack_t{{0x98, 0x3a}, {0x04}}, flags, stack_t{{0xa6, 0x0e}}); + test(script, stack_t{{0x98, 0x3a}, {0x84}}, flags, stack_t{{0xa6, 0x8e}}); + test(script, stack_t{{0x98, 0xba}, {0x04}}, flags, stack_t{{0xa6, 0x8e}}); + test(script, stack_t{{0x98, 0xba}, {0x84}}, flags, stack_t{{0xa6, 0x0e}}); + // 15000/4000 =3 + // 15000/-4000 =-3 + //-15000/4000 =-3 + //-15000/-4000 =3 + test(script, stack_t{{0x98, 0x3a}, {0xa0, 0x0f}}, flags, stack_t{{0x03}}); + test(script, stack_t{{0x98, 0x3a}, {0xa0, 0x8f}}, flags, stack_t{{0x83}}); + test(script, stack_t{{0x98, 0xba}, {0xa0, 0x0f}}, flags, stack_t{{0x83}}); + test(script, stack_t{{0x98, 0xba}, {0xa0, 0x8f}}, flags, stack_t{{0x03}}); + // 15000000/4000 =3750 + // 15000000/-4000 =-3750 + //-15000000/4000 =-3750 + //-15000000/-4000 =3750 + test(script, stack_t{{0xc0, 0xe1, 0xe4, 0x00}, {0xa0, 0x0f}}, flags, + stack_t{{0xa6, 0x0e}}); + test(script, stack_t{{0xc0, 0xe1, 0xe4, 0x00}, {0xa0, 0x8f}}, flags, + stack_t{{0xa6, 0x8e}}); + test(script, stack_t{{0xc0, 0xe1, 0xe4, 0x80}, {0xa0, 0x0f}}, flags, + stack_t{{0xa6, 0x8e}}); + test(script, stack_t{{0xc0, 0xe1, 0xe4, 0x80}, {0xa0, 0x8f}}, flags, + stack_t{{0xa6, 0x0e}}); + // 15000000/4 =3750000 + // 15000000/-4 =-3750000 + //-15000000/4 =-3750000 + //-15000000/-4 =3750000 + test(script, stack_t{{0xc0, 0xe1, 0xe4, 0x00}, {0x04}}, flags, + stack_t{{0x70, 0x38, 0x39}}); + test(script, stack_t{{0xc0, 0xe1, 0xe4, 0x00}, {0x84}}, flags, + stack_t{{0x70, 0x38, 0xb9}}); + test(script, stack_t{{0xc0, 0xe1, 0xe4, 0x80}, {0x04}}, flags, + stack_t{{0x70, 0x38, 0xb9}}); + test(script, stack_t{{0xc0, 0xe1, 0xe4, 0x80}, {0x84}}, flags, + stack_t{{0x70, 0x38, 0x39}}); +} } /// Entry points @@ -262,4 +347,12 @@ test_xor(STANDARD_LOCKTIME_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_CASE(op_div) { + test_div(SCRIPT_ENABLE_OPCODES_MONOLITH); + test_div(STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); + test_div(STANDARD_NOT_MANDATORY_VERIFY_FLAGS | + SCRIPT_ENABLE_OPCODES_MONOLITH); + test_div(STANDARD_LOCKTIME_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); +} + +BOOST_AUTO_TEST_SUITE_END()