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/btclog/v2"
"github.com/lightningnetwork/lnd/channeldb"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwire"
@ -235,6 +236,17 @@ func newPaymentSession(p *LightningPayment, selfNode route.Vertex,
}, 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
// routing the specified HTLC payment to the target node. Initially the first
// 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
}
for {
// Get a routing graph session.
graph, closeGraph, err := p.graphSessFactory.NewGraphSession()
if err != nil {
return nil, err
}
var path []*unifiedEdge
findPath := func(graph graphdb.NodeTraverser) error {
// We'll also obtain a set of bandwidthHints from the lower
// layer for each of our outbound channels. This will allow the
// 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.
bandwidthHints, err := p.getBandwidthHints(graph)
if err != nil {
// Close routing graph session.
if graphErr := closeGraph(); graphErr != nil {
log.Errorf("could not close graph session: %v",
graphErr)
}
return nil, err
return err
}
p.log.Debugf("pathfinding for amt=%v", maxAmt)
// Find a route for the current amount.
path, _, err := p.pathFinder(
path, _, err = p.pathFinder(
&graphParams{
additionalEdges: p.additionalEdges,
bandwidthHints: bandwidthHints,
@ -332,12 +333,42 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
p.selfNode, p.selfNode, p.payment.Target,
maxAmt, p.payment.TimePref, finalHtlcExpiry,
)
// Close routing graph session.
if err := closeGraph(); err != nil {
log.Errorf("could not close graph session: %v", err)
if err != nil {
// Wrap the error to distinguish path finding errors
// from other errors in this closure.
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 {
case err == errNoPathFound:
// Don't split if this is a legacy payment without mpp