Index: src/script/interpreter.cpp =================================================================== --- src/script/interpreter.cpp +++ src/script/interpreter.cpp @@ -335,9 +335,9 @@ return set_error(serror, SCRIPT_ERR_OP_COUNT); } - if (opcode == OP_SUBSTR || opcode == OP_LEFT || opcode == OP_RIGHT || - opcode == OP_INVERT || opcode == OP_2MUL || opcode == OP_2DIV || - opcode == OP_MUL || opcode == OP_LSHIFT || opcode == OP_RSHIFT) { + if (opcode == OP_LEFT || opcode == OP_RIGHT || 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); } @@ -1251,6 +1251,42 @@ stack.pop_back(); } break; + case OP_SPLIT: { + // (in position -- x1 x2) + if (stack.size() < 2) { + return set_error( + serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + } + valtype vch = stacktop(-2); + int64_t nPosition = CScriptNum(stacktop(-1), fRequireMinimal).getint(); + + // if nPosition is less than 0 or is larger than the input then throw error + if (nPosition < 0 || static_cast(nPosition) > vch.size()) { + return set_error( + serror, SCRIPT_ERR_INVALID_SPLIT_RANGE); + } + + stack.pop_back(); + stack.pop_back(); + + // initialize outputs + if (nPosition == 0) { + stack.push_back(valtype()); + stack.push_back(vch); + } + else if (static_cast(nPosition) == vch.size()) { + stack.push_back(vch); + stack.push_back(valtype()); + } + else { + valtype vchOut1, vchOut2; + vchOut1.insert(vchOut1.end(), vch.begin(), vch.begin() + nPosition); + vchOut2.insert(vchOut2.end(), vch.begin() + nPosition, vch.end()); + stack.emplace_back(move(vchOut1)); + stack.emplace_back(move(vchOut2)); + } + } break; + default: return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } Index: src/script/script.h =================================================================== --- src/script/script.h +++ src/script/script.h @@ -102,7 +102,7 @@ // splice ops OP_CAT = 0x7e, - OP_SUBSTR = 0x7f, + OP_SPLIT = 0x7f, OP_LEFT = 0x80, OP_RIGHT = 0x81, OP_SIZE = 0x82, Index: src/script/script.cpp =================================================================== --- src/script/script.cpp +++ src/script/script.cpp @@ -121,8 +121,8 @@ // splice ops case OP_CAT: return "OP_CAT"; - case OP_SUBSTR: - return "OP_SUBSTR"; + case OP_SPLIT: + return "OP_SPLIT"; case OP_LEFT: return "OP_LEFT"; case OP_RIGHT: Index: src/script/script_error.h =================================================================== --- src/script/script_error.h +++ src/script/script_error.h @@ -36,6 +36,7 @@ SCRIPT_ERR_INVALID_BITWISE_OPERATION, SCRIPT_ERR_DIV_BY_ZERO, SCRIPT_ERR_MOD_BY_ZERO, + SCRIPT_ERR_INVALID_SPLIT_RANGE, /* CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY */ SCRIPT_ERR_NEGATIVE_LOCKTIME, Index: src/script/script_error.cpp =================================================================== --- src/script/script_error.cpp +++ src/script/script_error.cpp @@ -52,6 +52,8 @@ return "Invalid division operation"; case SCRIPT_ERR_MOD_BY_ZERO: return "Invalid modulo operation"; + case SCRIPT_ERR_INVALID_SPLIT_RANGE: + return "Invalid OP_SPLIT range"; 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 @@ -354,6 +354,75 @@ test(script,stack_t{{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a},{0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14}},flags,stack_t{{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14}}); } + /// OP_SPLIT + + void test_split(uint32_t flags) { + CScript script; + script << OP_SPLIT; //inputs: x n; outputs: x1 x2 + + //two inputs required + test(script,stack_t(),flags,SCRIPT_ERR_INVALID_STACK_OPERATION); + test(script,stack_t{{0x01}},flags,SCRIPT_ERR_INVALID_STACK_OPERATION); + + //length of 2nd input greater than CScriptNum::nDefaultMaxNumSize + { + item maxlength_num_item(CScriptNum::nDefaultMaxNumSize,0x01); + item illegal_item=maxlength_num_item; + illegal_item.push_back(0x00); + test(script,stack_t{{0x01},illegal_item},flags,SCRIPT_ERR_UNKNOWN_ERROR); + } + + //if n == 0, then x1 is the empty array and x2 == x; + //execution of OP_SPLIT on empty array results in two empty arrays. + test(script,stack_t{{},{}},flags,stack_t{{},{}}); + test(script,stack_t{{0x01},{}},flags,stack_t{{},{0x01}}); //x 0 OP_SPLIT -> OP_0 x + test(script,stack_t{{0x01,0x02,0x03,0x04},{}},flags,stack_t{{},{0x01,0x02,0x03,0x04}}); + + //if n == len(x) then x1 == x and x2 is the empty array + test(script,stack_t{{0x01},{0x01}},flags,stack_t{{0x01},{}}); + test(script,stack_t{{0x01,0x02,0x03},{0x03}},flags,stack_t{{0x01,0x02,0x03},{}}); //x len(x) OP_SPLIT -> x OP_0 + + //if n > len(x), then the operator must fail; x (len(x) + 1) OP_SPLIT -> FAIL + test(script,stack_t{{},{0x01}},flags,SCRIPT_ERR_INVALID_SPLIT_RANGE); + test(script,stack_t{{0x01},{0x02}},flags,SCRIPT_ERR_INVALID_SPLIT_RANGE); + test(script,stack_t{{0x01,0x02,0x03},{0x04}},flags,SCRIPT_ERR_INVALID_SPLIT_RANGE); + test(script,stack_t{{0x01,0x02,0x03,0x04},{0x05}},flags,SCRIPT_ERR_INVALID_SPLIT_RANGE); + + //if n < 0 the operator must fail. + test(script,stack_t{{0x01,0x02,0x03,0x04},{0x81}},flags,SCRIPT_ERR_INVALID_SPLIT_RANGE); + + + test(script,stack_t{{0x01,0x02,0x03,0x04},{0x01}},flags,stack_t{{0x01},{0x02,0x03,0x04}}); + test(script,stack_t{{0x01,0x02,0x03,0x04},{0x02}},flags,stack_t{{0x01,0x02},{0x03,0x04}}); + test(script,stack_t{{0x01,0x02,0x03,0x04},{0x03}},flags,stack_t{{0x01,0x02,0x03},{0x04}}); + test(script,stack_t{{0x01,0x02,0x03,0x04},{0x04}},flags,stack_t{{0x01,0x02,0x03,0x04},{}}); + + //split of a max-len item + { + item maxlength_item(MAX_SCRIPT_ELEMENT_SIZE, 0x00); + test(script,stack_t{maxlength_item,{}},flags,stack_t{{},maxlength_item}); + } + } + + /// OP_CAT + OP_SPLIT + + void test_cat_split(const item& x, uint32_t flags) { + CScript script; + script << OP_SPLIT << OP_CAT; + // x n OP_SPLIT OP_CAT -> x - for all x and for all 0 <= n <= len(x) + test(script,stack_t{x,{}},flags,stack_t{x}); + for (uint8_t i=1; i<=x.size(); ++i) { + test(script,stack_t{x,{i}},flags,stack_t{x}); + } + } + + void test_cat_split(uint32_t flags) { + test_cat_split({},flags); + test_cat_split({0x01},flags); + test_cat_split({0x01,0x02},flags); + test_cat_split({0x01,0x02,0x03},flags); + } + } /// Entry points @@ -402,5 +471,19 @@ test_cat(STANDARD_LOCKTIME_VERIFY_FLAGS); } +BOOST_AUTO_TEST_CASE(op_split) { + test_split(0); + test_split(STANDARD_SCRIPT_VERIFY_FLAGS); + test_split(STANDARD_NOT_MANDATORY_VERIFY_FLAGS); + test_split(STANDARD_LOCKTIME_VERIFY_FLAGS); +} + +BOOST_AUTO_TEST_CASE(cat_split) { + test_cat_split(0); + test_cat_split(STANDARD_SCRIPT_VERIFY_FLAGS); + test_cat_split(STANDARD_NOT_MANDATORY_VERIFY_FLAGS); + test_cat_split(STANDARD_LOCKTIME_VERIFY_FLAGS); +} + BOOST_AUTO_TEST_SUITE_END()