diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -293,7 +293,6 @@ static bool IsOpcodeDisabled(opcodetype opcode, uint32_t flags) { switch (opcode) { - case OP_SPLIT: case OP_INVERT: case OP_2MUL: case OP_2DIV: @@ -306,6 +305,7 @@ return true; case OP_CAT: + case OP_SPLIT: case OP_AND: case OP_OR: case OP_XOR: @@ -1266,6 +1266,33 @@ popstack(stack); } break; + case OP_SPLIT: { + // (in position -- x1 x2) + if (stack.size() < 2) { + return set_error( + serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + } + + const valtype &data = stacktop(-2); + + // Make sure the split point is apropriate. + uint64_t position = + CScriptNum(stacktop(-1), fRequireMinimal).getint(); + if (position > data.size()) { + return set_error(serror, + SCRIPT_ERR_INVALID_SPLIT_RANGE); + } + + // Prepare the results in their own buffer as `data` + // will be invalidated. + valtype n1(data.begin(), data.begin() + position); + valtype n2(data.begin() + position, data.end()); + + // Replace existing stack values by the new values. + stacktop(-2) = std::move(n1); + stacktop(-1) = std::move(n2); + } break; + // // Conversion operations // 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 @@ -24,6 +24,7 @@ SCRIPT_ERR_INVALID_OPERAND_SIZE, SCRIPT_ERR_INVALID_NUMBER_RANGE, SCRIPT_ERR_IMPOSSIBLE_ENCODING, + SCRIPT_ERR_INVALID_SPLIT_RANGE, /* Failed verify operations */ SCRIPT_ERR_VERIFY, 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 @@ -41,6 +41,8 @@ "[-2^31...2^31]"; case SCRIPT_ERR_IMPOSSIBLE_ENCODING: return "The requested encoding is impossible to satisfy"; + case SCRIPT_ERR_INVALID_SPLIT_RANGE: + return "Invalid OP_SPLIT range"; case SCRIPT_ERR_BAD_OPCODE: return "Opcode missing or not understood"; case SCRIPT_ERR_DISABLED_OPCODE: 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 @@ -821,10 +821,10 @@ ["'a' 'b'", "CAT 'ab' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT enabled"], ["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], ["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT enabled"], -["'abc' 1 1", "SPLIT", "P2SH,STRICTENC", "DISABLED_OPCODE", "SPLIT disabled"], -["'abc' 1 1", "SPLIT", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "SPLIT disabled"], -["'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' 1", "SPLIT", "P2SH,STRICTENC", "DISABLED_OPCODE", "SPLIT disabled"], +["'abc' 1", "SPLIT 'bc' EQUALVERIFY 'a' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT enabled"], +["'abc' 1 0", "IF SPLIT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "SPLIT disabled"], +["'abc' 1 0", "IF SPLIT ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT enabled"], ["'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", "OK", "NUM2BIN enabled"], ["'abc' 2 0", "IF BIN2NUM ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "BIN2NUM disabled"], @@ -858,6 +858,31 @@ "P2SH,STRICTENC,MONOLITH_OPCODES", "PUSH_SIZE", "CAT oversized result" ], +["SPLIT"], +["", "SPLIT", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "SPLIT, empty stack"], +["'a'", "SPLIT", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "SPLIT, one parameter"], +["'abcdef' 3", "SPLIT 'def' EQUALVERIFY 'abc' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["'' 0", "SPLIT '' EQUALVERIFY '' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT, empty string"], +["'abc' 0", "SPLIT 'abc' EQUALVERIFY '' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT, boundary condition"], +["'abc' 3", "SPLIT '' EQUALVERIFY 'abc' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT, boundary condition"], +["'abc' 4", "SPLIT", "P2SH,STRICTENC,MONOLITH_OPCODES", "SPLIT_RANGE", "SPLIT, out of bounds"], +["'abc' -1", "SPLIT", "P2SH,STRICTENC,MONOLITH_OPCODES", "SPLIT_RANGE", "SPLIT, out of bounds"], +[ + "'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh'", + "145 SPLIT 'ataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh' EQUALVERIFY 'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxh' EQUAL", + "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT, maximum length" +], +[ + "'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh'", + "0 SPLIT 'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh' EQUALVERIFY '' EQUAL", + "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT, maximum length with empty string" +], +[ + "'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh'", + "520 SPLIT '' EQUALVERIFY 'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh' EQUAL", + "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT, maximum length with empty string" +], + ["NUM2BIN"], ["", "NUM2BIN 0 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "NUM2BIN, empty stack"], ["0", "NUM2BIN 0 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "NUM2BIN, one parameter"], diff --git a/src/test/monolith_opcodes.cpp b/src/test/monolith_opcodes.cpp --- a/src/test/monolith_opcodes.cpp +++ b/src/test/monolith_opcodes.cpp @@ -377,14 +377,39 @@ * String opcodes. */ static void CheckStringOp(const valtype &a, const valtype &b, - const valtype &expected) { - CheckBinaryOp(a, b, OP_CAT, expected); + const valtype &n) { + CheckBinaryOp(a, b, OP_CAT, n); // Check concatenation with empty elements. CheckBinaryOp(a, {}, OP_CAT, a); CheckBinaryOp(b, {}, OP_CAT, b); CheckBinaryOp({}, a, OP_CAT, a); CheckBinaryOp({}, b, OP_CAT, b); + + // Split n into a and b. + CheckTestResultForAllFlags({n}, CScript() << a.size() << OP_SPLIT, {a, b}); + + // Combine split and cat. + CheckTestResultForAllFlags({n}, CScript() << a.size() << OP_SPLIT << OP_CAT, + {n}); + CheckTestResultForAllFlags( + {a, b}, CScript() << OP_CAT << a.size() << OP_SPLIT, {a, b}); + + // Split away empty elements. + CheckTestResultForAllFlags({a}, CScript() << 0 << OP_SPLIT, {{}, a}); + CheckTestResultForAllFlags({b}, CScript() << 0 << OP_SPLIT, {{}, b}); + CheckTestResultForAllFlags({a}, CScript() << a.size() << OP_SPLIT, {a, {}}); + CheckTestResultForAllFlags({b}, CScript() << b.size() << OP_SPLIT, {b, {}}); + + // Out of bound split. + CheckErrorForAllFlags({a}, CScript() << (a.size() + 1) << OP_SPLIT, + SCRIPT_ERR_INVALID_SPLIT_RANGE); + CheckErrorForAllFlags({b}, CScript() << (b.size() + 1) << OP_SPLIT, + SCRIPT_ERR_INVALID_SPLIT_RANGE); + CheckErrorForAllFlags({n}, CScript() << (n.size() + 1) << OP_SPLIT, + SCRIPT_ERR_INVALID_SPLIT_RANGE); + CheckErrorForAllFlags({a}, CScript() << (-1) << OP_SPLIT, + SCRIPT_ERR_INVALID_SPLIT_RANGE); } BOOST_AUTO_TEST_CASE(string_opcodes_test) { @@ -465,10 +490,15 @@ // Check error conditions. CheckOpError({}, OP_CAT, SCRIPT_ERR_INVALID_STACK_OPERATION); + CheckOpError({}, OP_SPLIT, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckOpError({{}}, OP_CAT, SCRIPT_ERR_INVALID_STACK_OPERATION); + CheckOpError({{}}, OP_SPLIT, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckOpError({{0x00}}, OP_CAT, SCRIPT_ERR_INVALID_STACK_OPERATION); + CheckOpError({{0x00}}, OP_SPLIT, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckOpError({{0xab, 0xcd, 0xef}}, OP_CAT, SCRIPT_ERR_INVALID_STACK_OPERATION); + CheckOpError({{0xab, 0xcd, 0xef}}, OP_SPLIT, + SCRIPT_ERR_INVALID_STACK_OPERATION); } /** 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 @@ -64,6 +64,7 @@ {SCRIPT_ERR_PUBKEY_COUNT, "PUBKEY_COUNT"}, {SCRIPT_ERR_INVALID_OPERAND_SIZE, "OPERAND_SIZE"}, {SCRIPT_ERR_INVALID_NUMBER_RANGE, "INVALID_NUMBER_RANGE"}, + {SCRIPT_ERR_INVALID_SPLIT_RANGE, "SPLIT_RANGE"}, {SCRIPT_ERR_VERIFY, "VERIFY"}, {SCRIPT_ERR_EQUALVERIFY, "EQUALVERIFY"}, {SCRIPT_ERR_CHECKMULTISIGVERIFY, "CHECKMULTISIGVERIFY"},