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 Jan 31, 2023
1 parent 03bb9bf commit 47029e2
Showing 1 changed file with 143 additions and 0 deletions.
143 changes: 143 additions & 0 deletions cmd/lncli/cmd_payments.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,45 @@ var (
Name: "time_pref",
Usage: "(optional) expresses time preference (range -1 to 1)",
}

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

blindingPointFlag = cli.StringFlag{
Name: "blinding_point",
Usage: "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: "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: "the aggregate base fee for the blinded portion of " +
"the route, expressed in msat",
}

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

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

// paymentFlags returns common flags for sendpayment and payinvoice.
Expand Down Expand Up @@ -1050,6 +1089,12 @@ var queryRoutesCommand = cli.Command{
},
timePrefFlag,
cltvLimitFlag,
introductionNodeFlag,
blindingPointFlag,
blindedHopsFlag,
blindedBaseFlag,
blindedPPMFlag,
blindedCLTVFlag,
},
Action: actionDecorator(queryRoutes),
}
Expand All @@ -1070,9 +1115,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 @@ -1119,6 +1170,11 @@ func queryRoutes(ctx *cli.Context) error {
}
}

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

req := &lnrpc.QueryRoutesRequest{
PubKey: dest,
Amt: amt,
Expand All @@ -1129,6 +1185,7 @@ func queryRoutes(ctx *cli.Context) error {
OutgoingChanId: ctx.Uint64("outgoing_chanid"),
TimePref: ctx.Float64(timePrefFlag.Name),
IgnoredPairs: ignoredPairs,
BlindedPath: blindedRoute,
}

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

func parseBlindedPaymentParameters(ctx *cli.Context) (*lnrpc.BlindedPayment,
error) {

// If none of the blinded route params are set as we expect, then we
// don't need to return anything.
haveBlinded := ctx.IsSet(blindingPointFlag.Name) ||
ctx.IsSet(blindedHopsFlag.Name) ||
ctx.IsSet(introductionNodeFlag.Name) ||
ctx.IsSet(blindedBaseFlag.Name) ||
ctx.IsSet(blindedPPMFlag.Name) ||
ctx.IsSet(blindedCLTVFlag.Name)

if !haveBlinded {
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.BlindedPayment{
Route: &lnrpc.BlindedRoute{
IntroductionNode: introNode[:],
BlindingPoint: blindingPoint[:],
BlindedHops: make(
[]*lnrpc.BlindedHop, len(blindedHops),
),
},
RelayParameters: &lnrpc.BlindedRelay{
AggregateBaseFeeMsat: ctx.Uint64(
blindedBaseFlag.Name,
),
AggregateProportionalFeePpm: ctx.Uint64(
blindedPPMFlag.Name,
),
TotalCltvDelta: uint32(ctx.Uint64(
blindedCLTVFlag.Name,
)),
},
RelayConstraints: &lnrpc.BlindedConstraints{
CltvLimit: uint32(ctx.Uint(cltvLimitFlag.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.Route.BlindedHops[i] = &lnrpc.BlindedHop{
BlindedNode: hop[:],
EncryptedData: data,
}
}

return 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 47029e2

Please sign in to comment.