Is OP_CAT happening? The covenants proposal was recently assigned BIP number 347. But before we delve deeper, let’s explore what covenants are and why Bitcoiners may want them.
Is Bitcoin an ideal state of digital e-cash or do we want more from our coins on-chain?
Scratching the Surface: Bitcoin Scripts Limitations
To understand covenant proposals like OP_CAT, it's crucial to grok the fundamental limitations of Bitcoin Script as it is today. Under the hood, Bitcoin allows for the creation of basic smart contracts through codes that define the rules for locking and unlocking funds. However, Bitcoin Script, as a programming language, is fairly limited to basic logic that comes into play only when moving coins in a new transaction.
In Bitcoin today there is no way to pre-configure or dictate your coins' transaction paths, or how fast coins can move at the time they are being locked up (aside from hacky workflows using PSBT, partially signed bitcoin transactions, which cannot properly include transaction fees, prove deletion if unused, or prevent broadcasting later).
This simplicity, while core to Bitcoin's security model, introduces significant limitations in the scripting language’s ability to support even basic smart contracts.
Linear Execution Model
One limitation of Bitcoin Script is its operational model where opcodes are executed sequentially with no loops.
From this example of a P2PKH transaction, you can see how the script executes linearly: duplicating the public key, hashing it to an address, verifying the hash against the lock script, and finally checking the signature against the public key.
The absence of looping means that scripts are not Turing complete and are guaranteed to terminate, preventing issues like infinite loops that could potentially halt or significantly slow down the network. While this design choice allows resource usage to be statically bounded, it also limits Script’s capability to manage complex workflows.
Lack of Basic Arithmetic
Bitcoin Script has just under 100 nontrivial opcodes, and somewhat surprisingly there is no ability to multiply, divide, or combine objects on the stack. As many users interested in OP_CAT will know, Satoshi disabled several opcodes in Bitcoin in 2010, including OP_OR, OP_MUL (multiply), OP_DIV (divide), and OP_CAT (concatenate) among others. The disabled opcodes were removed because their original implementations had exploitable vulnerabilities that could compromise the network's security. But the absence of these opcodes makes it difficult to do basic math, which could be useful in simple scenarios like calculating transaction fees in a contract.
Lack of Transaction Data Visibility
Superficially, I think most people assume that Bitcoin smart contracts are able to see value amounts and any other parts of transaction data, since this information is already publicly viewable on the blockchain. But contrary to this assumption, contracts on Bitcoin are not able to set spend conditions based on transaction data, because Bitcoin Script has a very limited ability to see into transaction data at all.
If script had the ability to interpret more details within transaction data, we could build much more robust contracts that could do all the fun things like enforce specific spending conditions, create multi-stage transactions, and enable more advanced security features like vaults.
What Do We Do About It?
We know Bitcoin has these limitations, and over the years many different proposals have been discussed to introduce (or in some cases reintroduce) this functionality. Broader experiments with Bitcoin Script, such as Simplicity and others, aim to provide an alternative to stack-based constraints. Opcodes like OP_MULTISHA256, OP_LESS, and OP_LE32TOLE64 aim to upgrade Bitcoin's arithmetic abilities. Proposals like OP_CTV and OP_CAT that deal with introspection opcodes are grouped under the term covenants.
So what exactly is the difference between smart contracts and new term covenants?
Smart Contracts vs. Covenants
Smart contracts are self-executing transactions that transfer funds without intermediaries. In Bitcoin today, the smart contracts are limited to the act of locking and unlocking bitcoin with Bitcoin Script. Covenants aim to enhance Bitcoin's smart contracts functionality by enabling users to control how their funds are spent in future transactions.
By enabling Script to interpret transaction data, we effectively create a way for that data to be used in contract logic.
These are just some of the more interesting introspection opcodes for covenants functionality:
- OP_TXHASH: Provides the hash of a transaction’s inputs (or outputs), and gives Script the ability to verify and enforce conditions based on transaction data.
- OP_CSFS + OP_CAT: The two together allow scripts to check signatures against any data, not just the transaction itself. This means Script can verify conditions based on transaction data or external information, expanding the possibilities for validation within Bitcoin scripts.
These two opcodes are intentionally broad, enabling complex validation processes and introspection capabilities. Others are more narrow in scope and are designed to be a more limited form of covenants.
- OP_CHECKTEMPLATEVERIFY (CTV): Allows a transaction output to embed a template of a future spending transaction, enabling covenants in a more constrained way.
- OP_VAULT: Enables a specific form of covenant used for “vaulting”, which lets users specify a transaction destination but not actually move coins except after a delay.
Then there is OP_CAT on its own, which is not directly an introspection opcode…
- OP_CAT: Enables Script to concatenate two elements on the stack, which is useful for combining different pieces of information within a script.
OP_CAT does not seem to have any introspection abilities, so what is going on here?
OP_CAT: Unraveling All of the Possibilities
In 2021, Andrew Poelstra wrote about OP_CAT introspection tricks in a blog post. He provided specific examples but presumed readers had prior knowledge of similar techniques. Here, I will aim to simplify that explanation for better understanding.
In Bitcoin Script, there are only three primary opcodes that allow you to introspect the transaction data: CHECKLOCKTIMEVERIFY, CHECKSEQUENCEVERIFY, and CHECKSIG. Additionally, there are variants like CHECKSIGVERIFY, CHECKSIGADD, CHECKMULTISIG, and CHECKMULTISIGVERIFY, which are essentially minor variations of CHECKSIG. The first two only let you see if the check is verified, providing a fairly narrow functionality. CHECKSIG is similar, but the difference here is that it allows you to grab the signature and the public key on the stack. Interesting.
Traditionally, we think of concatenation as a function that joins two items together, but we can also use it to separate or split an item, in this case—the signature into an (r, s) pair.
How do we derive OP_SPLIT functionality from OP_CAT?
“If you have some big object you can split it into two by asking the user to spend time to provide the two pieces. You CAT them together and check equality basically. You can always invert every operation this way. With CAT by itself you can break apart signatures.” — Andrew Poelstra, TABConf 2021
What is happening here?
By requiring the user to provide the signature, public key, and transaction, you can split the signature into its component parts, then checking each part independently against the transaction data. This technique can be viewed as a form of splitting or combining, as it validates that the signature and public key are indeed the components of a valid transaction.
How does all this get us introspection?
“In Taproot where we have Schnorr signatures using OP_CAT and the Schnorr signature verification opcode it turns out that it is possible to get a form of non-recursive covenant where you literally get a transaction hash. Not even like a funny mangled transaction hash but a literal SHA2 hash of all the transaction data onto the stack.” — Andrew Poelstra, TABConf 2021
Poelstra goes on to demonstrate how you can get a SHA2 hash for transaction inputs or outputs left on the stack. We will skip the moon math here, but the implication is that with OP_CAT we can constrain parts of a transaction as a requirement of the unlocking script. We can constrain the send address or value being sent of that transaction, where the transaction hash serves as the key to unlock it.
Vaults
Using the same techniques give us transaction introspection and quickly give us a basic version of vaults. Following the logic outlined in Poelstra’s blog, a developer by the name of Rijndael proved that we can do this with OP_CAT alone in his implementation of Purrfect Vaults.
“Re-building a TXID on the stack to introspect previous transactions was actually easier than I expected.” — Rijndael
With vaults, users specify the next address that their funds must go to, providing mechanisms for fund recovery in case of key compromise, and reducing the incentive for private key theft.
Merkle Trees for Script
In Bitcoin today, Merkle trees are the data structure used for data verification, synchronization, and more or less ‘chaining’ the blockchain's transactions and blocks together. The OP_CAT opcode, which enables the concatenation of two stack variables, when used alongside SHA256 hashes of public keys, facilitates a straightforward Merkle tree verification process for scripts. This approach, initially proposed by Pieter Wuille in 2015, was successfully implemented in Liquid.
Imagine a tree structure brimming with various spending conditions, such as hash preimages, timelocks, and public keys, known as tree signatures.
Tree Signatures
OP_CAT enables the creation of Tree Signatures which:
“...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.” — BIP author Ethan Heilman, on the bitcoin-dev mailing list
This would enable the validation of any hashed content within the tree, maintaining data integrity and trustworthiness without adding unnecessary bulk or bloat to the blockchain.
What is interesting about all of this?
Recursive Covenants
If you have the ability to examine a transaction and apply constraints to certain parts of it, you can set up conditions that carry over through multiple transactions, effectively creating a chain of ongoing restrictions. This concept is called a recursive covenant. OP_CAT is a unique proposal because it gives us so much power for just 10 new lines of code. It has the ability to address all three of the initial limitations we covered at the onset of the post: transaction data visibility, better math functionality, and its linear execution model.
While OP_CAT may seem straightforward at first, it unlocks significant potential when leveraged creatively. It serves as a building block for even more functionality way beyond the scope of this discussion, like Post-Quantum Lamport Signatures.
Is this Safe?
Before OP_CAT was initially removed, when combined with OP_DUP (duplicate), and used repetitively to duplicate an initially-1-byte value on the stack, memory usage could be made to explode. This could have been used as a denial-of-service (DoS) attack as a result of increased memory consumption. The new proposal trivially prevents this attack by imposing a 520-byte limit on stack elements.
Is there a danger of a contract running forever?
If by this we mean, does OP_CAT change the execution model of Script to mean that it no longer statically bounds its resource usage (as a linear function of the Script size)? No.
Would covenants create a market for other coins on top of Bitcoin?
If you have a recursive covenant, yes, you can technically build up complex layer-2 applications, including NFTs, decentralized exchanges, and quantum cats. However, doing so is not trivial. It’s hard to see any serious markets do so.
Can you permanently "taint" coins by using CAT?
In the case of colored coins and NFTs, issuing these assets the user effectively 'burns' a satoshi, marking it in a way that signifies ownership of the ‘layer-2’ asset. This process is known as 'tainting' coins. But only the owner of a coin can mark their coin, and Bitcoin wallets will no longer recognize it (unless their authors explicitly add code to enable this). The resulting coins would not be accepted by bitcoin wallets. Probably they would be accepted by cryptocat wallets or something like that, but this is irrelevant to most bitcoin users.
Would this create an MEV problem on Bitcoin?
A key point of distinction between Bitcoin and Ethereum is transaction visibility. Unlike Ethereum, not all aspects of the contract are necessarily transparent, meaning that Bitcoin miners do not have the same ability to see internal contract state and front-run them.
The main concern of OP_CAT by economically minded Bitcoiners is the potential for Miner Extractable Value (MEV). As discussed more extensively in my previous post on the subject. Many users are concerned that if we make layer-2 contracts technically possible, MEV will become inevitable. But is this true? Specifically, does the technical feasibility of layer-2 coins on Bitcoin imply their inevitable creation and adoption?
You could imagine building simple swap contracts or comparatively inefficient NFTs, but building up something as complex as DEXs with automatic market makers seems extremely unlikely and is not ever something we have seen on Liquid despite the ‘technical possibility’ for it.
Is OP_CAT Really Perfect?
Hardly, far from it. Some folks would love to see recursive covenants, while others simply don’t want to see Bitcoin change at all.
A faction of Bitcoiners, "ossificationists", advocate for preserving Bitcoin in its current state and view any protocol upgrades with skepticism. They are particularly concerned that significant changes, like the introduction of covenants, could undermine the network's decentralization. Their argument hinges on the belief that it is best to stick closely to Bitcoin's original vision. The irony being that OP_CAT was initially part of Bitcoin, fuels a counterargument. Some believe that bringing OP_CAT back could actually realign Bitcoin with Satoshi's initial vision.
If you would like to see some of the security features that recursive covenants could make possible, OP_CAT would be nice, but definitely not as nice as a full-blown Lisp-esque scripting language. The problem here being that this would be a massive change to Bitcoin, that’s not likely to find its footing anytime soon.
Or maybe, you are on the other end, and you would prefer the simplicity of non-recursive like OP_CTV or OP_VAULT. Non-recursive covenants are simpler and easier to reason about, without the risk of creating an uncontrolled chain of constraints.
What if some version of recursive covenants were inevitable?
Over the years, developers have noticed that almost any extension to the transaction validation logic could be used to emulate the functionality of OP_CAT.
In the Script universe, there are two realms, based on the size of the stack elements. For stack elements larger than 4 bytes, you can compare for equality, interpret as a key a signature, or hash it. For stack elements less than or equal to 4 bytes, you can treat them as objects to do arithmetic or branching. With a RISC-V processor running on a BitVM, you can do literally anything. Anything that allows you to emulate OP_CAT, break stack elements up, or concatenate them together brings these two realms together, so that you can ‘do anything’ with Script.
Researchers like Andrew Poelstra expect we could do recursive covenants with pretty much any new opcode. If true, that would be a justification for working towards a way to do them well.
Is OP_CAT the Likely Path Forward for Covenants?
If covenants are not just interesting, but inevitable, how do we ensure it's implemented in such a way that gets more Bitcoin users sending trustlessly like Satoshi originally envisioned? While ossificationists remain divided, still OP_CAT continues to ascend as a strong contender in the covenants debate.
OP_CAT is not the most elegant chisel, but it's one with the best ratio of power to complexity, that would allow developers to carve up some amazing new features.
Note: an earlier version of this article was published in Bitcoin Magazine and can be read here.