Merge pull request #3440 from joostjager/buildroute

routing: add build route functionality
This commit is contained in:
Olaoluwa Osuntokun
2019-09-24 20:24:24 -07:00
committed by GitHub
12 changed files with 1007 additions and 145 deletions

View 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
}

View File

@@ -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)

View File

@@ -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,
}
}