cmd/lncli: modify sendtoroute cmd to make reading routes from stdin optional
In this commit we modify the existing sendtoroutes command such that users can either specify the route over stdin (using the special '-' flag), via the keyword argument, or via the positional argument.
This commit is contained in:
parent
bc9eca32ab
commit
0177309dcf
@ -1809,31 +1809,43 @@ var sendToRouteCommand = cli.Command{
|
|||||||
Usage: "send a payment over a predefined route",
|
Usage: "send a payment over a predefined route",
|
||||||
Description: `
|
Description: `
|
||||||
Send a payment over Lightning using a specific route. One must specify
|
Send a payment over Lightning using a specific route. One must specify
|
||||||
a list of routes to attempt and the payment hash.
|
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 --debug_send flag is provided for usage *purely* in test
|
There are three ways to specify routes:
|
||||||
environments. If specified, then the payment hash isn't required, as
|
* using the --routes parameter to manually specify a JSON encoded
|
||||||
it'll use the hash of all zeroes. This mode allows one to quickly test
|
set of routes in the format of the return value of queryroutes:
|
||||||
payment connectivity without having to create an invoice at the
|
(lncli sendtoroute --payment_hash=<pay_hash> --routes=<route>)
|
||||||
destination.
|
|
||||||
|
* passing the routes 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:
|
||||||
|
(lncli queryroutes --args.. | lncli sendtoroute --payment_hash= -
|
||||||
|
|
||||||
|
notice the '-' at the end, which signals that lncli should read
|
||||||
|
the route in from stdin
|
||||||
`,
|
`,
|
||||||
ArgsUsage: "(lncli queryroutes --dest=<pubkey> --amt=<amt> " +
|
|
||||||
"| lncli sendtoroute --payment_hash=<payment_hash>)",
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "payment_hash, r",
|
Name: "payment_hash, pay_hash",
|
||||||
Usage: "the hash to use within the payment's HTLC",
|
Usage: "the hash to use within the payment's HTLC",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.StringFlag{
|
||||||
Name: "debug_send",
|
Name: "routes, r",
|
||||||
Usage: "use the debug rHash when sending the HTLC",
|
Usage: "a json array string in the format of the response " +
|
||||||
|
"of queryroutes that denotes which routes to use",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: sendToRoute,
|
Action: sendToRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendToRoute(ctx *cli.Context) error {
|
func sendToRoute(ctx *cli.Context) error {
|
||||||
// Show command help if no arguments provieded
|
// Show command help if no arguments provided.
|
||||||
if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
|
if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
|
||||||
cli.ShowCommandHelp(ctx, "sendtoroute")
|
cli.ShowCommandHelp(ctx, "sendtoroute")
|
||||||
return nil
|
return nil
|
||||||
@ -1841,31 +1853,17 @@ func sendToRoute(ctx *cli.Context) error {
|
|||||||
|
|
||||||
args := ctx.Args()
|
args := ctx.Args()
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(os.Stdin)
|
var (
|
||||||
if err != nil {
|
rHash []byte
|
||||||
return err
|
err error
|
||||||
}
|
)
|
||||||
if len(b) == 0 {
|
|
||||||
return fmt.Errorf("queryroutes output is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
qroutes := &lnrpc.QueryRoutesResponse{}
|
|
||||||
err = jsonpb.UnmarshalString(string(b), qroutes)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to unmarshal json string from "+
|
|
||||||
"incoming array of routes: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var rHash []byte
|
|
||||||
if ctx.Bool("debug_send") &&
|
|
||||||
(ctx.IsSet("payment_hash") || args.Present()) {
|
|
||||||
return fmt.Errorf("do not provide a payment hash with debug send")
|
|
||||||
} else if !ctx.Bool("debug_send") {
|
|
||||||
switch {
|
switch {
|
||||||
case ctx.IsSet("payment_hash"):
|
case ctx.IsSet("payment_hash"):
|
||||||
rHash, err = hex.DecodeString(ctx.String("payment_hash"))
|
rHash, err = hex.DecodeString(ctx.String("payment_hash"))
|
||||||
case args.Present():
|
case args.Present():
|
||||||
rHash, err = hex.DecodeString(args.First())
|
rHash, err = hex.DecodeString(args.First())
|
||||||
|
|
||||||
|
args = args.Tail()
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("payment hash argument missing")
|
return fmt.Errorf("payment hash argument missing")
|
||||||
}
|
}
|
||||||
@ -1878,11 +1876,42 @@ func sendToRoute(ctx *cli.Context) error {
|
|||||||
return fmt.Errorf("payment hash must be exactly 32 "+
|
return fmt.Errorf("payment hash must be exactly 32 "+
|
||||||
"bytes, is instead %d", len(rHash))
|
"bytes, is instead %d", len(rHash))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var jsonRoutes string
|
||||||
|
switch {
|
||||||
|
// The user is specifying the routes explicitly via the key word
|
||||||
|
// argument.
|
||||||
|
case ctx.IsSet("routes"):
|
||||||
|
jsonRoutes = ctx.String("routes")
|
||||||
|
|
||||||
|
// The user is specifying the routes as a positional argument.
|
||||||
|
case args.Present() && args.First() != "-":
|
||||||
|
jsonRoutes = args.First()
|
||||||
|
|
||||||
|
// The user is signalling that we should read stdin in order to parse
|
||||||
|
// the set of target routes.
|
||||||
|
case args.Present() && args.First() == "-":
|
||||||
|
b, err := ioutil.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(b) == 0 {
|
||||||
|
return fmt.Errorf("queryroutes output is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonRoutes = string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &lnrpc.SendToRouteRequest{
|
req := &lnrpc.SendToRouteRequest{
|
||||||
PaymentHash: rHash,
|
PaymentHash: rHash,
|
||||||
Routes: qroutes.Routes,
|
Routes: routes.Routes,
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendToRouteRequest(ctx, req)
|
return sendToRouteRequest(ctx, req)
|
||||||
@ -1906,8 +1935,6 @@ func sendToRouteRequest(ctx *cli.Context, req *lnrpc.SendToRouteRequest) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
paymentStream.CloseSend()
|
|
||||||
|
|
||||||
printJSON(struct {
|
printJSON(struct {
|
||||||
E string `json:"payment_error"`
|
E string `json:"payment_error"`
|
||||||
P string `json:"payment_preimage"`
|
P string `json:"payment_preimage"`
|
||||||
|
Loading…
Reference in New Issue
Block a user