BIN-2024-0001 | OP_CAT |
---|---|
Revision | 000 (2024-01-01) |
Author | Ethan Heilman <ethan.r.heilman@gmail.com> |
Armin Sabouri <arminsdev@gmail.com> |
|
Layer | Consensus (soft fork) |
Status | Draft |
License | BSD-3-Clause |
Discussion | https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-October/022049.html |
Aliases | BIPs PR#1525 |
This BIP introduces OP_CAT
as a tapscript opcode which allows the concatenation of two values on the stack. OP_CAT
would be activated via a soft fork 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
.
This document is licensed under the 3-clause BSD license.
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.
Given the stack [x1, x2]
, where x2
is at the top of the stack, OP_CAT
will push x1 || x2
onto the stack. By ||
we denote concatenation. 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.
OP_CAT
pops two elements off the stack, concatenates them together in stack order, and pushes the resulting element onto the stack.
This opcode would be activated via a soft fork by redefining the tapscript opcode OP_SUCCESS126
(126 in decimal and 0x7e in hexadecimal) to OP_CAT
.
Bitcoin tapscript lacks a general purpose way of combining objects on the stack restricting the expressiveness and power of tapscript. This prevents among many other things the ability to construct and evaluate merkle trees and other hashed data structures in tapscript. OP_CAT
by adding a general purpose way to concatenate stack values would overcome this limitation and greatly increase the functionality of tapscript.
OP_CAT
aims to expand the toolbox of the tapscript developer with a simple, modular and useful opcode in the spirit of Unix 1. To demonstrate the usefulness of OP_CAT
below we provide a non-exhaustive list of some usecases that OP_CAT
would enable:
- Bitstream, a protocol for the atomic swap (fair exchange) of bitcoins for decryption keys, that enables decentralized file hosting systems paid in Bitcoin. While such swaps are currently possible on Bitcoin without
OP_CAT
they require the use of complex and computationally expensive Verifiable Computation cryptographic techniques.OP_CAT
would remove this requirement on Verifiable Computation, making such protocols far more practical to build in Bitcoin.2 - Tree signatures provide a multisignature script whose size can be logarithmic in the number of public keys and can encode spend conditions beyond n-of-m. For instance a transaction less than 1KB in size could support tree signatures with a thousand public keys. This also enables generalized logical spend conditions.3
- Post-Quantum Lamport signatures in Bitcoin transactions. Lamport signatures merely require the ability to hash and concatenate values on the stack.4 It is an open question if the quantum resistance of Lamport signatures can be preserved when used in a taproot output.
- Non-equivocation contracts5 in tapscript provide a mechanism to punish equivocation/double spending in Bitcoin payment channels.
OP_CAT
enables this by enforcing rules on the spending transaction's nonce. The capability is a useful building block for payment channels and other Bitcoin protocols. - Vaults6 which are a specialized covenant that allows a user to block a malicious party who has compromised the user's secret key from stealing the funds in that output. As shown by Poelstra7
OP_CAT
is sufficient to build vaults in Bitcoin. - Replicating CheckSigFromStack8 which would allow the creation of simple covenants and other advanced contracts without having to presign spending transactions, possibly reducing complexity and the amount of data that needs to be stored. Originally shown to work with Schnorr signatures, this result has been extended to ECDSA signatures9.
The opcode OP_CAT
was available in early versions of Bitcoin. However, OP_CAT
was removed because it enabled the construction of a script whose evaluation could have memory usage exponential in the size of the script.
For example, a script that pushed a 1-byte value on the stack and then repeated the opcodes OP_DUP
, OP_CAT
40 times would result in a stack value whose size was greater than 1 terabyte. This is no longer an issue because tapscript enforces a maximum stack element size of 520 bytes.
Our decision to reenable OP_CAT
by redefining a tapscript OP_SUCCESSx
opcode to OP_CAT
was motivated to leverage the tapscript softfork opcode upgrade path introduced in BIP342.
We specifically choose to use OP_SUCCESS126
rather than another OP_SUCCESSx
as OP_SUCCESS126
uses the same opcode value (126 in decimal and 0x7e in hexadecimal) that was used for OP_CAT
prior to it being disabled in Bitcoin. This removes a potential source of confusion that would exist if we had a opcode value different from the one used in the original OP_CAT
opcode.
While the OP_SUCCESSx
opcode upgrade path could enable us to increase the stack element size while reenabling OP_CAT
, we wanted to separate the decision to change the stack element size limit from the decision to reenable OP_CAT
. This BIP takes no position in favor or against increasing the stack element size limit.
OP_CAT
usage in an non-tapscript script will continue to trigger the SCRIPT_ERR_DISABLED_OPCODE
. The only change would be to OP_CAT
usage in tapscript. This change to tapscript would be activated a soft fork that redefines an OP_SUCCESSx
opcode (OP_SUCCESS126
) to OP_CAT
.
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;
This implementation is inspired by the original implementation of OP_CAT
as shown below. An alternative implementation of OP_CAT
can be found in Elements10.
The value of MAX_SCRIPT_ELEMENT_SIZE
is 520.
This implementation is inspired by the original implementation of OP_CAT as it existed in the Bitcoin codebase prior to the commit "misc changes" 4bd188c11 which disabled it:
case OP_CAT:
{
// (x1 x2 -- out)
if (stack.size() < 2)
return false;
valtype& vch1 = stacktop(-2);
valtype& vch2 = stacktop(-1);
vch1.insert(vch1.end(), vch2.begin(), vch2.end());
stack.pop_back();
if (stacktop(-1).size() > 5000)
return false;
}
break;
An alternative implementation of OP_CAT
can be found in Elements10.
We wish to acknowledge Dan Gould for encouraging and helping review this effort. We also want to thank Madars Virza, Jeremy Rubin, Andrew Poelstra, Bob Summerwill for their feedback, review and helpful comments.
Footnotes
-
R. Pike and B. Kernighan, "Program design in the UNIX environment", 1983 ↩
-
R. Linus, "BitStream: Decentralized File Hosting Incentivised via Bitcoin Payments", 2023 ↩
-
P. Wuille, "Multisig on steroids using tree signatures", 2015 ↩
-
J. Rubin, "[bitcoin-dev]
OP_CAT
Makes Bitcoin Quantum Secure [was CheckSigFromStack for Arithmetic Values]", 2021 ↩ -
T. Ruffing, A. Kate, D. Schröder, "Liar, Liar, Coins on Fire: Penalizing Equivocation by Loss of Bitcoins", 2015 ↩
-
M. Moser, I. Eyal, and E. G. Sirer, Bitcoin Covenants ↩
-
A. Poelstra, "CAT and Schnorr Tricks II", 2021 ↩
-
A. Poelstra, "CAT and Schnorr Tricks I", 2021 ↩
-
R. Linus, "Covenants with CAT and ECDSA", 2023 ↩
-
Roose S., Elements Project, "Re-enable several disabled opcodes", 2019 ↩ ↩2
-
S. Nakamoto, "misc changes", Aug 25 2010 ↩