Merge pull request #10057 from Abdulkbk/outchanids-to-queryroutes
routerrpc: accept mutliple outgoing chan ids in queryroutes
This commit is contained in:
7 files changed
+1874
-1757
No files matched your search
@@ -1158,10 +1158,11 @@ var queryRoutesCommand = cli.Command{
|
|||||||
Name: "use_mc",
|
Name: "use_mc",
|
||||||
Usage: "use mission control probabilities",
|
Usage: "use mission control probabilities",
|
||||||
},
|
},
|
||||||
cli.Uint64Flag{
|
cli.StringSliceFlag{
|
||||||
Name: "outgoing_chan_id",
|
Name: "outgoing_chan_id",
|
||||||
Usage: "(optional) the channel id of the channel " +
|
Usage: "(optional) the channel id of the channel " +
|
||||||
"that must be taken to the first hop",
|
"to use as the first hop. This flag can be " +
|
||||||
|
"specified multiple times in the same command.",
|
||||||
},
|
},
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
Name: "ignore_pair",
|
Name: "ignore_pair",
|
||||||
@@ -1275,12 +1276,25 @@ func queryRoutes(ctx *cli.Context) error {
|
|||||||
FinalCltvDelta: int32(ctx.Int("final_cltv_delta")),
|
FinalCltvDelta: int32(ctx.Int("final_cltv_delta")),
|
||||||
UseMissionControl: ctx.Bool("use_mc"),
|
UseMissionControl: ctx.Bool("use_mc"),
|
||||||
CltvLimit: uint32(ctx.Uint64(cltvLimitFlag.Name)),
|
CltvLimit: uint32(ctx.Uint64(cltvLimitFlag.Name)),
|
||||||
OutgoingChanId: ctx.Uint64("outgoing_chan_id"),
|
|
||||||
TimePref: ctx.Float64(timePrefFlag.Name),
|
TimePref: ctx.Float64(timePrefFlag.Name),
|
||||||
IgnoredPairs: ignoredPairs,
|
IgnoredPairs: ignoredPairs,
|
||||||
BlindedPaymentPaths: blindedRoutes,
|
BlindedPaymentPaths: blindedRoutes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outgoingChanIds := ctx.StringSlice("outgoing_chan_id")
|
||||||
|
if len(outgoingChanIds) != 0 {
|
||||||
|
req.OutgoingChanIds = make([]uint64, len(outgoingChanIds))
|
||||||
|
for i, chanID := range outgoingChanIds {
|
||||||
|
id, err := strconv.ParseUint(chanID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid outgoing_chan_id "+
|
||||||
|
"argument: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.OutgoingChanIds[i] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.IsSet("route_hints") {
|
if ctx.IsSet("route_hints") {
|
||||||
if len(blindedRoutes) > 0 {
|
if len(blindedRoutes) > 0 {
|
||||||
return fmt.Errorf("--route_hints should not be used " +
|
return fmt.Errorf("--route_hints should not be used " +
|
||||||
|
|||||||
@@ -122,6 +122,10 @@ circuit. The indices are only available for forwarding events saved after v0.20.
|
|||||||
to the database.
|
to the database.
|
||||||
|
|
||||||
## lncli Updates
|
## lncli Updates
|
||||||
|
* Previously, users could only specify one `outgoing_chan_id` when calling the
|
||||||
|
`lncli queryroutes` or the QueryRoutes RPC. With this change, multiple
|
||||||
|
`outgoing_chan_id` can be passed during the call.
|
||||||
|
|
||||||
|
|
||||||
## Code Health
|
## Code Health
|
||||||
|
|
||||||
@@ -155,6 +159,10 @@ reader of a payment request.
|
|||||||
| [`routerrpc.SendPaymentV2`](https://lightning.engineering/api-docs/api/lnd/router/send-payment-v2/index.html) | ✅ | `POST` | `/v2/router/send` |
|
| [`routerrpc.SendPaymentV2`](https://lightning.engineering/api-docs/api/lnd/router/send-payment-v2/index.html) | ✅ | `POST` | `/v2/router/send` |
|
||||||
| [`routerrpc.TrackPaymentV2`](https://lightning.engineering/api-docs/api/lnd/router/track-payment-v2/) | ✅ | `GET` | `/v2/router/track/{payment_hash}` |
|
| [`routerrpc.TrackPaymentV2`](https://lightning.engineering/api-docs/api/lnd/router/track-payment-v2/) | ✅ | `GET` | `/v2/router/track/{payment_hash}` |
|
||||||
|
|
||||||
|
* We are deprecating `OutgoingChanId` in favour of `OutgoingChanIds` in the
|
||||||
|
`QueryRoutes` RPC. This [transition](https://github.com/lightningnetwork/lnd/pull/10057) allows us to specify more than one outgoing channel
|
||||||
|
the pathfinder should use when finding a route.
|
||||||
|
|
||||||
# Technical and Architectural Updates
|
# Technical and Architectural Updates
|
||||||
## BOLT Spec Updates
|
## BOLT Spec Updates
|
||||||
|
|
||||||
|
|||||||
+1753
-1737
File diff suppressed because it is too large.
Load diff
@@ -3158,10 +3158,10 @@ message QueryRoutesRequest {
|
|||||||
map<uint64, bytes> dest_custom_records = 13;
|
map<uint64, bytes> dest_custom_records = 13;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The channel id of the channel that must be taken to the first hop. If zero,
|
Deprecated, use outgoing_chan_ids. The channel id of the channel that must
|
||||||
any channel may be used.
|
be taken to the first hop. If zero, any channel may be used.
|
||||||
*/
|
*/
|
||||||
uint64 outgoing_chan_id = 14 [jstype = JS_STRING];
|
uint64 outgoing_chan_id = 14 [jstype = JS_STRING, deprecated = true];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The pubkey of the last hop of the route. If empty, any hop may be used.
|
The pubkey of the last hop of the route. If empty, any hop may be used.
|
||||||
@@ -3196,6 +3196,12 @@ message QueryRoutesRequest {
|
|||||||
only, to 1 to optimize for reliability only or a value inbetween for a mix.
|
only, to 1 to optimize for reliability only or a value inbetween for a mix.
|
||||||
*/
|
*/
|
||||||
double time_pref = 18;
|
double time_pref = 18;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The channel ids of the channels allowed for the first hop. If empty, any
|
||||||
|
channel may be used.
|
||||||
|
*/
|
||||||
|
repeated uint64 outgoing_chan_ids = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
message NodePair {
|
message NodePair {
|
||||||
|
|||||||
@@ -1459,7 +1459,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "outgoing_chan_id",
|
"name": "outgoing_chan_id",
|
||||||
"description": "The channel id of the channel that must be taken to the first hop. If zero,\nany channel may be used.",
|
"description": "Deprecated, use outgoing_chan_ids. The channel id of the channel that must\nbe taken to the first hop. If zero, any channel may be used.",
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"required": false,
|
"required": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -1520,6 +1520,18 @@
|
|||||||
"required": false,
|
"required": false,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"format": "double"
|
"format": "double"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "outgoing_chan_ids",
|
||||||
|
"description": "The channel ids of the channels allowed for the first hop. If empty, any\nchannel may be used.",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uint64"
|
||||||
|
},
|
||||||
|
"collectionFormat": "multi"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -1629,7 +1641,7 @@
|
|||||||
"outgoing_chan_id": {
|
"outgoing_chan_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uint64",
|
"format": "uint64",
|
||||||
"description": "The channel id of the channel that must be taken to the first hop. If zero,\nany channel may be used."
|
"description": "Deprecated, use outgoing_chan_ids. The channel id of the channel that must\nbe taken to the first hop. If zero, any channel may be used."
|
||||||
},
|
},
|
||||||
"last_hop_pubkey": {
|
"last_hop_pubkey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -1663,6 +1675,14 @@
|
|||||||
"type": "number",
|
"type": "number",
|
||||||
"format": "double",
|
"format": "double",
|
||||||
"description": "The time preference for this payment. Set to -1 to optimize for fees\nonly, to 1 to optimize for reliability only or a value inbetween for a mix."
|
"description": "The time preference for this payment. Set to -1 to optimize for fees\nonly, to 1 to optimize for reliability only or a value inbetween for a mix."
|
||||||
|
},
|
||||||
|
"outgoing_chan_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uint64"
|
||||||
|
},
|
||||||
|
"description": "The channel ids of the channels allowed for the first hop. If empty, any\nchannel may be used."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -413,8 +413,18 @@ func (r *RouterBackend) parseQueryRoutesRequest(in *lnrpc.QueryRoutesRequest) (
|
|||||||
BlindedPaymentPathSet: blindedPathSet,
|
BlindedPaymentPathSet: blindedPathSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass along an outgoing channel restriction if specified.
|
// We set the outgoing channel restrictions if the user provides a
|
||||||
if in.OutgoingChanId != 0 {
|
// 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}
|
restrictions.OutgoingChannelIDs = []uint64{in.OutgoingChanId}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,25 +34,39 @@ var (
|
|||||||
node2 = route.Vertex{11}
|
node2 = route.Vertex{11}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
singleChanID = "singleChanID"
|
||||||
|
multiChanID = "multiChanID"
|
||||||
|
bothChanIds = "bothChanIds"
|
||||||
|
)
|
||||||
|
|
||||||
// TestQueryRoutes asserts that query routes rpc parameters are properly parsed
|
// TestQueryRoutes asserts that query routes rpc parameters are properly parsed
|
||||||
// and passed onto path finding.
|
// and passed onto path finding.
|
||||||
func TestQueryRoutes(t *testing.T) {
|
func TestQueryRoutes(t *testing.T) {
|
||||||
t.Run("no mission control", func(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) {
|
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) {
|
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) {
|
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,
|
func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool,
|
||||||
setTimelock bool) {
|
setTimelock bool, outgoingChanConfig string) {
|
||||||
|
|
||||||
ignoreNodeBytes, err := hex.DecodeString(ignoreNodeKey)
|
ignoreNodeBytes, err := hex.DecodeString(ignoreNodeKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -68,8 +82,9 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
lastHop = route.Vertex{64}
|
lastHop = route.Vertex{64}
|
||||||
outgoingChan = uint64(383322)
|
outgoingChan = uint64(383322)
|
||||||
|
outgoingChanIds = []uint64{383322, 383323}
|
||||||
)
|
)
|
||||||
|
|
||||||
hintNode, err := route.NewVertexFromStr(hintNodeKey)
|
hintNode, err := route.NewVertexFromStr(hintNodeKey)
|
||||||
@@ -102,7 +117,6 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool,
|
|||||||
}},
|
}},
|
||||||
UseMissionControl: useMissionControl,
|
UseMissionControl: useMissionControl,
|
||||||
LastHopPubkey: lastHop[:],
|
LastHopPubkey: lastHop[:],
|
||||||
OutgoingChanId: outgoingChan,
|
|
||||||
DestFeatures: []lnrpc.FeatureBit{lnrpc.FeatureBit_MPP_OPT},
|
DestFeatures: []lnrpc.FeatureBit{lnrpc.FeatureBit_MPP_OPT},
|
||||||
RouteHints: rpcRouteHints,
|
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,
|
findRoute := func(req *routing.RouteRequest) (*route.Route, float64,
|
||||||
error) {
|
error) {
|
||||||
|
|
||||||
@@ -165,8 +191,18 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool,
|
|||||||
t.Fatal("unexpected last hop")
|
t.Fatal("unexpected last hop")
|
||||||
}
|
}
|
||||||
|
|
||||||
if restrictions.OutgoingChannelIDs[0] != outgoingChan {
|
switch outgoingChanConfig {
|
||||||
t.Fatal("unexpected outgoing channel id")
|
case singleChanID:
|
||||||
|
require.Equal(
|
||||||
|
t, restrictions.OutgoingChannelIDs,
|
||||||
|
[]uint64{outgoingChan},
|
||||||
|
)
|
||||||
|
|
||||||
|
case multiChanID:
|
||||||
|
require.Equal(
|
||||||
|
t, restrictions.OutgoingChannelIDs,
|
||||||
|
outgoingChanIds,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !restrictions.DestFeatures.HasFeature(lnwire.MPPOptional) {
|
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)
|
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
|
// If no MaxTotalTimelock was set for the QueryRoutes request, make
|
||||||
// sure an error was returned.
|
// sure an error was returned.
|
||||||
if !setTimelock {
|
if !setTimelock {
|
||||||
|
|||||||
Reference in new issue
Block a user