mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-25 18:21:12 +02:00
Merge pull request #3440 from joostjager/buildroute
routing: add build route functionality
This commit is contained in:
94
cmd/lncli/cmd_build_route.go
Normal file
94
cmd/lncli/cmd_build_route.go
Normal file
@@ -0,0 +1,94 @@
|
||||
// +build routerrpc
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/lightningnetwork/lnd"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var buildRouteCommand = cli.Command{
|
||||
Name: "buildroute",
|
||||
Category: "Payments",
|
||||
Usage: "Build a route from a list of hop pubkeys.",
|
||||
Action: actionDecorator(buildRoute),
|
||||
Flags: []cli.Flag{
|
||||
cli.Int64Flag{
|
||||
Name: "amt",
|
||||
Usage: "the amount to send expressed in satoshis. If" +
|
||||
"not set, the minimum routable amount is used",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "final_cltv_delta",
|
||||
Usage: "number of blocks the last hop has to reveal " +
|
||||
"the preimage",
|
||||
Value: lnd.DefaultBitcoinTimeLockDelta,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hops",
|
||||
Usage: "comma separated hex pubkeys",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "outgoing_chan_id",
|
||||
Usage: "short channel id of the outgoing channel to " +
|
||||
"use for the first hop of the payment",
|
||||
Value: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func buildRoute(ctx *cli.Context) error {
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
if !ctx.IsSet("hops") {
|
||||
return errors.New("hops required")
|
||||
}
|
||||
|
||||
// Build list of hop addresses for the rpc.
|
||||
hops := strings.Split(ctx.String("hops"), ",")
|
||||
rpcHops := make([][]byte, 0, len(hops))
|
||||
for _, k := range hops {
|
||||
pubkey, err := route.NewVertexFromStr(k)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing %v: %v", k, err)
|
||||
}
|
||||
rpcHops = append(rpcHops, pubkey[:])
|
||||
}
|
||||
|
||||
var amtMsat int64
|
||||
hasAmt := ctx.IsSet("amt")
|
||||
if hasAmt {
|
||||
amtMsat = ctx.Int64("amt") * 1000
|
||||
if amtMsat == 0 {
|
||||
return fmt.Errorf("non-zero amount required")
|
||||
}
|
||||
}
|
||||
|
||||
// Call BuildRoute rpc.
|
||||
req := &routerrpc.BuildRouteRequest{
|
||||
AmtMsat: amtMsat,
|
||||
FinalCltvDelta: int32(ctx.Int64("final_cltv_delta")),
|
||||
HopPubkeys: rpcHops,
|
||||
OutgoingChanId: ctx.Uint64("outgoing_chan_id"),
|
||||
}
|
||||
|
||||
rpcCtx := context.Background()
|
||||
route, err := client.BuildRoute(rpcCtx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printJSON(route)
|
||||
|
||||
return nil
|
||||
}
|
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/walletunlocker"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
@@ -2381,22 +2382,23 @@ var sendToRouteCommand = cli.Command{
|
||||
Usage: "Send a payment over a predefined route.",
|
||||
Description: `
|
||||
Send a payment over Lightning using a specific route. One must specify
|
||||
a list of routes to attempt and the payment hash. This command can even
|
||||
be chained with the response to queryroutes. This command can be used
|
||||
to implement channel rebalancing by crafting a self-route, or even
|
||||
atomic swaps using a self-route that crosses multiple chains.
|
||||
the route to attempt and the payment hash. This command can even
|
||||
be chained with the response to queryroutes or buildroute. This command
|
||||
can be used to implement channel rebalancing by crafting a self-route,
|
||||
or even atomic swaps using a self-route that crosses multiple chains.
|
||||
|
||||
There are three ways to specify routes:
|
||||
There are three ways to specify a route:
|
||||
* using the --routes parameter to manually specify a JSON encoded
|
||||
set of routes in the format of the return value of queryroutes:
|
||||
route in the format of the return value of queryroutes or
|
||||
buildroute:
|
||||
(lncli sendtoroute --payment_hash=<pay_hash> --routes=<route>)
|
||||
|
||||
* passing the routes as a positional argument:
|
||||
* passing the route as a positional argument:
|
||||
(lncli sendtoroute --payment_hash=pay_hash <route>)
|
||||
|
||||
* or reading in the routes from stdin, which can allow chaining the
|
||||
response from queryroutes, or even read in a file with a set of
|
||||
pre-computed routes:
|
||||
* or reading in the route from stdin, which can allow chaining the
|
||||
response from queryroutes or buildroute, or even read in a file
|
||||
with a pre-computed route:
|
||||
(lncli queryroutes --args.. | lncli sendtoroute --payment_hash= -
|
||||
|
||||
notice the '-' at the end, which signals that lncli should read
|
||||
@@ -2474,25 +2476,37 @@ func sendToRoute(ctx *cli.Context) error {
|
||||
jsonRoutes = string(b)
|
||||
}
|
||||
|
||||
// Try to parse the provided json both in the legacy QueryRoutes format
|
||||
// that contains a list of routes and the single route BuildRoute
|
||||
// format.
|
||||
var route *lnrpc.Route
|
||||
routes := &lnrpc.QueryRoutesResponse{}
|
||||
err = jsonpb.UnmarshalString(jsonRoutes, routes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to unmarshal json string "+
|
||||
"from incoming array of routes: %v", err)
|
||||
}
|
||||
if err == nil {
|
||||
if len(routes.Routes) == 0 {
|
||||
return fmt.Errorf("no routes provided")
|
||||
}
|
||||
|
||||
if len(routes.Routes) == 0 {
|
||||
return fmt.Errorf("no routes provided")
|
||||
}
|
||||
if len(routes.Routes) != 1 {
|
||||
return fmt.Errorf("expected a single route, but got %v",
|
||||
len(routes.Routes))
|
||||
}
|
||||
|
||||
if len(routes.Routes) != 1 {
|
||||
return fmt.Errorf("expected a single route, but got %v",
|
||||
len(routes.Routes))
|
||||
route = routes.Routes[0]
|
||||
} else {
|
||||
routes := &routerrpc.BuildRouteResponse{}
|
||||
err = jsonpb.UnmarshalString(jsonRoutes, routes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to unmarshal json string "+
|
||||
"from incoming array of routes: %v", err)
|
||||
}
|
||||
|
||||
route = routes.Route
|
||||
}
|
||||
|
||||
req := &lnrpc.SendToRouteRequest{
|
||||
PaymentHash: rHash,
|
||||
Route: routes.Routes[0],
|
||||
Route: route,
|
||||
}
|
||||
|
||||
return sendToRouteRequest(ctx, req)
|
||||
|
@@ -6,5 +6,9 @@ import "github.com/urfave/cli"
|
||||
|
||||
// routerCommands will return nil for non-routerrpc builds.
|
||||
func routerCommands() []cli.Command {
|
||||
return []cli.Command{queryMissionControlCommand, resetMissionControlCommand}
|
||||
return []cli.Command{
|
||||
queryMissionControlCommand,
|
||||
resetMissionControlCommand,
|
||||
buildRouteCommand,
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user