diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -334,19 +334,18 @@ return set_error(serror, SCRIPT_ERR_OP_COUNT); } - 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_LSHIFT || - opcode == OP_RSHIFT) { + if (opcode == OP_SPLIT || opcode == OP_NUM2BIN || + opcode == OP_BIN2NUM || 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); } // 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_MOD)) { + (opcode == OP_CAT || opcode == OP_AND || opcode == OP_XOR || + opcode == OP_OR || opcode == OP_DIV || opcode == OP_MOD)) { // Disabled opcodes. return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); } @@ -1250,6 +1249,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()); + stack.pop_back(); + } break; + default: return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } 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 @@ -359,6 +359,61 @@ test(script, stack_t{{0xbb, 0xf0, 0x5d, 0x83}, {0x4e, 0x67, 0xab, 0x21}}, flags, stack_t{{0xbb, 0xf0, 0x5d, 0x83}}); } + +/// OP_CAT + +void test_cat(uint32_t flags) { + CScript script; + script << OP_CAT; + + // two inputs required + test(script, stack_t(), flags, SCRIPT_ERR_INVALID_STACK_OPERATION); + test(script, stack_t{{0x00}}, flags, SCRIPT_ERR_INVALID_STACK_OPERATION); + + // stack item with maximum length + item maxlength_item(MAX_SCRIPT_ELEMENT_SIZE, 0x00); + + // Concatenation producing illegal sized output + { + stack_t input_stack; + input_stack.push_back(maxlength_item); + item i; + i.push_back(0x00); + input_stack.push_back(i); + test(script, input_stack, flags, SCRIPT_ERR_PUSH_SIZE); + } + + // Concatenation of a max-sized item with empty is legal + { + stack_t input_stack; + input_stack.push_back(maxlength_item); + input_stack.push_back(item()); // empty item + test(script, input_stack, flags, stack_t{maxlength_item}); + } + { + stack_t input_stack; + input_stack.push_back(item()); // empty item + input_stack.push_back(maxlength_item); + test(script, input_stack, flags, stack_t{maxlength_item}); + } + + // Concatenation of a zero length operand + test(script, stack_t{{0x01}, {}}, flags, stack_t{{0x01}}); + test(script, stack_t{{}, {0x01}}, flags, stack_t{{0x01}}); + + // Concatenation of two empty operands results in empty item + test(script, stack_t{{}, {}}, flags, stack_t{{}}); + + // concatenating two operands generates the correct result + test(script, stack_t{{0x00}, {0x00}}, flags, stack_t{{0x00, 0x00}}); + test(script, stack_t{{0x01}, {0x02}}, flags, stack_t{{0x01, 0x02}}); + 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}}); +} } /// Entry points @@ -405,4 +460,12 @@ test_mod(STANDARD_LOCKTIME_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); } +BOOST_AUTO_TEST_CASE(op_cat) { + test_cat(SCRIPT_ENABLE_OPCODES_MONOLITH); + test_cat(STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); + test_cat(STANDARD_NOT_MANDATORY_VERIFY_FLAGS | + SCRIPT_ENABLE_OPCODES_MONOLITH); + test_cat(STANDARD_LOCKTIME_VERIFY_FLAGS | SCRIPT_ENABLE_OPCODES_MONOLITH); +} + BOOST_AUTO_TEST_SUITE_END()