The #Bitcoin #Lightning Spec Part 3/8: Peer Protocol for Channel Management
This is 19 pages of nuts-and-bolts of what format messages are, and how to respond to them. There’s nothing hugely surprising in there if you’re familiar with any previous implementation: channel setup, channel operation, and channel shutdown.
- Setup: the connecting side offers to create a channel with given parameters (open_channel), the other side sets its conditions with accept_channel, then the first side sends funding_created and receives the signature for the first commitment transaction in funding_signed to finish. Then they wait for funding_locked once the commitment transaction has sufficient confirms.
+-------+ +-------+
| |--(1)--- open_channel ----->| |
| |<-(2)-- accept_channel -----| |
| | | |
| A |--(3)-- funding_created --->| B |
| |<-(4)-- funding_signed -----| |
| | | |
| |--(5)--- funding_locked ---->| |
| |<-(6)--- funding_locked -----| |
+-------+ +-------+
- Normal HTLC operation: new commitment transactions are created and old ones revoked. add, fulfill and fail messages queue changes, and the commitsig message to commit; the reply is revoke_and_ack which revokes the previous commitment transaction. There’s also an update_fee to alter the fee rate.
+-------+ +-------+
| |--(1)---- add_htlc ------>| |
| |--(2)---- add_htlc ------>| |
| |<-(3)---- add_htlc -------| |
| | | |
| |--(4)---- commit ------>| |
| A | | B |
| |<-(5)--- revoke_and_ack-----| |
| |<-(6)---- commit -------| |
| | | |
| |--(7)--- revoke_and_ack---->| |
+-------+ +-------+
- Shutdown: first one side sends a shutdown message, echoed by the other. From then on, no new HTLCs can be added. Once they’re all cleared out, each side sends a signature for the final close transaction with a fee it wants: this fee can be no more than the final commitment transaction. If they don’t like the fee they received, they reply with a closer amount until they’re happy and broadcast the final close transaction.
+-------+ +-------+
| |--(1)----- shutdown ------->| |
| |<-(2)----- shutdown --------| |
| | | |
| | <complete all pending htlcs> | |
| A | ... | B |
| | | |
| |<-(3)-- closing_signed F1----| |
| |--(4)-- closing_signed F2--->| |
| | ... | |
| |--(?)-- closing_signed Fn--->| |
| |<-(?)-- closing_signed Fn----| |
+-------+ +-------+
There’s also an important section highlighting the Risks With HTLC Timeouts which you might want to read.
This part of the spec doesn’t actually specify how to generate the signatures, the keys or the transactions (that’s BOLT #3), but you can see something weird going on in that we send multiple signatures in every “commitsig” message, and also signatures in “revoke_and_ack”. This is due to two-stage HTLCs…
Two-stage HTLC outputs: HTLC Transactions
Mats Jerratsch pointed out an externality problem with the commitment transaction scheme in my deployable lightning paper, and proposed a solution.
Here’s the problem: previously HTLC-offered-by-me outputs were a three way test:
- you can have it if they have the payment preimage (ie. success),
- I can have it back after some block height (ie. timeout) AND it’s been in the chain for ‘to-self-delay’ blocks.
- you can have it immediately with a revocation preimage (ie. old commitment)
Note that the payment back to me must be delayed a little bit (‘to-self-delay’); this allows you a chance to use the revocation preimage to take the funds to penalize me if I try to spend an old transaction.
This means the actual delay before I can time out the payment is the greater of the timeout block height and the current block height plus that ‘to-self-delay’. When forwarding an HTLC, I have to allow for this and increase the time between expiry of the outgoing HTLC and the expiry of the outgoing HTLC. Across the network, this means every hop adds that ‘to-self-delay’, bloating the worst-case end-to-end timeout.
Mats’ solution harks back to the original lightning draft paper: we allow HTLCs to be timed out or to succeed via another transaction, which performs that to-self timeout. To do this, the HTLC output can be spent to a dedicated pre-signed “HTLC transaction”, which contains the ‘to-self-delay’. Critically, the revocation preimage would also let the other party steal from this HTLC transaction if it has been revoked.
Tadge Dryja refined this further: there’s no need for the original HTLC output script to allow use of the revocation preimage: that logic can be entirely done in the second-stage HTLC transactions. This does mean that the other party needs the signatures to push timed-out HTLCs into this second state (hence the signatures in “revoke_and_ack”) but saves space on-chain.
The downside is that we need to calculate (or validate) a signature for every HTLC in progress on the commit step: that’s 52 and 63 microseconds each on my laptop, then another calculate/validate for on revoke_and_ack for about half of the HTLCs (the ones I offered).
Bitcoin Fees: Who Pays?
Bitcoin fees are nontrivial: you need to ensure they’re sufficient to get a transaction into a block, perhaps sometime in the future, but you don’t want to overpay either. My prototype had a mechanism to split fees, and fee rates for both sides were set independently.
At Milan, simplicity won: the side asking for the channel to be created pays the fees, and gets to set the rate. The other side can simply abort if it considers the fee too low. No doubt this will be revisited in a future version of the spec as we gain experience.
One side note however: what fee do we use for those HTLC transactions we added? This wrinkle wasn’t considered in Milan, so the spec uses simplest thing, which is to use the same fee rate as the commitment transaction. Unfortunately this means that even if you’re not paying the fee in the commitment transaction, you’re paying the fee in your HTLC transactions, so now you care if it’s too large, as well as too small.