routing: extract getEdgeUnifiers

We split up the functionality in getRouteUnifiers into checking that all
edges exist via getEdgeUnifiers and then add a backward pass that will
be responsible for determining the sender amount.

We remove the node pub key from the error string, as in route building
this is duplicate info, which can be determined from the input keys,
further it's not available in the backward pass anymore.

We refactor the BuildRoute test to use the require library and add a
test case for a max HTLC violation on the last hop.
This commit is contained in:
bitromortac
2024-06-25 11:11:18 +02:00
parent 16f6284d97
commit 2edbe6b878
2 changed files with 118 additions and 94 deletions

View File

@@ -1393,13 +1393,12 @@ func (r *ChannelRouter) extractChannelUpdate(
// channels that satisfy all requirements.
type ErrNoChannel struct {
position int
fromNode route.Vertex
}
// Error returns a human-readable string describing the error.
func (e ErrNoChannel) Error() string {
return fmt.Sprintf("no matching outgoing channel available for "+
"node %v (%v)", e.position, e.fromNode)
"node index %v", e.position)
}
// BuildRoute returns a fully specified route based on a list of pubkeys. If
@@ -1429,6 +1428,15 @@ func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi,
sourceNode := r.cfg.SelfNode
// We check that each node in the route has a connection to others that
// can forward in principle.
unifiers, err := getEdgeUnifiers(
r.cfg.SelfNode, hops, outgoingChans, r.cfg.RoutingGraph,
)
if err != nil {
return nil, err
}
// If no amount is specified, we need to build a route for the minimum
// amount that this route can carry.
useMinAmt := amt == nil
@@ -1446,16 +1454,15 @@ func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi,
runningAmt = *amt
}
unifiers, senderAmt, err := getRouteUnifiers(
sourceNode, hops, useMinAmt, runningAmt, outgoingChans,
r.cfg.RoutingGraph, bandwidthHints,
senderAmt, err := senderAmtBackwardPass(
unifiers, useMinAmt, runningAmt, bandwidthHints,
)
if err != nil {
return nil, err
}
pathEdges, receiverAmt, err := getPathEdges(
sourceNode, senderAmt, unifiers, bandwidthHints, hops,
senderAmt, unifiers, bandwidthHints,
)
if err != nil {
return nil, err
@@ -1481,12 +1488,10 @@ func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi,
)
}
// getRouteUnifiers returns a list of edge unifiers for the given route.
func getRouteUnifiers(source route.Vertex, hops []route.Vertex,
useMinAmt bool, runningAmt lnwire.MilliSatoshi,
outgoingChans map[uint64]struct{}, graph Graph,
bandwidthHints *bandwidthManager) ([]*edgeUnifier, lnwire.MilliSatoshi,
error) {
// getEdgeUnifiers returns a list of edge unifiers for the given route.
func getEdgeUnifiers(source route.Vertex, hops []route.Vertex,
outgoingChans map[uint64]struct{},
graph Graph) ([]*edgeUnifier, error) {
// Allocate a list that will contain the edge unifiers for this route.
unifiers := make([]*edgeUnifier, len(hops))
@@ -1502,8 +1507,6 @@ func getRouteUnifiers(source route.Vertex, hops []route.Vertex,
fromNode = hops[i-1]
}
localChan := i == 0
// Build unified policies for this hop based on the channels
// known in the graph. Don't use inbound fees.
//
@@ -1514,19 +1517,34 @@ func getRouteUnifiers(source route.Vertex, hops []route.Vertex,
err := u.addGraphPolicies(graph)
if err != nil {
return nil, 0, err
return nil, err
}
// Exit if there are no channels.
edgeUnifier, ok := u.edgeUnifiers[fromNode]
if !ok {
log.Errorf("Cannot find policy for node %v", fromNode)
return nil, 0, ErrNoChannel{
fromNode: fromNode,
position: i,
}
return nil, ErrNoChannel{position: i}
}
unifiers[i] = edgeUnifier
}
return unifiers, nil
}
// senderAmtBackwardPass determines the amount that should be sent to fulfill
// min HTLC requirements. The minimal sender amount can be searched for by
// activating useMinAmt.
func senderAmtBackwardPass(unifiers []*edgeUnifier, useMinAmt bool,
runningAmt lnwire.MilliSatoshi,
bandwidthHints *bandwidthManager) (lnwire.MilliSatoshi, error) {
// Traverse hops backwards to accumulate fees in the running amounts.
for i := len(unifiers) - 1; i >= 0; i-- {
localChan := i == 0
edgeUnifier := unifiers[i]
// If using min amt, increase amt if needed.
if useMinAmt {
min := edgeUnifier.minAmt()
@@ -1538,11 +1556,10 @@ func getRouteUnifiers(source route.Vertex, hops []route.Vertex,
// Get an edge for the specific amount that we want to forward.
edge := edgeUnifier.getEdge(runningAmt, bandwidthHints, 0)
if edge == nil {
log.Errorf("Cannot find policy with amt=%v for node %v",
runningAmt, fromNode)
log.Errorf("Cannot find policy with amt=%v for hop %v",
runningAmt, i)
return nil, 0, ErrNoChannel{
fromNode: fromNode,
return 0, ErrNoChannel{
position: i,
}
}
@@ -1554,19 +1571,16 @@ func getRouteUnifiers(source route.Vertex, hops []route.Vertex,
log.Tracef("Select channel %v at position %v",
edge.policy.ChannelID, i)
unifiers[i] = edgeUnifier
}
return unifiers, runningAmt, nil
return runningAmt, nil
}
// getPathEdges returns the edges that make up the path and the total amount,
// including fees, to send the payment.
func getPathEdges(source route.Vertex, receiverAmt lnwire.MilliSatoshi,
unifiers []*edgeUnifier, bandwidthHints *bandwidthManager,
hops []route.Vertex) ([]*unifiedEdge,
lnwire.MilliSatoshi, error) {
func getPathEdges(receiverAmt lnwire.MilliSatoshi, unifiers []*edgeUnifier,
bandwidthHints *bandwidthManager) ([]*unifiedEdge, lnwire.MilliSatoshi,
error) {
// Now that we arrived at the start of the route and found out the route
// total amount, we make a forward pass. Because the amount may have
@@ -1576,15 +1590,7 @@ func getPathEdges(source route.Vertex, receiverAmt lnwire.MilliSatoshi,
for i, unifier := range unifiers {
edge := unifier.getEdge(receiverAmt, bandwidthHints, 0)
if edge == nil {
fromNode := source
if i > 0 {
fromNode = hops[i-1]
}
return nil, 0, ErrNoChannel{
fromNode: fromNode,
position: i,
}
return nil, 0, ErrNoChannel{position: i}
}
if i > 0 {