diff --git a/routing/heap.go b/routing/heap.go index 39a6f4ed..e7e1b587 100644 --- a/routing/heap.go +++ b/routing/heap.go @@ -24,9 +24,10 @@ type nodeWithDist struct { // amount that includes also the fees for subsequent hops. amountToReceive lnwire.MilliSatoshi - // incomingCltv is the expected cltv value for the incoming htlc of this - // node. This value does not include the final cltv. - incomingCltv uint32 + // incomingCltv is the expected absolute expiry height for the incoming + // htlc of this node. This value should already include the final cltv + // delta. + incomingCltv int32 // probability is the probability that from this node onward the route // is successful. diff --git a/routing/pathfind.go b/routing/pathfind.go index 2f64e311..f74d00d9 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -47,7 +47,8 @@ const ( // pathFinder defines the interface of a path finding algorithm. type pathFinder = func(g *graphParams, r *RestrictParams, cfg *PathFindingConfig, source, target route.Vertex, - amt lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy, error) + amt lnwire.MilliSatoshi, finalHtlcExpiry int32) ( + []*channeldb.ChannelEdgePolicy, error) var ( // DefaultPaymentAttemptPenalty is the virtual cost in path finding weight @@ -411,7 +412,7 @@ func getMaxOutgoingAmt(node route.Vertex, outgoingChan *uint64, // that need to be paid along the path and accurately check the amount // to forward at every node against the available bandwidth. func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig, - source, target route.Vertex, amt lnwire.MilliSatoshi) ( + source, target route.Vertex, amt lnwire.MilliSatoshi, finalHtlcExpiry int32) ( []*channeldb.ChannelEdgePolicy, error) { // Pathfinding can be a significant portion of the total payment @@ -542,10 +543,14 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig, weight: 0, node: target, amountToReceive: amt, - incomingCltv: 0, + incomingCltv: finalHtlcExpiry, probability: 1, } + // Calculate the absolute cltv limit. Use uint64 to prevent an overflow + // if the cltv limit is MaxUint32. + absoluteCltvLimit := uint64(r.CltvLimit) + uint64(finalHtlcExpiry) + // processEdge is a helper closure that will be used to make sure edges // satisfy our specific requirements. processEdge := func(fromVertex route.Vertex, @@ -590,11 +595,10 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig, timeLockDelta = edge.TimeLockDelta } - incomingCltv := toNodeDist.incomingCltv + - uint32(timeLockDelta) + incomingCltv := toNodeDist.incomingCltv + int32(timeLockDelta) // Check that we are within our CLTV limit. - if incomingCltv > r.CltvLimit { + if uint64(incomingCltv) > absoluteCltvLimit { return } diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index 1bec640a..c47f7348 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -823,6 +823,7 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc }, testPathFindingConfig, sourceNode.PubKeyBytes, target, paymentAmt, + startingHeight+finalHopCLTV, ) if test.expectFailureNoPath { if err == nil { @@ -1012,6 +1013,7 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) { }, r, testPathFindingConfig, sourceNode.PubKeyBytes, doge.PubKeyBytes, paymentAmt, + 0, ) } @@ -1383,7 +1385,7 @@ func TestNewRoutePathTooLong(t *testing.T) { graph: graph.graph, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, paymentAmt, + sourceNode.PubKeyBytes, target, paymentAmt, 0, ) if err != nil { t.Fatalf("path should have been found") @@ -1397,7 +1399,7 @@ func TestNewRoutePathTooLong(t *testing.T) { graph: graph.graph, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, paymentAmt, + sourceNode.PubKeyBytes, target, paymentAmt, 0, ) if err == nil { t.Fatalf("should not have been able to find path, supposed to be "+ @@ -1437,7 +1439,7 @@ func TestPathNotAvailable(t *testing.T) { graph: graph.graph, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, unknownNode, 100, + sourceNode.PubKeyBytes, unknownNode, 100, 0, ) if err != errNoPathFound { t.Fatalf("path shouldn't have been found: %v", err) @@ -1495,7 +1497,7 @@ func TestDestTLVGraphFallback(t *testing.T) { graph: ctx.graphParams.graph, }, r, testPathFindingConfig, - sourceNode.PubKeyBytes, target, 100, + sourceNode.PubKeyBytes, target, 100, 0, ) } @@ -1604,7 +1606,7 @@ func TestMissingFeatureDep(t *testing.T) { graph: ctx.graphParams.graph, }, r, testPathFindingConfig, - sourceNode.PubKeyBytes, target, 100, + sourceNode.PubKeyBytes, target, 100, 0, ) } @@ -1682,7 +1684,7 @@ func TestDestPaymentAddr(t *testing.T) { graph: ctx.graphParams.graph, }, r, testPathFindingConfig, - sourceNode.PubKeyBytes, target, 100, + sourceNode.PubKeyBytes, target, 100, 0, ) } @@ -1743,7 +1745,7 @@ func TestPathInsufficientCapacity(t *testing.T) { graph: graph.graph, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, payAmt, + sourceNode.PubKeyBytes, target, payAmt, 0, ) if err != errNoPathFound { t.Fatalf("graph shouldn't be able to support payment: %v", err) @@ -1776,7 +1778,7 @@ func TestRouteFailMinHTLC(t *testing.T) { graph: graph.graph, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, payAmt, + sourceNode.PubKeyBytes, target, payAmt, 0, ) if err != errNoPathFound { t.Fatalf("graph shouldn't be able to support payment: %v", err) @@ -1875,7 +1877,7 @@ func TestRouteFailDisabledEdge(t *testing.T) { graph: graph.graph, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, payAmt, + sourceNode.PubKeyBytes, target, payAmt, 0, ) if err != nil { t.Fatalf("unable to find path: %v", err) @@ -1903,7 +1905,7 @@ func TestRouteFailDisabledEdge(t *testing.T) { graph: graph.graph, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, payAmt, + sourceNode.PubKeyBytes, target, payAmt, 0, ) if err != nil { t.Fatalf("unable to find path: %v", err) @@ -1928,7 +1930,7 @@ func TestRouteFailDisabledEdge(t *testing.T) { graph: graph.graph, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, payAmt, + sourceNode.PubKeyBytes, target, payAmt, 0, ) if err != errNoPathFound { t.Fatalf("graph shouldn't be able to support payment: %v", err) @@ -1962,7 +1964,7 @@ func TestPathSourceEdgesBandwidth(t *testing.T) { graph: graph.graph, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, payAmt, + sourceNode.PubKeyBytes, target, payAmt, 0, ) if err != nil { t.Fatalf("unable to find path: %v", err) @@ -1986,7 +1988,7 @@ func TestPathSourceEdgesBandwidth(t *testing.T) { bandwidthHints: bandwidths, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, payAmt, + sourceNode.PubKeyBytes, target, payAmt, 0, ) if err != errNoPathFound { t.Fatalf("graph shouldn't be able to support payment: %v", err) @@ -2004,7 +2006,7 @@ func TestPathSourceEdgesBandwidth(t *testing.T) { bandwidthHints: bandwidths, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, payAmt, + sourceNode.PubKeyBytes, target, payAmt, 0, ) if err != nil { t.Fatalf("unable to find path: %v", err) @@ -2035,7 +2037,7 @@ func TestPathSourceEdgesBandwidth(t *testing.T) { bandwidthHints: bandwidths, }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, payAmt, + sourceNode.PubKeyBytes, target, payAmt, 0, ) if err != nil { t.Fatalf("unable to find path: %v", err) @@ -2872,7 +2874,7 @@ func (c *pathFindingTestContext) findPath(target route.Vertex, return findPath( &c.graphParams, &c.restrictParams, &c.pathFindingConfig, - c.source, target, amt, + c.source, target, amt, 0, ) } diff --git a/routing/payment_session.go b/routing/payment_session.go index e5474ddf..47732e01 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -113,6 +113,8 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment, return nil, err } + finalHtlcExpiry := int32(height) + int32(finalCltvDelta) + path, err := p.pathFinder( &graphParams{ graph: ss.Graph, @@ -121,7 +123,7 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment, }, restrictions, &ss.PathFindingConfig, ss.SelfNode.PubKeyBytes, payment.Target, - payment.Amount, + payment.Amount, finalHtlcExpiry, ) if err != nil { return nil, err diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go index 14f98449..55549c44 100644 --- a/routing/payment_session_test.go +++ b/routing/payment_session_test.go @@ -15,8 +15,8 @@ func TestRequestRoute(t *testing.T) { findPath := func(g *graphParams, r *RestrictParams, cfg *PathFindingConfig, source, target route.Vertex, - amt lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy, - error) { + amt lnwire.MilliSatoshi, finalHtlcExpiry int32) ( + []*channeldb.ChannelEdgePolicy, error) { // We expect find path to receive a cltv limit excluding the // final cltv delta (including the block padding). diff --git a/routing/router.go b/routing/router.go index 085db61f..55cefaae 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1431,27 +1431,29 @@ func (r *ChannelRouter) FindRoute(source, target route.Vertex, return nil, err } + // We'll fetch the current block height so we can properly calculate the + // required HTLC time locks within the route. + _, currentHeight, err := r.cfg.Chain.GetBestBlock() + if err != nil { + return nil, err + } + // Now that we know the destination is reachable within the graph, we'll // execute our path finding algorithm. + finalHtlcExpiry := currentHeight + int32(finalCLTVDelta) + path, err := findPath( &graphParams{ graph: r.cfg.Graph, bandwidthHints: bandwidthHints, }, restrictions, &r.cfg.PathFindingConfig, - source, target, amt, + source, target, amt, finalHtlcExpiry, ) if err != nil { return nil, err } - // We'll fetch the current block height so we can properly calculate the - // required HTLC time locks within the route. - _, currentHeight, err := r.cfg.Chain.GetBestBlock() - if err != nil { - return nil, err - } - // Create the route with absolute time lock values. route, err := newRoute( source, path, uint32(currentHeight), diff --git a/routing/router_test.go b/routing/router_test.go index 26803492..6fd9a630 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -2174,7 +2174,7 @@ func TestFindPathFeeWeighting(t *testing.T) { }, noRestrictions, testPathFindingConfig, - sourceNode.PubKeyBytes, target, amt, + sourceNode.PubKeyBytes, target, amt, 0, ) if err != nil { t.Fatalf("unable to find path: %v", err)