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",
|
||||
Usage: "use mission control probabilities",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
cli.StringSliceFlag{
|
||||
Name: "outgoing_chan_id",
|
||||
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{
|
||||
Name: "ignore_pair",
|
||||
@@ -1275,12 +1276,25 @@ func queryRoutes(ctx *cli.Context) error {
|
||||
FinalCltvDelta: int32(ctx.Int("final_cltv_delta")),
|
||||
UseMissionControl: ctx.Bool("use_mc"),
|
||||
CltvLimit: uint32(ctx.Uint64(cltvLimitFlag.Name)),
|
||||
OutgoingChanId: ctx.Uint64("outgoing_chan_id"),
|
||||
TimePref: ctx.Float64(timePrefFlag.Name),
|
||||
IgnoredPairs: ignoredPairs,
|
||||
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 len(blindedRoutes) > 0 {
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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.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
|
||||
## 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;
|
||||
|
||||
/*
|
||||
The channel id of the channel that must be taken to the first hop. If zero,
|
||||
any channel may be used.
|
||||
Deprecated, use outgoing_chan_ids. The channel id of the channel that must
|
||||
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.
|
||||
@@ -3196,6 +3196,12 @@ message QueryRoutesRequest {
|
||||
only, to 1 to optimize for reliability only or a value inbetween for a mix.
|
||||
*/
|
||||
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 {
|
||||
|
||||
@@ -1459,7 +1459,7 @@
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
@@ -1520,6 +1520,18 @@
|
||||
"required": false,
|
||||
"type": "number",
|
||||
"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": [
|
||||
@@ -1629,7 +1641,7 @@
|
||||
"outgoing_chan_id": {
|
||||
"type": "string",
|
||||
"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": {
|
||||
"type": "string",
|
||||
@@ -1663,6 +1675,14 @@
|
||||
"type": "number",
|
||||
"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."
|
||||
},
|
||||
"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,
|
||||
}
|
||||
|
||||
// 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}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user