diff --git a/routing/pathfind.go b/routing/pathfind.go index 3b1c44333..1d6ff4f44 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -96,17 +96,29 @@ type edgePolicyWithSource struct { edge *channeldb.ChannelEdgePolicy } -// newRoute returns a fully valid route between the source and target that's -// capable of supporting a payment of `amtToSend` after fees are fully -// computed. If the route is too long, or the selected path cannot support the -// fully payment including fees, then a non-nil error is returned. +// finalHopParams encapsulates various parameters for route construction that +// apply to the final hop in a route. These features include basic payment data +// such as amounts and cltvs, as well as more complex features like destination +// custom records. +type finalHopParams struct { + amt lnwire.MilliSatoshi + cltvDelta uint16 + records record.CustomSet +} + +// newRoute constructs a route using the provided path and final hop constraints. +// Any destination specific fields from the final hop params will be attached +// assuming the destination's feature vector signals support, otherwise this +// method will fail. If the route is too long, or the selected path cannot +// support the fully payment including fees, then a non-nil error is returned. // // NOTE: The passed slice of ChannelHops MUST be sorted in forward order: from -// the source to the target node of the path finding attempt. -func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex, +// the source to the target node of the path finding attempt. It is assumed that +// any feature vectors on all hops have been validated for transitive +// dependencies. +func newRoute(sourceVertex route.Vertex, pathEdges []*channeldb.ChannelEdgePolicy, currentHeight uint32, - finalCLTVDelta uint16, - destCustomRecords record.CustomSet) (*route.Route, error) { + finalHop finalHopParams) (*route.Route, error) { var ( hops []*route.Hop @@ -132,7 +144,7 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex, // If this is the last hop, then the hop payload will contain // the exact amount. In BOLT #4: Onion Routing // Protocol / "Payload for the Last Node", this is detailed. - amtToForward := amtToSend + amtToForward := finalHop.amt // Fee is not part of the hop payload, but only used for // reporting through RPC. Set to zero for the final hop. @@ -161,9 +173,9 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex, // As this is the last hop, we'll use the specified // final CLTV delta value instead of the value from the // last link in the route. - totalTimeLock += uint32(finalCLTVDelta) + totalTimeLock += uint32(finalHop.cltvDelta) - outgoingTimeLock = currentHeight + uint32(finalCLTVDelta) + outgoingTimeLock = currentHeight + uint32(finalHop.cltvDelta) } else { // Next, increment the total timelock of the entire // route such that each hops time lock increases as we @@ -204,8 +216,8 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex, // If this is the last hop, then we'll populate any TLV records // destined for it. - if i == len(pathEdges)-1 && len(destCustomRecords) != 0 { - currentHop.CustomRecords = destCustomRecords + if i == len(pathEdges)-1 && len(finalHop.records) != 0 { + currentHop.CustomRecords = finalHop.records } hops = append([]*route.Hop{currentHop}, hops...) diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index d65090f1f..104d47b5a 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -680,8 +680,12 @@ func TestFindLowestFeePath(t *testing.T) { t.Fatalf("unable to find path: %v", err) } route, err := newRoute( - paymentAmt, ctx.source, path, startingHeight, - finalHopCLTV, nil, + ctx.source, path, startingHeight, + finalHopParams{ + amt: paymentAmt, + cltvDelta: finalHopCLTV, + records: nil, + }, ) if err != nil { t.Fatalf("unable to create path: %v", err) @@ -830,8 +834,12 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc } route, err := newRoute( - paymentAmt, sourceVertex, path, startingHeight, - finalHopCLTV, nil, + sourceVertex, path, startingHeight, + finalHopParams{ + amt: paymentAmt, + cltvDelta: finalHopCLTV, + records: nil, + }, ) if err != nil { t.Fatalf("unable to create path: %v", err) @@ -1244,9 +1252,12 @@ func TestNewRoute(t *testing.T) { t.Run(testCase.name, func(t *testing.T) { route, err := newRoute( - testCase.paymentAmount, sourceVertex, - testCase.hops, startingHeight, finalHopCLTV, - nil, + sourceVertex, testCase.hops, startingHeight, + finalHopParams{ + amt: testCase.paymentAmount, + cltvDelta: finalHopCLTV, + records: nil, + }, ) if testCase.expectError { @@ -2210,8 +2221,12 @@ func TestRestrictOutgoingChannel(t *testing.T) { t.Fatalf("unable to find path: %v", err) } route, err := newRoute( - paymentAmt, ctx.source, path, startingHeight, - finalHopCLTV, nil, + ctx.source, path, startingHeight, + finalHopParams{ + amt: paymentAmt, + cltvDelta: finalHopCLTV, + records: nil, + }, ) if err != nil { t.Fatalf("unable to create path: %v", err) @@ -2336,8 +2351,12 @@ func testCltvLimit(t *testing.T, limit uint32, expectedChannel uint64) { finalHopCLTV = 1 ) route, err := newRoute( - paymentAmt, ctx.source, path, startingHeight, finalHopCLTV, - nil, + ctx.source, path, startingHeight, + finalHopParams{ + amt: paymentAmt, + cltvDelta: finalHopCLTV, + records: nil, + }, ) if err != nil { t.Fatalf("unable to create path: %v", err) @@ -2623,8 +2642,12 @@ func TestNoCycle(t *testing.T) { t.Fatalf("unable to find path: %v", err) } route, err := newRoute( - paymentAmt, ctx.source, path, startingHeight, - finalHopCLTV, nil, + ctx.source, path, startingHeight, + finalHopParams{ + amt: paymentAmt, + cltvDelta: finalHopCLTV, + records: nil, + }, ) if err != nil { t.Fatalf("unable to create path: %v", err) diff --git a/routing/payment_session.go b/routing/payment_session.go index 5168ba283..d8e765f17 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -129,8 +129,12 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment, // a route by applying the time-lock and fee requirements. sourceVertex := route.Vertex(ss.SelfNode.PubKeyBytes) route, err := newRoute( - payment.Amount, sourceVertex, path, height, finalCltvDelta, - payment.DestCustomRecords, + sourceVertex, path, height, + finalHopParams{ + amt: payment.Amount, + cltvDelta: finalCltvDelta, + records: payment.DestCustomRecords, + }, ) if err != nil { // TODO(roasbeef): return which edge/vertex didn't work diff --git a/routing/router.go b/routing/router.go index c8d4bebd1..e8b42173b 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1454,8 +1454,12 @@ func (r *ChannelRouter) FindRoute(source, target route.Vertex, // Create the route with absolute time lock values. route, err := newRoute( - amt, source, path, uint32(currentHeight), finalCLTVDelta, - destCustomRecords, + source, path, uint32(currentHeight), + finalHopParams{ + amt: amt, + cltvDelta: finalCLTVDelta, + records: destCustomRecords, + }, ) if err != nil { return nil, err @@ -2403,7 +2407,11 @@ func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi, } return newRoute( - receiverAmt, source, pathEdges, uint32(height), - uint16(finalCltvDelta), nil, + source, pathEdges, uint32(height), + finalHopParams{ + amt: receiverAmt, + cltvDelta: uint16(finalCltvDelta), + records: nil, + }, ) }