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_CAT: case OP_SPLIT: case OP_INVERT: case OP_2MUL: @@ -306,6 +305,7 @@ // Disabled opcodes. return true; + case OP_CAT: case OP_AND: case OP_OR: case OP_XOR: @@ -1247,6 +1247,25 @@ } } break; + // + // Byte string operations + // + case OP_CAT: { + // (x1 x2 -- out) + if (stack.size() < 2) { + return set_error( + serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + } + valtype &vch1 = stacktop(-2); + valtype &vch2 = stacktop(-1); + if (vch1.size() + vch2.size() > + MAX_SCRIPT_ELEMENT_SIZE) { + return set_error(serror, SCRIPT_ERR_PUSH_SIZE); + } + vch1.insert(vch1.end(), vch2.begin(), vch2.end()); + popstack(stack); + } break; + // // Conversion operations // 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 @@ -818,9 +818,9 @@ ["1", "2 3 2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], ["'a' 'b'", "CAT", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], -["'a' 'b'", "CAT", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "CAT disabled"], +["'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", "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"], @@ -830,6 +830,34 @@ ["'abc' 2 0", "IF BIN2NUM ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "BIN2NUM disabled"], ["'abc' 2 0", "IF BIN2NUM ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "BIN2NUM enabled"], +["CAT"], +["", "CAT", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "CAT, empty stack"], +["'a'", "CAT", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "CAT, one parameter"], +["'abcd' 'efgh'", "CAT 'abcdefgh' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"], +["'' ''", "CAT '' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT two empty strings"], +["'abc' ''", "CAT 'abc' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT with empty string"], +["'' 'def'", "CAT 'def' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT with empty string"], +[ + "'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxh' 'ataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh'", + "CAT 'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh' EQUAL", + "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT, maximum length" +], +[ + "'' 'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh'", + "CAT 'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh' EQUAL", + "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT, maximum length with empty string" +], +[ + "'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh' ''", + "CAT 'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh' EQUAL", + "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT, maximum length with empty string" +], +[ + "'a' 'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh'", + "CAT", + "P2SH,STRICTENC,MONOLITH_OPCODES", "PUSH_SIZE", "CAT oversized result" +], + ["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 @@ -83,8 +83,8 @@ CheckOpError(stack, OP_XOR, expected_error); } -static void CheckOp(const valtype &a, const valtype &b, opcodetype op, - const valtype &expected) { +static void CheckBinaryOp(const valtype &a, const valtype &b, opcodetype op, + const valtype &expected) { CheckTestResultForAllFlags({a, b}, CScript() << op, {expected}); } @@ -96,12 +96,12 @@ const valtype &expected_or, const valtype &expected_xor) { // Bitwise ops are commutative, so we check both ways. - CheckOp(a, b, OP_AND, expected_and); - CheckOp(b, a, OP_AND, expected_and); - CheckOp(a, b, OP_OR, expected_or); - CheckOp(b, a, OP_OR, expected_or); - CheckOp(a, b, OP_XOR, expected_xor); - CheckOp(b, a, OP_XOR, expected_xor); + CheckBinaryOp(a, b, OP_AND, expected_and); + CheckBinaryOp(b, a, OP_AND, expected_and); + CheckBinaryOp(a, b, OP_OR, expected_or); + CheckBinaryOp(b, a, OP_OR, expected_or); + CheckBinaryOp(a, b, OP_XOR, expected_xor); + CheckBinaryOp(b, a, OP_XOR, expected_xor); } static void RunTestForAllBitwiseOpcodesSizes(const valtype &a, const valtype &b, @@ -373,6 +373,104 @@ CheckAllBitwiseOpErrors({b, {}}, SCRIPT_ERR_INVALID_OPERAND_SIZE); } +/** + * String opcodes. + */ +static void CheckStringOp(const valtype &a, const valtype &b, + const valtype &expected) { + CheckBinaryOp(a, b, OP_CAT, expected); + + // 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); +} + +BOOST_AUTO_TEST_CASE(string_opcodes_test) { + // Check for empty string. + CheckStringOp({}, {}, {}); + + // Check for simple concats. + CheckStringOp({0x00}, {0x00}, {0x00, 0x00}); + CheckStringOp({0xab}, {0xcd}, {0xab, 0xcd}); + CheckStringOp({0xab, 0xcd, 0xef}, {0x12, 0x34, 0x56, 0x78}, + {0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78}); + + const valtype n{ + 0x7b, 0x59, 0xf8, 0x07, 0xc6, 0xc0, 0x70, 0xbc, 0x52, 0x7b, 0xf5, 0xaf, + 0xf5, 0xdd, 0xeb, 0xdc, 0x41, 0xaa, 0x07, 0xf6, 0x80, 0x8d, 0x5d, 0x4d, + 0xbc, 0x91, 0xcd, 0x0a, 0x14, 0x85, 0xd9, 0x98, 0xb6, 0xab, 0x2e, 0x37, + 0x76, 0x78, 0x34, 0x8b, 0x2b, 0xfb, 0x59, 0x3b, 0xea, 0x45, 0x46, 0x72, + 0x64, 0x64, 0x83, 0x73, 0xc3, 0x1d, 0xca, 0x86, 0x03, 0x91, 0xfc, 0xc0, + 0xc4, 0xdf, 0x17, 0x83, 0x22, 0x5d, 0x50, 0xc5, 0x31, 0x45, 0xaf, 0xbc, + 0xfd, 0xc8, 0xb9, 0x6a, 0x72, 0x8b, 0x3c, 0x9b, 0x77, 0x02, 0xd6, 0x18, + 0x62, 0x02, 0xc9, 0x1c, 0x66, 0x29, 0x5c, 0x66, 0xf3, 0x9a, 0x00, 0xc1, + 0x69, 0x47, 0x35, 0x2f, 0xe8, 0x32, 0x2a, 0xb5, 0xc4, 0x9f, 0x3c, 0xbf, + 0xc7, 0x1a, 0x2b, 0xb3, 0xa6, 0x9b, 0xde, 0xcf, 0xc5, 0x15, 0x8c, 0xac, + 0xd0, 0x7c, 0x38, 0xe4, 0x41, 0xe1, 0x81, 0x4e, 0x65, 0xa5, 0x24, 0x08, + 0x5b, 0xa3, 0x19, 0xf3, 0xc2, 0x80, 0x21, 0x01, 0x33, 0xaf, 0x84, 0x53, + 0x1a, 0x00, 0x79, 0x7e, 0x1f, 0xd1, 0x62, 0x53, 0x0d, 0x6a, 0x58, 0xde, + 0x16, 0x23, 0x70, 0x32, 0x81, 0x25, 0xbd, 0xa3, 0x92, 0xae, 0xfd, 0x7f, + 0x47, 0xa2, 0xf2, 0x34, 0x3d, 0xef, 0xc3, 0x71, 0xb1, 0x33, 0x9a, 0xfd, + 0x80, 0x4b, 0x96, 0xcb, 0xaa, 0xda, 0x77, 0x50, 0x58, 0xf7, 0x0c, 0xf3, + 0x75, 0xdf, 0x51, 0x96, 0x75, 0x9a, 0x78, 0xc3, 0xd3, 0xaf, 0xac, 0xee, + 0xf3, 0xcc, 0x79, 0xfb, 0x3f, 0xda, 0x51, 0x94, 0x8f, 0x59, 0x3d, 0xbc, + 0xef, 0x17, 0x47, 0xd4, 0x40, 0x80, 0x8a, 0x78, 0x86, 0x6c, 0x9e, 0x38, + 0xd2, 0x11, 0xaa, 0x94, 0x79, 0x9b, 0x61, 0xf3, 0xaa, 0xcf, 0x66, 0x7e, + 0xa7, 0x11, 0xe9, 0xad, 0x8a, 0xd4, 0x67, 0x23, 0xf9, 0x62, 0x9f, 0x55, + 0xc0, 0x5a, 0x0f, 0x0a, 0xfe, 0x28, 0xd8, 0x80, 0xaf, 0x71, 0x97, 0x65, + 0x49, 0xb1, 0xd3, 0x9c, 0xee, 0x7e, 0x4b, 0xeb, 0x06, 0x3b, 0xe1, 0x66, + 0xf9, 0xa7, 0x77, 0x4f, 0x6a, 0xd1, 0xa0, 0x16, 0xe0, 0xcf, 0xe3, 0x25, + 0x65, 0x08, 0x0f, 0x5e, 0x2c, 0x1e, 0x80, 0x35, 0x75, 0x40, 0x9a, 0xd1, + 0x14, 0xba, 0xaa, 0xa7, 0xfc, 0x3c, 0xf1, 0xeb, 0x16, 0x8d, 0x59, 0xb4, + 0xcf, 0x16, 0x9a, 0xe3, 0xf1, 0x9d, 0x31, 0x97, 0xe5, 0xa4, 0xcc, 0xae, + 0x1c, 0xa2, 0xe7, 0x88, 0x44, 0x05, 0x67, 0x28, 0x21, 0x9f, 0x3e, 0xe2, + 0xfc, 0x25, 0x8c, 0x63, 0x09, 0xde, 0x39, 0xfa, 0xae, 0x26, 0x9b, 0x43, + 0xdf, 0x06, 0x2f, 0xb7, 0xaf, 0xa2, 0x74, 0x1c, 0x17, 0x96, 0x84, 0x26, + 0x1a, 0xe2, 0xcd, 0x90, 0xa8, 0xc3, 0xb6, 0xeb, 0x53, 0xee, 0xdd, 0xf9, + 0x88, 0xc6, 0x05, 0xb5, 0xd4, 0xa3, 0xf0, 0x36, 0xc7, 0xf1, 0xb3, 0x04, + 0x0c, 0xa5, 0xea, 0x22, 0x5b, 0x56, 0x3d, 0x54, 0x0b, 0x69, 0xc2, 0xe1, + 0x4f, 0xa8, 0x28, 0x4e, 0xe2, 0x3d, 0x99, 0x9c, 0x3b, 0xdb, 0xf4, 0x92, + 0x5a, 0xb9, 0xce, 0xeb, 0x33, 0xb5, 0xae, 0x16, 0x58, 0x79, 0x31, 0x8f, + 0x1e, 0x7a, 0x1a, 0xee, 0xbe, 0x9f, 0xea, 0x89, 0xd6, 0x6c, 0x43, 0x76, + 0x94, 0x0d, 0x94, 0x50, 0x6d, 0xdd, 0xc2, 0x68, 0x80, 0x3e, 0x38, 0x51, + 0x51, 0xd1, 0xd5, 0x4e, 0xf7, 0x65, 0xe5, 0x42, 0x3c, 0xa8, 0x28, 0x19, + 0x02, 0xa7, 0xc9, 0x1c, 0x24, 0xa7, 0x91, 0xfe, 0xa1, 0xbc, 0xb9, 0x15, + 0xba, 0x49, 0xac, 0xeb, 0x81, 0xf7, 0xc1, 0xfc, 0xf9, 0x51, 0x0d, 0xa1, + 0xe8, 0x71, 0x2c, 0x4e, 0x59, 0xc1, 0x3a, 0x2a, 0xcc, 0x61, 0xee, 0xe5, + 0x2a, 0x88, 0xf8, 0xec, 0xbd, 0x90, 0xc0, 0x96, 0xe0, 0x93, 0x1f, 0x78, + 0xbe, 0x6b, 0xb1, 0x4c, 0x46, 0x2a, 0x86, 0xd9, 0x2d, 0x20, 0x29, 0xb4, + 0x44, 0x15, 0xb2, 0x7e}; + + BOOST_CHECK_EQUAL(n.size(), MAX_SCRIPT_ELEMENT_SIZE); + + for (size_t i = 0; i <= MAX_SCRIPT_ELEMENT_SIZE; i++) { + valtype a(n.begin(), n.begin() + i); + valtype b(n.begin() + i, n.end()); + + CheckStringOp(a, b, n); + + // One more char and we are oversize. + valtype extraA = a; + extraA.push_back(0xaf); + + valtype extraB = b; + extraB.push_back(0xad); + + CheckOpError({extraA, b}, OP_CAT, SCRIPT_ERR_PUSH_SIZE); + CheckOpError({a, extraB}, OP_CAT, SCRIPT_ERR_PUSH_SIZE); + CheckOpError({extraA, extraB}, OP_CAT, SCRIPT_ERR_PUSH_SIZE); + } + + // Check error conditions. + CheckOpError({}, OP_CAT, SCRIPT_ERR_INVALID_STACK_OPERATION); + CheckOpError({{}}, OP_CAT, SCRIPT_ERR_INVALID_STACK_OPERATION); + CheckOpError({{0x00}}, OP_CAT, SCRIPT_ERR_INVALID_STACK_OPERATION); + CheckOpError({{0xab, 0xcd, 0xef}}, OP_CAT, + SCRIPT_ERR_INVALID_STACK_OPERATION); +} + /** * Type conversion opcodes. */