github.com/decred/dcrlnd@v0.7.6/cmd/dcrlncli/wtclient.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/hex"
     5  	"errors"
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/decred/dcrlnd/lnrpc/wtclientrpc"
    10  	"github.com/urfave/cli"
    11  )
    12  
    13  // wtclientCommands will return nil for non-wtclientrpc builds.
    14  func wtclientCommands() []cli.Command {
    15  	return []cli.Command{
    16  		{
    17  			Name:     "wtclient",
    18  			Usage:    "Interact with the watchtower client.",
    19  			Category: "Watchtower",
    20  			Subcommands: []cli.Command{
    21  				addTowerCommand,
    22  				removeTowerCommand,
    23  				listTowersCommand,
    24  				getTowerCommand,
    25  				statsCommand,
    26  				policyCommand,
    27  			},
    28  		},
    29  	}
    30  }
    31  
    32  // getWtclient initializes a connection to the watchtower client RPC in order to
    33  // interact with it.
    34  func getWtclient(ctx *cli.Context) (wtclientrpc.WatchtowerClientClient, func()) {
    35  	conn := getClientConn(ctx, false)
    36  	cleanUp := func() {
    37  		conn.Close()
    38  	}
    39  	return wtclientrpc.NewWatchtowerClientClient(conn), cleanUp
    40  }
    41  
    42  var addTowerCommand = cli.Command{
    43  	Name:  "add",
    44  	Usage: "Register a watchtower to use for future sessions/backups.",
    45  	Description: "If the watchtower has already been registered, then " +
    46  		"this command serves as a way of updating the watchtower " +
    47  		"with new addresses it is reachable over.",
    48  	ArgsUsage: "pubkey@address",
    49  	Action:    actionDecorator(addTower),
    50  }
    51  
    52  func addTower(ctx *cli.Context) error {
    53  	ctxc := getContext()
    54  
    55  	// Display the command's help message if the number of arguments/flags
    56  	// is not what we expect.
    57  	if ctx.NArg() != 1 || ctx.NumFlags() > 0 {
    58  		return cli.ShowCommandHelp(ctx, "add")
    59  	}
    60  
    61  	parts := strings.Split(ctx.Args().First(), "@")
    62  	if len(parts) != 2 {
    63  		return errors.New("expected tower of format pubkey@address")
    64  	}
    65  	pubKey, err := hex.DecodeString(parts[0])
    66  	if err != nil {
    67  		return fmt.Errorf("invalid public key: %v", err)
    68  	}
    69  	address := parts[1]
    70  
    71  	client, cleanUp := getWtclient(ctx)
    72  	defer cleanUp()
    73  
    74  	req := &wtclientrpc.AddTowerRequest{
    75  		Pubkey:  pubKey,
    76  		Address: address,
    77  	}
    78  	resp, err := client.AddTower(ctxc, req)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	printRespJSON(resp)
    84  	return nil
    85  }
    86  
    87  var removeTowerCommand = cli.Command{
    88  	Name: "remove",
    89  	Usage: "Remove a watchtower to prevent its use for future " +
    90  		"sessions/backups.",
    91  	Description: "An optional address can be provided to remove, " +
    92  		"indicating that the watchtower is no longer reachable at " +
    93  		"this address. If an address isn't provided, then the " +
    94  		"watchtower will no longer be used for future sessions/backups.",
    95  	ArgsUsage: "pubkey | pubkey@address",
    96  	Action:    actionDecorator(removeTower),
    97  }
    98  
    99  func removeTower(ctx *cli.Context) error {
   100  	ctxc := getContext()
   101  
   102  	// Display the command's help message if the number of arguments/flags
   103  	// is not what we expect.
   104  	if ctx.NArg() != 1 || ctx.NumFlags() > 0 {
   105  		return cli.ShowCommandHelp(ctx, "remove")
   106  	}
   107  
   108  	// The command can have only one argument, but it can be interpreted in
   109  	// either of the following formats:
   110  	//
   111  	//   pubkey or pubkey@address
   112  	//
   113  	// The hex-encoded public key of the watchtower is always required,
   114  	// while the second is an optional address we'll remove from the
   115  	// watchtower's database record.
   116  	parts := strings.Split(ctx.Args().First(), "@")
   117  	if len(parts) > 2 {
   118  		return errors.New("expected tower of format pubkey@address")
   119  	}
   120  	pubKey, err := hex.DecodeString(parts[0])
   121  	if err != nil {
   122  		return fmt.Errorf("invalid public key: %v", err)
   123  	}
   124  	var address string
   125  	if len(parts) == 2 {
   126  		address = parts[1]
   127  	}
   128  
   129  	client, cleanUp := getWtclient(ctx)
   130  	defer cleanUp()
   131  
   132  	req := &wtclientrpc.RemoveTowerRequest{
   133  		Pubkey:  pubKey,
   134  		Address: address,
   135  	}
   136  	resp, err := client.RemoveTower(ctxc, req)
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	printRespJSON(resp)
   142  	return nil
   143  }
   144  
   145  var listTowersCommand = cli.Command{
   146  	Name:  "towers",
   147  	Usage: "Display information about all registered watchtowers.",
   148  	Flags: []cli.Flag{
   149  		cli.BoolFlag{
   150  			Name: "include_sessions",
   151  			Usage: "include sessions with the watchtower in the " +
   152  				"response",
   153  		},
   154  	},
   155  	Action: actionDecorator(listTowers),
   156  }
   157  
   158  func listTowers(ctx *cli.Context) error {
   159  	ctxc := getContext()
   160  
   161  	// Display the command's help message if the number of arguments/flags
   162  	// is not what we expect.
   163  	if ctx.NArg() > 0 || ctx.NumFlags() > 1 {
   164  		return cli.ShowCommandHelp(ctx, "towers")
   165  	}
   166  
   167  	client, cleanUp := getWtclient(ctx)
   168  	defer cleanUp()
   169  
   170  	req := &wtclientrpc.ListTowersRequest{
   171  		IncludeSessions: ctx.Bool("include_sessions"),
   172  	}
   173  	resp, err := client.ListTowers(ctxc, req)
   174  	if err != nil {
   175  		return err
   176  	}
   177  
   178  	printRespJSON(resp)
   179  
   180  	return nil
   181  }
   182  
   183  var getTowerCommand = cli.Command{
   184  	Name:      "tower",
   185  	Usage:     "Display information about a specific registered watchtower.",
   186  	ArgsUsage: "pubkey",
   187  	Flags: []cli.Flag{
   188  		cli.BoolFlag{
   189  			Name: "include_sessions",
   190  			Usage: "include sessions with the watchtower in the " +
   191  				"response",
   192  		},
   193  	},
   194  	Action: actionDecorator(getTower),
   195  }
   196  
   197  func getTower(ctx *cli.Context) error {
   198  	ctxc := getContext()
   199  
   200  	// Display the command's help message if the number of arguments/flags
   201  	// is not what we expect.
   202  	if ctx.NArg() != 1 || ctx.NumFlags() > 1 {
   203  		return cli.ShowCommandHelp(ctx, "tower")
   204  	}
   205  
   206  	// The command only has one argument, which we expect to be the
   207  	// hex-encoded public key of the watchtower we'll display information
   208  	// about.
   209  	pubKey, err := hex.DecodeString(ctx.Args().Get(0))
   210  	if err != nil {
   211  		return fmt.Errorf("invalid public key: %v", err)
   212  	}
   213  
   214  	client, cleanUp := getWtclient(ctx)
   215  	defer cleanUp()
   216  
   217  	req := &wtclientrpc.GetTowerInfoRequest{
   218  		Pubkey:          pubKey,
   219  		IncludeSessions: ctx.Bool("include_sessions"),
   220  	}
   221  	resp, err := client.GetTowerInfo(ctxc, req)
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	printRespJSON(resp)
   227  	return nil
   228  }
   229  
   230  var statsCommand = cli.Command{
   231  	Name:   "stats",
   232  	Usage:  "Display the session stats of the watchtower client.",
   233  	Action: actionDecorator(stats),
   234  }
   235  
   236  func stats(ctx *cli.Context) error {
   237  	ctxc := getContext()
   238  
   239  	// Display the command's help message if the number of arguments/flags
   240  	// is not what we expect.
   241  	if ctx.NArg() > 0 || ctx.NumFlags() > 0 {
   242  		return cli.ShowCommandHelp(ctx, "stats")
   243  	}
   244  
   245  	client, cleanUp := getWtclient(ctx)
   246  	defer cleanUp()
   247  
   248  	req := &wtclientrpc.StatsRequest{}
   249  	resp, err := client.Stats(ctxc, req)
   250  	if err != nil {
   251  		return err
   252  	}
   253  
   254  	printRespJSON(resp)
   255  	return nil
   256  }
   257  
   258  var policyCommand = cli.Command{
   259  	Name:   "policy",
   260  	Usage:  "Display the active watchtower client policy configuration.",
   261  	Action: actionDecorator(policy),
   262  	Flags: []cli.Flag{
   263  		cli.BoolFlag{
   264  			Name: "legacy",
   265  			Usage: "Retrieve the legacy tower client's current " +
   266  				"policy. (default)",
   267  		},
   268  		cli.BoolFlag{
   269  			Name:  "anchor",
   270  			Usage: "Retrieve the anchor tower client's current policy.",
   271  		},
   272  	},
   273  }
   274  
   275  func policy(ctx *cli.Context) error {
   276  	ctxc := getContext()
   277  
   278  	// Display the command's help message if the number of arguments/flags
   279  	// is not what we expect.
   280  	if ctx.NArg() > 0 || ctx.NumFlags() > 1 {
   281  		return cli.ShowCommandHelp(ctx, "policy")
   282  	}
   283  
   284  	var policyType wtclientrpc.PolicyType
   285  	switch {
   286  	case ctx.Bool("anchor"):
   287  		policyType = wtclientrpc.PolicyType_ANCHOR
   288  	case ctx.Bool("legacy"):
   289  		policyType = wtclientrpc.PolicyType_LEGACY
   290  
   291  	// For backwards compatibility with original rpc behavior.
   292  	default:
   293  		policyType = wtclientrpc.PolicyType_LEGACY
   294  	}
   295  
   296  	client, cleanUp := getWtclient(ctx)
   297  	defer cleanUp()
   298  
   299  	req := &wtclientrpc.PolicyRequest{
   300  		PolicyType: policyType,
   301  	}
   302  	resp, err := client.Policy(ctxc, req)
   303  	if err != nil {
   304  		return err
   305  	}
   306  
   307  	printRespJSON(resp)
   308  	return nil
   309  }