diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -337,7 +337,7 @@ 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_MOD || opcode == OP_LSHIFT || + opcode == OP_MUL || opcode == OP_LSHIFT || opcode == OP_RSHIFT) { // Disabled opcodes. return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); @@ -346,7 +346,7 @@ // if not monolith protocol upgrade (May 2018) then still disabled if (!fEnabledOpCodesMonolith && (opcode == OP_AND || opcode == OP_XOR || opcode == OP_OR || - opcode == OP_DIV)) { + opcode == OP_DIV || opcode == OP_MOD)) { // Disabled opcodes. return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); } @@ -910,6 +910,7 @@ case OP_ADD: case OP_SUB: case OP_DIV: + case OP_MOD: case OP_BOOLAND: case OP_BOOLOR: case OP_NUMEQUAL: @@ -947,6 +948,15 @@ bn = bn1 / bn2; break; + case OP_MOD: + // 2nd operand must not be 0 + if (bn2 == 0) { + return set_error(serror, + SCRIPT_ERR_MOD_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 @@ -281,6 +281,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 @@ -35,6 +35,7 @@ SCRIPT_ERR_UNBALANCED_CONDITIONAL, SCRIPT_ERR_INVALID_BITWISE_OPERATION, SCRIPT_ERR_DIV_BY_ZERO, + SCRIPT_ERR_MOD_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 @@ -50,6 +50,8 @@ return "Invalid bitwise operation (check length of inputs)"; case SCRIPT_ERR_DIV_BY_ZERO: return "Invalid division operation"; + case SCRIPT_ERR_MOD_BY_ZERO: + return "Invalid modulo 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 @@ -317,6 +317,48 @@ test(script, stack_t{{0xc0, 0xe1, 0xe4, 0x80}, {0x84}}, flags, stack_t{{0x70, 0x38, 0x39}}); } + +/// OP_MOD tests + +void test_mod(uint32_t flags) { + CScript script; + script << OP_MOD; + + 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); + + // mod by 0 + test(script, stack_t{{0x01, 0x05}, {}}, flags, SCRIPT_ERR_MOD_BY_ZERO); + + // 56488123%321 =148 + // 56488123%3 =1 + // 56488123%564881230 =56488123 + test(script, stack_t{{0xbb, 0xf0, 0x5d, 0x03}, {0x41, 0x01}}, flags, + stack_t{{0x94, 0x00}}); + test(script, stack_t{{0xbb, 0xf0, 0x5d, 0x03}, {0x03}}, flags, + stack_t{{0x01}}); + test(script, stack_t{{0xbb, 0xf0, 0x5d, 0x03}, {0x4e, 0x67, 0xab, 0x21}}, + flags, stack_t{{0xbb, 0xf0, 0x5d, 0x03}}); + + //-56488123%321 = -148 + //-56488123%3 = -1 + //-56488123%564881230 = -56488123 + test(script, stack_t{{0xbb, 0xf0, 0x5d, 0x83}, {0x41, 0x01}}, flags, + stack_t{{0x94, 0x80}}); + test(script, stack_t{{0xbb, 0xf0, 0x5d, 0x83}, {0x03}}, flags, + stack_t{{0x81}}); + test(script, stack_t{{0xbb, 0xf0, 0x5d, 0x83}, {0x4e, 0x67, 0xab, 0x21}}, + flags, stack_t{{0xbb, 0xf0, 0x5d, 0x83}}); +} } /// Entry points @@ -355,4 +397,12 @@ test_div(STANDARD_LOCKTIME_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); } +BOOST_AUTO_TEST_CASE(op_mod) { + test_mod(SCRIPT_ENABLE_OPCODES_MONOLITH); + test_mod(STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); + test_mod(STANDARD_NOT_MANDATORY_VERIFY_FLAGS | + SCRIPT_ENABLE_OPCODES_MONOLITH); + test_mod(STANDARD_LOCKTIME_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); +} + BOOST_AUTO_TEST_SUITE_END()