Author: Bangzhu, with: X, @bangzhu_x
Understanding OP_CAT
OP_CAT is a proposed restriction that recently received the BIP number 347. First, let's understand what a "covenant" is.
1. We must first understand the fundamental limitations of today's Bitcoin Script. Under the surface, Bitcoin allows the creation of basic smart contracts: using code to define the rules for locking and unlocking funds. However, its programming language, Bitcoin Script, is very limited and only works when transactions are about to move funds.
2. On Bitcoin today, you have no way to pre-configure or specify the transfer path of your coins, nor can you limit the withdrawal speed of a fund when locking it (unless you use an unconventional workflow based on PSBT (Pending Signature Bitcoin Transactions), but this can neither handle transaction fees well nor reliably delete or prevent broadcasts when you decide not to use it anymore). This simplicity, although core to Bitcoin's security model, still brings significant limitations to the ability of scripting languages such as Script to support (even basic) smart contracts. Linear execution mode.
Linear execution mode
1. One of the limitations of Bitcoin Script is its operation mode: opcodes are executed sequentially and there are no loops.
Taking this P2PKH transaction as an example, you can see that the script is executed linearly: copy the public key, hash the public key into an address, verify that the hash value is consistent with the locking script, and finally use the public key to check the transaction signature. The lack of loops means that the script is not Turing complete and guaranteed to terminate, preventing infinite loop operations that may cause node crashes or significantly slow down the entire network. While this design choice makes resource consumption statically bounded, it also limits Script's ability to manage complex workflows.
2. Lack of basic arithmetic Bitcoin Script has less than 100 important opcodes, which is sometimes surprising: it cannot multiply, divide, or combine objects in the stack. Many users interested in OP_CAT may know that Satoshi Nakamoto disabled several Bitcoin opcodes in 2010, including OP_OR, OP_MUL (multiplication), OP_DIV (division), and OP_CAT (string concatenation). These disabled opcodes were removed because their original implementations had exploitable vulnerabilities that could compromise the security of the network. But the lack of these opcodes makes it difficult for Script to implement basic math, which is useful in some simple scenarios, such as calculating transaction fees in a contract.
3. Transaction data is invisible Superficially, I think most people assume that Bitcoin smart contracts can see the amount and other data of a transaction because this information is publicly visible on the blockchain. But the opposite is true. Contracts in Bitcoin cannot set spending conditions based on transaction data because Bitcoin Script's ability to understand transaction data is very limited. If scripts had the ability to introspect more details in transaction data, we could develop much more robust smart contracts that can do all kinds of interesting things, such as enforcing a spending condition, creating transactions that are executed in stages, and enabling more advanced security features (such as "vaults").
4. So how do we do this? More advanced experiments in Bitcoin Script, such as the Simplicity language and others, aim to provide alternatives to stack-style constraints. Opcodes such as OP_MULTISHA256, OP_LESS, and OP_LE32TOLE64 aim to upgrade Bitcoin's computing power. Proposals to handle introspection opcodes, such as OP_CTV and OP_CAT, are classified as "restrictions." So, what is the difference between "smart contracts" and "restrictions"?
5. Smart Contracts vs. Restrictions Smart contracts are self-executing transactions that can transfer funds without an intermediary. On Bitcoin today, smart contracts are limited to operations that lock and unlock Bitcoin using Bitcoin Script. Restrictions are intended to enhance Bitcoin's smart contract functionality by allowing users to control how their funds are spent in future transactions. If Script is allowed to introspect transaction data, we are essentially allowing this data to be used in contract logic. Here are a few of the more interesting introspective opcodes proposed for constraints:
OP_TXHASH: Provides a hash of a transaction's inputs (or outputs), and gives scripts the ability to verify and enforce conditions based on the transaction data.
OP_CSFS + OP_CAT: These two opcodes allow scripts to check signatures on arbitrary data (not just the transaction itself). This means that scripts can verify conditions based on transaction data and external information, which expands the possibilities of verification operations within Bitcoin scripts.
These two opcodes are intentionally designed to be broad, so that complex verification processes and introspection capabilities can be supported. Others are intentionally narrow, designed to be more restrictive constraints.
OP_CHECKTEMPLATEVERIFY (CTV): Allows the output of a transaction to be embedded in the template of a subsequent spend transaction, enabling more restrictive constraints.
OP_VAULT: Enables a type of constraint specifically for Vault contracts, which allows users to specify a transaction's destination but not actually move funds until a delay has expired.
There is also OP_CAT, which is not an opcode that directly enables introspection capabilities...OP_CAT: allows Script to splice two elements in the stack back and forth, which can be used to combine fragments of information in the script.
OP_CAT: Unleashing All PossibilitiesIn Bitcoin Script, there are only three main opcodes that can introspect transaction data: CHECKLOCKTIMEVERIFY (absolute time lock),
CHECKSEQUENCEVERIFY (relative time lock), and CHECKSIG (check signature).
In addition, there are some variants of them, such as: CHECKSIGVERIFY, CHECKSIGADD, CHECKMULTISIG, and CHECKMULTISIGVERIFY, which are essentially micro-variants of CHECKSIG; the first two allow you to see if the check passes, and only provide very narrow functionality. CHECKSIG is similar, except that you can grab signatures and public keys from the stack.
Previously, we thought of concatenation as a function that concatenates two elements, but we can also use it to split one element — splitting a signature into (r, s) pairs.
How can OP_SPLIT (split) be derived from OP_CAT (splice)? "If you have some large object, you can split it in half by asking the user to provide the two pieces when spending. You CAT the two pieces and check for equality. Every operation can be reversed in this way. Using CAT, you can split the signature in half."
How does this work? The user first provides the signature, the public key, and the transaction being signed. You split the signature in half and then check each part separately with the transaction data. This technique can be seen as a split or a splice, because it verifies that the signature and public key are part of a valid transaction. How does this relate to introspection? "In Taproot, we already have Schnorr signatures. Using OP_CAT and the Schnorr signature verification opcode, it was shown that we can get a non-recursive constraint: you can actually get the hash of a transaction. Not the scribbled transaction hash, but the SHA2 hash of all the transaction data in the stack." How to get the SHA2 hash of the remaining transaction inputs or outputs in the stack. We'll skip the magic math, but the revelation is that with OP_CAT, we're making constraints on some part of the transaction a requirement for the unlocking script. We can constrain the sender's address, or the amount to be sent in a transaction, and the transaction hash will serve as the key to unlock the funds.
Vaults
Using the same trick, we have transaction introspection, and that immediately gives us a basic Vault contract. Following Poelstra's reasoning in the article, a developer named Rijndael showed that we can implement Purrfect Vaults with just OP_CAT.
Users can specify where funds go next, which provides a mechanism to recover funds if the key is compromised, reducing the incentive to steal private keys.
Merkle Trees in Scripts
On Bitcoin today, a data structure called a "Merkle Tree" is used to verify data, synchronize, and in a sense "bind" transactions and blocks. OP_CAT concatenates two variables from the stack, and when used with a SHA256 hash of a public key, allows a straightforward Merkle tree verifier to be implemented in a script. This approach, originally proposed by Pieter Wuille in 2015, has been implemented in Liquid. Tree Signatures provide a multi-signature script that scales logarithmically with the number of public keys, and can encode spending conditions other than n-of-m. For example, a transaction smaller than 1 KB can support tree signatures with 1000 public keys. It also opens up logically generalized spending conditions.
Recursive Constraints
If you can interpret a transaction and impose constraints on specific parts of it, you can build conditions that can be extended across multiple conditions, creating chains of continuous constraints. This concept is called "recursive constraints".
OP_CAT is a unique protocol because it gives us a lot of power in just 10 lines of code. It solves all the limitations we mentioned above: transaction data visibility, better math functions, and linear execution mode. Although OP_CAT may seem unremarkable at first glance, it unlocks a lot of potential that can be used creatively. It can also serve as a building block for more features that are beyond the scope of this article, such as quantum-resistant Lamport signatures.
Is it secure?
Before OP_CAT was initially removed, combined with OP_DUP (duplicate the stack), even if the object initially operated on in the stack was only 1 byte, by repeatedly applying this pair of opcodes, the size of the stack object would quickly expand until it burst the memory. This could be used as a DoS attack. The new proposal easily prevents this attack by imposing a 520-byte limit on stack elements.
Is there a risk of creating infinitely running contracts?
If the question means, does OP_CAT's change to the linear execution model of scripts mean that scripts can no longer statically constrain their resource usage (making it a linear function of the script size), then the answer is no.
Will the restrictions create a market for issuing other coins on Bitcoin?
If we have recursive restrictions, then technically you can build complex layer-2 applications with NFTs, decentralized exchanges, cats, and so on. However, it’s not easy to do. It’s hard to see how this could become a viable market.
Can you permanently “taint” a sum of money with CAT?
In the case of colored coins and NFTs, issuing an asset actually “burns” a satoshi, marking it to symbolize ownership of the “layer-2” asset. This process is called “tainting”. But only the owner of a sum of money can mark his money, and Bitcoin wallets can’t understand this (unless developers add additional code to enable this feature). The resulting coins will not be accepted by Bitcoin wallets. Maybe they will be accepted by cat wallets or something else, but it’s irrelevant to the vast majority of Bitcoin users.
Will it cause MEV problems for Bitcoin?
A key difference between Bitcoin and Ethereum is the visibility of transactions. Unlike Ethereum, not all aspects of contracts in Bitcoin are necessarily transparent, so Bitcoin miners do not have the same ability to see the internal state of a contract and preempt certain actions as Ethereum miners.
The main concern about OP_CAT is that it could affect economic incentives, resulting in "miner extractable value (MEV)". My previous article discusses this topic at length. Many users worry that if we make layer 2 contracts technically possible, MEV will inevitably come.
But is this true? Specifically, if you can issue layer 2 coins on Bitcoin, does that mean they will definitely be adopted? You can imagine developing simple swap contracts, or relatively inefficient NFTs, but developing something as complex as a decentralized exchange with an automated market maker is almost impossible to do; and, although "technically possible", we have never seen such things developed on Liquid.
Is OP_CAT relatively perfect?
A subset of Bitcoiners, who can be called “fixists,” favor keeping Bitcoin in its current state and are skeptical of any protocol upgrades. They are particularly concerned that major changes, such as the introduction of restrictions, could reduce the decentralization of the network. Their advocacy is based on a belief that it is better to stick with Bitcoin’s original vision.
The irony is that OP_CAT was part of the original Bitcoin, which leads to the exact opposite opinion. Some people believe that bringing OP_CAT back is more in line with Satoshi’s original vision.
If you want some of the safekeeping features that come with recursive restrictions, then OP_CAT is good, but definitely not as good as a full-blown Lisp-style scripting language. The problem is that introducing something like this would be a huge change and is unlikely to gain popularity quickly. Or maybe you're on the other side, and prefer the simplicity of non-recursive constraints like OP_CTV and OP_VAULT.
Non-recursive constraints are simpler and easier to analyze, without the risk of creating uncontrolled constraint chains. But what if some form of recursive constraint is bound to happen? Over the past few years, developers have noticed that almost any extension of transaction validation logic can be used to emulate the functionality of OP_CAT. In the Script universe, there are two worlds based on the size of the stack elements. For elements larger than 4 bytes, you can check equality, interpret them as public keys or signatures, and hash them. And for elements less than or equal to 4 bytes, you can perform operations on them. With a RISC-V processor running on a BitVM, you can do anything. Anything that allows you to emulate the functionality of OP_CAT (splitting stack elements or splicing them together) can merge these two worlds, allowing you to "do anything" in Script.
Some researchers, such as Andrew Poelstra, anticipate that we can make recursive restrictions with very few new opcodes. If this is true, there is reason to find the best way possible. Is OP_CAT the most likely restriction proposal to pass?
OP_CAT remains a strong contender in the restriction debate. OP_CAT is not the most elegant tool, but it has the highest power/complexity ratio, which will allow developers to create some amazing new features. More possibilities for the future of Bitcoin.