The Joy of Bitcoin Transaction Fees

In my lightning prototype, I punted on bitcoin fees; there’s a commandline option to set the fee amount, and that’s sent to the peer. This is clearly wrong, and when I wrote the first cut of the protocol spec, I decided to address it. That’s iterated several times as I’ve tried to make my implementation match…

Modern Bitcoin Fees

You should not be using fixed fees; miners choose transactions which pay most per byte, so you need to take into account the size of your transaction. Conditions also change with spikes in demand and the high variability of block times, so you should use a fee rate based on your best estimate. Even that may not be good enough, so in future you may use Replace By Fee and/or Child Pays For Parent to push a transaction through if you guessed too low.

For lightning commitment transactions, neither RBF nor CPFP apply: RBF isn’t possible because you only publish the commitment transaction when the other side has vanished (so they can’t sign a new transaction for you), and the outputs you can spend are all timelocked so CPFP won’t incentivize a miner. We may eventually end up with a trivial non-timelocked output just to apply CPFP, but meanwhile we rely on the fact that that commitment transactions are usually not published, so fees can be safely higher than normal.

Commitment Transactions: Who Pays the Fee?

In lightning channels, there are two commitment transactions: one held by A, one held by B. Each side needs the fee rate on its own commitment transaction high enough to enter the blockchain quickly, but doesn’t care about the other side’s. They might have different thresholds and estimates of the “right” amount. And of course, they don’t want to waste money paying too high fees.

For this reason, we split fees down the middle: if I want you to pay a high fee, I have to pay it too. (If I ask for something way too high, you’ll fail the connection and drop the last commitment transaction to the blockchain). Even if all the HTLCs are offered by one side, both are getting benefit from them so splitting evenly makes sense.

This almost works, but what if one side can’t afford it? This happens initially when one side has created the channel and holds all the funds. It can also happen later when fees increase (unavoidably: your additional HTLCs might be in flight while I send the new update_fee message). While fairness is important, timely inclusion is vital, so in this case the side which can afford it tops up the fee. It’s still possible for a fee hike plus in-flight HTLCs to mean neither of us can afford the fee, but it’ll be no worse than the old fee rate.

Obviously, you can’t offer a new HTLC if you can’t afford the fee; in fact the amount from first HTLC the receiver fulfils may go entirely towards paying their share of the transaction fee.

The spec currently recommends fees like so:

As the commitment transaction is only used in failure cases, it is
suggested that fee_rate be twice the amount estimated to allow entry
into the next block, and that nodes accept a fee_rate up to ten
times that same estimate.

Fee Application

In the protocol a fee_rate in millisatoshi per byte is sent as part of the handshake, and fee_update can be sent which will apply to their own commitment transaction as soon as it’s acknowledged by the other side. Converting this fee rate to the actual transaction amounts is a bit complicated however.

Ideally, you size the transaction and then multiply the fee rate and deduct the fees from the outputs. In practice, deducting fees may mean that one output is too small to justify inclusion (ie. a dust output), so the size may be reduced, reducing the fee. More subtly, the number of outputs is encoded as a varint meaning it takes 1 byte to encode up to 252 outputs, but 3 bytes to encode 253, so that may also change the transaction size.

Thus the spec says to use the formula 338 + 32*htlcs to estimate the transaction size, which reflects the worst case size (htlcs here excludes HTLCs so small they would be dust). We then multiply by the rate and round down to an even number of satoshi (for simplicity of fee-splitting).

Mutual Close: What Fee?

When we mutually terminate a channel, there are no HTLCs so there’s far less urgency, the transaction is smaller, and we’re definitely going to spend the money so optimizing fees is more important.

For this, each side sends a fee amount and a signature for the close transaction at that fee amount (divided evenly if possible, just like we do for commitment transactions). If you agree, you can use that signature to broadcast the transaction immediately, otherwise you can respond with your own fee level and signature. The protocol says fee amounts must always be at or below the last commitment transaction, and must approach each other, so this process will terminate.

If something goes wrong during this negotiation, you can either broadcast the last commitment transaction, or the last received close transaction.

Conclusion

You can read the work-in-progress BOLT#2 in my repository here which contains all the gory details. My implementation is slowly approaching it as well, but expect both to change over time.

Critique and recommendations on the spec are best sent to the mailing list, questions and random commentary welcome here…

Show your support

Clapping shows how much you appreciated Rusty Russell’s story.