Subject: Re: [bitcoin-dev] Covenants and feebumping

From: darosior <[email-masked]>
Date: Mon, 14 Mar 2022 14:49:30 +0000

Hi Jeremy,

Thanks for the feedback. I indeed only compared it to existing fee-bumping methods. But sponsors are pretty
similar to CPFP in usage anyways, they 'just' get rid of the complexity of managing transaction chains in the
mempool. That's great, don't get me wrong, just it's much less ideal than a solution not requiring additional
UTxOs to be reserved, managed, and additional onchain transactions.

Regarding chain efficiency. First, you wrote:
> As you've noted, an approach like precomitted different fee levels might work, but has substantial costs.

Well, i noted that it *does* work (at least for vaults). And it does incur a cost, but it's inferior to the
other solutions. Then, sure sponsors' -like CPFP's- cost can be amortized. The chain usage would still likely
be superior (depends on a case by case basis i'd say), but even then the "direct" chain usage cost isn't what
matters most. As mentioned, the cost of using funds not internal to the contract really is.

Regarding capital efficiency, again as noted in the post, it's the entire point to use funds internal to the
contract ("pre-committed"). Sure external funding (by the means of sponsors or any other technique) allows you
to allocate funds later on, or never. But we want contracts that are actually enforceable, i guess?
On the other hand, pre-committing to all the possible fee-bumped levels prevents you to dynamically add more
fees eventually. That's why you need to pre-commit to levels up to your assumed "max feerate before i close
the contract". For "cold contracts" (vaults), timelocks prevent the DOS of immediately using a large feerate.
For "hot contracts" a signature challenge is used to achieve the same. I know the latter is imperfect, since
the lower the uptime risk (increase the number of network monitors) the higher the DOS risk (as you duplicate
the key).. That's why i asked if anybody had some thoughts about this and if there was a cleverer way of doing
it.

> This is also true for vaults where you know you only want to open 1 per month let's say, and not
> your vaults> per month, which pre-committing requires.

Huh? Pre-committing here is to pre-commit to levels of the revocation ("Cancel") transaction. It has nothing
to do with "activating" (using Revault's terminology) a vault, done by sharing a signature for the Unvault
transaction.
You might have another vault design in mind whereby any deposited fund is unvault-able. In this case, and as
with any other active contract, i think you need to have funds ready to pay for the fees for the contract to
be enforceable. Whether these funds come from the contract's funds or from externally-reserved UTxOs.

> you don't need a salt, you just need a unique payout addr (e.g. hardened derivation) per revocation txn and
> you cannot guess the branch.

Yeah, i preferred to go with 8 more vbytes. First because relying on never reusing a derivation index is
brittle and also because it would make rescan much harder. Imagine having 256 fee levels, making 5 payments a
day for 200 days in a year. You'd have 256000 derivation indexes per year to scan for if restoring frombackup.

------- Original Message -------
Le dimanche 13 mars 2022 à 3:33 AM, Jeremy Rubin <[email-masked]> a écrit :

> Hi Antoine,
>
> I have a few high level thoughts on your post comparing these types of primitive to an explicit soft fork approach:
>
> 1) Transaction sponsors *is* a type of covenant. Precisely, it is very similar to an "Impossible Input" covenant in conjunction with a "IUTXO" I defined in my 2017 workshophttps://rubin.io/public/pdfs/multi-txn-contracts.pdf(I know, I know... self citation, not cool, but helps with context).
>
> However, for Sponsors itself we optimize the properties of how it works & is represented, as well as "tighten the hatches" on binding to specific TX vs merely spend of the outputs (which wouldn't work as well with APO).
>
> Perhaps thinking of something like sponsors as a form of covenant, rather than a special purpose thing, is helpful?
>
> There's a lot you could do with a general "observe other txns in {this block, the chain}" primitive. The catch is that for sponsors we don't *care* to enable people to use this as a "smart contracting primitive", we want to use it for fee bumping. So we don't care about programmability, we care about being able to use the covenant to bump fees.
>
> 2) On Chain Efficiency.
>
> A) Precommitted Levels
> As you've noted, an approach like precomitted different fee levels might work, but has substantial costs.
>
> However, with sponsors, the minimum viable version of this (not quite what is spec'd in my prior email, but it could be done this way if we care to optimize for bytes) would require 1 in and 1 out with only 32 bytes extra. So that's around 40 bytes outpoint + 64 bytes signature + 40 bytes output + 32 bytes metadata = 174 bytes per bump. Bumps in this way can also amortize, so bumping >1 txn at the same time would hit the limit of 32 bytes + 144/n bytes to bump more than one thing. You can imagine cases where this might be popular, like "close >1 of my LN channels" or "start withdrawals for 5 of my JamesOB vaulted coins"
>
> B) Fancy(er) Covenants
>
> We might also have something with OP_CAT and CSFS where bumps are done as some sort of covenant-y thing that lets you arbitrarily rewrite transactions.
>
> Not too much to say other than that it is difficult to get these down in size as the scripts become more complex, not to mention the (hotly discussed of late) ramifications of those covenants more generally.
>
> Absent a concrete fancy covenant with fee bumping, I can't comment.
>
> 3) On Capital Efficiency
>
> Something like a precommitted or covenant fee bump requires the fee capital to be pre-committed inside the UTXO, whereas for something like Sponsors you can use capital you get sometime later. In certain models -- e.g., channels -- where you might expect only log(N) of your channels to fail in a given epoch, you don't need to allocate as much capital as if you were to have to do it in-band. This is also true for vaults where you know you only want to open 1 per month let's say, and not <all of your vaults> per month, which pre-committing requires.
>
> 4) On Protocol Design
>
> It's nice that you can abstract away your protocol design concerns as a "second tier composition check" v.s. having to modify your protocol to work with a fee bumping thing.
>
> There are a myriad of ways dynamic txns (e.g. for Eltoo) can lead to RBF pinning and similar, Sponsor type things allow you to design such protocols to not have any native way of paying for fees inside the actual "Transaction Intents" and use an external system to create the intended effect. It seems (to me) more robust that we can prove that a Sponsors mechanism allows any transaction -- regardless of covenant stuff, bugs, pinning, etc -- to move forward.
>
> Still... careful protocol design may permit the use of optimized constructions! For example, in a vault rather than assigning *no fee* maybe you can have a single branch with a reasonable estimated fee. If you are correct or overshot (let's say 50% chance?) then you don't need to add a sponsor. If you undershot, not to worry, just add a sponsor. Adopted broadly, this would cut the expected value of using sponsors by <however good you are at estimating future fees>. This basically enables all protocols to try to be more efficient, but backstop that with a guaranteed to work safe mechanism.
>
> There was something else I was going to say but I forgot about it... if it comes to me I'll send a follow up email.
>
> Cheers,
>
> Jeremy
>
> p.s.
>
>>
>
>> Of course this makes for a perfect DoS: it would be trivial for a miner to infer that you are usinga specific vault standard and guess other leaves and replace the witness to use the highest-feeratespending path. You could require a signature from any of the participants. Or, at the cost of anadditional depth, in the tree you could "salt" each leaf by pairing it with -say- an OP_RETURN leaf.
>
> you don't need a salt, you just need a unique payout addr (e.g. hardened derivation) per revocation txn and you cannot guess the branch.
>
> --
> [@JeremyRubin](https://twitter.com/JeremyRubin)
>
> On Sat, Mar 12, 2022 at 10:34 AM darosior via bitcoin-dev <[email-masked]> wrote:
>
>> The idea of a soft fork to fix dynamic fee bumping was recently put back on the table. It might
>> sound radical, as what prevents today reasonable fee bumping for contracts with presigned
>> transactions (pinning) has to do with nodes' relay policy. But the frustration is understandable
>> given the complexity of designing fee bumping with today's primitives. [0]
>> Recently too, there was a lot of discussions around covenants. Covenants (conceptually, not talking
>> about any specific proposal) seem to open lots of new use cases and to be desired by (some?) Bitcoin
>> application developers and users.
>> I think that fee bumping using covenants has attractive properties, and it requires a soft fork that
>> is already desirable beyond (trying) to fix fee bumping. However i could not come up with a solution
>> as neat for other protocols than vaults. I'd like to hear from others about 1) taking this route for
>> fee bumping 2) better ideas on applying this to other protocols.
>>
>> In a vault construction you have a UTxO which can only be spent by an Unvaulting transaction, whose
>> output triggers a timelock before the expiration of which a revocation transaction may be confirmed.
>> The revocation transaction being signed in advance (typically before sharing the signature for the
>> Unvault transaction) you need fee bumping in order for the contract to actually be enforceable.
>>
>> Now, with a covenant you could commit to the revocation tx instead of presigning it. And using a
>> Taproot tree you could commit to different versions of it with increasing feerate. Any network
>> monitor (the brooadcaster, a watchtower, ..) would be able to RBF the revocation transaction if it
>> doesn't confirm by spending using a leaf with a higher-feerate transaction being committed to.
>>
>> Of course this makes for a perfect DoS: it would be trivial for a miner to infer that you are using
>> a specific vault standard and guess other leaves and replace the witness to use the highest-feerate
>> spending path. You could require a signature from any of the participants. Or, at the cost of an
>> additional depth, in the tree you could "salt" each leaf by pairing it with -say- an OP_RETURN leaf.
>> But this leaves you with a possible internal blackmail for multi-party contracts (although it's less
>> of an issue for vaults, and not one for single-party vaults).
>> What you could do instead is attaching an increasing relative timelock to each leaf (as the committed
>> revocation feerate increases, so does the timelock). You need to be careful to note wreck miner
>> incentives here (see [0], [1], [2] on "miner harvesting"), but this enables the nice property of a
>> feerate which "adapts" to the block space market. Another nice property of this approach is the
>> integrated anti fee sniping protection if the revocation transaction pays a non-trivial amount of
>> fees.
>>
>> Paying fees from "shared" funds instead of a per-watchtower fee-bumping wallet opened up the
>> blackmail from the previous section, but the benefits of paying from internal funds shouldn't be
>> understated.
>> No need to decide on an amount to be refilled. No need to bother the user to refill the fee-bumping
>> wallet (before they can participate in more contracts, or worse before a deadline at which all
>> contracts are closed). No need for a potentially large amount of funds to just sit on a hot wallet
>> "just in case". No need to duplicate this amount as you replicate the number of network monitors
>> (which is critical to the security of such contracts).
>> In addition, note how modifying the feerate of the revocation transaction in place is less expensive
>> than adding a (pair of) new input (and output), let alone adding an entire new transaction to CPFP.
>> Aside, and less importantly, it can be made to work with today's relay rules (just use fee thresholds
>> adapted to the current RBF thresholds, potentially with some leeway to account for policy changes).
>> Paying from shared funds (in addition to paying from internal funds) also prevents pervert
>> incentives for contracts with more than 2 parties. In case one of the parties breaches it, all
>> remaining parties have an incentive to enforce the contract.. But only one would otherwise pay for
>> it! It would open up the door to some potential sneaky techniques to wait for another party to pay
>> for the fees, which is at odd with the reactive security model.
>>
>> Let's examine how it could be concretely designed. Say you have a vault wallet software for a setup
>> with 5 participants. The revocation delay is 144 blocks. You assume revocation to be infrequent (if
>> one happens it's probably a misconfigured watchtower that needs be fixed before the next
>> unvaulting), so you can afford infrequent overpayments and larger fee thresholds. Participants
>> assume the vault will be spent within a year and assume a maximum possible feerate for this year of
>> 10ksat/vb.
>> They create a Taproot tree of depth 7. First leaf is the spending path (open to whomever the vault
>> pays after the 144 blocks). Then the leaf `i` for `i` in `[1, 127]` is a covenant to the revocation
>> transaction with a feerate `i * 79` sats/vb and a relative timelock of `i - 1` blocks.
>> Assuming the covenant to the revocation transaction is 33 bytes [3], that's a witness of:
>> 1 + 33 + 1 + 33 + 7 * 32 = 292 WU (73 vb)
>> ^^^^^^ ^^^^^^^^^^^^^^
>> witscript control block
>> for any of the revocation paths. The revocation transaction is 1-input 1-output, so in total it's
>> 10.5 + 41 + 73 + 43 = 167.5 vb
>> ^^^^ ^^^^^^^^^^^ ^^^^
>> header input|witness output
>> The transaction size is not what you'd necessarily want to optimize for first, still, it is smaller
>> in this case than using other feebumping primitives and has a smaller footprint on the UTxO set. For
>> instance for adding a feebumping input and change output assuming all Taproot inputs and outputs
>> (CPFP is necessarily even larger):
>> 5 * 64 + 1 + 5 * (32 + 1) + 1 + 33 = 520 WU (105 vb)
>> ^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^
>> witness witscript control
>> 10.5 + 41 + 105 + 41 + 16.5 + 2 * 43 = 300 vb
>> ^^^^ ^^^^^^^^ ^^^^^^^^^ ^^^^^^
>> header input|witness fb input|witness outputs
>> From there, you can afford more depths at the tiny cost of 8 more vbytes each. You might want them
>> for:
>> - more granularity (if you can afford large enough timelocks)
>> - optimizing for the spending path rather than the revocation one
>> - adding a hashlock to prevent nuisance (with the above script a third party could malleate a
>> spending path into a revocation one). You can use the OP_RETURN trick from above to prevent that.
>>
>> Unfortunately, the timelocked-covenant approach to feebumping only applies to bumping the first
>> transaction of a chain (you can't pay for the parent with a timelock) so for instance it's not
>> usable for HTLC transactions in Lightning to bump the parent commitment tx. The same goes for
>> bumping the update tx in Coinpool.
>> It could be worked around by having a different covenant per participant (paying the fee from either
>> of the participants' output) behind a signature check. Of course it requires funds to already be in
>> the contract (HTLC, Coinpool leaf) to pay for your own unilateral close, but if you don't have any
>> fund in the contract it doesn't make sense to try to feebump it in the first place. The same goes
>> for small amounts: you'd only allocate up to the value of the contract (minus a dust preference) in
>> fees in order to enforce it.
>> This is less nice for external monitors as it requires a private key (or another secret) to be
>> committed to in advance) to be able to bump [4] and does not get rid of the "who's gonna pay for the
>> enforcement" issue in >2-parties contracts. Still, it's more optimal and usable than CPFP or adding
>> a pair of input/output for all the reasons mentioned above.
>>
>> Thoughts?
>> Antoine
>>
>> [0] https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-November/019614.html
>> [1] https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-November/019615.html
>> [2] https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-December/019627.html
>> [3] That's obviously close to the CTV construction. But using another more flexible (and therefore
>> less optimized) construction would not be a big deal. It might in fact be necessary for more
>> elaborated (realistic?) usecases than the simple one detailed here.
>> [4] https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2022-February/019879.html
>> _______________________________________________
>> bitcoin-dev mailing list
>> [email-masked]
>> https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev