lnd.xprv/cmd/lncli/wtclient.go

282 lines
6.9 KiB
Go
Raw Normal View History

package main
import (
"context"
"encoding/hex"
"errors"
"fmt"
"strings"
"github.com/lightningnetwork/lnd/lnrpc/wtclientrpc"
"github.com/urfave/cli"
)
// wtclientCommands will return nil for non-wtclientrpc builds.
func wtclientCommands() []cli.Command {
return []cli.Command{
{
Name: "wtclient",
Usage: "Interact with the watchtower client.",
Category: "Watchtower",
Subcommands: []cli.Command{
addTowerCommand,
removeTowerCommand,
listTowersCommand,
getTowerCommand,
statsCommand,
policyCommand,
},
},
}
}
// getWtclient initializes a connection to the watchtower client RPC in order to
// interact with it.
func getWtclient(ctx *cli.Context) (wtclientrpc.WatchtowerClientClient, func()) {
conn := getClientConn(ctx, false)
cleanUp := func() {
conn.Close()
}
return wtclientrpc.NewWatchtowerClientClient(conn), cleanUp
}
var addTowerCommand = cli.Command{
Name: "add",
Usage: "Register a watchtower to use for future sessions/backups.",
Description: "If the watchtower has already been registered, then " +
"this command serves as a way of updating the watchtower " +
"with new addresses it is reachable over.",
ArgsUsage: "pubkey@address",
Action: actionDecorator(addTower),
}
func addTower(ctx *cli.Context) error {
// Display the command's help message if the number of arguments/flags
// is not what we expect.
if ctx.NArg() != 1 || ctx.NumFlags() > 0 {
return cli.ShowCommandHelp(ctx, "add")
}
parts := strings.Split(ctx.Args().First(), "@")
if len(parts) != 2 {
return errors.New("expected tower of format pubkey@address")
}
pubKey, err := hex.DecodeString(parts[0])
if err != nil {
return fmt.Errorf("invalid public key: %v", err)
}
address := parts[1]
client, cleanUp := getWtclient(ctx)
defer cleanUp()
req := &wtclientrpc.AddTowerRequest{
Pubkey: pubKey,
Address: address,
}
resp, err := client.AddTower(context.Background(), req)
if err != nil {
return err
}
printRespJSON(resp)
return nil
}
var removeTowerCommand = cli.Command{
Name: "remove",
Usage: "Remove a watchtower to prevent its use for future " +
"sessions/backups.",
Description: "An optional address can be provided to remove, " +
"indicating that the watchtower is no longer reachable at " +
"this address. If an address isn't provided, then the " +
"watchtower will no longer be used for future sessions/backups.",
ArgsUsage: "pubkey | pubkey@address",
Action: actionDecorator(removeTower),
}
func removeTower(ctx *cli.Context) error {
// Display the command's help message if the number of arguments/flags
// is not what we expect.
if ctx.NArg() != 1 || ctx.NumFlags() > 0 {
return cli.ShowCommandHelp(ctx, "remove")
}
// The command can have only one argument, but it can be interpreted in
// either of the following formats:
//
// pubkey or pubkey@address
//
// The hex-encoded public key of the watchtower is always required,
// while the second is an optional address we'll remove from the
// watchtower's database record.
parts := strings.Split(ctx.Args().First(), "@")
if len(parts) > 2 {
return errors.New("expected tower of format pubkey@address")
}
pubKey, err := hex.DecodeString(parts[0])
if err != nil {
return fmt.Errorf("invalid public key: %v", err)
}
var address string
if len(parts) == 2 {
address = parts[1]
}
client, cleanUp := getWtclient(ctx)
defer cleanUp()
req := &wtclientrpc.RemoveTowerRequest{
Pubkey: pubKey,
Address: address,
}
resp, err := client.RemoveTower(context.Background(), req)
if err != nil {
return err
}
printRespJSON(resp)
return nil
}
var listTowersCommand = cli.Command{
Name: "towers",
Usage: "Display information about all registered watchtowers.",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "include_sessions",
Usage: "include sessions with the watchtower in the " +
"response",
},
},
Action: actionDecorator(listTowers),
}
func listTowers(ctx *cli.Context) error {
// Display the command's help message if the number of arguments/flags
// is not what we expect.
if ctx.NArg() > 0 || ctx.NumFlags() > 1 {
return cli.ShowCommandHelp(ctx, "towers")
}
client, cleanUp := getWtclient(ctx)
defer cleanUp()
req := &wtclientrpc.ListTowersRequest{
IncludeSessions: ctx.Bool("include_sessions"),
}
resp, err := client.ListTowers(context.Background(), req)
if err != nil {
return err
}
var listTowersResp = struct {
Towers []*Tower `json:"towers"`
}{
Towers: make([]*Tower, len(resp.Towers)),
}
for i, tower := range resp.Towers {
listTowersResp.Towers[i] = NewTowerFromProto(tower)
}
printJSON(listTowersResp)
return nil
}
var getTowerCommand = cli.Command{
Name: "tower",
Usage: "Display information about a specific registered watchtower.",
ArgsUsage: "pubkey",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "include_sessions",
Usage: "include sessions with the watchtower in the " +
"response",
},
},
Action: actionDecorator(getTower),
}
func getTower(ctx *cli.Context) error {
// Display the command's help message if the number of arguments/flags
// is not what we expect.
if ctx.NArg() != 1 || ctx.NumFlags() > 1 {
return cli.ShowCommandHelp(ctx, "tower")
}
// The command only has one argument, which we expect to be the
// hex-encoded public key of the watchtower we'll display information
// about.
pubKey, err := hex.DecodeString(ctx.Args().Get(0))
if err != nil {
return fmt.Errorf("invalid public key: %v", err)
}
client, cleanUp := getWtclient(ctx)
defer cleanUp()
req := &wtclientrpc.GetTowerInfoRequest{
Pubkey: pubKey,
IncludeSessions: ctx.Bool("include_sessions"),
}
resp, err := client.GetTowerInfo(context.Background(), req)
if err != nil {
return err
}
printJSON(NewTowerFromProto(resp))
return nil
}
var statsCommand = cli.Command{
Name: "stats",
Usage: "Display the session stats of the watchtower client.",
Action: actionDecorator(stats),
}
func stats(ctx *cli.Context) error {
// Display the command's help message if the number of arguments/flags
// is not what we expect.
if ctx.NArg() > 0 || ctx.NumFlags() > 0 {
return cli.ShowCommandHelp(ctx, "stats")
}
client, cleanUp := getWtclient(ctx)
defer cleanUp()
req := &wtclientrpc.StatsRequest{}
resp, err := client.Stats(context.Background(), req)
if err != nil {
return err
}
printRespJSON(resp)
return nil
}
var policyCommand = cli.Command{
Name: "policy",
Usage: "Display the active watchtower client policy configuration.",
Action: actionDecorator(policy),
}
func policy(ctx *cli.Context) error {
// Display the command's help message if the number of arguments/flags
// is not what we expect.
if ctx.NArg() > 0 || ctx.NumFlags() > 0 {
return cli.ShowCommandHelp(ctx, "policy")
}
client, cleanUp := getWtclient(ctx)
defer cleanUp()
req := &wtclientrpc.PolicyRequest{}
resp, err := client.Policy(context.Background(), req)
if err != nil {
return err
}
printRespJSON(resp)
return nil
}