Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions cmd/lncli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/golang/protobuf/proto"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/walletunlocker"
"github.com/urfave/cli"
"golang.org/x/crypto/ssh/terminal"
Expand Down Expand Up @@ -2040,11 +2041,18 @@ func closedChannels(ctx *cli.Context) error {
return nil
}

var cltvLimitFlag = cli.UintFlag{
Name: "cltv_limit",
Usage: "the maximum time lock that may be used for " +
"this payment",
}
var (
cltvLimitFlag = cli.UintFlag{
Name: "cltv_limit",
Usage: "the maximum time lock that may be used for " +
"this payment",
}

lastHopFlag = cli.StringFlag{
Name: "last_hop",
Usage: "pubkey of the last hop to use for this payment",
}
)

// paymentFlags returns common flags for sendpayment and payinvoice.
func paymentFlags() []cli.Flag {
Expand All @@ -2065,6 +2073,7 @@ func paymentFlags() []cli.Flag {
"payment",
},
cltvLimitFlag,
lastHopFlag,
cli.Uint64Flag{
Name: "outgoing_chan_id",
Usage: "short channel id of the outgoing channel to " +
Expand Down Expand Up @@ -2281,6 +2290,16 @@ func sendPaymentRequest(ctx *cli.Context, req *lnrpc.SendRequest) error {
req.FeeLimit = feeLimit

req.OutgoingChanId = ctx.Uint64("outgoing_chan_id")
if ctx.IsSet(lastHopFlag.Name) {
lastHop, err := route.NewVertexFromStr(
ctx.String(lastHopFlag.Name),
)
if err != nil {
return err
}
req.LastHopPubkey = lastHop[:]
}

req.CltvLimit = uint32(ctx.Int(cltvLimitFlag.Name))

amt := req.Amt
Expand Down
259 changes: 135 additions & 124 deletions lnrpc/routerrpc/router.pb.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions lnrpc/routerrpc/router.proto
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ message SendPaymentRequest {
*/
uint64 outgoing_chan_id = 8 [jstype = JS_STRING];

/**
The pubkey of the last hop of the route. If empty, any hop may be used.
*/
bytes last_hop_pubkey = 14;

/**
An optional maximum total time lock for the route. This should not exceed
lnd's `--max-cltv-expiry` setting. If zero, then the value of
Expand Down
11 changes: 11 additions & 0 deletions lnrpc/routerrpc/router_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,17 @@ func (r *RouterBackend) extractIntentFromSendRequest(
payIntent.OutgoingChannelID = &rpcPayReq.OutgoingChanId
}

// Pass along a last hop restriction if specified.
if len(rpcPayReq.LastHopPubkey) > 0 {
lastHop, err := route.NewVertexFromBytes(
rpcPayReq.LastHopPubkey,
)
if err != nil {
return nil, err
}
payIntent.LastHop = &lastHop
}

// Take the CLTV limit from the request if set, otherwise use the max.
cltvLimit, err := ValidateCLTVLimit(
uint32(rpcPayReq.CltvLimit), r.MaxTotalTimelock,
Expand Down
1,103 changes: 557 additions & 546 deletions lnrpc/rpc.pb.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions lnrpc/rpc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,11 @@ message SendRequest {
*/
uint64 outgoing_chan_id = 9 [jstype = JS_STRING];

/**
The pubkey of the last hop of the route. If empty, any hop may be used.
*/
bytes last_hop_pubkey = 13;

/**
An optional maximum total time lock for the route. This should not exceed
lnd's `--max-cltv-expiry` setting. If zero, then the value of
Expand Down
5 changes: 5 additions & 0 deletions lnrpc/rpc.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -3661,6 +3661,11 @@
"format": "uint64",
"description": "*\nThe channel id of the channel that must be taken to the first hop. If zero,\nany channel may be used."
},
"last_hop_pubkey": {
"type": "string",
"format": "byte",
"description": "*\nThe pubkey of the last hop of the route. If empty, any hop may be used."
},
"cltv_limit": {
"type": "integer",
"format": "int64",
Expand Down
11 changes: 11 additions & 0 deletions routing/pathfind.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,10 @@ type RestrictParams struct {
// hop. If nil, any channel may be used.
OutgoingChannelID *uint64

// LastHop is the pubkey of the last node before the final destination
// is reached. If nil, any node may be used.
LastHop *route.Vertex

// CltvLimit is the maximum time lock of the route excluding the final
// ctlv. After path finding is complete, the caller needs to increase
// all cltv expiry heights with the required final cltv delta.
Expand Down Expand Up @@ -562,6 +566,13 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
// Expand all connections using the optimal policy for each
// connection.
for fromNode, unifiedPolicy := range u.policies {
// Apply last hop restriction if set.
if r.LastHop != nil &&
pivot == target && fromNode != *r.LastHop {

continue
}

policy := unifiedPolicy.getPolicy(
amtToSend, g.bandwidthHints,
)
Expand Down
Loading