Skip to content

Commit

Permalink
OP_CAT in tapscript
Browse files Browse the repository at this point in the history
Implement OP_CAT as a new Tapscript op code by redefining the opcode OP_SUCCESS126 (126 in decimal and 0x7e in hexadecimal). This is the same opcode value used by the original OP_CAT.

When evaluated, the OP_CAT instruction:

Pops the top two values off the stack,
concatenates the popped values together in stack order,
and then pushes the concatenated value on the top of the stack.
OP_CAT fails if there are fewer than two values on the stack or if a concatenated value would have a combined size greater than the maximum script element size of 520 bytes.

See BIP 347 for a deeper description.
  • Loading branch information
0xBEEFCAF3 committed Aug 31, 2024
1 parent e96f657 commit 540526e
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 6 deletions.
3 changes: 2 additions & 1 deletion src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERI
SCRIPT_VERIFY_CONST_SCRIPTCODE |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION |
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE};
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE |
SCRIPT_VERIFY_DISCOURAGE_OP_CAT};

/** For convenience, standard but not mandatory verify flags. */
static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS};
Expand Down
38 changes: 33 additions & 5 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,10 +451,16 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) {
return set_error(serror, SCRIPT_ERR_OP_COUNT);
}

// When OP_SUCCESS disabled opcodes (CVE-2010-5137) are
// redefined in tapscript, remove them from the if below
// and put them here
if (opcode == OP_CAT) {
return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes (CVE-2010-5137).
}
}

if (opcode == OP_CAT ||
opcode == OP_SUBSTR ||
if (opcode == OP_SUBSTR ||
opcode == OP_LEFT ||
opcode == OP_RIGHT ||
opcode == OP_INVERT ||
Expand Down Expand Up @@ -518,6 +524,19 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
case OP_NOP:
break;

case OP_CAT:
{
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;

case OP_CHECKLOCKTIMEVERIFY:
{
if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) {
Expand Down Expand Up @@ -1800,10 +1819,19 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
}
// New opcodes will be listed here. May use a different sigversion to modify existing opcodes.
if (IsOpSuccess(opcode)) {
if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS);
if (opcode == OP_CAT) {
if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_CAT) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_CAT);
} else if (!(flags & SCRIPT_VERIFY_OP_CAT)) {
return set_success(serror);
}
} else {
// OP_SUCCESS behaviour
if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS);
}
return set_success(serror);
}
return set_success(serror);
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ enum : uint32_t {
// Making unknown public key versions (in BIP 342 scripts) non-standard
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),

// Support OP_CAT in tapscript
SCRIPT_VERIFY_OP_CAT = (1U << 21),
SCRIPT_VERIFY_DISCOURAGE_OP_CAT = (1U << 22),

// Constants to point to the highest flag in use. Add new flags above this line.
//
SCRIPT_VERIFY_END_MARKER
Expand Down
1 change: 1 addition & 0 deletions src/script/script_error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ std::string ScriptErrorString(const ScriptError serror)
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION:
return "Taproot version reserved for soft-fork upgrades";
case SCRIPT_ERR_DISCOURAGE_OP_SUCCESS:
case SCRIPT_ERR_DISCOURAGE_OP_CAT:
return "OP_SUCCESSx reserved for soft-fork upgrades";
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE:
return "Public key version reserved for soft-fork upgrades";
Expand Down
3 changes: 3 additions & 0 deletions src/script/script_error.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ typedef enum ScriptError_t
SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG,
SCRIPT_ERR_TAPSCRIPT_MINIMALIF,

/* OP_CAT re-activation */
SCRIPT_ERR_DISCOURAGE_OP_CAT,

/* Constant scriptCode */
SCRIPT_ERR_OP_CODESEPARATOR,
SCRIPT_ERR_SIG_FINDANDDELETE,
Expand Down
2 changes: 2 additions & 0 deletions src/test/transaction_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ static std::map<std::string, unsigned int> mapFlagNames = {
{std::string("DISCOURAGE_UPGRADABLE_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE},
{std::string("DISCOURAGE_OP_SUCCESS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS},
{std::string("DISCOURAGE_UPGRADABLE_TAPROOT_VERSION"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION},
{std::string("OP_CAT"), (unsigned int)SCRIPT_VERIFY_OP_CAT},
{std::string("DISCOURAGE_OP_CAT"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_CAT},
};

unsigned int ParseScriptFlags(std::string strFlags)
Expand Down

0 comments on commit 540526e

Please sign in to comment.