-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Add the option on path creator to specify the incoming channel on blinded path #9127
base: master
Are you sure you want to change the base?
Conversation
Hello @ellemouton if you think I'm on the right way I can add the option to choose a channel also. |
Maybe change the name to "incoming_node" is a better idea! But I'll wait for others opinion also. |
thanks @MPins - in tokyo at the moment for the LN summit so will take a look at this a bit later this week or next week |
I think it makes sense to just specify the incoming channel, I don't see a lot of use cases for the whole blinded route, but maybe we already prepare the code so that it will be easy to just upgrade to the whole blinded path if it has benefits. |
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.
Thank you for your first contribution 🎉
Please squash all commits related to the lnrpc change into one.
Moreover while reviewing this PR I found out that we have a bug in your probabiliy calculation. We basically should have the prob. of full certainty for the last hop however we need to change the logic in our probability estimator which still seems to not account for the blinded usecase see here (only when from == Self
do we set the full local probability, that needs to now be inverted for the last hop as well):
Lines 509 to 526 in 2d33317
func (m *MissionControl) GetProbability(fromNode, toNode route.Vertex, | |
amt lnwire.MilliSatoshi, capacity btcutil.Amount) float64 { | |
m.mu.Lock() | |
defer m.mu.Unlock() | |
now := m.cfg.clock.Now() | |
results, _ := m.state.getLastPairResult(fromNode) | |
// Use a distinct probability estimation function for local channels. | |
if fromNode == m.cfg.selfNode { | |
return m.estimator.LocalPairProbability(now, results, toNode) | |
} | |
return m.estimator.PairProbability( | |
now, results, toNode, amt, capacity, | |
) | |
} |
In most cases it would be sufficient to specify the node, the channel must be specified in cases where there is more than one channel with the same node. Of course, specifying the channel covers both use cases. I'm not sure if we should keep both options for the user, perhaps it would be more intuitive to specify the income node for most users. |
I prefer the channel, because than you have more control rather than the pubkey. But at the end not sure if its really worth it to treat different channels to the same peer differently because we have non-strict forwarding. So probably both options are ok ... |
Thank you for your carefully revision. I'll be working on it some time on the following days. About the probability, I invest some time trying to understanding it, but I didn't get the bug on it! From my understanding, probability is about sending the payment, right? How the payment node would even know that we are choosing the income channel/node? I'm probably missing something, I would appreciate if you give me directions to expand my understanding. |
I think we can move forward with both options, but in my opinion they should be exclusive. I mean, if the channel is chosen, the node cannot be chosen. (just to avoid redundant info) |
So my initial idea was, that we can know the probability of the last channel in a blinded path with very high accuracy because its our own channel and we know the liquidity distribution when creating the blinded path. However looking into the codebase I think we should keep it as is, we do allow MPP payments for blinded paths so the amount can be splitted among the incoming channels in general. Moreover when we specify the incoming channel to receive on (via this PR) we already made the decision that this is the right channel to receive on the full amount, so maybe we can keep the probability estimation of the last hop/channel as is. Given the fact that we include all routes with a minimum route prob. of 1% we are good as is I think. |
Got it ... besides that, as it is blinded I can't think a way of considering that it is our own node without breaking the main reason for using a blinded path, that is not revealing our own node on a invoice. |
Hmm not sure what you mean, but I was just referring to the creator of the blinded path taking the incoming channel distribution directly into account without really relying on the MC data. The path would still be blinded for the sender so the sender would just have a sorted list of blinded paths in the bolt11 invoice. |
Never mind ... I was thinking that the GetProbability func would be called when the payment is being processed by the sender. |
I just saw that it is called by FindBlindedPaths ... Thank you, I have a better understanding of the whole process now. I think you right ... maybe it should be changed ... not sure if it should be included in this PR or maybe on a specific one for that. |
Have a basic question here.
So why are we then giving the user the control of selecting a node in the cli, instead of directly specifying a channel? |
The idea to have the option to specify the node instead of the channel is because in the most cases it would be enough and from the node runner perspective it would be more intuitive specifying just the income node. |
Good question, I also tend to only support the incoming channel id why:
|
Imagine your peer has 2 channels with you and different policies, now when creating the blinded path and using the nodeid, you cannot control the particular channel and the sender will probably use the route with the better constraints. We already have something similar when sending a payment where we can select the outgoing channel. We should prob. stick to this form. |
I think that
When sending payment you can also choose the last hop (penultimate node in the path) to route through for the payment, but it is other use case. So I think you both are right, I'm going to take the node option out. |
Good observation that we also allow the last hop in the payment flow, but LND does unify the edges in the sending flow and selects the most expensive policy that's why it makes sense to have this last hop setting. |
Perfect ... I'm going to take the node option out. Thank you @saubyk for starting this exchange of ideas. |
Thanks. Not to add the confusion, but I do understand the logic of selecting the node from a UX standpoint and especially when you imagine a user doing it in a UI. Selecting a pub key (with an alias) is much easier than selecting a channel ID. But I think that problem can be addressed at the application level, where the UI can establish that node-channel linkage and present a user friendly way for the user to make a selection. |
The problem is that you dont actually every have this control due to non-strict forwarding. At the end of the day, the peer may choose any channel to fwd on as long as the peer on the other end is the same peer. Regarding the impl here and if we only want to specify final hop or list of hops, I think things should be made general enough for both:
|
The way it is done here the user can specify the last hop many times (peers or channels, not both). If I understand you correctly it should be kept this way. Right? |
@ellemouton what's your take on this, we cannot really know whether peers have a universal policy to us?
|
c3f90f5
to
e82adc7
Compare
Hello @ellemouton and @ziggie1984 this is a kindly reminder that the PR is ready for your review. Thanks. |
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.
Very close this one as well 🤝
-
Had an edge case where we need to make sure if the min_distance is specified and is smaller than the chained_channels array, that we either error out or set it to the length of the array.
-
Moreover I think when looking for the routes, we should just lookup the channel directly without looping through all the channels of a node, because the callbacks are designed in a way that we cannot easily abort them and have to loop through all the channels.
Apart from that, kudos to your patience working on this PR, it's definitly not easy to get a direction change in between and having a bit of a time delay in reviews 🧘♂️
lnrpc/lightning.proto
Outdated
generated blinded paths as incoming hops. The first channel in the list | ||
is the one closest to this one. |
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.
Nit: take the descrption from the lncli cmd which is good: A list of chained channels that should be used in any of our generated blinded paths as incoming hops starting with the channel which points to the destination/target. These chained channels need to be connected otherwise the blinded route generation will fail.
cmd/commands/cmd_invoice.go
Outdated
cli.StringSliceFlag{ | ||
Name: "blinded_path_incoming_channel_list", | ||
Usage: "The chained channels (specified via channel " + | ||
"id) starting from the receiver node which " + |
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.
nit: starting form a channel which points to the receiver node.
cmd/commands/cmd_invoice.go
Outdated
@@ -168,7 +175,7 @@ func addInvoice(ctx *cli.Context) error { | |||
|
|||
blindedPathCfg, err := parseBlindedPathCfg(ctx) | |||
if err != nil { | |||
return fmt.Errorf("could not parse blinded path config: %w", |
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.
why don't we wrap anymore ?
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.
Never mind ... should be wrapping.
routing/pathfind.go
Outdated
@@ -1192,6 +1192,10 @@ type blindedPathRestrictions struct { | |||
// nodeOmissionSet holds a set of node IDs of nodes that we should | |||
// ignore during blinded path selection. | |||
nodeOmissionSet fn.Set[route.Vertex] | |||
|
|||
// incomingChainedChannels holds a route of chained channels that we | |||
// should use as incoming hops during blinded path selection. |
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.
Nit: please also add the detailed description that the first entry in the list is the channel which points to the destination/target.
}, | ||
}) | ||
|
||
// Assert that it contains two paths with 3 hops, both with dave as the |
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.
Nit: Maybe add to the comment the destination always is the last hop, I personally had to look it up.
// 2) Extend the search to include 2 hops other than the destination. | ||
// 2) Now with bob-dave as the incoming channel. | ||
paths, err = ctx.findBlindedPaths(&blindedPathRestrictions{ | ||
minNumHops: 1, |
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.
can you add a testcase minNumHops = 0, and maxNum = 1, and check how many paths are returned ?
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.
currently this would return 2 paths, one being only the intro-node and the other one being the incomin-channel.
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.
Sure ... Actually it will return 3 paths, one being only the intro-node and the others with one hop other than the destination hop.
dave
bob,dave
charlie,dave
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.
exactly and I think the "only dave route" is not what we want, if we specify an incoming channel but have mistakenly set the global minimum to 0.
@@ -773,3 +773,19 @@ func (r *Route) String() string { | |||
b.String(), r.TotalTimeLock, | |||
) | |||
} | |||
|
|||
// ChanIDString returns the route's channel IDs as a formatted string. |
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.
Nice, please keep the commit msg smaller than 50 chars for the header.
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.
Thank you .. I'm participating on btc++, that's why it is taking more time to addresss your last review.
cmd/commands/cmd_invoice.go
Outdated
@@ -168,7 +175,7 @@ func addInvoice(ctx *cli.Context) error { | |||
|
|||
blindedPathCfg, err := parseBlindedPathCfg(ctx) | |||
if err != nil { | |||
return fmt.Errorf("could not parse blinded path config: %w", |
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.
Never mind ... should be wrapping.
@ellemouton: review reminder |
Add an option to the addinvoice rpc which allows to specify the blinded path via a list of chained channels.
Including the blinded path incoming channel list argument to the addinvoice command, parsing and verifying that it has one or a comma separeted list of channels id.
20f0ecf
to
80cf806
Compare
Adding the incoming channel list parameter to FindBlindedPaths function on router.go, adding the ability to restrict the blinded route to the chained channel list for incoming channel. The first entry in the list is the channel which points to the destination/target.
This commit makes sure that when an incoming channel list is supplied for the blinded route, that it changes the other blinding restrictions accordingly. Moreover it introduces a sanity check when the number of paths for the blinded route is set to 0.
ChanIDString returns a string representation of the route's channel IDs, formatting them in the order they appear in the route (e.g., "chanID1 -> chanID2").
Discarded routes with a success probability lower than the minimum threshold are now logged accordingly when finding a blinded path.
Hello @ziggie1984 and @ellemouton ready for the next review 😊 . |
Fixes #8993
Change Description
Add the option on path creator to specify the incoming chained channel list
Steps to Test
lncli addinvoice --blind --blinded_path_incoming_channel_list channel_id1 amount
lncli addinvoice --blind --blinded_path_incoming_channel_list channel_id1,channel_id2 amount
Pull Request Checklist
Testing
Code Style and Documentation
[skip ci]
in the commit message for small changes.📝 Please see our Contribution Guidelines for further guidance.