Skip to content

[feature]: Availability of UpdateAddHTLC ExtraData Across Restarts #7297

@carlaKC

Description

@carlaKC

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:

  1. Route blinding: a blinding_point is transmitted to peers as an UpdateAddHTLC tlv.
  2. 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 UpdateAddHTLC tlv.
  3. Local Reputation: peers that wish to communicate reputation information about a forwarded HTLC to mitigate long-hold jamming would use an UpdateAddHTLC tlv.

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.

On Restart:

⚠️ The restored HTLC does not have any extra data attached, because it is restored from a 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.

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 LogUpdate for the incoming HTLC has been stores in our forwarding package under fwdPackagesKey

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

⚠️ The restored HTLC on the incoming channel does not have any additional data attached to it, and it can no longer be restored from 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:


In the receiver's commitment

We have sent a CommitmentSignature to the remote peer that includes the HTLC.

On Restart:


Irrevocably committed by the receiver

We have received a RevokeAndAck from the remote peer that revokes the state without the HTLC.

On restart:

  • The outgoing HTLC will be restored to our localUpdateLog from the remote commitment, so no extra data will be present.

⚠️ The restored HTLC does not have any extra data attached, because it is restored from a 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

  1. Migrate channeldb.HTLC to a TLV stream, then store specific fields as they come into use in the protocol.
  2. Restore extra data from forwarding packages

Metadata

Metadata

Assignees

Labels

HTLCbrainstormingLong term ideas/discussion/requests for feedbackspec

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions