TeachMeBitcoin

Lock funds, pre-commit to specific distribution transactions

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

19. Covenant Script Proposals (OP_CTV)

Overview

Covenants are restrictions on how UTXOs can be spent — not just who can spend them. Bitcoin currently lacks native covenant opcodes, but several proposals exist. OP_CHECKTEMPLATEVERIFY (OP_CTV, BIP 119) is the most mature proposal, introduced by Jeremy Rubin. It commits to a specific template of the spending transaction, enabling use cases like vaults, payment pools, and congestion control.

What OP_CTV Does

OP_CTV (opcode 0xb3) verifies that the current spending transaction matches a pre-committed template hash. The "template hash" commits to specific fields of the transaction:

CTV hash commits to:
  - nVersion (4 bytes)
  - nLockTime (4 bytes)
  - scriptSig hash (if any inputs have scriptSig)
  - Input count
  - Sequences hash
  - Output count
  - Outputs hash
  - Input index (the index of the input being checked)

DOES NOT commit to:
  - Input outpoints (which UTXO is being spent)
  - Input amounts

Script Usage

CTV locking script:
<32-byte template_hash> OP_CTV

CTV unlocking script:
(empty — the script itself is the entire check)

Or combined with other conditions:
<pubkey> OP_CHECKSIGVERIFY
<template_hash> OP_CTV

Computing the CTV Hash

def compute_ctv_hash(tx: Transaction, input_index: int) -> bytes:
    import struct
    h = hashlib.sha256()
    h.update(struct.pack('<i', tx.version))           # nVersion
    h.update(struct.pack('<I', tx.locktime))          # nLockTime
    if any(inp.script_sig for inp in tx.inputs):
        scriptsig_hash = sha256d(b"".join(
            compact_size(len(inp.script_sig)) + inp.script_sig
            for inp in tx.inputs
        ))
        h.update(scriptsig_hash)
    h.update(struct.pack('<I', len(tx.inputs)))       # input count
    seq_hash = sha256d(b"".join(
        struct.pack('<I', inp.sequence) for inp in tx.inputs
    ))
    h.update(seq_hash)                                # sequences hash
    h.update(struct.pack('<I', len(tx.outputs)))      # output count
    outputs_hash = sha256d(b"".join(
        serialize_output(out) for out in tx.outputs
    ))
    h.update(outputs_hash)                            # outputs hash
    h.update(struct.pack('<I', input_index))          # input index
    return h.digest()

Use Cases

1. Non-interactive payment channels (udi channels):

# Lock funds, pre-commit to specific distribution transactions
# Recipient can claim their portion without sender interaction after commitment

2. Congestion control trees:

Root CTV → Branch CTV_1 → Branch CTV_2 → Leaf payments
           Branch CTV_3 → ...

Allows one on-chain transaction to "commit" to thousands of future payments,
settled lazily during low-fee periods.

3. Vaults (described in detail in Section 20).

Opposition and Controversy

OP_CTV remains unactivated due to controversy in the Bitcoin community:

Arguments for activation:
  - Simple, well-defined semantics
  - Enables vaults without trusted setup
  - Non-recursive (cannot create unbounded covenant chains)
  - Congestion control use case is valuable

Arguments against:
  - Insufficient ecosystem demand demonstrated
  - Alternative proposals (OP_VAULT, TXHASH, MATT) may be superior
  - Activation process (speedy trial vs. BIP 8) disputed
  - Some argue covenants reduce fungibility
☕ Help support TeachMeBitcoin

TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation:

Ethereum: 0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin: bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana: 4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J
Address copied to clipboard!