Why Bitcoin's Script Cannot Express Recursive Covenants Without Additional Opcode Extensions

You are sitting with a UTXO you want to encumber forever. Not just for the next hop, but for every hop after that, every future owner, every transaction this coin ever touches. You want to write a rule into the locking script that says: this coin may only ever move to an output that carries this same rule. A covenant that breeds copies of itself.

You cannot do it. Not today, not without opcodes Bitcoin's Script does not have. Understanding why requires a short tour of what Script can actually see, and the one thing it is deliberately blind to.

What Script Actually Sees When It Runs

Bitcoin Script is a stack-based, intentionally limited language. When a node evaluates a spending transaction, the script runs against data the spender provides in the witness or scriptSig. It can check signatures, hash preimages, timelocks, multisig combinations. Those are the tools in the box.

What Script cannot natively do is inspect the transaction that contains it. No opcode exists to push the current transaction's outputs onto the stack. It cannot ask: where is this coin going next? It operates on what the spender hands it, not on the transaction envelope itself.

This is not an oversight. Satoshi's design explicitly treated Script as a predicate over spending conditions, not a general-purpose program that could reach outside itself. The result is a clean security model and a deliberate blind spot, and the two things are not unrelated.

The Covenant Problem, Precisely

A covenant, in Bitcoin terms, is a restriction on how a UTXO can be spent that goes beyond who can sign. A simple example: a UTXO that can only be sent to a specific address, or only in amounts below some threshold.

A non-recursive covenant is already within reach for some use cases. You can approximate one by committing to a specific next scriptPubKey inside the locking script and using a signature scheme (SIGHASH_SINGLE tricks, pre-signed transaction chains) to enforce it. Clunky, but functional for one hop.

Recursion is the hard part. A recursive covenant needs to verify that the output it creates carries the same covenant script. That means the script must be able to:

  1. Know its own scriptPubKey, its own bytecode.
  2. Push that value onto the stack.
  3. Compare it against the output scriptPubKey in the spending transaction.

Step one is already broken. Script has no opcode to introspect its own code at runtime. Step three is broken too, because Script has no opcode to read output scriptPubKeys from the current transaction at all.

Think of it like a photocopier with no camera. It can stamp an expiry date on whatever paper you feed in. It cannot look at its own output tray and stamp a copy of itself onto the next sheet.

The Introspection Gap: Why OP_CHECKSIG Is Not Enough

Some readers notice that OP_CHECKSIG does involve the transaction: signatures under SIGHASH_ALL commit to all outputs, and the script verifies that signature. Couldn't you exploit that to sneak in output inspection?

You can get partway there. The SIGHASH mechanism lets a script enforce that the spending transaction was signed by a particular key over a particular set of outputs. If you pre-generate a signature with a known-private-key covenant trick (sometimes called a key-deletion scheme), you can force a specific next transaction shape. Researchers including Rubin and Moser have explored this territory in published work.

But this only enforces a fixed next transaction. It does not propagate. The second hop has no way to re-derive or re-enforce the rule. You have achieved one generation of covenant, not infinite descent. The recursive property, that every future UTXO in the lineage carries the restriction, requires that the script can verify the output script of the transaction being constructed, not just a pre-committed signature over one specific future state.

The gap is precise. Script needs a way to push output scriptPubKeys (or the full serialized transaction outputs) onto the stack during execution. It has no such opcode.

What the Proposed Extensions Actually Add

Several opcode proposals exist to close this gap, each approaching the problem from a slightly different angle, and the distinctions between them matter more than the discourse usually acknowledges.

OP_CHECKTEMPLATEVERIFY (CTV, BIP 119), proposed by Jeremy Rubin, lets a script commit to a hash of a template transaction: its output count, amounts, and scriptPubKeys. When the coin is spent, the script checks that the spending transaction matches the template. This enables non-recursive covenants efficiently and opens the door to congestion-control constructs and payment pools. It does not, by itself, enable full recursive covenants, because the script cannot dynamically copy itself into the template. The template is fixed at locking time. Recursion requires either a fixed-point trick (where the script is designed so the template hash happens to equal the hash of a script that checks for itself) or an additional introspection opcode.

OP_CAT, the opcode that concatenates two stack items and was disabled in 2010, is a candidate enabler. With OP_CAT restored, a script can assemble an output scriptPubKey on the stack and compare it against something. Combined with a transaction introspection opcode, you get the recursive covenant machinery.

OP_TXHASH and OP_CHECKSIGFROMSTACK, proposed more recently, let a script hash specific fields of the current transaction and then verify a signature over that hash. This is a more surgical introspection tool: select exactly which transaction fields to commit to, push that hash onto the stack, check it. With the right combination, a script can verify that a specific output in the current transaction carries a specific script, enabling genuine recursion.

OP_VAULT (BIP 345), proposed by James O'Beirne, takes a higher-level approach. It bakes vault semantics directly into two new opcodes (OP_VAULT and OP_VAULT_RECOVER), so the recursive covenant pattern for a time-locked recovery mechanism does not require assembling the plumbing yourself. It is a domain-specific covenant rather than a general-purpose introspection primitive.

None of these are in Bitcoin mainnet. Each involves tradeoffs in expressiveness, implementation complexity, and the surface area opened for unintended Script constructs.

A Concrete Mini-Scenario: The Vault That Leaks

Consider two developers, Priya and Tomás, both building self-custodied cold storage vaults on Bitcoin.

Priya builds hers using pre-signed transactions. She generates a vault address, pre-signs a recovery transaction to a cold key with a 144-block timelock (roughly one day), and deletes the hot private key. This works. One covenant hop, enforced by a pre-computed signature. But if she ever needs to top up the vault, she re-runs the whole setup ceremony. And if she wants to sweep into a new vault, she cannot propagate the covenant. The new UTXO is just a normal coin.

Tomás wants something stricter: any coin that ever exits this vault must itself be a vault. Recursive. With today's Script, he cannot express it. He could simulate one generation by pre-signing again, but the second-hop coin, the third, every coin that touches the system, would need fresh pre-signing. That is not a covenant. It is a very tedious signing ceremony dressed up as one.

With OP_VAULT or OP_TXHASH plus OP_CAT, Tomás's design becomes expressible in the script itself, no ceremony required. Every UTXO in the lineage carries the enforcement rule. That is the difference, and it is not a small one.

One Honest Limitation the Proposals Don't Advertise Loudly

Recursive covenants introduce a genuine analysis burden that their proponents sometimes understate, and I think the understatement is a mistake that will eventually cost the proposals credibility.

If a UTXO's spending rules can propagate indefinitely, full nodes evaluating a chain of transactions must reason about arbitrarily deep covenant chains. In theory, a pathological script could require exponential verification work. Proposals like CTV are carefully designed to avoid this: template hashes are computed once and checked against a commitment, so verification is O(1) per hop regardless of chain depth. But more expressive combinations, particularly OP_CAT with general introspection, reopen the question.

The Bitcoin development community's caution here is not conservatism for its own sake. It reflects a real constraint: anything that makes script execution potentially unbounded is a denial-of-service vector, and Bitcoin nodes run on modest hardware by design. That constraint deserves to be stated plainly, not buried in footnotes.

The expressiveness you gain from recursive covenants is real. So is the verification discipline required to deploy them safely. Both things are true, and pretending otherwise helps no one.

The script language Bitcoin ships with today is not broken. It does exactly what it was designed to do. The covenant debate is really asking whether the design goal should expand, and if so, how much of the transaction envelope the script should be allowed to see. That is a values question as much as a technical one, which is precisely why it has been debated for years without resolution.

If you understand that the missing piece is output introspection, not just "more features," you already understand the debate better than most of the people having it. The question worth asking next is whether the nodes that would have to enforce these rules have been consulted, or just the developers who want to write them.