routing: refactor pathfinding loop

In preparation for the next commit where we will start hiding underlying
graph details such as that a graph session needs to be "closed" after
pathfinding is done with it, we refactor things here so that the main
pathfinding logic is done in a call-back.
This commit is contained in:
Elle Mouton 2025-02-13 09:22:09 +02:00
parent 99c9440520
commit dfe2314a2a
No known key found for this signature in database
GPG Key ID: D7D916376026F177

View File

@ -6,6 +6,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btclog/v2" "github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
@ -235,6 +236,17 @@ func newPaymentSession(p *LightningPayment, selfNode route.Vertex,
}, nil }, nil
} }
// pathFindingError is a wrapper error type that is used to distinguish path
// finding errors from other errors in path finding loop.
type pathFindingError struct {
error
}
// Unwrap returns the underlying error.
func (e *pathFindingError) Unwrap() error {
return e.error
}
// RequestRoute returns a route which is likely to be capable for successfully // RequestRoute returns a route which is likely to be capable for successfully
// routing the specified HTLC payment to the target node. Initially the first // routing the specified HTLC payment to the target node. Initially the first
// set of paths returned from this method may encounter routing failure along // set of paths returned from this method may encounter routing failure along
@ -295,13 +307,8 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
maxAmt = *p.payment.MaxShardAmt maxAmt = *p.payment.MaxShardAmt
} }
for { var path []*unifiedEdge
// Get a routing graph session. findPath := func(graph graphdb.NodeTraverser) error {
graph, closeGraph, err := p.graphSessFactory.NewGraphSession()
if err != nil {
return nil, err
}
// We'll also obtain a set of bandwidthHints from the lower // We'll also obtain a set of bandwidthHints from the lower
// layer for each of our outbound channels. This will allow the // layer for each of our outbound channels. This will allow the
// path finding to skip any links that aren't active or just // path finding to skip any links that aren't active or just
@ -310,19 +317,13 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
// attempt, because concurrent payments may change balances. // attempt, because concurrent payments may change balances.
bandwidthHints, err := p.getBandwidthHints(graph) bandwidthHints, err := p.getBandwidthHints(graph)
if err != nil { if err != nil {
// Close routing graph session. return err
if graphErr := closeGraph(); graphErr != nil {
log.Errorf("could not close graph session: %v",
graphErr)
}
return nil, err
} }
p.log.Debugf("pathfinding for amt=%v", maxAmt) p.log.Debugf("pathfinding for amt=%v", maxAmt)
// Find a route for the current amount. // Find a route for the current amount.
path, _, err := p.pathFinder( path, _, err = p.pathFinder(
&graphParams{ &graphParams{
additionalEdges: p.additionalEdges, additionalEdges: p.additionalEdges,
bandwidthHints: bandwidthHints, bandwidthHints: bandwidthHints,
@ -332,12 +333,42 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
p.selfNode, p.selfNode, p.payment.Target, p.selfNode, p.selfNode, p.payment.Target,
maxAmt, p.payment.TimePref, finalHtlcExpiry, maxAmt, p.payment.TimePref, finalHtlcExpiry,
) )
if err != nil {
// Close routing graph session. // Wrap the error to distinguish path finding errors
if err := closeGraph(); err != nil { // from other errors in this closure.
log.Errorf("could not close graph session: %v", err) return &pathFindingError{err}
} }
return nil
}
for {
// Get a routing graph session.
graph, closeGraph, err := p.graphSessFactory.NewGraphSession()
if err != nil {
return nil, err
}
err = findPath(graph)
// First, close routing graph session.
// NOTE: this will be removed in an upcoming commit.
if graphErr := closeGraph(); graphErr != nil {
log.Errorf("could not close graph session: %v",
graphErr)
}
// If there is an error, and it is not a path finding error, we
// return it immediately.
if err != nil && !lnutils.ErrorAs[*pathFindingError](err) {
return nil, err
} else if err != nil {
// If the error is a path finding error, we'll unwrap it
// to check the underlying error.
//
//nolint:errorlint
err = err.(*pathFindingError).Unwrap()
}
// Otherwise, we'll switch on the path finding error.
switch { switch {
case err == errNoPathFound: case err == errNoPathFound:
// Don't split if this is a legacy payment without mpp // Don't split if this is a legacy payment without mpp