From 4a22ec841368acf2187a9e3e47caac817c4ca44c Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 15 May 2024 15:08:19 +0200 Subject: [PATCH] routing: pass BlindedPaymentPathSet around everywhere Building on from the previous commit, here we pass the PathSet around everywhere where we previously passed around the single BlindedPayment. --- lnrpc/routerrpc/router_backend.go | 12 +++--- routing/pathfind.go | 23 +++++++---- routing/pathfind_test.go | 63 ++++++++++++++++++------------- routing/payment_session.go | 14 ++----- routing/router.go | 51 +++++++++---------------- routing/router_test.go | 17 ++++++--- rpcserver.go | 6 +-- 7 files changed, 94 insertions(+), 92 deletions(-) diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 487da5356..faf53c4b6 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -388,10 +388,10 @@ func (r *RouterBackend) parseQueryRoutesRequest(in *lnrpc.QueryRoutesRequest) ( fromNode, toNode, amt, capacity, ) }, - DestCustomRecords: record.CustomSet(in.DestCustomRecords), - CltvLimit: cltvLimit, - DestFeatures: destinationFeatures, - BlindedPayment: blindedPathSet.GetPath(), + DestCustomRecords: record.CustomSet(in.DestCustomRecords), + CltvLimit: cltvLimit, + DestFeatures: destinationFeatures, + BlindedPaymentPathSet: blindedPathSet, } // Pass along an outgoing channel restriction if specified. @@ -420,7 +420,7 @@ func (r *RouterBackend) parseQueryRoutesRequest(in *lnrpc.QueryRoutesRequest) ( return routing.NewRouteRequest( sourcePubKey, targetPubKey, amt, in.TimePref, restrictions, - customRecords, routeHintEdges, blindedPathSet.GetPath(), + customRecords, routeHintEdges, blindedPathSet, finalCLTVDelta, ) } @@ -1007,7 +1007,7 @@ func (r *RouterBackend) extractIntentFromSendRequest( if err != nil { return nil, err } - payIntent.BlindedPayment = pathSet.GetPath() + payIntent.BlindedPathSet = pathSet // Replace the target node with the target public key // of the blinded path set. diff --git a/routing/pathfind.go b/routing/pathfind.go index d76c5ea22..ee920c564 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -136,9 +136,8 @@ type finalHopParams struct { // NOTE: If a non-nil blinded path is provided it is assumed to have been // validated by the caller. func newRoute(sourceVertex route.Vertex, - pathEdges []*unifiedEdge, currentHeight uint32, - finalHop finalHopParams, blindedPath *sphinx.BlindedPath) ( - *route.Route, error) { + pathEdges []*unifiedEdge, currentHeight uint32, finalHop finalHopParams, + blindedPathSet *BlindedPaymentPathSet) (*route.Route, error) { var ( hops []*route.Hop @@ -153,8 +152,14 @@ func newRoute(sourceVertex route.Vertex, // backwards below, this next hop gets closer and closer to the // sender of the payment. nextIncomingAmount lnwire.MilliSatoshi + + blindedPath *sphinx.BlindedPath ) + if blindedPathSet != nil { + blindedPath = blindedPathSet.GetPath().BlindedPath + } + pathLength := len(pathEdges) for i := pathLength - 1; i >= 0; i-- { // Now we'll start to calculate the items within the per-hop @@ -437,9 +442,9 @@ type RestrictParams struct { // the payee. Metadata []byte - // BlindedPayment is necessary to determine the hop size of the + // BlindedPaymentPathSet is necessary to determine the hop size of the // last/exit hop. - BlindedPayment *BlindedPayment + BlindedPaymentPathSet *BlindedPaymentPathSet } // PathFindingConfig defines global parameters that control the trade-off in @@ -1365,9 +1370,11 @@ func getProbabilityBasedDist(weight int64, probability float64, func lastHopPayloadSize(r *RestrictParams, finalHtlcExpiry int32, amount lnwire.MilliSatoshi) uint64 { - if r.BlindedPayment != nil { - blindedPath := r.BlindedPayment.BlindedPath.BlindedHops - blindedPoint := r.BlindedPayment.BlindedPath.BlindingPoint + if r.BlindedPaymentPathSet != nil { + paymentPath := r.BlindedPaymentPathSet. + LargestLastHopPayloadPath() + blindedPath := paymentPath.BlindedPath.BlindedHops + blindedPoint := paymentPath.BlindedPath.BlindingPoint encryptedData := blindedPath[len(blindedPath)-1].CipherText finalHop := route.Hop{ diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index f67a8fa59..1200035bb 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -3277,6 +3277,11 @@ func TestBlindedRouteConstruction(t *testing.T) { require.NoError(t, blindedPayment.Validate()) + blindedPathSet, err := NewBlindedPaymentPathSet( + []*BlindedPayment{blindedPayment}, + ) + require.NoError(t, err) + // Generate route hints from our blinded payment and a set of edges // that make up the graph we'll give to route construction. The hints // map is keyed by source node, so we can retrieve our blinded edges @@ -3382,7 +3387,7 @@ func TestBlindedRouteConstruction(t *testing.T) { route, err := newRoute( sourceVertex, edges, currentHeight, finalHopParams, - blindedPath, + blindedPathSet, ) require.NoError(t, err) require.Equal(t, expectedRoute, route) @@ -3409,31 +3414,38 @@ func TestLastHopPayloadSize(t *testing.T) { amtToForward = lnwire.MilliSatoshi(10000) finalHopExpiry int32 = 144 - oneHopBlindedPayment = &BlindedPayment{ - BlindedPath: &sphinx.BlindedPath{ - BlindedHops: []*sphinx.BlindedHopInfo{ - { - CipherText: encrypedData, - }, + oneHopPath = &sphinx.BlindedPath{ + BlindedHops: []*sphinx.BlindedHopInfo{ + { + CipherText: encrypedData, }, - BlindingPoint: blindedPoint, }, + BlindingPoint: blindedPoint, } - twoHopBlindedPayment = &BlindedPayment{ - BlindedPath: &sphinx.BlindedPath{ - BlindedHops: []*sphinx.BlindedHopInfo{ - { - CipherText: encrypedData, - }, - { - CipherText: encrypedData, - }, + + twoHopPath = &sphinx.BlindedPath{ + BlindedHops: []*sphinx.BlindedHopInfo{ + { + CipherText: encrypedData, + }, + { + CipherText: encrypedData, }, - BlindingPoint: blindedPoint, }, + BlindingPoint: blindedPoint, } ) + oneHopBlindedPayment, err := NewBlindedPaymentPathSet( + []*BlindedPayment{{BlindedPath: oneHopPath}}, + ) + require.NoError(t, err) + + twoHopBlindedPayment, err := NewBlindedPaymentPathSet( + []*BlindedPayment{{BlindedPath: twoHopPath}}, + ) + require.NoError(t, err) + testCases := []struct { name string restrictions *RestrictParams @@ -3454,7 +3466,7 @@ func TestLastHopPayloadSize(t *testing.T) { { name: "Blinded final hop introduction point", restrictions: &RestrictParams{ - BlindedPayment: oneHopBlindedPayment, + BlindedPaymentPathSet: oneHopBlindedPayment, }, amount: amtToForward, finalHopExpiry: finalHopExpiry, @@ -3462,7 +3474,7 @@ func TestLastHopPayloadSize(t *testing.T) { { name: "Blinded final hop of a two hop payment", restrictions: &RestrictParams{ - BlindedPayment: twoHopBlindedPayment, + BlindedPaymentPathSet: twoHopBlindedPayment, }, amount: amtToForward, finalHopExpiry: finalHopExpiry, @@ -3490,12 +3502,11 @@ func TestLastHopPayloadSize(t *testing.T) { } var finalHop route.Hop - if tc.restrictions.BlindedPayment != nil { - blindedPath := tc.restrictions.BlindedPayment. - BlindedPath.BlindedHops - - blindedPoint := tc.restrictions.BlindedPayment. - BlindedPath.BlindingPoint + if tc.restrictions.BlindedPaymentPathSet != nil { + path := tc.restrictions.BlindedPaymentPathSet. + LargestLastHopPayloadPath() + blindedPath := path.BlindedPath.BlindedHops + blindedPoint := path.BlindedPath.BlindingPoint //nolint:lll finalHop = route.Hop{ diff --git a/routing/payment_session.go b/routing/payment_session.go index f320ce0dc..00b4ab70e 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -5,7 +5,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btclog" - sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" @@ -206,13 +205,13 @@ func newPaymentSession(p *LightningPayment, selfNode route.Vertex, return nil, err } - if p.BlindedPayment != nil { + if p.BlindedPathSet != nil { if len(edges) != 0 { return nil, fmt.Errorf("cannot have both route hints " + "and blinded path") } - edges, err = p.BlindedPayment.toRouteHints() + edges, err = p.BlindedPathSet.ToRouteHints() if err != nil { return nil, err } @@ -342,7 +341,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, // can split. Split payments to blinded paths won't have // MPP records. if p.payment.PaymentAddr == nil && - p.payment.BlindedPayment == nil { + p.payment.BlindedPathSet == nil { p.log.Debugf("not splitting because payment " + "address is unspecified") @@ -407,11 +406,6 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, return nil, err } - var blindedPath *sphinx.BlindedPath - if p.payment.BlindedPayment != nil { - blindedPath = p.payment.BlindedPayment.BlindedPath - } - // With the next candidate path found, we'll attempt to turn // this into a route by applying the time-lock and fee // requirements. @@ -424,7 +418,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, records: p.payment.DestCustomRecords, paymentAddr: p.payment.PaymentAddr, metadata: p.payment.Metadata, - }, blindedPath, + }, p.payment.BlindedPathSet, ) if err != nil { return nil, err diff --git a/routing/router.go b/routing/router.go index 5551c5345..1bf6a56c7 100644 --- a/routing/router.go +++ b/routing/router.go @@ -477,10 +477,10 @@ type RouteRequest struct { // in blinded payment. FinalExpiry uint16 - // BlindedPayment contains an optional blinded path and parameters - // used to reach a target node via a blinded path. This field is + // BlindedPathSet contains a set of optional blinded paths and + // parameters used to reach a target node blinded paths. This field is // mutually exclusive with the Target field. - BlindedPayment *BlindedPayment + BlindedPathSet *BlindedPaymentPathSet } // RouteHints is an alias type for a set of route hints, with the source node @@ -494,7 +494,7 @@ type RouteHints map[route.Vertex][]AdditionalEdge func NewRouteRequest(source route.Vertex, target *route.Vertex, amount lnwire.MilliSatoshi, timePref float64, restrictions *RestrictParams, customRecords record.CustomSet, - routeHints RouteHints, blindedPayment *BlindedPayment, + routeHints RouteHints, blindedPathSet *BlindedPaymentPathSet, finalExpiry uint16) (*RouteRequest, error) { var ( @@ -504,11 +504,8 @@ func NewRouteRequest(source route.Vertex, target *route.Vertex, err error ) - if blindedPayment != nil { - if err := blindedPayment.Validate(); err != nil { - return nil, fmt.Errorf("invalid blinded payment: %w", - err) - } + if blindedPathSet != nil { + blindedPayment := blindedPathSet.GetPath() introVertex := route.NewVertex( blindedPayment.BlindedPath.IntroductionPoint, @@ -539,13 +536,13 @@ func NewRouteRequest(source route.Vertex, target *route.Vertex, requestExpiry = blindedPayment.CltvExpiryDelta } - requestHints, err = blindedPayment.toRouteHints() + requestHints, err = blindedPathSet.ToRouteHints() if err != nil { return nil, err } } - requestTarget, err := getTargetNode(target, blindedPayment) + requestTarget, err := getTargetNode(target, blindedPathSet) if err != nil { return nil, err } @@ -559,15 +556,15 @@ func NewRouteRequest(source route.Vertex, target *route.Vertex, CustomRecords: customRecords, RouteHints: requestHints, FinalExpiry: requestExpiry, - BlindedPayment: blindedPayment, + BlindedPathSet: blindedPathSet, }, nil } -func getTargetNode(target *route.Vertex, blindedPayment *BlindedPayment) ( - route.Vertex, error) { +func getTargetNode(target *route.Vertex, + blindedPathSet *BlindedPaymentPathSet) (route.Vertex, error) { var ( - blinded = blindedPayment != nil + blinded = blindedPathSet != nil targetSet = target != nil ) @@ -576,6 +573,8 @@ func getTargetNode(target *route.Vertex, blindedPayment *BlindedPayment) ( return route.Vertex{}, ErrTargetAndBlinded case blinded: + blindedPayment := blindedPathSet.GetPath() + // If we're dealing with an edge-case blinded path that just // has an introduction node (first hop expected to be the intro // hop), then we return the unblinded introduction node as our @@ -597,16 +596,6 @@ func getTargetNode(target *route.Vertex, blindedPayment *BlindedPayment) ( } } -// blindedPath returns the request's blinded path, which is set if the payment -// is to a blinded route. -func (r *RouteRequest) blindedPath() *sphinx.BlindedPath { - if r.BlindedPayment == nil { - return nil - } - - return r.BlindedPayment.BlindedPath -} - // FindRoute attempts to query the ChannelRouter for the optimum path to a // particular target destination to which it is able to send `amt` after // factoring in channel capacities and cumulative fees along the route. @@ -664,7 +653,7 @@ func (r *ChannelRouter) FindRoute(req *RouteRequest) (*route.Route, float64, totalAmt: req.Amount, cltvDelta: req.FinalExpiry, records: req.CustomRecords, - }, req.blindedPath(), + }, req.BlindedPathSet, ) if err != nil { return nil, 0, err @@ -926,14 +915,10 @@ type LightningPayment struct { // BlindedPayment field. RouteHints [][]zpay32.HopHint - // BlindedPayment holds the information about a blinded path to the - // payment recipient. This is mutually exclusive to the RouteHints + // BlindedPathSet holds the information about a set of blinded paths to + // the payment recipient. This is mutually exclusive to the RouteHints // field. - // - // NOTE: a recipient may provide multiple blinded payment paths in the - // same invoice. Currently, LND will only attempt to use the first one. - // A future PR will handle multiple blinded payment paths. - BlindedPayment *BlindedPayment + BlindedPathSet *BlindedPaymentPathSet // OutgoingChannelIDs is the list of channels that are allowed for the // first hop. If nil, any channel may be used. diff --git a/routing/router_test.go b/routing/router_test.go index f631669a9..0ffd75db2 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -2223,11 +2223,6 @@ func TestNewRouteRequest(t *testing.T) { finalExpiry: unblindedCltv, err: ErrExpiryAndBlinded, }, - { - name: "invalid blinded payment", - blindedPayment: &BlindedPayment{}, - err: ErrNoBlindedPath, - }, } for _, testCase := range testCases { @@ -2236,9 +2231,19 @@ func TestNewRouteRequest(t *testing.T) { t.Run(testCase.name, func(t *testing.T) { t.Parallel() + var blindedPathInfo *BlindedPaymentPathSet + if testCase.blindedPayment != nil { + blindedPathInfo, err = NewBlindedPaymentPathSet( + []*BlindedPayment{ + testCase.blindedPayment, + }, + ) + require.NoError(t, err) + } + req, err := NewRouteRequest( source, testCase.target, 1000, 0, nil, nil, - testCase.routeHints, testCase.blindedPayment, + testCase.routeHints, blindedPathInfo, testCase.finalExpiry, ) require.ErrorIs(t, err, testCase.err) diff --git a/rpcserver.go b/rpcserver.go index ff7aa613a..86bc6415c 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -5105,7 +5105,7 @@ type rpcPaymentIntent struct { paymentAddr *[32]byte payReq []byte metadata []byte - blindedPayment *routing.BlindedPayment + blindedPathSet *routing.BlindedPaymentPathSet destCustomRecords record.CustomSet @@ -5248,7 +5248,7 @@ func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPayme if err != nil { return payIntent, err } - payIntent.blindedPayment = pathSet.GetPath() + payIntent.blindedPathSet = pathSet // Replace the destination node with the target public // key of the blinded path set. @@ -5417,7 +5417,7 @@ func (r *rpcServer) dispatchPaymentIntent( DestFeatures: payIntent.destFeatures, PaymentAddr: payIntent.paymentAddr, Metadata: payIntent.metadata, - BlindedPayment: payIntent.blindedPayment, + BlindedPathSet: payIntent.blindedPathSet, // Don't enable multi-part payments on the main rpc. // Users need to use routerrpc for that.