Skip to content

Instantly share code, notes, and snippets.

@t-bast
Last active May 24, 2022 13:24
Show Gist options
  • Save t-bast/9efeacd94ddcc75ab878b9dd979179a1 to your computer and use it in GitHub Desktop.
Save t-bast/9efeacd94ddcc75ab878b9dd979179a1 to your computer and use it in GitHub Desktop.

A different approach to solve RBF for off-chain contracts

Key observation: on-chain fees for off-chain application settlement should be endogenous, not exogenous. In more plain english, there is no good reason to bring funds from "outside" of an off-chain contract to pay the fees necessary to settle that contract on-chain. Any party in the contract is ready to burn at most their contract balance in on-chain fees, but it wouldn't make sense to burn more, even for a scorched earth strategy. This is something I brought up a while ago in lightning/bolts#845

We could achieve this with an extension to SIGHASH_ANYPREVOUT, which I'm calling SIGHASH_ANYAMOUNT. This could be a flag to complement a SIGHASH_ANYPREVOUT / SIGHASH_ANYPREVOUTANYSCRIPT and add the following behavior:

  • in some place that is covered by the signature (taproot annex?), commit to:
    • the list of outputs for which the amount should be ignored
  • when hashing the tx:
    • ignore the input txid and vout (this is provided by SIGHASH_ANYPREVOUT)
    • ignore the input amount
    • set output amount to 0 (or some placeholder) for the committed outputs

If we had something like this, here is how we would design off-chain contracts:

  • set the fee to 0 in all pre-signed shared transactions
  • set a CSV on every output
  • when sending our signature to our peer, use SIGHASH_ANYPREVOUT | SIGHASH_ANYAMOUNT to exclude their balance output from the signed data

If we do that, our peer is able to lower their balance output to set the feerate without invalidating our signature. Because of the CSV on every output, there is no way for an attacker to do any kind of pinning and transactions are very easy to RBF. We don't have to handle the complexity of anchor outputs, and don't have to maintain an external utxo pool for fee-bumping (which really is a huge win).

Let's detail how that would work today for a lightning channel between Alice and Bob. The scripts would be completely unchanged (as detailed in https://github.com/t-bast/lightning-docs/blob/master/lightning-txs.md#anchor-outputs) but we remove the anchor outputs.

The commitment transaction would contain the following outputs:

  • Alice's main output
  • Bob's main output
  • Incoming HTLC outputs
  • Outgoing HTLC outputs

When Alice signs Bob's commitment transaction, she uses SIGHASH_ANYPREVOUT | SIGHASH_ANYAMOUNT to exclude Bob's main output. When Bob signs Alice's commitment transaction, he uses SIGHASH_ANYPREVOUT | SIGHASH_ANYAMOUNT to exclude Alice's main output. The commitment transactions are then very easy to RBF, each participant can simply lower their main output to pay the on-chain fees (they always have such an output because of the channel reserve). Since the pre-signed transaction pays no fees, participants can only decrease their main output, they can't inflate it to siphon funds from their peer.

The HTLC transactions would contain a single output (as is done today) and we wouldn't use SIGHASH_SINGLE | SIGHASH_ANYONECANPAY. Instead, we would just sign them with SIGHASH_ANYPREVOUT | SIGHASH_ANYAMOUNT applied to the only output. The SIGHASH_ANYPREVOUT part ensure that the signature remains valid for a tweaked commitment transaction that contains a lower main output to pay for fees. The SIGHASH_ANYAMOUNT part ensures that the HTLC recipient can lower the output value to pay for fees (up to the full HTLC amount for scorched earth strategies against malicious peers).

There are a few subtleties to explore:

  • excluding more than one output's amount from the signed data allows the remote peer to shift funds from one output to another: is this a bug or a feature?
  • this should probably never be used in conjunction with SIGHASH_SINGLE or SIGHASH_ANYONECANPAY otherwise peers may move funds outside of restrictive output scripts, which is likely a design bug
@remyers
Copy link

remyers commented May 24, 2022

A few initial observations:

  • SIGHASH_ANYAMOUNT has some overlap with what I've seen described as SIGHASH_GROUP[1][2]. The significant difference is that SIGHASH_ANYAMOUNT ignores the amount, but keeps the script_pubkey so that we can ensure the spending tx requires CSV. I like this addition and perhaps the annex can also include a bit field for each excluded output that describes what parts are excluded.

  • SIGHASH_ANYONECANPAY is currently implicit for any use of APO or APO-AS. Exogenous (bring-your-own-fees) is required for update_tx because otherwise it could uses endogenous funds from one party that in a later update_tx belongs to the other.

  • Perhaps instead of full SIGHASH_ANYONECANPAY behavior, APO-AS should commit to the scripts of all inputs except the one being signed for. This would allow the update_tx to have an additional APO input that is used to pay fees at commit time. Because the script of the second input is committed to in advance, we know the witness size of the fee input is bounded (so no RBF rule #3 issues). APO here also means we don't have to pre-commit to a particular tx that will pay our fees; we can create a UTXO with a matching amount and script when needed.

[1] SIGHASH_GROUP discussion: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-July/019243.html
[2] SIGHASH_GROUP BIP draft: https://github.com/ariard/bitcoin/blob/2021-10-annex-sighashgroup/bip-sighashgroup.mediawiki

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