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

[1/4] Route Blinding Receives: Groundwork #8752

Merged
merged 11 commits into from
Jul 11, 2024

Conversation

ellemouton
Copy link
Collaborator

@ellemouton ellemouton commented May 14, 2024

To make review a bit more manageable, I've split out some of the groundwork needed for
the main route blinding receives PR. This PR thus has no functional changes.

Tracking Issue: #6690

PR Overview:

  1. a couple of refactors & interface expansions so that we have easy access to the BlindedPayment associated with an edge in path finding later on.
  2. Remove the need to communicate the "TLV Option" in a blinded path feature bit vector. This bit can be assumed for any nodes in a blinded path.
  3. Add fields to BlindedRouteData that will be required for receives. Namely, PathID and Padding.
  4. Bolt11 blinded payment path encoding

EDIT: bLIP proposal here: lightning/blips#39

Copy link
Contributor

coderabbitai bot commented May 14, 2024

Important

Review skipped

Auto reviews are limited to specific labels.

Labels to auto review (1)
  • llm-review

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@saubyk saubyk added this to the v0.18.1 milestone May 14, 2024
@ellemouton ellemouton force-pushed the rb-receives-setup branch 2 times, most recently from 485b765 to 451bda7 Compare May 15, 2024 09:02
@ellemouton ellemouton force-pushed the rb-receives-setup branch 2 times, most recently from e92e850 to d537c35 Compare May 15, 2024 11:10
@ellemouton ellemouton changed the title [1/3] Route Blinding Receives: Groundwork [1/4] Route Blinding Receives: Groundwork May 16, 2024
@saubyk saubyk requested a review from bitromortac May 16, 2024 15:26
Copy link
Collaborator

@bitromortac bitromortac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome that you managed to do end-to-end payments to blinded routes using Bolt 11, very cool work and a big achievement 🎉! I have looked a bit ahead how everything fits together in the next PRs, but will study them more in depth.

routing/additional_edge.go Outdated Show resolved Hide resolved
routing/pathfind_test.go Outdated Show resolved Hide resolved
routing/pathfind.go Outdated Show resolved Hide resolved
record/blinded_data.go Show resolved Hide resolved
record/blinded_data.go Outdated Show resolved Hide resolved
record/blinded_data.go Show resolved Hide resolved
zpay32/invoice.go Show resolved Hide resolved
zpay32/blinded_path.go Show resolved Hide resolved
zpay32/invoice_test.go Outdated Show resolved Hide resolved
@ellemouton ellemouton force-pushed the rb-receives-setup branch from d537c35 to 04d97a4 Compare May 28, 2024 15:25
Copy link
Collaborator Author

@ellemouton ellemouton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @bitromortac 🙏

Updated & responded

routing/pathfind.go Outdated Show resolved Hide resolved
record/blinded_data.go Outdated Show resolved Hide resolved
record/blinded_data.go Show resolved Hide resolved
record/blinded_data.go Show resolved Hide resolved
zpay32/blinded_path.go Show resolved Hide resolved
routing/pathfind.go Outdated Show resolved Hide resolved
zpay32/invoice_test.go Outdated Show resolved Hide resolved
@ellemouton ellemouton force-pushed the rb-receives-setup branch 3 times, most recently from 85a1b2a to aded983 Compare May 28, 2024 15:54
@ellemouton ellemouton requested a review from bitromortac May 28, 2024 18:03
@ziggie1984 ziggie1984 self-requested a review June 5, 2024 13:39
Copy link
Collaborator

@ziggie1984 ziggie1984 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really cool change to allow for Blinded Paths in Bolt11 invoices, this PR looks really good ⭐️, will approve as soon as I reviewed the second one to gain more context.

zpay32/blinded_path.go Show resolved Hide resolved
// EncodeBlindedHop writes the passed BlindedHopInfo to the given writer.
//
// 1) Blinded node pub key: 33 bytes
// 2) Cipher text length: 2 bytes
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't the length of the cypertext a bigsize type, at least that's also what is used below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at least that's also what is used below.

Not sure i see this?
The code below uses a fixed 2 byte length. We can change the encoding to bigsize if you think that would be better though?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, hmm so yeah I was thinking of bigsize type because the spec says so or ?

type: 10 (encrypted_recipient_data)
data:
[...*byte:encrypted_data]

but maybe I am misunderstanding ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool yeah I can update it to that. Just went for the "let's force it to be small" approach since bigsize will have some overhead but I think you are right that it would be better. Will update

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool - updated to use bigSize

rpcserver.go Show resolved Hide resolved
}

numHops := len(p.Hops)
if numHops > math.MaxUint8 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Is the limit of the number of hops als the overall onion package size (1300 bytes) ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes definitely - the question is: should we do that check here? also - if we do, it probably makes sense to force it to be a number way less than 1300 right cause that 1300 still needs to include other hop payloads. So im wondering how we can determine a good magic number for this... perhaps just checking < 1300 is fine for now?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, hmm do we validate the consistency of data in other Encode/Decode methods ? Probably overkill to add the specific check here, maybe keep it as is and and the reason in the comment. Open for both ways.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok given the maximum number of hops determined calculated in the bLIP (7), i've updated the check here to be more strict.

zpay32/invoice.go Show resolved Hide resolved
zpay32/invoice_test.go Show resolved Hide resolved
Copy link
Collaborator Author

@ellemouton ellemouton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the review @ziggie1984! 🙏

zpay32/blinded_path.go Show resolved Hide resolved
// EncodeBlindedHop writes the passed BlindedHopInfo to the given writer.
//
// 1) Blinded node pub key: 33 bytes
// 2) Cipher text length: 2 bytes
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at least that's also what is used below.

Not sure i see this?
The code below uses a fixed 2 byte length. We can change the encoding to bigsize if you think that would be better though?

zpay32/invoice.go Show resolved Hide resolved
zpay32/invoice_test.go Show resolved Hide resolved
}

numHops := len(p.Hops)
if numHops > math.MaxUint8 {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes definitely - the question is: should we do that check here? also - if we do, it probably makes sense to force it to be a number way less than 1300 right cause that 1300 still needs to include other hop payloads. So im wondering how we can determine a good magic number for this... perhaps just checking < 1300 is fine for now?

rpcserver.go Show resolved Hide resolved
@lightninglabs-deploy
Copy link

@bitromortac: review reminder
@ellemouton, remember to re-request review from reviewers when ready

@ellemouton ellemouton requested a review from ziggie1984 June 17, 2024 23:55
Copy link
Collaborator

@ziggie1984 ziggie1984 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had some final comments, I think we are almost good to go :)

// include this final delta in their aggregate delta. A
// sender-set delta may be added to account for block arrival
// during payment, but we do not set it in this test.
// Create final hop parameters for payment amount = 110.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: So we will not allow an additional block arrival padding for the blinded path case ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah good point! - I think I was sort of assuming that the recipient would add this buffer to the communicated blinded path cltv delta but after looking again at the spec and the proposal doc - it looks like you are right and the sender still sets this. Will update!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, although... check this reply from t-bast: https://github.com/lightning/bolts/pull/798/files#r1059339014

In it he mentions that it is potentially no longer necessary for the sender to account for this buffer since the receiver can take care of it with dummy hops (obvs then we need to make sure to actually add these dummy hops).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've opened an issue on the spec repo to get some general clarification around CLTV delta's and blinded paths:

lightning/bolts#1174

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that seems to be an ok approach, to let the receiver handle this, since it is already tasked with building routes that are specially selected

zpay32/blinded_path.go Show resolved Hide resolved
}

numHops := len(p.Hops)
if numHops > math.MaxUint8 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, hmm do we validate the consistency of data in other Encode/Decode methods ? Probably overkill to add the specific check here, maybe keep it as is and and the reason in the comment. Open for both ways.

// EncodeBlindedHop writes the passed BlindedHopInfo to the given writer.
//
// 1) Blinded node pub key: 33 bytes
// 2) Cipher text length: 2 bytes
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, hmm so yeah I was thinking of bigsize type because the spec says so or ?

type: 10 (encrypted_recipient_data)
data:
[...*byte:encrypted_data]

but maybe I am misunderstanding ?

zpay32/invoice_test.go Show resolved Hide resolved
})
}

introNode := path.Hops[0].BlindedNodePub
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the introduction point a unblinded pubkey in the lnrpc.BlindedPath struct, I wonder why we add the blinded nodekey of the first hope ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah so this is cause the first hop's key will be the real node key. Did this for space saving in the invoice since we never need the intro node's blinded pub key as the sender.

but can update depending on how that discussion goes :)

Copy link
Collaborator Author

@ellemouton ellemouton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the review @ziggie1984 !

will start on updates asap

routing/pathfind.go Outdated Show resolved Hide resolved
// include this final delta in their aggregate delta. A
// sender-set delta may be added to account for block arrival
// during payment, but we do not set it in this test.
// Create final hop parameters for payment amount = 110.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah good point! - I think I was sort of assuming that the recipient would add this buffer to the communicated blinded path cltv delta but after looking again at the spec and the proposal doc - it looks like you are right and the sender still sets this. Will update!

// EncodeBlindedHop writes the passed BlindedHopInfo to the given writer.
//
// 1) Blinded node pub key: 33 bytes
// 2) Cipher text length: 2 bytes
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool yeah I can update it to that. Just went for the "let's force it to be small" approach since bigsize will have some overhead but I think you are right that it would be better. Will update

zpay32/invoice_test.go Show resolved Hide resolved
})
}

introNode := path.Hops[0].BlindedNodePub
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah so this is cause the first hop's key will be the real node key. Did this for space saving in the invoice since we never need the intro node's blinded pub key as the sender.

but can update depending on how that discussion goes :)

@saubyk saubyk added the P2 should be fixed if one has time label Jun 25, 2024
@ellemouton
Copy link
Collaborator Author

ellemouton commented Jun 25, 2024

Adding a write-up here in the hopes that it will provide some clarity to reviewers of what my current understanding of things is: please let me know if i'm missing something 🙏 cc @bitromortac & @ziggie1984

CLTVs, CLTV-deltas and invoice expiry in a payment

Normal LN payment:

  • Recipient (D) communicates via invoice the min_final_expiry_delta that they would accept. Meaning that if the current block height is x then the want the minimum difference between the CLTV value of the incoming HTLC to be min_final_expiry_delta. In otherwords, it is the buffer they want to be able to claim the HTLC if their peer goes on-chain. If the incoming payment has a CLTV value that would result in a safety buffer, then the recipient will reject the payment. The sender will want the payment to resolve as quickly as possible and wont want their funds on their outgoing channel to be tied up for too long if something goes wrong, so they are insentivised to use the smallest possible CLTV for the path. So they will try to use a value as close to (but not less than) x (current block height) + min_final_expiry_delta. However, the sender also wants the payment to succeed and so will want to avoid the scenario where they initiate the payment at block x but then a block or two are mined before the recipient gets it and so the recipient will fail the payment. So to avoid this, the sender will add a few padding blocks (in LND, a padding of 3 is used.

  • The Sender (A) finds a route to the recipient: A -> B -> C -> D. The sender constructs the parameters of the route starting at the final hop, C-> D. The sender sets the final outgoing_cltv_value to:

    C->D: outgoing_cltv_value = x (current block height) + b (block buffer) + min_final_cltv_expiry_delta = cltv_1

For the other hops:

B->C `outgoing_cltv_value` = `cltv_1` + C's advertised `cltv_expiry_delta` = `cltv_2`


A-> `outgoing_cltv_value` = `cltv_2` + B's advertised `cltv_expiry_delta`
  • The invoice (Bolt11) also contains an expiry field in seconds and an invoice creation field (unix timestamp). These are completely separate from the CLTV expiry. There is no way for a routing node of the payment to know that it is routing a payment that will fail due to the recipient marking it as expired. So it could very well be that the actual payment parameters are valid long past the expiration of the invoice.

  • Note that in this flow the sender, A, is responsible for deciding to add a block buffer to the provided min_cltv_expiry_delta so as to make sure that the payment still can work if a block or two are mined while the htlc chain is being set up.

Route Blinding:

  • For route blinding, the recipient is given a way to be able to enforce an expiry on the blinded part of the route. This is nice because the payment can fail earlier (since each hop can check if the expiry has passed) and it gives some protection to the blinded hops so that after the path has expired, they can change their routing parameters. This also allows us to tie invoice expiry a bit closer to the actual path expiry. We have 2 options here afaict:
    1. re-use the existing expiry field, assume a 10-min block time, also look at the invoice creation time and use these to determine the approximate block at which the path expires.
    2. add a new max_cltv field to the invoice which very explicitly communicates this.
  • Re having payment_constraints for recipient:
    • If we go with 1) above, then I think it makes sense to add the payment constraint with the absolute max_cltv_expiry in the recipient's encrypted data since then they dont need to store this separately in order to do the check when the payment comes in. I also like this cause then, just like all other hops on the blinded path, we repeat the same check against the "absolute source of truth". Otherwise we either need to persist this absolute truth somewhere else or we need to use the invoice "expiry" to work out a "rough" expiry. I think it is ok for the sender to work out a rough expiry but not so much the reciever.

    • If we go with 2) above, then maybe there is then not a reason to include the payment constraints for the recipient

    • the OG reason I incluced the payment_constraints for the recipient was cause of this example.

  • The flow is then as follows:

    1. Recipient, D has:

      • current block height, x
      • the min_final_cltv_expiry_delta which Dave requires for the last CLTV value.
      • the absolute block height at which the path will expire: max_cltv_expiry
      • Unlike the normal flow, in this flow the recipient, D, is responsible for adding a block buffer to min_final_cltv_expiry_delta.
    2. D chooses a blinded path to himself: B -> C -> D. Let B's CLTV delta be b_delta and C's be c_delta.

    3. D calculates the accumulated blinded path CLTV delta as follows: total_cltv_delta = min_final_cltv_expiry_delta + block_buffer (optional) + b_delta + c_delta (according to the example in the proposals doc but not explicitly stated in BOLT 4. See this issue for discussion on this

    4. D also constructs encrypted packets for each node on the blinded path. He uses this blob to communicate a max_cltv of min_final_cltv_expiry_delta + c_delta + max_cltv_expiry + block_buffer to C (according to the example) and a max_cltv of min_final_cltv_expiry_delta + c_delta + b_delta + max_cltv_expiry + block_buffer to B. In D's packet to himself, he may include min_final_cltv_expiry_delta + block_buffer + max_cltv_expiry so that he doesnt need to explicitly remember it.

    5. D sends the sender, A:
      5.1) the blinded path (B -> B(C) -> B(D))
      5.2) the encrypted data to deliver to each hop and total_cltv_delta.
      5.3) Note that D does not send min_final_cltv_delta to A (as is done in the normal payment) and A is instead expected to know that this value is now included in the total_cltv_delta.
      5.4) D does, however, also send A the max_expiry_delta so that A has an idea of when they need to use the route by.

Notes to self and to reviewers:

  • I need to make sure that the receiver does in fact add the block buffer & correctly sets max_cltv_expiry for each node & also correctly sets it in invoice.
  • Make sure that sender never uses the min_final_cltv_expiry_delta from the invoice.
  • sender should correctly assume that min_final_cltv_expiry_delta is included in accumulated route CLTV delta
  • make sure that the edge case where receiver is intro node is correctly handled by the sender since here the "additional edge" containing the min_final_cltv_exiry wont automatically be accounted for. We need to go extract it & apply it to final_hop params.

See progress on:

  1. this issue
  2. bLIP proposal

NOTE: has been edited (01/07/2024)

@bitromortac
Copy link
Collaborator

Thanks a lot for the writeup and thorough analysis @ellemouton!

some small corrections for iv):

D also constructs encrypted packets for each node on the blinded path. He uses this blob to communicate a max_cltv_expiry of x + min_final_cltv_expiry_delta + c_delta + max_cltv_expiry to C (according to the example) and a max_cltv_expiry of x + min_final_cltv_expiry_delta + c_delta + b_delta + max_cltv_expiry to B. In D's packet to himself, he may include max_cltv_expiry so that he doesnt need to explicitly remember it.

Concerning the points:

  1. The sender adding a buffer seems to be incompatible with the max cltv expiries, therefore I think leaving that to the recipient should be the best approach, it would just add it to it's min_final_cltv_expiry_delta,
  2. which may need to be added to the last max_cltv_expiry, see [2/4] Route Blinding Receives: Receive and send to a single blinded path in an invoice. #8735 (comment)?
  3. I would prefer if we could reuse the invoices expiry field, to not add more complexity. Perhaps one could compute it such that we statistically know that if the expiry of the invoice is reached, the path is still usable for a bit. We could use something like expiry = now + total_expiry_delta * 9 min, to make the invoice expire before the path. As the path expiry is just a probing protection, I think it makes sense to not add an extra field. I would also argue against per-blinded-path expiries, since the receiver can make them uniform for all paths and in the end it is a single invoice that's about to be paid?

Copy link
Collaborator

@bitromortac bitromortac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this looks ready 🎉 (there are still some discussions around the encoding, but I like the current state of it). I have a suggestion concerning the padding.

routing/pathfind.go Show resolved Hide resolved
routing/pathfind.go Outdated Show resolved Hide resolved
record/blinded_data.go Show resolved Hide resolved
record/blinded_data.go Show resolved Hide resolved
zpay32/blinded_path.go Show resolved Hide resolved
zpay32/invoice_test.go Show resolved Hide resolved
// include this final delta in their aggregate delta. A
// sender-set delta may be added to account for block arrival
// during payment, but we do not set it in this test.
// Create final hop parameters for payment amount = 110.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that seems to be an ok approach, to let the receiver handle this, since it is already tasked with building routes that are specially selected

@ellemouton ellemouton force-pushed the rb-receives-setup branch from 55ed46c to 6307874 Compare July 1, 2024 10:47
@ziggie1984
Copy link
Collaborator

ziggie1984 commented Jul 1, 2024

CLTVs, CLTV-deltas and invoice expiry in a payment

Thank you for the explanation, I think I now understood the whole ctlv expiry for blinded paths.

  1. I would also go with solution 1) seems less hacky to add an extra field to BOLT11 which kind of was not planned to be used for Blinded Paths in the first place ?
  2. I would also include the buffer not really as optional, I find it quite useful to account for found blocks during the payment process.

But all those changes will only affect the follow up PRs regarding the CLTV Delta (I think PR 2)

Copy link
Collaborator

@ziggie1984 ziggie1984 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🔥

Copy link
Collaborator Author

@ellemouton ellemouton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ziggie1984 🙏

Ready for a final look @bitromortac 🙏

routing/pathfind.go Show resolved Hide resolved
record/blinded_data.go Show resolved Hide resolved
zpay32/invoice_test.go Show resolved Hide resolved
This commit is purely a refactor. In it, we let the `BlindedEdge` struct
carry a pointer to the `BlindedPayment` that it was derived from. This
is done now because later on in the PR series, we will need more
information about the `BlindedPayment` that an edge was derived from.

Since we now pass in the whole BlindedPayment, we swap out the
`cipherText` member for a `hopIndex` member so that we dont carry around
two sources of truth in the same struct.
Expand the AdditionalEdges interface with a BlindedPayment method. In
upcoming commits, we will want to know if an AdditionalEdge was derived
from a blinded payment or not and we will also need some information
from the blinded payment it was derived from. So we expand the interface
here to avoid needing to do type casts later on. The new method may
return nil if the edge was not derived from a blinded payment.
Add a constructor for unified edge. In upcoming commits, we will add a
new member to unifiedEdge and a constructor forces us to not forget to
populate a required member.
Later on in this series, we will need to know during path finding if an
edge we are traversing was derived from a blinded payment path. In
preparation for that, we add a BlindedPayment member to the
`unifiedEdge` struct.

The reason we will need this later on is because: In the case where we
receive multiple blinded paths from the receipient, we will first swap
out the final hop node of each path with a single unified target node so
that path finding can work as normal. Once we have selected a route
though, we will want to know which path an edge belongs to so that we
can swap the correct destination node back in.
For the final hop in a blinded route, the SCID and RelayInfo fields will
_not_ be set. So these fields need to be converted to optional records.

The existing BlindedRouteData constructor is also renamed to
`NewNonFinalBlindedRouteData` in preparation for a
`NewFinalBlindedRouteData` constructor which will be used to construct
the blinded data for the final hop which will contain a much smaller set
of data. The SCID and RelayInfo parameters of the constructor are left
as non-pointers in order to force the caller to set them in the case
that the constructor is called for non-final nodes. The other option
would be to create a single constructor where all parameters are
optional but I think this makes it easier for the caller to make a
mistake.
Add the PathID (tlv type 6) field to BlindedRouteData. This will be used
for the final hop of a blinded route. A new constructor is also added
for BlindedRouteData which can specifically be used for the final hop.
When we start creating blinded paths to ourselves, we will want to be
able to pad the data for each hop so that the `encrypted_recipient_data`
for each hop is the same. We add a `PadBy` method that allows a caller
to add a certain number of bytes to the padding field. Note that adding
n bytes won't always mean that the encoded payload will increase by size
n since there will be overhead for the type and lenght fields for the new
TLV field. This will also be the case when the number of bytes added
results in a BigSize bucket jump for TLV length field. The
responsibility of ensuring that the final payloads are the same size is
left to the caller who may need to call PadBy iteratively to achieve the
goal. I decided to leave this to the caller since doing this at the
actual TLV level will be quite intrusive & I think it is uneccessary to
touch that code for this unique use case.
Copy link
Collaborator

@bitromortac bitromortac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🎉, I have a few questions left. Would we wait for all of the PRs to have ACKs to merge this?

zpay32/decode.go Show resolved Hide resolved
zpay32/blinded_path.go Outdated Show resolved Hide resolved
lnrpc/lightning.proto Show resolved Hide resolved
@ellemouton
Copy link
Collaborator Author

Would we wait for all of the PRs to have ACKs to merge this?

Personally, I think we should just merge since this is not practically usable till the second PR is merged anyways. So we can always throw in small changes at the start of that PR if we realise we need to change something

In this commit, the ability is added to encode blinded payment paths and
add them to a Bolt 11 invoice.
This commit adds a blinded_paths field to the PayReq proto message. A
new helper called `CreateRPCBlindedPayments` is then added to convert
the zpay32 type to the existing `lnrpc.BlindedPaymentPath` type and add
this to the `PayReq` in the `DecodePayReq` rpc method.
Only include the final hop's cltv delta in the total timelock
calculation if the route does not include a blinded path. This is
because in a blinded path, the final hops final cltv delta will be
included in the blinded path's accumlated cltv delta value.

With this commit, we remove the responsibility of remembering not to set
the `finalHop.cltvDelta` from the caller of `newRoute`. The relevant
test is updated accordingly.
Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🍩

func NewBlindedEdge(policy *models.CachedEdgePolicy, payment *BlindedPayment,
hopIndex int) (*BlindedEdge, error) {

if payment == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 re defensive programming

// NewFinalHopBlindedRouteData creates the data that's provided for the final
// hop in a blinded route.
func NewFinalHopBlindedRouteData(constraints *PaymentConstraints,
pathID []byte) *BlindedRouteData {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking here, but we could also consider making them to individual types further encode the distinction in the types themselves.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good idea 👍 that would make things much nicer and easier to reason about

// 6) Encoded BlindedHops.
func (p *BlindedPaymentPath) Encode(w io.Writer) error {
var relayInfo [26]byte
binary.BigEndian.PutUint32(relayInfo[:4], p.FeeBaseMsat)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking, you can also use binary.Write to avoid having to do the slicing in the buffer to place things properly: https://pkg.go.dev/encoding/binary#Write

Can make things easier to review, and also you don't need to worry about the alignment arithmetic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool yeah good idea - will add a commit in the follow up PR 👍

//
// NOTE: This is optional and should not be set at the same time as
// RouteHints.
BlindedPaymentPaths []*BlindedPaymentPath
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😎

@@ -355,6 +376,13 @@ func validateInvoice(invoice *Invoice) error {
return fmt.Errorf("no payment hash found")
}

if len(invoice.RouteHints) != 0 &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@Roasbeef Roasbeef merged commit f464dac into lightningnetwork:master Jul 11, 2024
29 of 34 checks passed
@ellemouton ellemouton deleted the rb-receives-setup branch July 11, 2024 05:53
@saubyk saubyk mentioned this pull request Jul 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blinded paths P2 should be fixed if one has time
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants