Is OP_CAT happening? The covenants proposal was just assigned BIP number #347. But before we delve deeper, let\u2019s explore what covenants are and why Bitcoiners may want them.<\/p>\n
Is Bitcoin an ideal state of digital e-cash or do we want more from our coins on-chain?<\/p>\n
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.<\/p>\n
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\u2019re 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).<\/em><\/p>\n This simplicity, while core to Bitcoin’s security model, introduces significant limitations in the scripting language\u2019s ability to support even basic smart contracts.<\/p>\n One limitation of Bitcoin Script is its operational model where opcodes are executed sequentially with no loops.<\/p>\n 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.<\/p>\n 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\u2019s capability to manage complex workflows.<\/p>\n 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<\/a>, 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.<\/p>\n 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.<\/p>\n 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.<\/p>\n 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<\/a> are grouped under the term covenants.<\/p>\n So what exactly is the difference between smart contracts and new term covenants?<\/p>\n 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.<\/p>\n By enabling Script to interpret transaction data, we effectively create a way for that data to be used in contract logic.<\/p>\n These are just some of the more interesting introspection opcodes for covenants functionality:<\/p>\n OP_TXHASH: Provides the hash of a transaction\u2019s 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.<\/p>\n 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.<\/p>\n 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 \u201cvaulting\u201d, which lets users specify a transaction destination but not actually move coins except after a delay.<\/p>\n Then there is OP_CAT on its own, which is not directly an introspection opcode\u2026<\/p>\n OP_CAT: Enables Script to concatenate two elements on the stack, which is useful for combining different pieces of information within a script.<\/p>\n OP_CAT doesn\u2019t seem to have any introspection abilities, so what\u2019s going on here?<\/p>\n In 2021, Andrew Poelstra wrote about OP_CAT introspection tricks in a blog post<\/a>. He provided specific examples but presumed readers had prior knowledge of similar techniques. Here, I’ll aim to simplify that explanation for better understanding.<\/p>\n 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.<\/p>\n 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\u2014the signature into an (r, s) pair.<\/p>\n How do we derive OP_SPLIT functionality from OP_CAT?<\/p>\n \u201cIf 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.\u201d \u2014 Andrew Poelstra, TABConf 2021<\/a><\/p>\n What is happening here?<\/p>\n 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.<\/p>\n How does all this get us introspection?<\/p>\n \u201cIn 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.\u201d \u2014 Andrew Poelstra, TABConf 2021<\/a><\/p>\n Poelstra goes on to demonstrate how you can get a SHA2 hash for transaction inputs or outputs left on the stack. We\u2019ll 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.<\/p>\n Using the same techniques give us transaction introspection and quickly give us a basic version of vaults. Following the logic outlined in Poelstra\u2019s blog, a developer by the name of Rijndael proved that we can do this with OP_CAT alone in his implementation of Purrfect Vaults<\/a>.<\/p>\n \u201cRe-building a TXID on the stack to introspect previous transactions was actually easier than I expected.\u201d \u2014 Rijndael<\/p>\n 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.<\/p>\n In Bitcoin today, Merkle Trees are the data structure used for data verification, synchronization, and more or less \u2018chaining\u2019 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 the Liquid network.<\/p>\n Imagine a tree structure brimming with various spending conditions, such as hash preimages, timelocks, and public keys, known as tree signatures.<\/p>\n OP_CAT enables the creation of Tree Signatures which:<\/p>\n \u201c…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.\u201d \u2014 BIP author Ethan Heilman, on the bitcoin-dev mailing list<\/a><\/p>\n 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.<\/p>\n What is interesting about all of this?<\/p>\n 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.<\/p>\n 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.<\/p>\n 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.<\/p>\n Is there a danger of a contract running forever?<\/p>\n 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.<\/p>\n Would covenants create a market for other coins on top of Bitcoin?<\/p>\n 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\u2019s hard to see any serious markets do so.<\/p>\n Can you permanently “taint” coins by using CAT?<\/p>\n 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 \u2018layer-2\u2019 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.<\/p>\n Would this create an MEV problem on Bitcoin?<\/p>\n 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 don\u2019t have the same ability to see internal contract state and front-run them.<\/p>\nLinear Execution Model<\/h2>\n
Lack of Basic Arithmetic<\/h2>\n
Lack of Transaction Data Visibility<\/h2>\n
What do we do about it?<\/h2>\n
Smart Contracts vs. Covenants<\/h3>\n
OP_CAT: Unraveling All of the Possibilities<\/h2>\n
Transaction Introspection<\/h3>\n
Vaults<\/h4>\n
Merkle Trees for Script<\/h4>\n
Tree Signatures<\/h4>\n
Recursive Covenants<\/h3>\n
Is This Safe?<\/h4>\n