diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index ab53b4ba9..34cd7047e 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -413,8 +413,18 @@ func (r *RouterBackend) parseQueryRoutesRequest(in *lnrpc.QueryRoutesRequest) ( BlindedPaymentPathSet: blindedPathSet, } - // Pass along an outgoing channel restriction if specified. - if in.OutgoingChanId != 0 { + // We set the outgoing channel restrictions if the user provides a + // list of channel ids. We also handle the case where the user + // provides the deprecated `OutgoingChanId` field. + switch { + case len(in.OutgoingChanIds) > 0 && in.OutgoingChanId != 0: + return nil, errors.New("outgoing_chan_id and " + + "outgoing_chan_ids cannot both be set") + + case len(in.OutgoingChanIds) > 0: + restrictions.OutgoingChannelIDs = in.OutgoingChanIds + + case in.OutgoingChanId != 0: restrictions.OutgoingChannelIDs = []uint64{in.OutgoingChanId} } diff --git a/lnrpc/routerrpc/router_backend_test.go b/lnrpc/routerrpc/router_backend_test.go index 608f734bb..080fc7531 100644 --- a/lnrpc/routerrpc/router_backend_test.go +++ b/lnrpc/routerrpc/router_backend_test.go @@ -34,25 +34,39 @@ var ( node2 = route.Vertex{11} ) +var ( + singleChanID = "singleChanID" + multiChanID = "multiChanID" + bothChanIds = "bothChanIds" +) + // TestQueryRoutes asserts that query routes rpc parameters are properly parsed // and passed onto path finding. func TestQueryRoutes(t *testing.T) { t.Run("no mission control", func(t *testing.T) { - testQueryRoutes(t, false, false, true) + testQueryRoutes(t, false, false, true, singleChanID) }) t.Run("no mission control and msat", func(t *testing.T) { - testQueryRoutes(t, false, true, true) + testQueryRoutes(t, false, true, true, singleChanID) }) t.Run("with mission control", func(t *testing.T) { - testQueryRoutes(t, true, false, true) + testQueryRoutes(t, true, false, true, singleChanID) }) t.Run("no mission control bad cltv limit", func(t *testing.T) { - testQueryRoutes(t, false, false, false) + testQueryRoutes(t, false, false, false, singleChanID) + }) + + t.Run("both outgoing chan id and chan ids", func(t *testing.T) { + testQueryRoutes(t, true, false, true, bothChanIds) + }) + + t.Run("multiple outgoing chan ids", func(t *testing.T) { + testQueryRoutes(t, false, true, true, multiChanID) }) } func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool, - setTimelock bool) { + setTimelock bool, outgoingChanConfig string) { ignoreNodeBytes, err := hex.DecodeString(ignoreNodeKey) if err != nil { @@ -68,8 +82,9 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool, } var ( - lastHop = route.Vertex{64} - outgoingChan = uint64(383322) + lastHop = route.Vertex{64} + outgoingChan = uint64(383322) + outgoingChanIds = []uint64{383322, 383323} ) hintNode, err := route.NewVertexFromStr(hintNodeKey) @@ -102,7 +117,6 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool, }}, UseMissionControl: useMissionControl, LastHopPubkey: lastHop[:], - OutgoingChanId: outgoingChan, DestFeatures: []lnrpc.FeatureBit{lnrpc.FeatureBit_MPP_OPT}, RouteHints: rpcRouteHints, } @@ -124,6 +138,18 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool, } } + switch outgoingChanConfig { + case singleChanID: + request.OutgoingChanId = outgoingChan + + case multiChanID: + request.OutgoingChanIds = outgoingChanIds + + case bothChanIds: + request.OutgoingChanId = outgoingChan + request.OutgoingChanIds = outgoingChanIds + } + findRoute := func(req *routing.RouteRequest) (*route.Route, float64, error) { @@ -165,8 +191,18 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool, t.Fatal("unexpected last hop") } - if restrictions.OutgoingChannelIDs[0] != outgoingChan { - t.Fatal("unexpected outgoing channel id") + switch outgoingChanConfig { + case singleChanID: + require.Equal( + t, restrictions.OutgoingChannelIDs, + []uint64{outgoingChan}, + ) + + case multiChanID: + require.Equal( + t, restrictions.OutgoingChannelIDs, + outgoingChanIds, + ) } if !restrictions.DestFeatures.HasFeature(lnwire.MPPOptional) { @@ -230,6 +266,13 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool, resp, err := backend.QueryRoutes(context.Background(), request) + // If we're using both OutgoingChanId and OutgoingChanIds, we should get + // an error. + if outgoingChanConfig == bothChanIds { + require.Error(t, err) + return + } + // If no MaxTotalTimelock was set for the QueryRoutes request, make // sure an error was returned. if !setTimelock {