Index: src/script/interpreter.cpp =================================================================== --- src/script/interpreter.cpp +++ src/script/interpreter.cpp @@ -335,10 +335,9 @@ return set_error(serror, SCRIPT_ERR_OP_COUNT); } - 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_LSHIFT || - opcode == OP_RSHIFT) { + 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) { // Disabled opcodes. return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); } @@ -1234,6 +1233,24 @@ } } 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); } Index: src/test/data/script_tests.json =================================================================== --- src/test/data/script_tests.json +++ src/test/data/script_tests.json @@ -817,9 +817,6 @@ ["NOP", "2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], ["1", "2 3 2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], -["'a' 'b'", "CAT", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], -["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], - ["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], ["'abc'", "IF INVERT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "INVERT disabled"], Index: src/test/op_code.cpp =================================================================== --- src/test/op_code.cpp +++ src/test/op_code.cpp @@ -304,6 +304,56 @@ 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 @@ -345,5 +395,12 @@ test_mod(STANDARD_LOCKTIME_VERIFY_FLAGS); } +BOOST_AUTO_TEST_CASE(op_cat) { + test_cat(0); + test_cat(STANDARD_SCRIPT_VERIFY_FLAGS); + test_cat(STANDARD_NOT_MANDATORY_VERIFY_FLAGS); + test_cat(STANDARD_LOCKTIME_VERIFY_FLAGS); +} + BOOST_AUTO_TEST_SUITE_END()