-
Notifications
You must be signed in to change notification settings - Fork 2k
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
base: master
Are you sure you want to change the base?
Conversation
Important Review SkippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the 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? TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Additionally, you can add CodeRabbit Configration File (
|
485b765
to
451bda7
Compare
e92e850
to
d537c35
Compare
@bitromortac: review reminder |
There was a problem hiding this 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.
// on `var_onion_option`, we can assume that all nodes | ||
// in a blinded path support TLV onions without this | ||
// being explicitly communicated to us. | ||
if isBlindedEdge && isTLVOptionBit(feature) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Up to the commit here isBlindedEdge
could also be extracted from blindedPath
comparing node pubkeys, which is why I think the previous addition of of BlindedPayment()
to the AdditionalEdge
interface isn't needed. But I see that you later change this to blindedPathSet
. I'm not yet sure how that will work, maybe it would be better to shift the additions of BlindedPayment
to a commit where it becomes important.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this whole PR is just set-up for the follow up PRs. It tries to get all the plumbing out the way in prep for those PRs so that there is less noise there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes at the moment there is only a single blinded payment that this edge could have come from but eventually (in upcoming PR), it will be a set of paths. Then we dont want to have to iterate through every path etc etc to find the path this edge is part of. Also, if this is an introduction node, then it could be that this node is the intro node to multiple different payments in which case we cant just compare pub keys to know which one it is part of.
) | ||
} | ||
|
||
if constraints != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this represents data that is used by the receiver, right? are constraints then needed in the receiver case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes this is used by the receiver.
im not sure I follow the second part of the question?
// is the same size and so each call will overwrite the Padding record. | ||
// Note that calling PadBy with an n value of 0 will still result in a zero | ||
// length TLV entry being added. | ||
func (b *BlindedRouteData) PadBy(n int) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't it make sense to implement the exact version? We could assume the type to be constant size and the length to be variable to make it easier. We would just need to know the bigint transitions, right? It would make this call easier and safer to use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
implement the exact version?
this cannot be done without going and changing the internals of the TLV package. Or do you mean doing the iterative stuff here instead of letting the caller do it?
if you mean that, then the reason why not that is cause only the caller knows the sizes of all the packages. it could be that all of them need to be buffered. Ie, when we call this the first time, we dont necessarily know the final size to pad by.
(more detail in commit message)
|
||
// BlindedPaymentPath holds all the information a payer needs to know about a | ||
// blinded path to a receiver of a payment. | ||
type BlindedPaymentPath struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to put this into the zpay32 package, just wanted to note that there is also routing.BlindedPayment
, with similar data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just checking if there is an actionable item here or just a note?
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.
We can make some assumptions about the feature set of a blinded edge without having to be explicity told what those features are. Specifically, this applies to the `var_onion_option` feature bits which can be assumed for any node advertising `option_route_blinding`. See [this](https://github.com/lightning/bolts/blob/db278ab9b2baa0b30cfe79fb3de39280595938d3/proposals/route-blinding.md?plain=1#L219) suggestion in the route blinding proposal. This will allow us to condense the information that a recipient needs to send to the sender. This change is tested by updating the TestBlindedRouteConstruction test to no longer set the Features field in the BlindedPayment struct.
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.
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.
d537c35
to
04d97a4
Compare
There was a problem hiding this 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
// on `var_onion_option`, we can assume that all nodes | ||
// in a blinded path support TLV onions without this | ||
// being explicitly communicated to us. | ||
if isBlindedEdge && isTLVOptionBit(feature) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this whole PR is just set-up for the follow up PRs. It tries to get all the plumbing out the way in prep for those PRs so that there is less noise there.
) | ||
} | ||
|
||
if constraints != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes this is used by the receiver.
im not sure I follow the second part of the question?
// is the same size and so each call will overwrite the Padding record. | ||
// Note that calling PadBy with an n value of 0 will still result in a zero | ||
// length TLV entry being added. | ||
func (b *BlindedRouteData) PadBy(n int) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
implement the exact version?
this cannot be done without going and changing the internals of the TLV package. Or do you mean doing the iterative stuff here instead of letting the caller do it?
if you mean that, then the reason why not that is cause only the caller knows the sizes of all the packages. it could be that all of them need to be buffered. Ie, when we call this the first time, we dont necessarily know the final size to pad by.
(more detail in commit message)
|
||
// BlindedPaymentPath holds all the information a payer needs to know about a | ||
// blinded path to a receiver of a payment. | ||
type BlindedPaymentPath struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just checking if there is an actionable item here or just a note?
// on `var_onion_option`, we can assume that all nodes | ||
// in a blinded path support TLV onions without this | ||
// being explicitly communicated to us. | ||
if isBlindedEdge && isTLVOptionBit(feature) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes at the moment there is only a single blinded payment that this edge could have come from but eventually (in upcoming PR), it will be a set of paths. Then we dont want to have to iterate through every path etc etc to find the path this edge is part of. Also, if this is an introduction node, then it could be that this node is the intro node to multiple different payments in which case we cant just compare pub keys to know which one it is part of.
94f2078
to
85a1b2a
Compare
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.
85a1b2a
to
aded983
Compare
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:
BlindedPayment
associated with an edge in path finding later on.BlindedRouteData
that will be required for receives. Namely, PathID and Padding.