Skip to content

Commit

Permalink
lncli: add blinded route cli flags to query routes
Browse files Browse the repository at this point in the history
Note: This commit can be dropped before merge, it's mostly added
to make the PR easier to manually test against other
implementations that have bolt 12 invoices implemented already!
  • Loading branch information
carlaKC committed Aug 29, 2023
1 parent cf5466e commit bc72859
Showing 1 changed file with 144 additions and 9 deletions.
153 changes: 144 additions & 9 deletions cmd/lncli/cmd_payments.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,47 @@ var (
Name: "time_pref",
Usage: "(optional) expresses time preference (range -1 to 1)",
}

introductionNodeFlag = cli.StringFlag{
Name: "introduction_node",
Usage: "(blinded paths) the hex encoded, cleartext node ID " +
"of the node to use for queries to a blinded route",
}

blindingPointFlag = cli.StringFlag{
Name: "blinding_point",
Usage: "(blinded paths) the hex encoded blinding point to " +
"use if querying a route to a blinded path, this " +
"value *must* be set for queries to a blinded path",
}

blindedHopsFlag = cli.StringSliceFlag{
Name: "blinded_hops",
Usage: "(blinded paths) the blinded hops to include in the " +
"query, formatted as <blinded_node_id>:" +
"<hex_encrypted_data>. These hops must be provided " +
"*in order* starting with the introduction point and " +
"ending with the receiving node",
}

blindedBaseFlag = cli.Uint64Flag{
Name: "blinded_base_fee",
Usage: "(blinded paths) the aggregate base fee for the " +
"blinded portion of the route, expressed in msat",
}

blindedPPMFlag = cli.Uint64Flag{
Name: "blinded_ppm_fee",
Usage: "(blinded paths) the aggregate proportional fee for " +
"the blinded portion of the route, expressed in " +
"parts per million",
}

blindedCLTVFlag = cli.Uint64Flag{
Name: "blinded_cltv",
Usage: "(blinded paths) the total cltv delay for the " +
"blinded portion of the route",
}
)

// paymentFlags returns common flags for sendpayment and payinvoice.
Expand Down Expand Up @@ -1054,6 +1095,12 @@ var queryRoutesCommand = cli.Command{
},
timePrefFlag,
cltvLimitFlag,
introductionNodeFlag,
blindingPointFlag,
blindedHopsFlag,
blindedBaseFlag,
blindedPPMFlag,
blindedCLTVFlag,
},
Action: actionDecorator(queryRoutes),
}
Expand All @@ -1074,9 +1121,15 @@ func queryRoutes(ctx *cli.Context) error {
switch {
case ctx.IsSet("dest"):
dest = ctx.String("dest")

case args.Present():
dest = args.First()
args = args.Tail()

// If we have a blinded path set, we don't have to specify a
// destination.
case ctx.IsSet(introductionNodeFlag.Name):

default:
return fmt.Errorf("dest argument missing")
}
Expand Down Expand Up @@ -1123,16 +1176,22 @@ func queryRoutes(ctx *cli.Context) error {
}
}

blindedRoutes, err := parseBlindedPaymentParameters(ctx)
if err != nil {
return err
}

req := &lnrpc.QueryRoutesRequest{
PubKey: dest,
Amt: amt,
FeeLimit: feeLimit,
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,
PubKey: dest,
Amt: amt,
FeeLimit: feeLimit,
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,
}

route, err := client.QueryRoutes(ctxc, req)
Expand All @@ -1144,6 +1203,82 @@ func queryRoutes(ctx *cli.Context) error {
return nil
}

func parseBlindedPaymentParameters(ctx *cli.Context) (
[]*lnrpc.BlindedPaymentPath, error) {

// Return nil if we don't have a blinding set, as we don't have a
// blinded path.
if !ctx.IsSet(blindingPointFlag.Name) {
return nil, nil
}

// If any one of our blinding related flags is set, we expect the
// full set to be set and we'll error our accordingly.
introNode, err := route.NewVertexFromStr(
ctx.String(introductionNodeFlag.Name),
)
if err != nil {
return nil, fmt.Errorf("decode introduction node: %w", err)
}

blindingPoint, err := route.NewVertexFromStr(ctx.String(
blindingPointFlag.Name,
))
if err != nil {
return nil, fmt.Errorf("decode blinding point: %w", err)
}

blindedHops := ctx.StringSlice(blindedHopsFlag.Name)

pmt := &lnrpc.BlindedPaymentPath{
BlindedPath: &lnrpc.BlindedPath{
IntroductionNode: introNode[:],
BlindingPoint: blindingPoint[:],
BlindedHops: make(
[]*lnrpc.BlindedHop, len(blindedHops),
),
},
BaseFeeMsat: ctx.Uint64(
blindedBaseFlag.Name,
),
ProportionalFeeMsat: ctx.Uint64(
blindedPPMFlag.Name,
),
TotalCltvDelta: uint32(ctx.Uint64(
blindedCLTVFlag.Name,
)),
}

for i, hop := range blindedHops {
parts := strings.Split(hop, ":")
if len(parts) != 2 {
return nil, fmt.Errorf("blinded hops should be "+
"expressed as "+
"blinded_node_id:hex_encrypted_data, got: %v",
hop)
}

hop, err := route.NewVertexFromStr(parts[0])
if err != nil {
return nil, fmt.Errorf("hop: %v node: %w", i, err)
}

data, err := hex.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("hop: %v data: %w", i, err)
}

pmt.BlindedPath.BlindedHops[i] = &lnrpc.BlindedHop{
BlindedNode: hop[:],
EncryptedData: data,
}
}

return []*lnrpc.BlindedPaymentPath{
pmt,
}, nil
}

// retrieveFeeLimitLegacy retrieves the fee limit based on the different fee
// limit flags passed. This function will eventually disappear in favor of
// retrieveFeeLimit and the new payment rpc.
Expand Down

0 comments on commit bc72859

Please sign in to comment.