Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature]: Blinded Forwarding in LND #7298

Closed
carlaKC opened this issue Jan 6, 2023 · 2 comments · Fixed by #8160
Closed

[feature]: Blinded Forwarding in LND #7298

carlaKC opened this issue Jan 6, 2023 · 2 comments · Fixed by #8160
Labels
brainstorming Long term ideas/discussion/requests for feedback enhancement Improvements to existing features / behaviour spec

Comments

@carlaKC
Copy link
Collaborator

carlaKC commented Jan 6, 2023

This issue outlines a proposal for adding support for forwarding payments in blinded routes. Receiving payments via blinded routes is out of scope for this issue.

Parent: #5594
Related: #7200 + #7267

Background

Nodes that support forwarding blinded payments in the Lightning Network need to perform some additional tasks to help forward payments to an obfuscated destination. There are two different roles that a node in a blinded path can play in blinded forwarding:

  • Introduction node: act as the connecting node between the unblinded and blinded sections of the path, receiving an onion containing a blinding point that is used to decrypt forwarding information.
  • Relaying node: relay payments within the blinded path, receiving an update_add_htlc with a blinding point that is used to decrypt the onion and forwarding information contained in it.

For easy reference, the difference between these two nodes is as follows:

Introduction Node Intermediate Node
Blinding Point Onion Payload UpdateAddHTLC
Encrypted Data Onion Payload Onion Payload
Onion Decryption Node Privkey Blinded Privkey

Introduction Node

The introduction node acts as a bridge between the unblinded and blinded portions of the route. It processes payments in a blinded route in the following sequence:

  • Receive update_add_htlc message with onion payload
  • Decrypt onion payload as usual, extracting blinding_point and encrypted_data TLVs
  • Tweak ephemeral key with blinding point to decrypt encrypted_data
  • Calculate forwarding fee/delta from encrypted_data payload
  • Send update_add_htlc with next blinding point in blinding_point TLV

Relaying Node

A relaying node receives an update_add_htlc message with a blinding point TLV included, which is used to decrypt the onion. It processed payments in a blinded route with the following sequence:

  • Receive update_add_htlc message with onion payload and blinding_point TLV
  • Tweak ephemeral key with blinding blinding_point to decrypt onion
  • Extract encrypted_data from onion and use blinding_point to decrypt it
  • Calculate forwarding fee/delta from encrypted_data payload
  • Send update_add_htlc with next blinding point in blinding_point TLV

Implementation

  • Bump onion dependency to lightning-onion/57
  • Update onion payload to include encrypted data and blinding point TLVs
  • Add understanding of blinding point TLV to UpdateAddHTLC
  • Add BlindingPoint to PaymentDescriptor type

Processing HTLCs

The link is our first point of contact with HTLCs that will need to be updated to accommodate HTLCs in blinded routes. At a high level, the incoming link will need to:

The outgoing link will simply need to forward an UpdateAddHTLC to the next peer including the next blinding point. In the case of a payment success, there is no additional handling required for HTLCs that are relayed within a blinded route.

Payments that are included in blinded routes also require custom error handling, because the errors need to be generated using the blinding point tweaked ephemeral key. The introduction and relay nodes in a blinded route also handle errors differently to prevent probing, so we need to be aware of our position in the blinded route to relay errors in a spec-compliant way:

  • Introduction node: Send an update malformed HTLC using invalid onion blinding code and containing sha256(onion).
  • Relaying (non-final) node: Send an update fail HTLC error using invalid onion blinding code and containing sha256(onion).

Restarts

As outlined in #7297, once a HTLC is irrevocably committed to both parties' commitments, we only have it stored as a channeldb.HTLC in our state machine. Assuming that we do not want to migrate channeldb.HTLC, we employ the following workarounds to ensure that we always have the blinding point for the HTLC available.

Workaround 1: Restore Blinding Point Pending Remote Updates

If we restart when an incoming HTLC is irrevocably committed to our local commit, but has not yet been added to the remote peer’s commitment, we will not have the blinding point populated in our remote update log (see “Irrevocably committed by the receiver” in #7297). This HTLC still needs its blinding_point to be able to forward correctly to the outgoing peer, so we supplement the remoteUpdateLog with our pending remote updates (which still have the full wire message stored).

Workaround 2: Store Blinding Point in Error Encryptor

The ErrorEncryptor interface that is used to encrypt HTLC errors is currently provided by the SphinxErrorEncryptor implementation. Encoding and decoding of the error encryptor is paired with a type, which allows us to easily extend our persistence to store a blinding point along with the obfuscator to ensure that we use the correct shared secret for error obfuscation.

On-Chain Resolution

We also make use of channeldb.HTLC in our incomingContestResolver where we will need the HTLC’s blinding point to ReconstructHopIterator when we decode our payload. The embedded htlcSuccessResolver that contains this HTLC already has an “ad-hoc” migration where we try to read more bytes out of the bucket, then fail if they’re not present. We can either look at a migration for the resolver, or add another ad-hoc field at the end to include the blinding point.

@carlaKC carlaKC added the enhancement Improvements to existing features / behaviour label Jan 6, 2023
@carlaKC
Copy link
Collaborator Author

carlaKC commented Jan 6, 2023

@calvinrzachman and I are working on an updated verion of #7195 to address this!

Since it's a larger change, we're looking to break it up into parts:

  1. Forwarding Logic for blinded routes
  2. Error handling for blinded routes (separate PR to 1, but reviewed + merged together)
  3. Handling for receiving via blinded paths / dummy hops

-> Would appreciate some feedback from the LL team if this breakdown would work/ other suggestions if desired!

@Roasbeef Roasbeef added brainstorming Long term ideas/discussion/requests for feedback spec labels Jan 6, 2023
@Roasbeef
Copy link
Member

Roasbeef commented Jan 13, 2023

Thanks for such a clear proposal!

Add BlindingPoint to PaymentDescriptor type

One thing I've reaaally been meaning to do over the year is: get rid of the mega PaymentDescriptor type and replace that with a sort of LogEntry interface (so no need to carry around the dozen other fields when you just want to insert an entry that settles another HTLC. If you're up to the task, this could make sense to do before the set of larger changes, as they'll all be dependent on this, as we'd add a new type instantiation for this new case.

Here's a mega old commit fragment from an abandoned branch (early flavor of dynamic commitments) if you'd like to pick up the task: Roasbeef@16d036e#diff-8896f76f365f8d3c7b6aec4e89140c923aeaf5a5ec1487b9c6cb9fd37cf5cab7R13-R34.

The embedded htlcSuccessResolver that contains this HTLC already has an “ad-hoc” migration where we try to read more bytes out of the bucket, then fail if they’re not present. We can either look at a migration for the resolver, or add another ad-hoc field at the end to include the blinding point.

I think the migration has the potential to be pretty painless, given an active node should only have a hand full of active HTLC resolvers. This may also be useful for some of the taproot work as well, since these resolvers now either need to recompute the tapscript inclusion proof, or they can just grab the fully encoded control block from disk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
brainstorming Long term ideas/discussion/requests for feedback enhancement Improvements to existing features / behaviour spec
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants