Skip to content

Instantly share code, notes, and snippets.

@jonasnick
Last active January 27, 2025 17:00
Show Gist options
  • Save jonasnick/e9627f56d04732ca83e94d448d4b5a51 to your computer and use it in GitHub Desktop.
Save jonasnick/e9627f56d04732ca83e94d448d4b5a51 to your computer and use it in GitHub Desktop.

Technical Evaluation of Selected Bitcoin Consensus Changes

Preface

This document provides a technical assessment of specific Bitcoin consensus changes in the spirit of RFC 7282. The primary aim is to identify any technical objections to proposed changes. As part of an iterative process, this document may be updated as new information becomes available.

The scope is limited to consensus rule changes that qualify as softforks. However, all softforks, regardless of their specific changes, carry inherent risks:

  • Chain splits can occur if full node implementations don't enforce exactly identical rules. This requires unambiguous specifications and bug-free implementations.
  • Softforks typically result in a complexity increase of the consensus rules. All full node implementations must permanently consider, maintain and test these new rules.
  • If less than 100% but more than 50% of hashrate enforces the softfork, users who haven't updated may experience blockchain reorganizations. These can be long and frequent, depending on the enforcing hashrate. Note that hashrate may signal support without actually enforcing the rules.
  • If the majority hashrate doesn't enforce the softfork, a chain split occurs between updated and non-updated users.

Thus, a proposed softfork must outweigh these risks by

  1. Significantly and demonstrably improving security, scalability, privacy or self-custody. For example, if the softfork is supposed to enable new applications or improve upon existing ones, there should be compelling evidence of:
    • Technical feasibility of building the application
    • Significant user benefits
    • Likely adoption
    • Necessity of consensus changes (i.e., the application cannot be implemented using existing rules)

In addition, we consider the following evaluation criteria:

  1. Technical soundness of the specification
  2. Impact on Bitcoin's incentive structure and potential centralization risks
  3. Compatibility with Bitcoin's core social contract (e.g., resistance to censorship)
  4. Analysis of alternatives
    • Simpler proposals with equivalent benefits
    • Similarly complex proposals with additional benefits
    • More complex proposals offering substantially greater benefits

BIP 119 CHECKTEMPLATEVERIFY

Soundness of the specification

I have not reviewed the specification in detail. The specification suggests that the implementation is not particularly difficult, but also not entirely straightforward due to the mandatory caching mechanism. For example, a data race bug was discovered during peer review of the Bitcoin Core PR, but only after the CTV activation client was published.

Applications

The BIP proposes several use cases as motivation: vaults, non-interactive payment channel creation, congestion controlled batching, efficient construction of discreet log contracts, and payment pools.

Vaults

Vaults based on CTV behave quite differently from traditional vaults. In a CTV-based vault, funds can be unvaulted only to a predefined hot wallet. If the user detects unauthorized unvaulting, they can sweep the funds being unvaulted to the cold wallet. From the hot wallet, funds can then be sent to their final destination. This design provides no protection against the hot wallet itself being compromised. Traditional vaults, in contrast, allow direct unvaulting to the intended final destination.

The primary advantage of this design is that users can replenish their hot wallet from a vault managed by an online computer, eliminating the need to access a cold wallet. Implementations must ensure that an adversary who compromises both the vault and hot wallet cannot delete state in a way that prevents creation of committed unvaulting and sweep transactions. However, there are several aspects of this vault design that impact usability:

  • The amounts that can be unvaulted per transaction are fixed at the time the vault receives new funds.
  • It requires monitoring infrastructure that remains uncompromised when the vault is compromised.
  • It requires an additional transaction compared to sending directly from the cold wallet to the hot wallet.
  • A further transaction may be needed to CPFP the unvaulting transaction.

Given the requirement for infrastructure to verify the legitimacy of unvault transactions, similar security properties might be achievable through a standard multisig setup with the user's wallet.

While it's possible to build a vault that resembles the OP_CTV vault using presigned transactions, this approach has significant drawbacks: whenever a user receives funds, they need to access their cold wallet, create the presigned transaction, store the presigned transaction such that its not deletable by the adversary, and delete the key. The cold wallet cannot batch-generate the presigned transactions because typically the user does not know which amounts they are going to receive in the future. No wonder no one has built a vault based on presigned transactions!

Congestion control

The congestion control protocol offers potential benefits in smoothing blockspace demand, but it comes with increased overall blockspace requirements. Unlike a single transaction that batches multiple payments, OP_CTV-based congestion control creates a transaction tree that consumes more blockspace. Additionally, transactions intended for broadcast during low-demand periods would likely need fee-bumping capabilities, requiring an anchor output and an additional CPFP transaction, further increasing blockspace usage.

The potential adoption of OP_CTV-based congestion control remains very uncertain, primarily due to its requirement for off-chain coordination between senders and receivers. The protocol must ensure that the receivers are guaranteed to receive a payment in the future. As a fallback mechanism, receivers need the ability to submit their portion of the transaction tree to the blockchain independently. This necessitates that receivers obtain and verify their relevant transaction tree segments before accepting payments.

While a similar congestion control system could theoretically be built using presigning, the required interactivity makes this approach impractical except in specific, limited scenarios.

DLCs

OP_CTV offers significant performance improvements for DLCs. The magnitude of this improvement can genuinely be described as dramatic. However, DLCs have not achieved meaningful adoption on Bitcoin, and their limited use doesn't appear to stem from performance limitations.

Payment Pools

OP_CTV-based payment pools allow multiple parties to share a single UTXO, potentially offering significant scalability benefits. However, these benefits depend heavily on achieving a workable user experience, which remains uncertain given the complex design space of payment pools and similar constructions.

OP_CTV alone appears to provide limited benefits. As noted in the blog post introducing OP_CTV-based payment pools, these systems can be implemented using presigning instead: "Do Payment Pools Need CTV? Not necessarily. [...] we already need everyone online for an update."

Other Applications

Peerswap

OP_CTV could enhance the Peerswap protocol by allowing to determine the swap amount after opening transaction is confirmed. Compared to building this feature using presigned transactions, OP_CTV significantly reduces the protocol's interactivity and complexity.

Federated Sidechains with Emergency Withdrawal Procedure

Federated sidechains can benefit from an Emergency Withdrawal Procedure (as implemented in the Liquid sidechain, for example). This simply means that federation-controlled coins can be spent in two ways:

  1. by providing t signatures from the federation members
  2. by providing t' signatures from some set of emergency keys but only after delta time has passed since the output was created

The problem with this implementation of the procedure is that federations must regularly spend outputs just to prevent the emergency spending path from activating, which consumes unnecessary block space. With OP_CTV, it would be possible to replace the emergency spending path with <txhash> OP_CTV, where <txhash> is the hash of a "kickoff" transaction. The kickoff transaction would create an output containing the emergency spending path.

This approach eliminates the need to regularly spend federation outputs since they wouldn't contain an emergency path. In case of an emergency, the emergency response unit could broadcast a kickoff transaction and then spend its emergency path once the timelock expires.

While it's possible to implement a kickoff transaction with presigning instead of OP_CTV, this would be more complex and less powerful because it can only be used for the federation's change outputs and not for peg-ins.

Summary of Technical Evaluation

BIP 119 CHECKTEMPLATEVERIFY is an interesting point in the covenant design space, offering specific improvements without enabling general/recursive covenants and doing that very efficiently. However, at this point I cannot support a softfork consisting of OP_CTV alone because the benefits do not outweigh the softfork coordination cost. There is a significant chance that CTV would see limited adoption after activation. As detailed above, OP_CTV-based vaults and congestion control mechanisms have low adoption potential. While OP_CTV offers optimizations for existing protocols (such as DLCs and peerswap), these protocols currently have low usage on Bitcoin.

@moonsettler
Copy link

There are multiple things in these objections that I disagree with. However, the main issue with this evaluation is that there is no soft fork proposal to activate CTV alone.

Correct. This was obviously a lot of effort put into something that is not in fact on the table for activation at this point in time.
And it's obviously thoughtful, really hard to disagree with many observations as well.

@ariard
Copy link

ariard commented Jan 16, 2025

There is a difference between academics and how things work. My goal is to make bitcoin projects better in terms of UX, security etc, and not write papers.

How do you plan to convince people that cov X is better than let’s say cov Y if you’re not able to explain how things works ?
Whatever the communication medium, be it a blog post, an academic paper, a bunch of nostr notes, ...

One can write code today for joinpool with no covenant changes, it's already a coinjoin improvement.

@1440000bytes
Copy link

@jonasnick I apologize on behalf of moon for this comment.

Main goal of this exercise was to discuss objections and reach some sort of technical consensus. I have made some changes in the table. Hopefully, we will gather more information from rationales and agree on some combination of opcodes with further reviews.

@moonsettler
Copy link

moonsettler commented Jan 18, 2025

I find these changes aesthetically pleasing! You forgot the "without rationale" table?

I will update my rationale with evaluating C3PO and C4!

@jonasnick
Copy link
Author

@1440000bytes No need to apologize for other peoples comments. I appreciate how you're contributing to the process by reacting to feedback on the table, read the rationales and summarize them.

On https://github.com/jamesob/simple-ctv-vault?tab=readme-ov-file#vault-basics:

This paragraph is about Mallory attempting "to steal the vaulted coins", not the hot wallet coins. As a consequence of being able to steal from the hot wallet, the attacker would steal vaulted coins in the following way (paragraph slightly rephrased):

if an attacker Mallory gains control of the user Alice's hot wallet and wants to steal the vaulted coins, Mallory has waits for Alice to broadcast the a unvault transaction. If Alice is watching the mempool/chain, she will see that the unvault transaction has been unexpectedly correctly broadcast, and she can immediately sweep the balance to her cold wallet, while Mallory waits the block delay to succeed in stealing funds from the hot wallet.

I will add all the documentation with a playground to test different types of pools on https://joinpool.space/ in a few weeks.

Cool, sounds helpful.

@ariard

Moreover, w.r.t payment pool the fault-tolerance of the construction is a dimension to analyze. If 1 participant can provoke the force-close of the payment pool by broadcasting its subset of the branch of transactions, and as such provoke a fee-denominated on-chain cost for all the remaining participants, this is not very fault-tolerant and a construction very exposed to DoS attack.

Interesting point. I assume you would have to set it up such that the party who initiates the unilaterally exits has to pay the fee. Not sure if this is always possible.

@1440000bytes
Copy link

This paragraph is about Mallory attempting "to steal the vaulted coins", not the hot wallet coins. As a consequence of being able to steal from the hot wallet, the attacker would steal vaulted coins in the following way (paragraph slightly rephrased):

if an attacker Mallory gains control of the user Alice's hot wallet and wants to steal the vaulted coins, Mallory has waits for Alice to broadcast the a unvault transaction. If Alice is watching the mempool/chain, she will see that the unvault transaction has been unexpectedly correctly broadcast, and she can immediately sweep the balance to her cold wallet, while Mallory waits the block delay to succeed in stealing funds from the hot wallet.

Mallory won't wait for unvault transaction to be broadcasted if they are aware of the vault setup and got access to hot wallet. Attackers are normally impatient. However, we can think of different ways to secure hot wallet if attacker is patient enough to wait for unvault transaction:

  1. Unvault with 2 transactions (small amount, rest)
  2. Unvault to a CTV address in hot wallet with template that allows spending to specific address after 2 blocks and any address after N+2 blocks
  3. Other possible ways

James has created a proof of concept for a simple single-hop vault. However, there are different ways to setup a CTV vault. I think @shesek is working on different types of vaults using covenants with miniscript.

Interesting point. I assume you would have to set it up such that the party who initiates the unilaterally exits has to pay the fee. Not sure if this is always possible.

It is possible.

@moonsettler
Copy link

Key deletion is a possible way to prevent the unvaulting to be hijacked.
LNhance vault uses that to make sure the unvaulting is atomic to the intended target and immutable once signed.

The real problem with CTV based vaults is the amount inflexibility.

@jonasnick
Copy link
Author

Unvault with 2 transactions (small amount, rest)

It's reasonable to assume that the attacker knows about the wallet's inner workings, so they'd just wait until the rest arrives.

Unvault to a CTV address in hot wallet with template that allows spending to specific address after 2 blocks and any address after N+2 blocks

That specific address needs to be committed to at the time the coins are received in the vault? Then I don't see how it can be anything but a hot wallet address and therefore doesn't prevent the attack.

@moonsettler
Copy link

moonsettler commented Jan 19, 2025

This relies on some secure enclave to enforce key deletion by overwriting the previous chain tip private key with it's hash.
Not ideal, not the worst.

# S: 500000000
# i: iterator, increased with each withdrawal
# i-lock : S+i

final-ctv-hash {nLockTime: i-lock, out: ??}

# vault
<withdraw-sig> <final-ctv-hash> <i-lock> | CLTV PC CSFS <staging-ctv-hash> CTV DROP

# staging
<withdraw-sig> <i-lock> <final-ctv-hash> | CTV SWAP CLTV PC CSFS


# vault IKEY is burned after signing the key delegations
IF
  # witness: <sig-IK(n)> <staging-ctv-hash(n)> <P(n)> <sig-P(n)-stage>
  OVER CHECKSIGV SWAP CTV PC IKEY CSFS
ELSE
  # witness: <sig-P(recovery)>
  <P(recovery)> CHECKSIGV <recovery-ctv-hash> CTV
ENDIF

# sign stagig and final withdrawal TXs with p(n)
# burn p(n) key; p(n) := sha256(p(n)) mod m

# staging(n)
IF
  # witness: <sig-P(n)>
  2016 CSV 0NEQ <P(n)> CHECKSIGV
ELSE
  # witness: <sig-P(recovery)>
  <P(recovery)> CHECKSIGV <recovery-ctv-hash> CTV
ENDIF

minsc by @shesek

@jonasnick
Copy link
Author

@1440000bytes

In 2016, bitfinex lost 119756 BTC from 2-of-3 (bitfinex, bitgo, backup) multisig. This could have been avoided if a vault was used.

IIRC the problem was that this was essentially a 1-of-3 because bitfinex held the API keys to bitgo, who allowed just any transaction to be signed.

Even if two out of three were compromised int this attack, a traditional cold wallet would have helped, which would have had to be accessed whenever the hot wallet needs to be replenished. In the vault setup, we already assume that a cold wallet exists that remains uncompromised. If it was compromised, then the sweep-to-cold transaction during the unvault process wouldn't help.

The downside of using a traditional cold wallet is that it needs to be accessed more frequently than in a vault setup, where it would only be accessed in case actual theft attempts had been prevented. If it is accessed more frequently than typically the procedures for accessing it have weaker security.

I assume that in a "bitfinex" vault setup there would be watchtowers that publish sweep transactions whenever they detect unauthorized unvaults. How to detect that may differ between actual implementations, but we can imagine, for example, weekly unvaulting limits. This works as long as one watchtower remains honest and works correctly.

CTV Vault-like Behavior with Multisig

It seems like it's possible reproduce setups similar to a CTV vault using multisig with the watchtowers. The design space seems large, but here's one attempt:

The multisig wallet would use the policy or(and(thresh(2, X1, X2, X3), multi(W1, ..., Wn)), <cold wallet>). So, the coins can be spend either with

  • signatures from X1, X2, X3 and a signature from all watchtowers or
  • signatures from the cold wallet.
    We assume the watchtowers have an API that allows submitting a transaction and returns a signature on the transaction if the transaction output is authorized, for example if it's below weekly limits.

We can distinguish the following cases:

  • Happy case: the custody service can send funds from the multisig wallet directly to the intended recipient after requesting signatures from the watchtowers.
  • The adversary compromises two out of {X1, X2, X3} and all watchtowers: the entire wallet can be emptied by the attacker. This would be the same outcome with a CTV vault because the situation would allow the adversary to publish unvaulting transactions and no honest watchtower would be present to publish the sweep-to-cold transaction. Arguably, this requires more effort by the adversary because the watchtowers need to be persistently and "thoroughly" compromised compared to the effort of just copying multisig keys.
  • The adversary compromises two out of {X1, X2, X3} and all but one watchtower: the adversary is not able to create unauthorized transactions.
  • The adversary compromises cold wallet: the entire wallet can be emptied by the attacker. This is no worse than the CTV vault when the adversary has compromised the cold wallet. That's because an adversary against the CTV wallet can initiate an unvault, wait for the sweep-to-cold transaction(s) and then steal from the cold wallet.
  • The watchtower does not sign: The cold wallet can still spend the funds and send it to a new policy with better watchtowers. A similar situation arises with the CTV vault case when a dishonest watchtower publishes a sweep-to-cold transaction for a legitimate unvault, which would also require accessing the cold wallet.

The main downside of this approach is that watchtowers need to be reliably online all the time to provide signatures or the cold wallet needs to be accessed to select new watchtowers. In a CTV vault, watchtowers just need to be online frequently enough to detect unauthorized unvaults. On the upside, you just need one transaction to send to the intended destination instead of two (or three if you take transaction fees into account). The policy could be implemented using a MuSig2 multisignature between the watchtowers to keep the script and witness small. Another advantage is that you can send to the recipient immediately without having to wait for the unvaulting delay. However, having no delay also prevents you from realizing something fishy is going on during that time through some other means than the watchtowers.

If this multisig wallet setup works, it could be built and experimented with today and CTV could be seen as a variant with different trade-offs (e.g. improving the watchtower liveness requirement).

@moonsettler
Copy link

moonsettler commented Jan 19, 2025

If you stubbornly insist on CTV only, you can probably do something similar with taptree (more expensive) to the LNhance vault.

You can hardcode the public keys to tapleaves, unvaulting means you sign two consecutive transactions then delete the key in SE before giving those transactions out. The first transaction of has the CSV relative time lock all CTV vault has, it also spends to an output that requires the signature from the deleted key. Therefore the withdrawal can only go to where it was initiated to.

This means a vault compromised after withdrawal is initiated can not recover the original private key as it would mean finding the preimage of a SHA256 hash, which is practically impossible.

The important bit CTV adds is the mandatory staging with relative timelock. Not even a compromised SE can bypass that. Ofc the design space is huge. And ofc that even if the recovery key gets compromised that path is locked to the deep-freeze recovery address with CTV. There would be no way to enforce this with presigned transactions for at the time unknown TXIDs. It's also better UX.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment