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

WIP: Route Blinding [1/n]: TLV Record Definition and Blind Hop Processing #7195

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

calvinrzachman
Copy link
Contributor

This (definitely draft) PR equips the ChannelLink to process HTLCs inside a blinded route. The bulk of the processing for blind hops in done by the incoming link (ie: the link on which the HTLC ADD arrives). The incoming link will:

  • Decrypt the onion using the ephemeral (route) blinding point if it is available on the UpdateAddHTLC TLV extension.
  • Decrypt the route blinding payload and provide the forwarding information therein to the Switch.
  • Compute the ephemeral (route) blinding point the next hop in the route will need to decrypt the onion and include that as a TLV extension to the UpdateAddHTLC message.

The outgoing link simply needs to forward the UpdateAddHTLC message it is given by the Switch. Everything will have been assembled for successful forwarding by the incoming link.

TODO: Several branches are still far too dirty to post publicly. Clean up branches for following:

  • this branch
    • Consider more deeply the HTLC update retransmission (in-memory and from disk) code path & error handling.
  • Route Blinding [2/n]: Dummy Hop Processing
    • May require some modifications to the HTLC Switch/Link (possibly channel state machine?) so that onions can be reprocessed.
  • Route Blinding [3/n]: Blind Path Creation/Build Blind Route RPC
  • Route Blinding [4/n]: Introduction Node and Blind Path Selection
    • In attempting to define optimality and automate the discovery of a particular introduction node and blinded path, one reaches the point where the rubber seemingly really meets the road.

Comments

As of now this PR punts on dummy hop processing, integrating blinded routes with invoices, an algorithm for selecting the introduction node, and automated blinded route pathfinding. With this PR alone, LND won't be able to create or receive payment over a blinded route just yet, but it will be available for others to use while building blinded routes. Last I heard, a large portion of nodes on the network run LND, so even this modest start to an implementation could provide a boon to anonymity set sizes.

I tried to split things up such that the things I left out would not, if considered, fundamentally change the approach, though lacking a complete knowledge of BOLT-12 or Trampoline Routing, it is very possible that I did not succeed in this.

The route-blinding spec is still a living document, so I may be behind on some of the recent updates, particularly those on error handling. There is also a fair bit of cleanup left to do - far more than I would like for posting this at present - but after reading this, I wanted to get this up sooner and get some information on where Lightning Labs is at in this area before spending too much more time deep in the weeds.

Steps to Test

  • Took a stab at some automated tests at the htlcswitch package level.
    • go test -v -run ^TestChannelLinkBlindedPathPayment$ github.com/lightningnetwork/lnd/htlcswitch --tags=dev
  • Have a local branch with an itest level which sets up a linear network Alice <---> Bob <---> Carol <---> Dave, creates a blinded route via RPC, concatenates the route to a normal route.Route{} and then verifies that payment can be sent successfully. Can post once it's a bit cleaner.
  • TODO(11/20/22): Deploy a sim-net on Polar to test interoperability with other implementations: Use eclair or a further modified LND (which includes ability to build basic blinded routes) to generate a blinded route which routes across eclair/LND nodes, and see if the payment succeeds!

Pull Request Checklist

Testing

  • Your PR passes all CI checks.
  • Tests covering the positive and negative (error paths) are included.
  • Bug fixes contain tests triggering the bug to prevent regressions.

Code Style and Documentation

📝 Please see our Contribution Guidelines for further guidance.

equip the onion payload with additional fields for route blinding,
namely the recipient_encrypted_data and blinding_point fields.
These records comprise the route blinding payload which processing nodes
will need in order to forward payments in the blinded portion of a route.
See ‘Requiring external module code from your own repository fork’ from
https://go.dev/doc/modules/managing-dependencies

NOTE: this commmit can be removed once the 'lightning-onion' dependency
is merged and in use by lnd.
Add ability to parse route blinding payload after it is decrypted,
and validate presence and omission of TLV payloads according to BOLT-04.

This does not perform all BOLT-04 TLV payload validation. We now have
validation to do across two levels of TLV payloads (top level onion TLV
payload & route blinding TLV payload) and the contents of the UpdateAddHTLC
message. What is present in one payload effects our expectation of what is
present in the other payload. As a result, we will need to validate the
payloads together, which means waiting til after the route blinding
payload is decrypted. We preserve the normal validation in the
case we are forwarding for a normal (not blinded) route.

When an onion contains an encrypted route blinding payload, we validate
what we are able to as soon as the route blinding payload is parsed.
Although enforcing different portions of the onion and route blinding
payload validation in different stages of processing for a blind hop is
a bit more complex, it provides a benefit over deferring validation in
that we are able to fail early on in the processing and avoid unecessary
computation only to fail later.
The ChannelLink’s decision about whether it is processing a final/exit hop
used to be plugged (almost) directly into the sphinx package’s observation
of 32 all 0x00-bytes for the onion HMAC. Somewhere along the line the condition
which defines whether a node is the final/exit hop appears to have changed from
32 0x00-bytes for the onion HMAC to an empty short_channel_id in the top level
onion TLV payload. It looks like things may have changed w.r.t determining the
final/exit hop when moving TLV onion payloads?

- https://github.com/lightningnetwork/lnd/blob/23cc8389f2e7db968e859f2ee2426b0906c2dd5d/htlcswitch/iterator_test.go#L78-L79

We will return to the old days where the sphinx implementation encountering an
all-zero 32 byte onion HMAC signals we are the final hop. We can no longer rely
on the presence of a next_hop in the top level onion TLV payload as it will not
be there for blinded hops.
The ChannelLink must have access to our persistent node ID key in order
to process hops in a blinded route.

TODO(9/20/22): Ensure that we do not undo the hardwork done to tighten up
signing abstractions and remove all raw private key handling in: lightningnetwork#3929
Add the ephemeral (route) blinding point as a TLV extension to the
UpdateAddHTLC message.

If an HTLC has an ephemeral blinding point, it will be needed to decrypt
the onion as the onion encryptor is expected to have encrypted the onion to
a blinded form of our node ID, rather than to our persistent node ID.
This places the route blinding point in our in-memory update log,
so it is available for use during onion & route blinding payload
decryption for nodes inside a blinded route.

The blinding point may also be used to determine how errors, both
local and downstream, will be processed.

A forwarding node has two channel links involved in forwarding an HTLC,
both of which have their own update logs, payment descriptors, &
channel state machines. When an HTLC is forwarded, it is received and
added to a remote update log by the incoming link and added to a local
update log prior to commitment by the outgoing link. If we would like
the channel state machine to preserve whether or not the HTLC had an
ephemeral blinding point, then the blinding point must be looked
after in both locations.

TODO(11/21/22):
- When processing a failure, the HTLC flows through the switch in
  the opposite direction. We need to decide which link will handle
  determining how errors are to be forwarded.

Consider HTLC update retransmission (in-memory and from disk) code path.
It seems to me we have two types of retransmission:
- In memory retransmission (ie: our node had an internal error processing
  the HTLC update, or our peer restarted)
- From disk retransmission (ie: our link/switch/node restarted - power
  outage, crashed due to bug, etc.)

We will need to make sure that HTLC updates (adds, settle, fails) for
blind hops are processed correctly after a restart.
Define an opaque (non-privacy-leaking) error type which is to be used
ubiquitously for errors which occur inside a blinded route regardless
of the internal or downstream reason for failure.
We can no longer perform the totality of the validation when the onion TLV
payload is parsed. The validation we perform depends on the contents of the
top level onion TLV payload, the route blinding payload, and the UpdateAddHTLC
message.
Equip the channel link to process HTLCs inside a blinded route.
The link will decrypt and parse the route blinding payload, use data
from the payload to make a forwarding "decision", and compute the
ephemeral (route) blinding point for the next hop in route.
This is still a work in progress. Go over error handling with a
microscope as apparently it is fraught with danger.
allow a user to specify whether they would like to enable the
HTLCSwitch to process HTLCs on a blind route
…ind hops

We expand the capability of the mockHopIterator such that it is able to
handle both normal and blind hops. The destinction is made via a check
for sentinel value which delineates the byte boundary between serialized
hops.

The implemenation is functional yet somewhat propietary in the sense that
it is specific to handling blind hops and not as general as it could be.
This lack of generality leaves the door open to eventual inclusion of the
full blown TLV serialization scheme into the mock iterator.

Without this or something like TLV we do not have a way to know
whether we should or even how to deserialize a variable length field like
the route blinding payload.
Add several test cases which verify that the link supports route blinding.
More specifically we verify that the ChannelLink can process an onion packet
"sent" to a blinded node ID public key rather than the usual persistent node
ID public key, unblind the next hop in the route and forward the HTLC.

We also verify that a ChannelLink processing a blind hop returns the opaque
InvalidOnionBlinding error under the expected scenarios. This provides
assurance that would be probers will always receive the same (hopefully)
opaque/non-privacy leaking error message regardless of the internal reason
for failure. It also provides indirect assurance that the Link is actually
validating the blind hop as specified by BOLT-04!!
@calvinrzachman
Copy link
Contributor Author

Submitting on the off chance that no-one has a branch which does this yet. If someone already has an implementation, I can consider this a knowledge-accumulating attempt to get the conversation rolling for route blinding on LND, close this one out, and try to provide competent review when the official LL PR drops. Looking forward to it. Thanks!

@carlaKC
Copy link
Collaborator

carlaKC commented Nov 22, 2022

Submitting on the off chance that no-one has a branch which does this yet. If someone already has an implementation, I can consider this a knowledge-accumulating attempt to get the conversation rolling for route blinding on LND, close this one out, and try to provide competent review when the official LL PR drops. Looking forward to it. Thanks!

Super exciting to see somebody working on this 🚀 I'm about half-way through working on forwarding blinded routes here, but it's not as advanced as this PR! Need to take a better look at your approach, but would be interested to chat about collaborating on this. Going to ping you on LND slack to figure out the best way to communicate :)

@Roasbeef
Copy link
Member

Oops, forgot to comment on this, thanks for getting things going here @calvinrzachman! We have some lofty plans in progress to get a form of blinded paths that supports forwarding and receiving/sending independent on the invoicing mechanism used in time for lnd 0.17. At the current pace, this would mean a release sometime in Spring 2023.

@lucasdcf lucasdcf closed this Dec 19, 2022
@lucasdcf lucasdcf reopened this Dec 19, 2022
@calvinrzachman
Copy link
Contributor Author

calvinrzachman commented Dec 19, 2022

@Roasbeef Thanks for the reply! I like the sound of "lofty plans"! Gotta aim up 🚀 I have been hanging with @carlaKC on this for the past couple weeks. Thankfully she has been steering me in the right direction, so we should be able to avoid duplicating work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants