routing/pathfind: ensure max htlc is considered during path finding

In this commit, we update the path finding logic to
ignore a channel if the HTLC value (including the fees
at the point) exceeds the max HTLC value (if set) of the
link.
This commit is contained in:
Valentine Wallace 2018-12-08 18:32:49 -08:00
parent 348a66ed13
commit 648adaea69
2 changed files with 97 additions and 0 deletions

@ -576,6 +576,13 @@ func findPath(g *graphParams, r *restrictParams,
return
}
// If this edge was constructed from a hop hint, we won't have access to
// its max HTLC. Therefore, only consider discarding this edge here if
// the field is set.
if edge.MaxHTLC != 0 && edge.MaxHTLC < amountToSend {
return
}
// Compute fee that fromNode is charging. It is based on the
// amount that needs to be sent to the next node in the route.
//

@ -1451,6 +1451,96 @@ func TestRouteFailMinHTLC(t *testing.T) {
}
}
// TestRouteFailMaxHTLC tests that if we attempt to route an HTLC which is
// larger than the advertised max HTLC of an edge, then path finding fails.
func TestRouteFailMaxHTLC(t *testing.T) {
t.Parallel()
// Set up a test graph:
// roasbeef <--> firstHop <--> secondHop <--> target
// We will be adjusting the max HTLC of the edge between the first and
// second hops.
var firstToSecondID uint64 = 1
testChannels := []*testChannel{
symmetricTestChannel("roasbeef", "first", 100000, &testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: 100000001,
}),
symmetricTestChannel("first", "second", 100000, &testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: 100000002,
}, firstToSecondID),
symmetricTestChannel("second", "target", 100000, &testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: 100000003,
}),
}
graph, err := createTestGraphFromChannels(testChannels)
if err != nil {
t.Fatalf("unable to create graph: %v", err)
}
defer graph.cleanUp()
sourceNode, err := graph.graph.SourceNode()
if err != nil {
t.Fatalf("unable to fetch source node: %v", err)
}
ignoredEdges := make(map[edgeLocator]struct{})
ignoredVertexes := make(map[Vertex]struct{})
// First, attempt to send a payment greater than the max HTLC we are
// about to set, which should succeed.
target := graph.aliasMap["target"]
payAmt := lnwire.MilliSatoshi(100001)
_, err = findPath(
&graphParams{
graph: graph.graph,
},
&restrictParams{
ignoredNodes: ignoredVertexes,
ignoredEdges: ignoredEdges,
feeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
)
if err != nil {
t.Fatalf("graph should've been able to support payment: %v", err)
}
// Next, update the middle edge policy to only allow payments up to 100k
// msat.
_, midEdge, _, err := graph.graph.FetchChannelEdgesByID(firstToSecondID)
midEdge.MessageFlags = 1
midEdge.MaxHTLC = payAmt - 1
if err := graph.graph.UpdateEdgePolicy(midEdge); err != nil {
t.Fatalf("unable to update edge: %v", err)
}
// We'll now attempt to route through that edge with a payment above
// 100k msat, which should fail.
_, err = findPath(
&graphParams{
graph: graph.graph,
},
&restrictParams{
ignoredNodes: ignoredVertexes,
ignoredEdges: ignoredEdges,
feeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
)
if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err)
}
}
// TestRouteFailDisabledEdge tests that if we attempt to route to an edge
// that's disabled, then that edge is disqualified, and the routing attempt
// will fail. We also test that this is true only for non-local edges, as we'll