Skip to content

Conversation

MPins
Copy link
Contributor

@MPins MPins commented Dec 5, 2024

Fixes #9076

MaxNumPaths restriction moved from FindBlindedPaths to BuildBlindedPaymentPaths this way we can interact through all possibly routes.

If the reviewers agreed to this approach we will have to modify the TestFindBlindedPathsWithMC because it is considering that MaxNumPaths retriction is applied on FindBlindedPaths function.

Copy link
Contributor

coderabbitai bot commented Dec 5, 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.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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>, please review it.
    • 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 gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @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 using 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 generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this 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.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration 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.

@MPins MPins changed the title route blinding: use all valid routes during path construction iteration instead of the currently capped set by MaxNumPaths Use all valid routes during path construction iteration instead of the currently capped set by MaxNumPaths Dec 5, 2024
@MPins MPins changed the title Use all valid routes during path construction iteration instead of the currently capped set by MaxNumPaths use all valid routes during blinded path construction Dec 5, 2024
@MPins MPins changed the title use all valid routes during blinded path construction Use all valid routes during blinded path construction Dec 5, 2024
@ziggie1984 ziggie1984 self-requested a review December 5, 2024 18:06
@ziggie1984
Copy link
Collaborator

ziggie1984 commented Dec 7, 2024

This seems like the simpler solution to implement. Since we’re already querying all routes in FindBlindedPaths, it makes sense to just return them. However, it might be worthwhile to introduce a hard constant cap during the pathfinding process to prevent the array size from growing exponentially. Currently, the algorithm explores all possible paths within the given constraints. Instead, we could stop after finding, say, 100 routes in total, and then apply filtering to select the MaxNumPaths in BuildBlindedPaymentPaths, as proposed in this PR.

Probably the constraints of the Min/Max Hop number prevents these high numbers for routes but I think having a cap is kind of a sanity check.

@MPins
Copy link
Contributor Author

MPins commented Dec 9, 2024

This seems like the simpler solution to implement. Since we’re already querying all routes in FindBlindedPaths, it makes sense to just return them. However, it might be worthwhile to introduce a hard constant cap during the pathfinding process to prevent the array size from growing exponentially. Currently, the algorithm explores all possible paths within the given constraints. Instead, we could stop after finding, say, 100 routes in total, and then apply filtering to select the MaxNumPaths in BuildBlindedPaymentPaths, as proposed in this PR.

Probably the constraints of the Min/Max Hop number prevents these high numbers for routes but I think having a cap is kind of a sanity check.

Before the proposed changes we are querying all routes, sorting the routes based on probability and them capping them based on MaxNumPaths before returning from FindBlindedPaths. After the changes we are capping them just one step later after FindBlindedPaths return them to BuildBlindedPaymentPaths. The hard constant cap should be introduced after the sorting! I might be wrong, but it seems that we are getting too little introducing it!

@ziggie1984
Copy link
Collaborator

ziggie1984 commented Dec 9, 2024

The hard constant cap should be introduced after the sorting! I might be wrong, but it seems that we are getting too little introducing it!

I think we need to do way more when creating the path rather than creating the path and later checking the probability this does not seem efficient. I think we should build the path in the first place based on probability not just running a depth first search on all available paths. But I am not sure how difficult that might be because we went with the recusrive approach.

@MPins
Copy link
Contributor Author

MPins commented Dec 10, 2024

The hard constant cap should be introduced after the sorting! I might be wrong, but it seems that we are getting too little introducing it!

I think we need to do way more when creating the path rather than creating the path and later checking the probability this does not seem efficient. I think we should build the path in the first place based on probability not just running a depth first search on all available paths. But I am not sure how difficult that might be because we went with the recursive approach.

I think we might compute the total probability and sort the path list during the creation of it (into processNodeForBlindedPath), but I'm not sure it's worth it. What do you think?

@ziggie1984
Copy link
Collaborator

yes let's keep it as is and focus only on the underlying issue.

@MPins
Copy link
Contributor Author

MPins commented Dec 19, 2024

Hello @ziggie1984 gently reminder of review 😉

@ziggie1984
Copy link
Collaborator

unit-test fail !

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.

Looking good I am missing 2 things here:

  1. Can you add some logging stats how many routes are find in total, then how many are filtered out because of the probability as for example debug information. I am very curious whether routes will fail often at the level where the final route and relay information is accumulated.
  2. I tend to introduce a max route variable for the blinded path finding in general, maybe a default of 50 routes, otherwise we look for everything which with a big number of blinded hops might be wasteful.

@MPins MPins force-pushed the issue-9076 branch 2 times, most recently from 41573d0 to c1251d7 Compare January 20, 2025 23:28
@MPins MPins requested a review from ziggie1984 January 20, 2025 23:44
@MPins
Copy link
Contributor Author

MPins commented Jan 24, 2025

Looking good I am missing 2 things here:

  1. Can you add some logging stats how many routes are find in total, then how many are filtered out because of the probability as for example debug information. I am very curious whether routes will fail often at the level where the final route and relay information is accumulated.
  2. I tend to introduce a max route variable for the blinded path finding in general, maybe a default of 50 routes, otherwise we look for everything which with a big number of blinded hops might be wasteful.

Hello Ziggie, I had addressed your comments, thank you! Ready for your next review!

Copy link
Contributor

@Abdulkbk Abdulkbk 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 PR. I looked through the changes and left some comments/questions.

Copy link
Contributor Author

@MPins MPins left a comment

Choose a reason for hiding this comment

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

Thank you @Abdulkbk for your review!

@MPins
Copy link
Contributor Author

MPins commented Jan 31, 2025

@ziggie1984 and @Abdulkbk , it is ready for your review. 🤝

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.

Looking good, I want to get @ellemouton opinion on the recursive abort criteria in case we have a maximum number of paths.

Missing release notes for LND 20.

for _, route := range routes {
if len(bestRoutes) >= int(restrictions.MaxNumPaths) {
if len(allRoutes) >= maxRoutes {
Copy link
Collaborator

Choose a reason for hiding this comment

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

that is not what I meant, my idea was to exit the recursion early when enough paths are found,

I was more thinking of restricting the recursion in processNodeForBlindedPath but I would like to get @ellemouton opinion on this and also this can probably done in another PR.

Currently our DFS exhausts all Paths which exist in the graph, so I was thinking we could abort the DFS after we already have a hopSet of let's say 100 ?

Imagine we have a minRouteLen of 2 and a maxRouteLen of 6, each node has more than 100 channels, when reaching the depth of 6, we could easily accumulate > 1000 routes which seems unreasonable.

But the above restrictions we do not need, because really makes not difference we already have all the paths in memory.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You right, thanks for the explanation.

The point is that, in the current implementation, we first retrieve all the routes and then sort them to prioritize those with the highest success probability.

Instead, we should modify the process to evaluate the probability of each route during the iteration, keeping only the best ones.

Copy link
Collaborator

Choose a reason for hiding this comment

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

seems like this is removed but not in this commit, can you make sure this is removed also in this commit

@MPins
Copy link
Contributor Author

MPins commented Feb 28, 2025

Hello @ellemouton , @ziggie1984 and @Abdulkbk ready for the next round 😊

@ellemouton
Copy link
Collaborator

However, it might be worthwhile to introduce a hard constant cap during the pathfinding process to prevent the array size from growing exponentially

Yeah might still be worth having some hard bound.

Looking good, I want to get @ellemouton opinion on the recursive abort criteria in case we have a maximum number of paths.

this is no longer relevant yeah?

But from context: yeah could be cool to stop once we have found enough but hard to do since then we dont have all probabilities to compare against... something to ponder on.

Comment on lines 696 to 702
log.Debugf("Found %v routes, discarded %v low probability routes",
len(paths), len(paths)-len(routes))

bestRoutes = append(bestRoutes, route.route)
// Return all routes.
allRoutes := make([]*route.Route, 0, len(routes))
for _, route := range routes {
allRoutes = append(allRoutes, route.route)
Copy link
Collaborator

Choose a reason for hiding this comment

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

i think we can now just all the probability stuff on the caller side. No need to do it here or to pass in probabilitySrc probabilitySource anymore. We can now just add r.server.defaultMC.GetProbability to the AddInvoiceConfig and do sorting there.

Copy link
Collaborator

Choose a reason for hiding this comment

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

the only reason it was here before was cause we wanted to adhere to MaxNumPaths here.

Copy link
Contributor Author

@MPins MPins Mar 13, 2025

Choose a reason for hiding this comment

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

Hello @ellemouton, just to make sure ... basically do you mean do the probability stuff into the invoicesrpc.AddInvoice ... if I understand correctly it is just for code organization, right?

@MPins MPins force-pushed the issue-9076 branch 4 times, most recently from 48d10d0 to 1b389e9 Compare March 17, 2025 19:03
@MPins MPins requested a review from ellemouton March 17, 2025 19:43
@MPins
Copy link
Contributor Author

MPins commented Mar 17, 2025

Hello @ellemouton, @ziggie1984 and @Abdulkbk ready for the next review!

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.

Nice refactor, I think we can go even further and remove some function of the invoice cfg and do the calculation before entering the invoice package.

rpcserver.go Outdated
return r.server.chanRouter.FindBlindedPaths(
r.selfNode, amt,
r.server.defaultMC.GetProbability,
QueryBlindedPaths: func() ([][]blindedpath.BlindedHop, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we still need this function and cannot just add the routes to the AddInvoice call, it can all be predetermined before triggering the invoice creation call.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would like to hear @ellemouton 's opinion as well 😊

Copy link
Collaborator

Choose a reason for hiding this comment

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

i vote for keeping logic in one area & having only one location that needs to worry about calling various things. Otherwise you need to repeat a bunch of set-up calls each time you call AddInvoice from a different call-site.

Copy link
Contributor Author

@MPins MPins left a comment

Choose a reason for hiding this comment

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

Thanks! 👍

rpcserver.go Outdated
return r.server.chanRouter.FindBlindedPaths(
r.selfNode, amt,
r.server.defaultMC.GetProbability,
QueryBlindedPaths: func() ([][]blindedpath.BlindedHop, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would like to hear @ellemouton 's opinion as well 😊

@MPins
Copy link
Contributor Author

MPins commented Apr 1, 2025

Hello @ellemouton, this is a friendly reminder that we need your opinion to move forward!

@ellemouton
Copy link
Collaborator

Thanks for the ping! I'll prio for tomorrow 🙏

Copy link
Collaborator

@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.

not sure that this is doing anything other than refactor at the moment? I also find the "all changes in 1 commit" very hard to review fyi

rpcserver.go Outdated
return r.server.chanRouter.FindBlindedPaths(
r.selfNode, amt,
r.server.defaultMC.GetProbability,
QueryBlindedPaths: func() ([][]blindedpath.BlindedHop, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

i vote for keeping logic in one area & having only one location that needs to worry about calling various things. Otherwise you need to repeat a bunch of set-up calls each time you call AddInvoice from a different call-site.

@MPins
Copy link
Contributor Author

MPins commented Apr 11, 2025

not sure that this is doing anything other than refactor at the moment? I also find the "all changes in 1 commit" very hard to review fyi

Thank you for the review! Sorry for keeping everything in one commit — I initially thought it made sense since it was all related to the same bug. But I agree it made the review harder. I’ll split it up to make things easier to follow.

Anyway, as I mentioned in reply to your comments, the bug is addressed because the cap for maxnumpaths is now passed from FindBlindedPaths to BuildBlindedPaymentPaths.

@MPins MPins force-pushed the issue-9076 branch 2 times, most recently from 0460004 to c788928 Compare April 29, 2025 23:46
MPins added 2 commits April 29, 2025 20:57
MaxNumPaths restriction moved from FindBlindedPaths to BuildBlindedPaymentPaths
this way we can interact through all possibly routes when creating a new
blinded path.

The FindBlindedPaths function was moved from router.go
to pathfind.go, since the probability logic was moved to
the function BlindedPathsToRoutes on addinvoice.go.

Add the function BlindedPathsToRoutes which converts blinded paths into routess,
filtering out low-probability routes based on a given ProbabilitySource, and
returns the valid ones sorted by success probability.
@MPins
Copy link
Contributor Author

MPins commented Apr 30, 2025

Hello @ellemouton and @ziggie1984, I’ve reordered the commits as requested. But I wasn’t able to split the “multi” commit, as the changes are tightly coupled due to dependencies between them.

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.

So I think this PR still needs some work.

I agree with Elle that the current PR is a mix of Refactor + Logic change which makes it hard to review So I propose a new commit structure.

Moreover I don't think we should remove the responsibility of the chanRouter in this code but open for discussion on this one.


// probabilitySrc is a helper that returns the mission control success
// probability of a forward between two vertices.
probabilitySrc := func(from route.Vertex, to route.Vertex,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: probabilitySrc => mockProbabilitySrc

QueryBlindedRoutes: func(amt lnwire.MilliSatoshi) (
[]*route.Route, error) {

return r.server.chanRouter.FindBlindedPaths(
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should not remove the responsibility of the chanRouter here. given the description of it, it should be the source which handles all graph queries see description (questions regarding graph reachability:

lnd/routing/router.go

Lines 317 to 323 in b068d79

// ChannelRouter is the layer 3 router within the Lightning stack. Below the
// ChannelRouter is the HtlcSwitch, and below that is the Bitcoin blockchain
// itself. The primary role of the ChannelRouter is to respond to queries for
// potential routes that can support a payment amount, and also general graph
// reachability questions. The router will prune the channel graph
// automatically as new blocks are discovered which spend certain known funding
// outpoints, thereby closing their respective channels.

Copy link
Collaborator

Choose a reason for hiding this comment

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

If we still one to remove from there I definitely agree with Elle, that the commit structure right now is very hard to review because we mix refactor with a code change. So depending on our decision of where we want to do the blinded path finding we should first move code between packages, and then make the functional change, otherwise it is difficult to understand what really changed.

If we decide to remove the responsibilty of the chanRouter the commit structure should look something like this:

  1. Move func (r *ChannelRouter) FindBlindedPaths into its own package and add an additonal func param for the graph we previouly got for the chanRouter + change the corresponding tests to call the right func.
  2. Move the max num of paths out (still keeping the probability) where it is + adding new tests etc ...

made an example here: https://github.com/ziggie1984/lnd/tree/new-commit-structure

and I think we don't have to do further refactor in this PR, meaning that we do not need to pull out the probability stuff here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, thank you!

Copy link
Contributor Author

@MPins MPins May 22, 2025

Choose a reason for hiding this comment

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

@ziggie1984 the path is already being created outside of router.go by findBlindedPathsprocessNodeForBlindedPath in pathfind.go, so it might make sense to relocate this logic elsewhere. As @ellemouton previously mentioned, it's preferable to keep related logic in one place, minimizing the number of components that need to orchestrate multiple things.

With that in mind, I’d like to propose remodeling this PR — including the commit structure. Specifically:

1- create a commit to move the `MaxNumPaths` restriction from `FindBlindedPaths` to `BuildBlindedPaymentPaths`.

2- add one or more commits to reorganize the place of the logic accordingly.

3- if needed, add a commit for further refactoring/cleanup.

Does that sound good to both of you?

Copy link
Collaborator

Choose a reason for hiding this comment

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

So I would prefer laying out your idea how you wanna refactor the code, and only then do the functional change. Then it makes more sense because normally for me the flow should be:

You prepare the code (Refactor) so that the functional change becomes more clear. Otherwise why do the functional change and then refactor ? Somehow that would not make so much sense to me because the functional change was done beforehand, does it make sense ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ziggie1984 I think the functional changes are quite clear when looking at the code directly. However, the refactoring introduces complexity that makes the flow harder to follow.

Since the refactoring was inspired by this comment from @ellemouton (#9334 (comment)) I think it would help to clarify the intent of each refactoring step in the commit messages after the functional change.

After reviewing everything, I’m now unsure whether the refactoring is the right move. While @ellemouton reasoning makes sense, we can see similar usage of the ProbabilityEstimator logic in the FindRoute → findPath flow, which suggests the current structure is already consistent with other parts of the codebase.

Copy link
Collaborator

Choose a reason for hiding this comment

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

As mentioned in my other comment, I am also hesitant ripping the logic out of the ChanRouter because it should be responsible of finding the paths. But I think we should have a design call on the way forward here.

@@ -547,35 +547,59 @@ func genBlindedRouteData(rand *rand.Rand) *record.BlindedRouteData {
// an example mentioned in this spec document:
// https://github.com/lightning/bolts/blob/master/proposals/route-blinding.md
// This example does not use any dummy hops.
// Added an additional node (Dave), increasing the possible paths, which can
// then be filtered using the MaxNumPaths (maximum blinded paths) option.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think Elle mentioned this in your other PR, I am in favour of always adding new unit-tests rather than trying to change old ones. With the new commit structure this should be easy to do. I don'T think we need to check the blinded path values in this new test but rather only check if the right amount of route is returned.

@lightninglabs-deploy
Copy link

@Abdulkbk: review reminder
@MPins, remember to re-request review from reviewers when ready

@MPins MPins marked this pull request as draft June 25, 2025 04:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

route blinding: use all valid routes during path construction iteration instead of the currently capped set
6 participants