Bitcoin Vault
Research
CTV, CCV, and OP_VAULT are three proposed Bitcoin covenant designs for self-custody vaults.
We ran them head-to-head on regtest, measured everything, and found out which one breaks first —
and when.
How They Work
Vault Lifecycles
The CTV vault lifecycle is entirely deterministic. When you create the vault, you commit to every possible
future transaction upfront by hashing a template that locks in the outputs, amounts, and destinations. Funds
enter through a deposit to a P2WSH address whose script contains a CTV hash. To withdraw, you broadcast the
pre-signed unvault transaction that moves funds into a timelocked intermediate state. During the timelock
window, a watchtower can broadcast the pre-signed cold sweep to recover funds if the withdrawal looks
unauthorized. If no one intervenes, the hot wallet withdrawal completes after the delay. There is no partial
withdrawal — the entire vault balance moves as a single unit every time. This rigidity is what makes CTV
auditable: you can regenerate the full transaction tree from the initial parameters and verify that no
unauthorized paths exist. The total lifecycle costs 426 vB across three transactions (deposit 154 vB, trigger
164 vB, withdraw 108 vB). CTV uses two keys — a hot key for spending and a cold key for recovery — making it
the simplest key management model of the three designs.
Under the hood, CTV vaults use bare CTV scripts wrapped in P2WSH — no Taproot involved. The core opcode
OP_CHECKTEMPLATEVERIFY takes a 32-byte hash on the stack and verifies that the spending transaction matches
the committed template exactly: same outputs, same amounts, same sequence numbers. The vault script encodes
two spending paths. The first is the unvault path, which requires the CTV hash to match and the hot key
signature. The second is the cold sweep path, which requires only the cold key signature with no template
check, allowing emergency recovery to any destination. The unvault output itself contains another CTV-locked
script with its own two paths: a timelocked hot withdrawal (requiring CSV delay plus hot key) and a cold
sweep. This creates a hash-chain of pre-committed transactions where every state transition is fully specified
at vault creation time. The P2WSH approach means witness scripts are revealed only at spend time, but the
entire tree structure is knowable from the root hash. No script path is hidden — there is no Taproot key-path
shortcut, no internal key optimization, just bare script verification against committed templates.
CCV vaults store state inside Taproot output tweaks, which lets them do something CTV cannot: partial
withdrawals. When you deposit into a CCV vault, your funds land in a P2TR output whose internal key is a NUMS
point (nothing-up-my-sleeve, meaning no one can spend via the key path). The spending rules live entirely in
Taproot script leaves. To trigger a withdrawal, you provide a signature from the trigger key and the CCV
opcode verifies that the spending transaction creates valid successor outputs — a timelocked withdrawal output
and optionally a change output that returns remaining funds to a new vault with the same contract rules. This
change-back mechanism is the revault: you can withdraw 0.5 BTC from a 10 BTC vault and the other 9.5 BTC stays
vaulted under identical protections. Recovery is keyless — anyone who knows the vault address can broadcast
the recovery transaction, which sends all funds to the pre-committed recovery address. The total lifecycle
costs 418 vB (deposit 154 vB, trigger 154 vB, withdraw 110 vB), making it the cheapest of the three. CCV uses
only one key for the trigger, with recovery requiring no key at all.
CCV's internal machinery relies on OP_CHECKCONTRACTVERIFY to enforce state transitions across transactions.
Each vault output is a P2TR address with a NUMS internal key (specifically the generator point negated so no
discrete log is known). The contract rules live in Taproot leaf scripts. When a trigger transaction spends the
vault, CCV checks that the outputs conform to the contract: the withdrawal amount goes to a timelocked script,
and any change goes back to a new vault output with the same Taptree structure. The mode flags in CCV control
what gets verified — mode 0 checks the output's scriptPubKey matches a committed template, mode 1 verifies
amounts, mode 2 checks both. These mode bytes are critical because any value outside [0, 1, 2] causes the
interpreter to execute OP_SUCCESS, which makes the script trivially spendable by anyone. The minivault variant
uses CCV alone without CTV, while the full vault combines both opcodes. State is carried forward through
Taproot tweaks: the contract's Taptree is reattached to each successor output, ensuring the same spending
rules apply to change outputs across an unlimited number of revault cycles.
OP_VAULT uses a dedicated opcode pair — OP_VAULT and OP_VAULT_RECOVER — purpose-built for vault contracts,
unlike CTV and CCV which are general-purpose covenant opcodes adapted for vault use. The lifecycle begins with
a deposit to a P2TR address whose internal key is the recovery pubkey. The trigger key holder starts a
withdrawal by broadcasting a trigger transaction that invokes OP_VAULT, which enforces a timelock delay and
commits to a specific withdrawal destination. During the delay, a watchtower can broadcast an authorized
recovery transaction using OP_VAULT_RECOVER, which requires the recoveryauth key signature. If no recovery
happens, the withdrawal completes after the timelock expires. OP_VAULT's distinctive feature is authorized
recovery: unlike CCV where anyone can trigger recovery, here only the recoveryauth key holder can do it. This
prevents griefing attacks where random observers force funds back to cold storage. The cost is a three-key
model — trigger, recoveryauth, and recovery address — which is the most complex key management of the three
designs. The lifecycle costs 567 vB (deposit 154 vB, trigger 292 vB, withdraw 121 vB), roughly 36% more than
CCV, largely due to the 2-input fee wallet pattern that adds 80–90 vB to trigger and recovery transactions.
OP_VAULT's internal structure uses P2TR with the recovery pubkey as the Taproot internal key. This is a
deliberate design choice: if all three keys are available and cooperating, a key-path spend (using the
recovery pubkey) can bypass the script entirely. The Taptree contains leaf scripts for OP_VAULT (trigger path)
and OP_VAULT_RECOVER (recovery path). When OP_VAULT executes, it verifies that the spending transaction
creates an output with a specific structure — a timelocked script committing to the declared withdrawal
destination. The opcode enforces the delay parameter and output matching in consensus, meaning a miner cannot
forge a valid trigger. OP_VAULT_RECOVER checks the recoveryauth signature and ensures funds move to the
committed recovery address. The recovery address is baked into the vault at creation and is immutable — even
with both the trigger key and recoveryauth key compromised, an attacker cannot redirect recovery funds. This
is why dual-key compromise (TM6) results in denial-of-service rather than theft: the attacker can trigger and
then recover in a loop, but funds always land at the legitimate recovery address. The fee wallet pattern uses
a separate UTXO to pay transaction fees, avoiding the need to deduct fees from the vault amount, but adding an
extra input to every transaction.
The timelock race is the fundamental security mechanism shared by all three vault designs. When an
unauthorized party triggers a withdrawal — whether through key theft, malware, or coercion — a clock starts.
The withdrawal is timelocked: it cannot complete until a specified number of blocks have been mined. During
this window, a watchtower (software monitoring the blockchain for vault-related transactions) detects the
unauthorized trigger and broadcasts a recovery transaction to sweep funds back to cold storage. The attacker's
only option is to try to prevent the recovery transaction from confirming before the timelock expires. In CTV,
the attacker can attempt fee pinning — chaining descendant transactions off the unvault output to block CPFP
fee bumping, exploiting the 25-transaction descendant limit. In CCV and OP_VAULT, the attacker can attempt
watchtower exhaustion — splitting the vault into many small UTXOs and triggering them all, forcing the
watchtower to pay recovery fees for each one. The race outcome depends on the fee environment: at low fees,
pinning is cheap and CTV is most vulnerable; at high fees, recovery is expensive and CCV/OP_VAULT watchtowers
can be bled dry. This fee-dependent inversion means no single vault design wins the race under all conditions.
Threat Models
Where the Vulnerabilities Are
CRITICAL Fee Pinning (TM1)
Fee pinning exploits CTV's transaction structure to block emergency recovery. When the unvault transaction
broadcasts, its output can be spent by anyone who knows the pre-committed template — because CTV scripts are
public once revealed. An attacker chains 25 low-fee descendant transactions off the unvault output, hitting
Bitcoin Core's descendant limit (MAX_DESCENDANT_COUNT). The cold sweep transaction, which needs to spend that
same output via CPFP, is rejected by mempools because the descendant chain is full. The watchtower cannot bump
fees, cannot replace the junk transactions (they don't signal RBF), and cannot get the recovery transaction
into any block. When the timelock expires, the attacker's hot withdrawal confirms and the funds are gone. This
attack costs almost nothing — the 25 junk transactions can pay minimum relay fees. It is unique to CTV because
its P2WSH structure exposes the unvault output to third-party spending, and the pre-committed transaction
template cannot include anchor outputs or other fee-management mechanisms added after vault creation. CCV and
OP_VAULT are immune because their Taproot structures don't expose intermediate outputs to third-party
descendant chaining.
CRITICAL CCV Mode Bypass (TM8)
CCV mode bypass is the highest-severity developer footgun we found. The CCV opcode accepts a mode byte that
controls verification behavior: mode 0 checks scriptPubKey, mode 1 checks amount, mode 2 checks both. Any mode
value outside this set — 3, 4, 255, anything — triggers OP_SUCCESS, a consensus rule designed for future
soft-fork extensibility. OP_SUCCESS makes the entire script evaluate to true unconditionally: no signature
needed, no covenant check, no constraints at all. We built a structurally complete vault contract with one
leaf script accidentally using mode=3 and drained it with a 110 vB transaction containing no signature. The
funds moved to an arbitrary destination with zero authentication. This is not a bug in the CCV specification —
it is the intended behavior for forward compatibility. But it means a single byte error in vault construction
creates a script that looks correct, passes every static analysis check, deploys successfully, accepts
deposits normally, and then allows anyone who notices the mode flag to steal everything. The attack surface
exists only in CCV because CTV has no mode flags and OP_VAULT uses dedicated opcodes with no extensibility
trap.
SEVERE Trigger Key Theft (TM3 + TM5)
Trigger key theft is the canonical vault attack: an adversary compromises the hot key used to initiate
withdrawals and attempts to steal funds before the watchtower can react. In all three vault designs, the
attack follows the same pattern — the attacker triggers an unauthorized withdrawal, the timelock delay begins,
and the watchtower races to broadcast recovery. The difference is recovery cost. CTV recovery uses a
pre-signed cold sweep at 180 vB. CCV recovery is the cheapest at 122 vB because keyless recovery requires no
signature, just knowledge of the vault address. OP_VAULT recovery is the most expensive at 246 vB because the
2-input fee wallet pattern adds overhead and the recoveryauth signature adds witness data. Across all three
designs, the watchtower wins this race as long as it is online and funded — the timelock gives it blocks, not
seconds, to respond. The real danger is trigger key theft combined with watchtower failure, which escalates to
fund theft in CTV (attacker completes the hot withdrawal) and CCV (attacker completes withdrawal to their
chosen destination). In OP_VAULT, even with watchtower failure, the attacker cannot redirect recovery funds
because the recovery address is immutable.
SEVERE Recovery Griefing (TM2)
Recovery griefing is the direct consequence of CCV's keyless recovery design. Since anyone can trigger
recovery without any key or signature — they just need to know the vault's address and construct a valid
recovery transaction — a griefer can watch for trigger transactions and immediately broadcast recovery,
sending funds back to cold storage before the legitimate withdrawal completes. No funds are stolen, but the
vault owner is permanently denied access to their hot wallet. Every withdrawal attempt gets front-run by the
griefer, creating an infinite denial-of-service loop. The griefer pays only the recovery transaction fee (122
vB × fee rate), which is cheap compared to the damage inflicted. CTV has moderate griefing exposure because
the cold sweep requires a cold key signature — only someone with the cold key can grief. OP_VAULT has the best
griefing resistance because recovery requires the recoveryauth key signature, making anonymous griefing
impossible. The structural tradeoff is that CCV's keyless recovery is simultaneously its greatest strength
(funds recoverable even if all keys are lost) and its greatest weakness (anyone can trigger recovery). You
cannot have both open recovery access and griefing resistance — they are fundamentally opposed design goals.
SEVERE Watchtower Exhaustion (TM4 + TM7)
Watchtower exhaustion turns CCV and OP_VAULT's partial withdrawal feature into an attack vector. The attacker,
holding the trigger key, splits the vault into hundreds or thousands of small UTXOs using repeated partial
withdrawals, each creating a new independent vault. Then the attacker triggers withdrawals on all of them
simultaneously. The watchtower must now broadcast a separate recovery transaction for every single UTXO,
paying fees on each one. At 122 vB per recovery for CCV and 246 vB per recovery for OP_VAULT, the watchtower's
fee budget is quickly exhausted. Once the budget runs out, remaining triggered UTXOs complete their
unauthorized withdrawals uncontested. OP_VAULT fails roughly twice as fast as CCV because its recovery
transactions cost twice as much. CTV is completely immune to this attack because it does not support partial
withdrawals — there is no way to split a CTV vault into smaller pieces. The attacker cannot create the initial
UTXO fragmentation that the attack requires. This is the clearest example of the fee-dependent security
inversion: at low fees, the splitting phase is cheap and the attack is viable against CCV and OP_VAULT. At
high fees, splitting is expensive but recovery is also expensive, creating a complex cost tradeoff.
HIGH Dual-Key
Compromise (TM6)
Dual-key compromise examines what happens when an attacker obtains both the trigger key and the recoveryauth
key in an OP_VAULT system. This sounds like it should be game over — and in CTV it is, because holding both
the hot key and cold key gives the attacker complete control. But OP_VAULT's design produces a surprising
result: denial-of-service, not theft. The attacker can trigger a withdrawal and then immediately use the
recoveryauth key to invoke authorized recovery, sending funds back to the recovery address. They can repeat
this loop indefinitely, preventing the legitimate owner from ever completing a withdrawal. However, they
cannot redirect funds because the recovery address was set at vault creation and is immutable — it is embedded
in the Taproot internal key structure and cannot be changed by anyone, including someone holding all three
keys. The attacker is stuck in a trigger-recover loop where funds always return to the legitimate recovery
address. To actually steal funds, the attacker would need to compromise the recovery address itself (a
separate key or multisig), making this effectively a three-key attack threshold. CCV's single-key model means
this specific scenario does not apply — there is only one trigger key and recovery is keyless.