routing: add RouteOrigin interface for multi-source pathfinding#10764
routing: add RouteOrigin interface for multi-source pathfinding#10764calvinrzachman wants to merge 4 commits intolightningnetwork:elle-base-branch-payment-servicefrom
Conversation
The findPath function previously accepted a concrete source vertex that the path-finding loop terminated at. This introduces a RouteOrigin interface with a single method, IsOrigin(v) bool, and replaces the source parameter on findPath with it. The default singleOrigin implementation wraps a single vertex and produces identical behavior for all existing callers. RouteOrigin is the source-end counterpart to AdditionalEdge, which extends the graph at the destination end via route hints. After the path-finding loop, we verify the settled node is a valid origin before unraveling the forward path. When the heap empties without reaching an origin, we return errNoPathFound.
findPath now returns the origin vertex it settled on as its first return value, alongside the path and probability. For singleOrigin callers this is always the source they passed in; for multi-origin callers it tells them which gateway the pathfinder selected, so newRoute can set SourcePubKey correctly. SessionSource gains an optional Origin field which the payment session threads through as a functional option to path finding; when unset, behavior is identical to before.
FindRoute constructs singleOrigin{req.Source} when calling findPath,
which means it always terminates at a single source vertex. An external
controller using the RouteOrigin interface for multi-source pathfinding
in the payment session has no way to apply the same behavior to
FindRoute (used by QueryRoutes).
RouteRequest now accepts an optional Origin field. When set, FindRoute
uses it instead of singleOrigin{req.Source}. When nil, behavior is
identical to before.
When a multi-origin set includes the target vertex, routeToSelf was incorrectly set to true, disabling the guard that prevents the target from re-entering the distance map. This allowed the target to compete with real origins at equal cost, with the heap tiebreak producing platform-dependent results. Restrict routeToSelf to singleOrigin callers where circular self-payment is the actual intent.
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a flexible mechanism for multi-source pathfinding in lnd. By replacing the concrete source vertex with a RouteOrigin interface, the pathfinder can now efficiently determine the optimal route from a set of potential gateway nodes. This change is particularly beneficial for external payment controllers that manage multiple dispatch backends, allowing them to route payments through the most cost-effective gateway without requiring complex graph modifications. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
🟠 PR Severity: HIGH
🟠 High (4 files)
AnalysisAll changed files are in the Test files ( To override, add a |
There was a problem hiding this comment.
Code Review
This pull request refactors pathfinding to support multiple origins via a new RouteOrigin interface, allowing Dijkstra's algorithm to terminate at any node in a set. This change enables multi-backend payment services to find the cheapest path from several gateway nodes. A review comment correctly identifies a potential nil pointer dereference in findPath when accessing the distance map for the selected source, suggesting a more robust check for origin presence in the map.
| distance[source].probability, len(pathEdges), | ||
| distance[source].netAmountReceived-amt) | ||
|
|
||
| return pathEdges, distance[source].probability, nil | ||
| return source, pathEdges, distance[source].probability, nil |
There was a problem hiding this comment.
There is a potential nil pointer dereference here if source is not present in the distance map. While the unraveling loop at line 1213 checks for presence in distance, it returns errNoPathFound if the node is missing. However, if source == target and routeToSelf is false (which can happen with a multiOrigin implementation where the target is in the origin set), the target node is never added to the distance map. In this specific case, the unraveling loop will return errNoPathFound at line 1219, so the code won't reach line 1250. Nevertheless, relying on this side effect for safety is fragile. Consider adding an explicit check or ensuring source is always in distance if it's a valid origin.
Change Description
Path finding currently terminates at a single concrete source vertex, which assumes the node doing the routing is the node dispatching HTLCs. An external router controller that pathfinds centrally and dispatches from multiple lnd "gateway" backends needs routes that can originate from any of those backends.
This PR introduces a
RouteOrigininterface that generalizes where routes can originate from.RouteOriginreplaces the concrete source parameter with an interface method (IsOrigin(v) bool) so the path-finding algorithm (a modified version of Dijkstra running from target to source) can terminate at any vertex in a caller-provided set, naturally selecting whichever provides the cheapest path.This is the source-end counterpart to what
AdditionalEdgedoes at the destination end. Route hints inject extra edges near the target so the pathfinder can reach nodes the graph doesn't know about.RouteOriginworks similarly at the source: it's equivalent to adding a virtual super-sources*with zero-weight edges to each origin and running single-source Dijkstra.RouteOriginachieves the same result withouts*or extra edges in the graph by widening the termination check. Subpaths of shortest paths are shortest paths, so stripping the virtual edge leaves the optimal path from the selected origin. And the min-heap ensures the first origin popped has the smallest distance among all origins in the set (no cheaper one can remain in the queue).The default
singleOriginwraps a single vertex and produces identical behavior for all existing callers so should present no functional change for standard lnd.RouteOrigininterface andsingleOriginimplementation. Replace the concretesourceparameter onfindPathwithorigin RouteOrigin.findPathreturns the origin vertex it selected as its first return value.RequestRoutepasses this tonewRoutesoroute.SourcePubKeyis set correctly regardless of origin type.SessionSourcegains an optionalOriginfield, threaded to the payment session via functional options.RouteRequestaccepts an optionalOriginfield soFindRoute(used byQueryRoutes) can use multi-origin, same as the payment session and theSendPaymentV2flow.routeToSelfto single-origin callers. When a multi-origin set includes the target vertex, circular routing is not the intent. Rather we want a direct route from another origin.