Skip to content

Commit 8cf6aca

Browse files
committed
lncli: Add --route_hints flag to sendpayment, queryroutes (fixes lightningnetwork#6601)
Adds an integration test to verify SendPayment successfully uses invoice route hints for payments involving private channels.
1 parent 06f1ef4 commit 8cf6aca

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

cmd/commands/cmd_payments.go

+67
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"crypto/rand"
77
"encoding/hex"
8+
"encoding/json"
89
"errors"
910
"fmt"
1011
"io"
@@ -254,6 +255,13 @@ var SendPaymentCommand = cli.Command{
254255
Name: "keysend",
255256
Usage: "will generate a pre-image and encode it in the sphinx packet, a dest must be set [experimental]",
256257
},
258+
cli.StringFlag{
259+
Name: "route_hints",
260+
Usage: "route hints to reach the destination, " +
261+
"encoded as a JSON string. " +
262+
"See the RouteHint struct in the RPC " +
263+
"definition for the full format.",
264+
},
257265
),
258266
Action: SendPayment,
259267
}
@@ -362,6 +370,19 @@ func SendPayment(ctx *cli.Context) error {
362370

363371
req.PaymentAddr = payAddr
364372

373+
if ctx.IsSet("route_hints") {
374+
routeHintsJSON := ctx.String("route_hints")
375+
var routeHints []*lnrpc.RouteHint
376+
377+
err := json.Unmarshal([]byte(routeHintsJSON), &routeHints)
378+
if err != nil {
379+
return fmt.Errorf("error unmarshaling route_hints "+
380+
"json: %w", err)
381+
}
382+
383+
req.RouteHints = routeHints
384+
}
385+
365386
return SendPaymentRequest(
366387
ctx, req, conn, conn, routerRPCSendPayment,
367388
)
@@ -473,6 +494,19 @@ func SendPayment(ctx *cli.Context) error {
473494

474495
req.PaymentAddr = payAddr
475496

497+
if ctx.IsSet("route_hints") {
498+
routeHintsJSON := ctx.String("route_hints")
499+
var routeHints []*lnrpc.RouteHint
500+
501+
err := json.Unmarshal([]byte(routeHintsJSON), &routeHints)
502+
if err != nil {
503+
return fmt.Errorf("error unmarshaling route_hints "+
504+
"json: %w", err)
505+
}
506+
507+
req.RouteHints = routeHints
508+
}
509+
476510
return SendPaymentRequest(ctx, req, conn, conn, routerRPCSendPayment)
477511
}
478512

@@ -928,6 +962,19 @@ func payInvoice(ctx *cli.Context) error {
928962
Cancelable: ctx.Bool(cancelableFlag.Name),
929963
}
930964

965+
if ctx.IsSet("route_hints") {
966+
routeHintsJSON := ctx.String("route_hints")
967+
var routeHints []*lnrpc.RouteHint
968+
969+
err := json.Unmarshal([]byte(routeHintsJSON), &routeHints)
970+
if err != nil {
971+
return fmt.Errorf("error unmarshaling route_hints "+
972+
"json: %w", err)
973+
}
974+
975+
req.RouteHints = routeHints
976+
}
977+
931978
return SendPaymentRequest(ctx, req, conn, conn, routerRPCSendPayment)
932979
}
933980

@@ -1154,6 +1201,13 @@ var queryRoutesCommand = cli.Command{
11541201
blindedBaseFlag,
11551202
blindedPPMFlag,
11561203
blindedCLTVFlag,
1204+
cli.StringFlag{
1205+
Name: "route_hints",
1206+
Usage: "route hints to reach the destination, " +
1207+
"encoded as a JSON string. " +
1208+
"See the RouteHint struct in the RPC " +
1209+
"definition for the full format.",
1210+
},
11571211
},
11581212
Action: actionDecorator(queryRoutes),
11591213
}
@@ -1248,6 +1302,19 @@ func queryRoutes(ctx *cli.Context) error {
12481302
BlindedPaymentPaths: blindedRoutes,
12491303
}
12501304

1305+
if ctx.IsSet("route_hints") {
1306+
routeHintsJSON := ctx.String("route_hints")
1307+
var routeHints []*lnrpc.RouteHint
1308+
1309+
err := json.Unmarshal([]byte(routeHintsJSON), &routeHints)
1310+
if err != nil {
1311+
return fmt.Errorf("error unmarshaling route_hints "+
1312+
"json: %w", err)
1313+
}
1314+
1315+
req.RouteHints = routeHints
1316+
}
1317+
12511318
route, err := client.QueryRoutes(ctxc, req)
12521319
if err != nil {
12531320
return err

itest/list_on_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ var allTestCases = []*lntest.TestCase{
177177
Name: "send direct payment simple taproot",
178178
TestFunc: testSendDirectPaymentSimpleTaproot,
179179
},
180+
{
181+
Name: "sendpayment with route hints",
182+
TestFunc: testSendPaymentWithRouteHints,
183+
},
180184
{
181185
Name: "immediate payment after channel opened",
182186
TestFunc: testPaymentFollowingChannelOpen,

itest/lnd_payment_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -1396,3 +1396,48 @@ func testSendPaymentKeysendMPPFail(ht *lntest.HarnessTest) {
13961396
_, err = ht.ReceivePaymentUpdate(client)
13971397
require.Error(ht, err)
13981398
}
1399+
1400+
// testSendPaymentWithRouteHints tests sending a payment using route hints
1401+
// derived from a private channel.
1402+
func testSendPaymentWithRouteHints(ht *lntest.HarnessTest) {
1403+
// Setup a three-node network: Alice -> Bob -> Carol.
1404+
alice := ht.NewNode("Alice", nil)
1405+
defer ht.Shutdown(alice)
1406+
bob := ht.NewNode("Bob", nil)
1407+
defer ht.Shutdown(bob)
1408+
carol := ht.NewNode("Carol", nil)
1409+
defer ht.Shutdown(carol)
1410+
1411+
ht.ConnectNodes(alice, bob)
1412+
ht.ConnectNodes(bob, carol)
1413+
1414+
// Fund Alice and Bob.
1415+
ht.FundCoins(btcutil.SatoshiPerBitcoin, alice)
1416+
ht.FundCoins(btcutil.SatoshiPerBitcoin, bob)
1417+
1418+
// Open channels: Alice -> Bob (public), Bob -> Carol (private).
1419+
const chanAmt = btcutil.Amount(1_000_000)
1420+
aliceBobChanPoint := ht.OpenChannel(
1421+
alice, bob, lntest.OpenChannelParams{Amt: chanAmt},
1422+
)
1423+
defer ht.CloseChannel(alice, aliceBobChanPoint)
1424+
bobCarolChanPoint := ht.OpenChannel(bob, carol,
1425+
lntest.OpenChannelParams{Amt: chanAmt, Private: true},
1426+
)
1427+
defer ht.CloseChannel(bob, bobCarolChanPoint)
1428+
1429+
sendReq := &routerrpc.SendPaymentRequest{
1430+
PaymentRequest: carol.RPC.AddInvoice(&lnrpc.Invoice{
1431+
Value: 10_000,
1432+
Private: true, // include route hints
1433+
}).PaymentRequest,
1434+
FeeLimitSat: int64(chanAmt),
1435+
TimeoutSeconds: 60,
1436+
}
1437+
1438+
// Send payment and assert success.
1439+
ht.AssertPaymentStatusFromStream(
1440+
alice.RPC.SendPayment(sendReq),
1441+
lnrpc.Payment_SUCCEEDED,
1442+
)
1443+
}

0 commit comments

Comments
 (0)