Index: src/script/interpreter.cpp =================================================================== --- src/script/interpreter.cpp +++ src/script/interpreter.cpp @@ -337,8 +337,8 @@ if (opcode == OP_CAT || opcode == OP_SUBSTR || opcode == OP_LEFT || opcode == OP_RIGHT || opcode == OP_INVERT || opcode == OP_2MUL || - opcode == OP_2DIV || opcode == OP_MUL || opcode == OP_MOD || - opcode == OP_LSHIFT || opcode == OP_RSHIFT) { + opcode == OP_2DIV || opcode == OP_MUL || opcode == OP_LSHIFT || + opcode == OP_RSHIFT) { // Disabled opcodes. return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); } @@ -894,6 +894,7 @@ case OP_ADD: case OP_SUB: case OP_DIV: + case OP_MOD: case OP_BOOLAND: case OP_BOOLOR: case OP_NUMEQUAL: @@ -931,6 +932,14 @@ } 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); Index: src/script/script.h =================================================================== --- src/script/script.h +++ src/script/script.h @@ -279,6 +279,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); Index: src/script/script_error.h =================================================================== --- src/script/script_error.h +++ 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, Index: src/script/script_error.cpp =================================================================== --- src/script/script_error.cpp +++ 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: Index: src/test/data/script_tests.json =================================================================== --- src/test/data/script_tests.json +++ src/test/data/script_tests.json @@ -826,7 +826,6 @@ ["2 0 IF 2MUL ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "2MUL disabled"], ["2 0 IF 2DIV ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "2DIV disabled"], ["2 2 0 IF MUL ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "MUL disabled"], -["2 2 0 IF MOD ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "MOD disabled"], ["2 2 0 IF LSHIFT ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "LSHIFT disabled"], ["2 2 0 IF RSHIFT ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "RSHIFT disabled"], @@ -844,7 +843,6 @@ ["2 DUP MUL", "4 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], ["2 2MUL", "4 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], ["2 2DIV", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], -["7 3 MOD", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], ["2 2 LSHIFT", "8 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], ["2 1 RSHIFT", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], Index: src/test/op_code.cpp =================================================================== --- src/test/op_code.cpp +++ src/test/op_code.cpp @@ -272,6 +272,38 @@ 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 @@ -306,5 +338,12 @@ test_div(STANDARD_LOCKTIME_VERIFY_FLAGS); } +BOOST_AUTO_TEST_CASE(op_mod) { + test_mod(0); + test_mod(STANDARD_SCRIPT_VERIFY_FLAGS); + test_mod(STANDARD_NOT_MANDATORY_VERIFY_FLAGS); + test_mod(STANDARD_LOCKTIME_VERIFY_FLAGS); +} + BOOST_AUTO_TEST_SUITE_END()