-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
Background
The UpdateAddHTLC message in LND is extended with ExtraData which allows spec upgrades to transmit extra fields via a TLV stream. LND currently stores the extra data associated with a HTLC in our LogUpdates (where we store the full wire message), but not in channeldb.HTLC in our state machine. Understandably so, because there are no TLVs in use that need these fields and storing the full blob in the state machine would be unnecessary bloat.
Use Cases
There are a few upcoming specification proposals that start to use these fields:
- Route blinding: a
blinding_pointis transmitted to peers as anUpdateAddHTLCtlv. - Paid Jamming Countermeasures: any jamming mitigation that requires "proof of work" from a sender before they can add a HTLC (upfront fees / anonymous routing tokens / proof of burn etc) will transmit this value in an
UpdateAddHTLCtlv. - Local Reputation: peers that wish to communicate reputation information about a forwarded HTLC to mitigate long-hold jamming would use an
UpdateAddHTLCtlv.
Availability of Extra Data
While working on route blinding, we started to take a look at the availability of this data across restarts (since it's not stored in our state machine). Using the terminology outlined in the spec, this section outlines the availability of data at various stages of the HTLC lifecycle and how we can recover it after restart (it at all).
This example walks through the full HTLC lifecycle forwarded from A -> B -> C from the perspective of B.
Incoming HTLC (A -> B)
Pending on the receiver
We have received an UpdateAddHTLC from our remote peer.
On Restart: We will forget the HTLC, because we do not ACKed it, and expect our remote peer to replay it.
In the receiver commitment:
We have received a CommitmentSignature from the remote peer that includes the HTLC.
On Restart: We will forget the HTLC, because we have not ACKed it, and expect our remote peer to replay it.
Irrevocably committed by the receiver:
We have sent a RevokeAndAck to the remote peer that revokes the state without the HTLC.
- Our LocalCommitment is persisted in the openChannelBucket with the incoming
channeldb.HTLC - The full LogUpdate is stored in the unsignedAckedUpdates bucket.
On Restart:
- We will restore the incoming HTLC in our remoteUpdateLog from our local commitment
channeldb.HTLC which strips those fields.
🔧 Workaround: We can restore this data from our unsignedAckedUpdates (see route blinding example)
In sender commitment
We have sent the remote peer a CommitSignature that includes the incoming HTLC.
- LocalCommitment and unsignedAckedUpdates are stored as above.
- The
CommitDifffor the signed remote commitment is persisted in the openChannelBucket under the commitDIffKey. This diff includes the incoming HTLC stored as achanneldb.HTLC
On Restart: As above, we restore the HTLC to our remoteUpdateLog from our localCommitment. We can also use the workaround described above to restore any additional data attached to the HTLC.
Irrevocably committed by sender
We have received a RevokeAndAck from the remote peer that revokes state without the HTLC.**
- The RemoteCommitment is persisted in the openChannelBucket with the incoming
channeldb.HTLC - Our commitDiff and unsignedAckedUpdates have been cleared in the openChannelBucket
- A
LogUpdatefor the incoming HTLC has been stores in our forwarding package underfwdPackagesKey
On Restart:
- We will restore the incoming HTLC in our remoteUpdateLog
- The add for the downstream link will be replayed with any additional data provided, because the forwarding package stores the full
LogUpdate
unsignedAckedUpdates. This is not a problem for forwarding the HTLC, but will become an issue if we want to use the data on HTLC settle or fail.
Outgoing HTLC (B -> C)
Pending on the receiver
We have sent an UpdateAddHTLC to our remote peer.
- The HTLC is not yet saved in the state machine for the outgoing channel.
- The forwarded htlc's open circuit has been persisted in under the circuitAddKey when the switch forwarded the HTLC to the outgoing link
On Restart:
- The incoming link will resolve forwarding packages, reprocessing all remote adds.
- The outgoing HTLC will extra data attached because it is restored from a LogUpdate.
In the receiver's commitment
We have sent a CommitmentSignature to the remote peer that includes the HTLC.
- The forward is part of the outgoing link's keystones when we update our commitment, so the full circuit is persisted in the keystoneBkt.
- We have persisted the pending CommitDiff saved under the commitDiffKey, including a LogUpdate for the added HTLC
On Restart:
- The HTLC is still not saved in the outgoing channel's commitment state.
- It will not be reforwarded by the incoming link because it has a keystone, so is dropped on forwarding.
- We retrieve our pendingRemoteCommitDiff from disk and restorePendingLocalUpdates which restores the HTLC from a LogUpdate to our localUpdateLog
- On channel re-establish we will re-transmit the UpdateAddHTLC to the remote peer.
Irrevocably committed by the receiver
We have received a RevokeAndAck from the remote peer that revokes the state without the HTLC.
- When we received the revocation, the previous CommitDiff is stored as our new remote commitment state, and the CommitDiff is cleared.
- The outgoing HTLC is stored as a channeldb.HTLC on that commitment.
- We do not save the HTLC's LogUpdate in our remoteUnsignedLocalUpdates because they are restored from the remote commitment.
On restart:
- The outgoing HTLC will be restored to our
localUpdateLogfrom the remote commitment, so no extra data will be present.
channeldb.HTLC which strips those fields. This is not a problem for forwarding the HTLC, since we have already sent the peer an UpdateAddHTLC, but will be an issue if we want to interact with the data on settle/fail.
From this point onwards, we lose the extra data associated with the HTLC after restart. If you like complex switches and you cannot lie want to see the details of how I walked through this all, my full notes on the life and times of a HTLC in lnd are here!
Options to Explore
- Migrate
channeldb.HTLCto a TLV stream, then store specific fields as they come into use in the protocol. - Restore extra data from forwarding packages