diff --git a/routing/payment_session.go b/routing/payment_session.go index 9537bf45..3fec06ee 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -138,6 +138,16 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment, "edges, %v vertexes", len(pruneView.edges), len(pruneView.vertexes)) + // If a route cltv limit was specified, we need to subtract the final + // delta before passing it into path finding. The optimal path is + // independent of the final cltv delta and the path finding algorithm is + // unaware of this value. + var cltvLimit *uint32 + if payment.CltvLimit != nil { + limit := *payment.CltvLimit - uint32(finalCltvDelta) + cltvLimit = &limit + } + // TODO(roasbeef): sync logic amongst dist sys // Taking into account this prune view, we'll attempt to locate a path @@ -154,6 +164,7 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment, IgnoredEdges: pruneView.edges, FeeLimit: payment.FeeLimit, OutgoingChannelID: payment.OutgoingChannelID, + CltvLimit: cltvLimit, }, p.mc.selfNode.PubKeyBytes, payment.Target, payment.Amount, diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go new file mode 100644 index 00000000..6904d7f9 --- /dev/null +++ b/routing/payment_session_test.go @@ -0,0 +1,60 @@ +package routing + +import ( + "testing" + + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/lnwire" +) + +func TestRequestRoute(t *testing.T) { + const ( + height = 10 + ) + + findPath := func(g *graphParams, r *RestrictParams, + source, target Vertex, amt lnwire.MilliSatoshi) ( + []*channeldb.ChannelEdgePolicy, error) { + + // We expect find path to receive a cltv limit excluding the + // final cltv delta. + if *r.CltvLimit != 22 { + t.Fatal("wrong cltv limit") + } + + path := []*channeldb.ChannelEdgePolicy{ + { + Node: &channeldb.LightningNode{}, + }, + } + + return path, nil + } + + session := &paymentSession{ + mc: &missionControl{ + selfNode: &channeldb.LightningNode{}, + }, + pruneViewSnapshot: graphPruneView{}, + pathFinder: findPath, + } + + cltvLimit := uint32(30) + finalCltvDelta := uint16(8) + + payment := &LightningPayment{ + CltvLimit: &cltvLimit, + FinalCLTVDelta: &finalCltvDelta, + } + + route, err := session.RequestRoute(payment, height, finalCltvDelta) + if err != nil { + t.Fatal(err) + } + + // We expect an absolute route lock value of height + finalCltvDelta + if route.TotalTimeLock != 18 { + t.Fatalf("unexpected total time lock of %v", + route.TotalTimeLock) + } +} diff --git a/routing/router.go b/routing/router.go index ee0f621d..7d836a89 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1523,6 +1523,10 @@ type LightningPayment struct { // if there isn't a route with lower fees than this limit. FeeLimit lnwire.MilliSatoshi + // CltvLimit is the maximum time lock that is allowed for attempts to + // complete this payment. + CltvLimit *uint32 + // PaymentHash is the r-hash value to use within the HTLC extended to // the first hop. PaymentHash [32]byte