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

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"context"
     7  	"encoding/hex"
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"math"
    13  	"os"
    14  	"strconv"
    15  	"strings"
    16  	"sync"
    17  
    18  	"github.com/decred/dcrd/chaincfg/chainhash"
    19  	"github.com/decred/dcrd/wire"
    20  	"github.com/decred/dcrlnd/lnrpc"
    21  	"github.com/decred/dcrlnd/routing/route"
    22  	"github.com/decred/dcrlnd/signal"
    23  	"github.com/matheusd/protobuf-hex-display/json"
    24  	"github.com/matheusd/protobuf-hex-display/jsonpb"
    25  	"github.com/matheusd/protobuf-hex-display/proto"
    26  	"github.com/urfave/cli"
    27  	"google.golang.org/grpc/codes"
    28  	"google.golang.org/grpc/status"
    29  )
    30  
    31  // TODO(roasbeef): cli logic for supporting both positional and unix style
    32  // arguments.
    33  
    34  // TODO(roasbeef): expose all fee conf targets
    35  
    36  const defaultRecoveryWindow int32 = 20
    37  
    38  const (
    39  	defaultUtxoMinConf = 1
    40  )
    41  
    42  func getContext() context.Context {
    43  	shutdownInterceptor, err := signal.Intercept()
    44  	if err != nil {
    45  		_, _ = fmt.Fprintln(os.Stderr, err)
    46  		os.Exit(1)
    47  	}
    48  
    49  	ctxc, cancel := context.WithCancel(context.Background())
    50  	go func() {
    51  		<-shutdownInterceptor.ShutdownChannel()
    52  		cancel()
    53  	}()
    54  	return ctxc
    55  }
    56  
    57  func printJSON(resp interface{}) {
    58  	b, err := json.Marshal(resp)
    59  	if err != nil {
    60  		fatal(err)
    61  	}
    62  
    63  	var out bytes.Buffer
    64  	json.Indent(&out, b, "", "\t")
    65  	out.WriteString("\n")
    66  	out.WriteTo(os.Stdout)
    67  }
    68  
    69  func printRespJSON(resp proto.Message) {
    70  	jsonMarshaler := &jsonpb.Marshaler{
    71  		EmitDefaults: true,
    72  		OrigName:     true,
    73  		Indent:       "    ",
    74  	}
    75  
    76  	jsonStr, err := jsonMarshaler.MarshalToString(resp)
    77  	if err != nil {
    78  		fmt.Println("unable to decode response: ", err)
    79  		return
    80  	}
    81  
    82  	fmt.Println(jsonStr)
    83  }
    84  
    85  // actionDecorator is used to add additional information and error handling
    86  // to command actions.
    87  func actionDecorator(f func(*cli.Context) error) func(*cli.Context) error {
    88  	return func(c *cli.Context) error {
    89  		if err := f(c); err != nil {
    90  			s, ok := status.FromError(err)
    91  
    92  			// If it's a command for the UnlockerService (like
    93  			// 'create' or 'unlock') but the wallet is already
    94  			// unlocked, then these methods aren't recognized any
    95  			// more because this service is shut down after
    96  			// successful unlock. That's why the code
    97  			// 'Unimplemented' means something different for these
    98  			// two commands.
    99  			if s.Code() == codes.Unimplemented &&
   100  				(c.Command.Name == "create" ||
   101  					c.Command.Name == "unlock") {
   102  				return fmt.Errorf("wallet is already unlocked")
   103  			}
   104  
   105  			// dcrlnd might be active, but not possible to contact
   106  			// using RPC if the wallet is encrypted. If we get
   107  			// error code Unimplemented, it means that dcrlnd is
   108  			// running, but the RPC server is not active yet (only
   109  			// WalletUnlocker server active) and most likely this
   110  			// is because of an encrypted wallet.
   111  			if ok && s.Code() == codes.Unimplemented {
   112  				return fmt.Errorf("wallet is encrypted. " +
   113  					"Please unlock using 'dcrlncli unlock', " +
   114  					"or set password using 'dcrlncli create'" +
   115  					" if this is the first time starting dcrlnd")
   116  			}
   117  			return err
   118  		}
   119  		return nil
   120  	}
   121  }
   122  
   123  var newAddressCommand = cli.Command{
   124  	Name:      "newaddress",
   125  	Category:  "Wallet",
   126  	Usage:     "Generates a new address.",
   127  	ArgsUsage: "address-type",
   128  	Flags: []cli.Flag{
   129  		cli.StringFlag{
   130  			Name: "account",
   131  			Usage: "(optional) the name of the account to " +
   132  				"generate a new address for",
   133  		},
   134  	},
   135  	Description: `
   136  	Generate a wallet new address. Address-types has to be:
   137  	    - p2pkh: Pay to public key hash(default)`,
   138  	Action: actionDecorator(newAddress),
   139  }
   140  
   141  func newAddress(ctx *cli.Context) error {
   142  	ctxc := getContext()
   143  	client, cleanUp := getClient(ctx)
   144  	defer cleanUp()
   145  
   146  	// Display the command's help message if we do not have the expected
   147  	// number of arguments/flags.
   148  	if ctx.NArg() != 1 || ctx.NumFlags() > 1 {
   149  		return cli.ShowCommandHelp(ctx, "newaddress")
   150  	}
   151  
   152  	// Map the string encoded address type, to the concrete typed address
   153  	// type enum. An unrecognized address type will result in an error.
   154  	stringAddrType := ctx.Args().First()
   155  	var addrType lnrpc.AddressType
   156  	switch stringAddrType { // TODO(roasbeef): make them ints on the cli?
   157  	case "p2pkh":
   158  		addrType = lnrpc.AddressType_PUBKEY_HASH
   159  	case "":
   160  		addrType = lnrpc.AddressType_PUBKEY_HASH
   161  	default:
   162  		return fmt.Errorf("invalid address type %v, support address type "+
   163  			"are: p2pkh", stringAddrType)
   164  	}
   165  
   166  	addr, err := client.NewAddress(ctxc, &lnrpc.NewAddressRequest{
   167  		Type:    addrType,
   168  		Account: ctx.String("account"),
   169  	})
   170  	if err != nil {
   171  		return err
   172  	}
   173  
   174  	printRespJSON(addr)
   175  	return nil
   176  }
   177  
   178  var estimateFeeCommand = cli.Command{
   179  	Name:      "estimatefee",
   180  	Category:  "On-chain",
   181  	Usage:     "Get fee estimates for sending decred on-chain to multiple addresses.",
   182  	ArgsUsage: "send-json-string [--conf_target=N]",
   183  	Description: `
   184  	Get fee estimates for sending a transaction paying the specified amount(s) to the passed address(es).
   185  
   186  	The 'send-json-string' param encodes addresses and the amount to send respectively in the following format:
   187  
   188  	    {"ExampleAddr": NumCoinsInAtoms, "SecondAddr": NumCoins}
   189  	`,
   190  	Flags: []cli.Flag{
   191  		cli.Int64Flag{
   192  			Name: "conf_target",
   193  			Usage: "(optional) the number of blocks that the transaction *should* " +
   194  				"confirm in",
   195  		},
   196  	},
   197  	Action: actionDecorator(estimateFees),
   198  }
   199  
   200  func estimateFees(ctx *cli.Context) error {
   201  	ctxc := getContext()
   202  	var amountToAddr map[string]int64
   203  
   204  	jsonMap := ctx.Args().First()
   205  	if err := json.Unmarshal([]byte(jsonMap), &amountToAddr); err != nil {
   206  		return err
   207  	}
   208  
   209  	client, cleanUp := getClient(ctx)
   210  	defer cleanUp()
   211  
   212  	resp, err := client.EstimateFee(ctxc, &lnrpc.EstimateFeeRequest{
   213  		AddrToAmount: amountToAddr,
   214  		TargetConf:   int32(ctx.Int64("conf_target")),
   215  	})
   216  	if err != nil {
   217  		return err
   218  	}
   219  
   220  	printRespJSON(resp)
   221  	return nil
   222  }
   223  
   224  var txLabelFlag = cli.StringFlag{
   225  	Name:  "label",
   226  	Usage: "(optional) a label for the transaction",
   227  }
   228  
   229  var sendCoinsCommand = cli.Command{
   230  	Name:      "sendcoins",
   231  	Category:  "On-chain",
   232  	Usage:     "Send decred on-chain to an address.",
   233  	ArgsUsage: "addr amt",
   234  	Description: `
   235  	Send amt coins in atoms to the BASE58 encoded decred address addr.
   236  
   237  	Fees used when sending the transaction can be specified via the '--conf_target', or
   238  	'--atoms_per_byte' optional flags.
   239  
   240  	Positional arguments and flags can be used interchangeably but not at the same time!
   241  	`,
   242  	Flags: []cli.Flag{
   243  		cli.StringFlag{
   244  			Name:  "addr",
   245  			Usage: "The BASE58 encoded decred address to send coins to on-chain",
   246  		},
   247  		cli.BoolFlag{
   248  			Name: "sweepall",
   249  			Usage: "If set, then the amount field will be ignored, " +
   250  				"and the wallet will attempt to sweep all " +
   251  				"outputs within the wallet to the target " +
   252  				"address",
   253  		},
   254  		cli.Int64Flag{
   255  			Name:  "amt",
   256  			Usage: "The number of decred denominated in atoms to send",
   257  		},
   258  		cli.Int64Flag{
   259  			Name: "conf_target",
   260  			Usage: "The number of blocks that the " +
   261  				"transaction *should* confirm in, will be " +
   262  				"used for fee estimation (optional)",
   263  		},
   264  		cli.Int64Flag{
   265  			Name: "atoms_per_byte",
   266  			Usage: "A manual fee expressed in " +
   267  				"atoms/byte that should be used when crafting " +
   268  				"the transaction (optional)",
   269  		},
   270  		cli.Uint64Flag{
   271  			Name: "min_confs",
   272  			Usage: "(optional) the minimum number of confirmations " +
   273  				"each one of your outputs used for the transaction " +
   274  				"must satisfy",
   275  			Value: defaultUtxoMinConf,
   276  		},
   277  		txLabelFlag,
   278  	},
   279  	Action: actionDecorator(sendCoins),
   280  }
   281  
   282  func sendCoins(ctx *cli.Context) error {
   283  	var (
   284  		addr string
   285  		amt  int64
   286  		err  error
   287  	)
   288  	ctxc := getContext()
   289  	args := ctx.Args()
   290  
   291  	if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
   292  		cli.ShowCommandHelp(ctx, "sendcoins")
   293  		return nil
   294  	}
   295  
   296  	if ctx.IsSet("conf_target") && ctx.IsSet("atoms_per_byte") {
   297  		return fmt.Errorf("either --conf_target or --atoms_per_byte should be " +
   298  			"set, but not both")
   299  	}
   300  
   301  	switch {
   302  	case ctx.IsSet("addr"):
   303  		addr = ctx.String("addr")
   304  	case args.Present():
   305  		addr = args.First()
   306  		args = args.Tail()
   307  	default:
   308  		return fmt.Errorf("address argument missing")
   309  	}
   310  
   311  	switch {
   312  	case ctx.IsSet("amt"):
   313  		amt = ctx.Int64("amt")
   314  	case args.Present():
   315  		amt, err = strconv.ParseInt(args.First(), 10, 64)
   316  	case !ctx.Bool("sweepall"):
   317  		return fmt.Errorf("amount argument missing")
   318  	}
   319  	if err != nil {
   320  		return fmt.Errorf("unable to decode amount: %v", err)
   321  	}
   322  
   323  	if amt != 0 && ctx.Bool("sweepall") {
   324  		return fmt.Errorf("amount cannot be set if attempting to " +
   325  			"sweep all coins out of the wallet")
   326  	}
   327  
   328  	client, cleanUp := getClient(ctx)
   329  	defer cleanUp()
   330  
   331  	minConfs := int32(ctx.Uint64("min_confs"))
   332  	req := &lnrpc.SendCoinsRequest{
   333  		Addr:             addr,
   334  		Amount:           amt,
   335  		TargetConf:       int32(ctx.Int64("conf_target")),
   336  		AtomsPerByte:     ctx.Int64("atoms_per_byte"),
   337  		SendAll:          ctx.Bool("sweepall"),
   338  		Label:            ctx.String(txLabelFlag.Name),
   339  		MinConfs:         minConfs,
   340  		SpendUnconfirmed: minConfs == 0,
   341  	}
   342  	txid, err := client.SendCoins(ctxc, req)
   343  	if err != nil {
   344  		return err
   345  	}
   346  
   347  	printRespJSON(txid)
   348  	return nil
   349  }
   350  
   351  var listUnspentCommand = cli.Command{
   352  	Name:      "listunspent",
   353  	Category:  "On-chain",
   354  	Usage:     "List UTXOs available for spending.",
   355  	ArgsUsage: "[min-confs [max-confs]] [--unconfirmed_only]",
   356  	Description: `
   357  	For each spendable UTXO currently in the wallet, with at least 'min_confs'
   358  	confirmations, and at most 'max_confs' confirmations, lists the txid,
   359  	index, amount, address, address type, scriptPubkey and number of
   360  	confirmations.  Use '--min_confs=0' to include unconfirmed coins. To list
   361  	all coins with at least min_confs confirmations, omit the second
   362  	argument or flag '--max_confs'. To list all confirmed and unconfirmed
   363  	coins, no arguments are required. To see only unconfirmed coins, use
   364  	'--unconfirmed_only' with '--min_confs' and '--max_confs' set to zero or
   365  	not present.
   366  	`,
   367  	Flags: []cli.Flag{
   368  		cli.Int64Flag{
   369  			Name:  "min_confs",
   370  			Usage: "The minimum number of confirmations for a UTXO",
   371  		},
   372  		cli.Int64Flag{
   373  			Name:  "max_confs",
   374  			Usage: "The maximum number of confirmations for a UTXO",
   375  		},
   376  		cli.BoolFlag{
   377  			Name: "unconfirmed_only",
   378  			Usage: "When min_confs and max_confs are zero, " +
   379  				"setting false implicitly overrides 'max_confs' " +
   380  				"to be MaxInt32, otherwise 'max_confs' remains " +
   381  				"zero. An error is returned if the value is " +
   382  				"true and both 'min_confs' and 'max_confs' are " +
   383  				"non-zero. (default: false)",
   384  		},
   385  	},
   386  	Action: actionDecorator(listUnspent),
   387  }
   388  
   389  func listUnspent(ctx *cli.Context) error {
   390  	var (
   391  		minConfirms int64
   392  		maxConfirms int64
   393  		err         error
   394  	)
   395  	ctxc := getContext()
   396  	args := ctx.Args()
   397  
   398  	if ctx.IsSet("max_confs") && !ctx.IsSet("min_confs") {
   399  		return fmt.Errorf("max_confs cannot be set without " +
   400  			"min_confs being set")
   401  	}
   402  
   403  	switch {
   404  	case ctx.IsSet("min_confs"):
   405  		minConfirms = ctx.Int64("min_confs")
   406  	case args.Present():
   407  		minConfirms, err = strconv.ParseInt(args.First(), 10, 64)
   408  		if err != nil {
   409  			cli.ShowCommandHelp(ctx, "listunspent")
   410  			return nil
   411  		}
   412  		args = args.Tail()
   413  	}
   414  
   415  	switch {
   416  	case ctx.IsSet("max_confs"):
   417  		maxConfirms = ctx.Int64("max_confs")
   418  	case args.Present():
   419  		maxConfirms, err = strconv.ParseInt(args.First(), 10, 64)
   420  		if err != nil {
   421  			cli.ShowCommandHelp(ctx, "listunspent")
   422  			return nil
   423  		}
   424  		args = args.Tail()
   425  	}
   426  
   427  	unconfirmedOnly := ctx.Bool("unconfirmed_only")
   428  
   429  	// Force minConfirms and maxConfirms to be zero if unconfirmedOnly is
   430  	// true.
   431  	if unconfirmedOnly && (minConfirms != 0 || maxConfirms != 0) {
   432  		cli.ShowCommandHelp(ctx, "listunspent")
   433  		return nil
   434  	}
   435  
   436  	// When unconfirmedOnly is inactive, we will override maxConfirms to be
   437  	// a MaxInt32 to return all confirmed and unconfirmed utxos.
   438  	if maxConfirms == 0 && !unconfirmedOnly {
   439  		maxConfirms = math.MaxInt32
   440  	}
   441  
   442  	client, cleanUp := getClient(ctx)
   443  	defer cleanUp()
   444  
   445  	req := &lnrpc.ListUnspentRequest{
   446  		MinConfs: int32(minConfirms),
   447  		MaxConfs: int32(maxConfirms),
   448  	}
   449  	resp, err := client.ListUnspent(ctxc, req)
   450  	if err != nil {
   451  		return err
   452  	}
   453  
   454  	// Parse the response into the final json object that will be printed
   455  	// to stdout. At the moment, this filters out the raw txid bytes from
   456  	// each utxo's outpoint and only prints the txid string.
   457  	var listUnspentResp = struct {
   458  		Utxos []*Utxo `json:"utxos"`
   459  	}{
   460  		Utxos: make([]*Utxo, 0, len(resp.Utxos)),
   461  	}
   462  	for _, protoUtxo := range resp.Utxos {
   463  		utxo := NewUtxoFromProto(protoUtxo)
   464  		listUnspentResp.Utxos = append(listUnspentResp.Utxos, utxo)
   465  	}
   466  
   467  	printJSON(listUnspentResp)
   468  
   469  	return nil
   470  }
   471  
   472  var sendManyCommand = cli.Command{
   473  	Name:      "sendmany",
   474  	Category:  "On-chain",
   475  	Usage:     "Send decred on-chain to multiple addresses.",
   476  	ArgsUsage: "send-json-string [--conf_target=N] [--atoms_per_byte=P]",
   477  	Description: `
   478  	Create and broadcast a transaction paying the specified amount(s) to the passed address(es).
   479  
   480  	The 'send-json-string' param decodes addresses and the amount to send
   481  	respectively in the following format:
   482  
   483  	    {"ExampleAddr": NumCoinsInAtoms, "SecondAddr": NumCoins}
   484  	`,
   485  	Flags: []cli.Flag{
   486  		cli.Int64Flag{
   487  			Name: "conf_target",
   488  			Usage: "The number of blocks that the transaction *should* " +
   489  				"confirm in, will be used for fee estimation (optional)",
   490  		},
   491  		cli.Int64Flag{
   492  			Name: "atoms_per_byte",
   493  			Usage: "Manual fee expressed in atom/byte that should be " +
   494  				"used when crafting the transaction (optional)",
   495  		},
   496  		cli.Uint64Flag{
   497  			Name: "min_confs",
   498  			Usage: "(optional) the minimum number of confirmations " +
   499  				"each one of your outputs used for the transaction " +
   500  				"must satisfy",
   501  			Value: defaultUtxoMinConf,
   502  		},
   503  		txLabelFlag,
   504  	},
   505  	Action: actionDecorator(sendMany),
   506  }
   507  
   508  func sendMany(ctx *cli.Context) error {
   509  	ctxc := getContext()
   510  	var amountToAddr map[string]int64
   511  
   512  	jsonMap := ctx.Args().First()
   513  	if err := json.Unmarshal([]byte(jsonMap), &amountToAddr); err != nil {
   514  		return err
   515  	}
   516  
   517  	if ctx.IsSet("conf_target") && ctx.IsSet("atoms_per_byte") {
   518  		return fmt.Errorf("either conf_target or atoms_per_byte should be " +
   519  			"set, but not both")
   520  	}
   521  
   522  	client, cleanUp := getClient(ctx)
   523  	defer cleanUp()
   524  
   525  	minConfs := int32(ctx.Uint64("min_confs"))
   526  	txid, err := client.SendMany(ctxc, &lnrpc.SendManyRequest{
   527  		AddrToAmount:     amountToAddr,
   528  		TargetConf:       int32(ctx.Int64("conf_target")),
   529  		AtomsPerByte:     ctx.Int64("atoms_per_byte"),
   530  		Label:            ctx.String(txLabelFlag.Name),
   531  		MinConfs:         minConfs,
   532  		SpendUnconfirmed: minConfs == 0,
   533  	})
   534  	if err != nil {
   535  		return err
   536  	}
   537  
   538  	printRespJSON(txid)
   539  	return nil
   540  }
   541  
   542  var connectCommand = cli.Command{
   543  	Name:      "connect",
   544  	Category:  "Peers",
   545  	Usage:     "Connect to a remote dcrlnd peer.",
   546  	ArgsUsage: "<pubkey>@host",
   547  	Description: `
   548  	Connect to a peer using its <pubkey> and host.
   549  
   550  	A custom timeout on the connection is supported. For instance, to timeout
   551  	the connection request in 30 seconds, use the following:
   552  
   553  	lncli connect <pubkey>@host --timeout 30s
   554  	`,
   555  	Flags: []cli.Flag{
   556  		cli.BoolFlag{
   557  			Name: "perm",
   558  			Usage: "If set, the daemon will attempt to persistently " +
   559  				"connect to the target peer. " +
   560  				"If not, the call will be synchronous.",
   561  		},
   562  		cli.DurationFlag{
   563  			Name: "timeout",
   564  			Usage: "The connection timeout value for current request. " +
   565  				"Valid uints are {ms, s, m, h}.\n" +
   566  				"If not set, the global connection " +
   567  				"timeout value (default to 120s) is used.",
   568  		},
   569  	},
   570  	Action: actionDecorator(connectPeer),
   571  }
   572  
   573  func connectPeer(ctx *cli.Context) error {
   574  	ctxc := getContext()
   575  	client, cleanUp := getClient(ctx)
   576  	defer cleanUp()
   577  
   578  	targetAddress := ctx.Args().First()
   579  	splitAddr := strings.Split(targetAddress, "@")
   580  	if len(splitAddr) != 2 {
   581  		return fmt.Errorf("target address expected in format: " +
   582  			"pubkey@host:port")
   583  	}
   584  
   585  	addr := &lnrpc.LightningAddress{
   586  		Pubkey: splitAddr[0],
   587  		Host:   splitAddr[1],
   588  	}
   589  	req := &lnrpc.ConnectPeerRequest{
   590  		Addr:    addr,
   591  		Perm:    ctx.Bool("perm"),
   592  		Timeout: uint64(ctx.Duration("timeout").Seconds()),
   593  	}
   594  
   595  	lnid, err := client.ConnectPeer(ctxc, req)
   596  	if err != nil {
   597  		return err
   598  	}
   599  
   600  	printRespJSON(lnid)
   601  	return nil
   602  }
   603  
   604  var disconnectCommand = cli.Command{
   605  	Name:      "disconnect",
   606  	Category:  "Peers",
   607  	Usage:     "Disconnect a remote dcrlnd peer identified by public key.",
   608  	ArgsUsage: "<pubkey>",
   609  	Flags: []cli.Flag{
   610  		cli.StringFlag{
   611  			Name: "node_key",
   612  			Usage: "The hex-encoded compressed public key of the peer " +
   613  				"to disconnect from",
   614  		},
   615  	},
   616  	Action: actionDecorator(disconnectPeer),
   617  }
   618  
   619  func disconnectPeer(ctx *cli.Context) error {
   620  	ctxc := getContext()
   621  	client, cleanUp := getClient(ctx)
   622  	defer cleanUp()
   623  
   624  	var pubKey string
   625  	switch {
   626  	case ctx.IsSet("node_key"):
   627  		pubKey = ctx.String("node_key")
   628  	case ctx.Args().Present():
   629  		pubKey = ctx.Args().First()
   630  	default:
   631  		return fmt.Errorf("must specify target public key")
   632  	}
   633  
   634  	req := &lnrpc.DisconnectPeerRequest{
   635  		PubKey: pubKey,
   636  	}
   637  
   638  	lnid, err := client.DisconnectPeer(ctxc, req)
   639  	if err != nil {
   640  		return err
   641  	}
   642  
   643  	printRespJSON(lnid)
   644  	return nil
   645  }
   646  
   647  // TODO(roasbeef): also allow short relative channel ID.
   648  
   649  var closeChannelCommand = cli.Command{
   650  	Name:     "closechannel",
   651  	Category: "Channels",
   652  	Usage:    "Close an existing channel.",
   653  	Description: `
   654  	Close an existing channel. The channel can be closed either cooperatively,
   655  	or unilaterally with '--force'.
   656  
   657  	A unilateral channel closure means that the latest commitment
   658  	transaction will be broadcast to the network. As a result, any settled
   659  	funds will be time locked for a few blocks before they can be spent.
   660  
   661  	In the case of a cooperative closure, one can manually set the fee to
   662  	be used for the closing transaction via either the '--conf_target' or
   663  	'--atoms_per_byte' arguments. This will be the starting value used during
   664  	fee negotiation. This is optional.
   665  
   666  	In the case of a cooperative closure, one can manually set the address
   667  	to deliver funds to upon closure. This is optional, and may only be used
   668  	if an upfront shutdown address has not already been set. If neither are
   669  	set the funds will be delivered to a new wallet address.
   670  
   671  	To view which 'funding_txids' or 'output_indexes' can be used for a channel close,
   672  	see the 'channel_point' values within the 'listchannels' command output.
   673  	The format for a 'channel_point' is 'funding_txid:output_index'.`,
   674  	ArgsUsage: "funding_txid [output_index]",
   675  	Flags: []cli.Flag{
   676  		cli.StringFlag{
   677  			Name:  "funding_txid",
   678  			Usage: "The txid of the channel's funding transaction",
   679  		},
   680  		cli.IntFlag{
   681  			Name: "output_index",
   682  			Usage: "The output index for the funding output of the funding " +
   683  				"transaction",
   684  		},
   685  		cli.BoolFlag{
   686  			Name:  "force",
   687  			Usage: "Attempt an uncooperative closure",
   688  		},
   689  		cli.BoolFlag{
   690  			Name:  "block",
   691  			Usage: "Block until the channel is closed",
   692  		},
   693  		cli.Int64Flag{
   694  			Name: "conf_target",
   695  			Usage: "The number of blocks that the " +
   696  				"transaction *should* confirm in, will be " +
   697  				"used for fee estimation. If not set, " +
   698  				"then the conf-target value set in the main " +
   699  				"lnd config will be used.",
   700  		},
   701  		cli.Int64Flag{
   702  			Name: "atoms_per_byte",
   703  			Usage: "A manual fee expressed in " +
   704  				"atom/byte that should be used when crafting " +
   705  				"the transaction (optional)",
   706  		},
   707  		cli.StringFlag{
   708  			Name: "delivery_addr",
   709  			Usage: "An address to deliver funds " +
   710  				"upon cooperative channel closing, may only " +
   711  				"be used if an upfront shutdown address is not " +
   712  				"already set (optional)",
   713  		},
   714  	},
   715  	Action: actionDecorator(closeChannel),
   716  }
   717  
   718  func closeChannel(ctx *cli.Context) error {
   719  	ctxc := getContext()
   720  	client, cleanUp := getClient(ctx)
   721  	defer cleanUp()
   722  
   723  	// Show command help if no arguments and flags were provided.
   724  	if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
   725  		cli.ShowCommandHelp(ctx, "closechannel")
   726  		return nil
   727  	}
   728  
   729  	channelPoint, err := parseChannelPoint(ctx)
   730  	if err != nil {
   731  		return err
   732  	}
   733  
   734  	// TODO(roasbeef): implement time deadline within server
   735  	req := &lnrpc.CloseChannelRequest{
   736  		ChannelPoint:    channelPoint,
   737  		Force:           ctx.Bool("force"),
   738  		TargetConf:      int32(ctx.Int64("conf_target")),
   739  		AtomsPerByte:    ctx.Int64("atoms_per_byte"),
   740  		DeliveryAddress: ctx.String("delivery_addr"),
   741  	}
   742  
   743  	// After parsing the request, we'll spin up a goroutine that will
   744  	// retrieve the closing transaction ID when attempting to close the
   745  	// channel. We do this because `executeChannelClose` can block, so we
   746  	// would like to present the closing transaction ID to the user as soon
   747  	// as it is broadcasted.
   748  	var wg sync.WaitGroup
   749  	txidChan := make(chan string, 1)
   750  
   751  	wg.Add(1)
   752  	go func() {
   753  		defer wg.Done()
   754  
   755  		printJSON(struct {
   756  			ClosingTxid string `json:"closing_txid"`
   757  		}{
   758  			ClosingTxid: <-txidChan,
   759  		})
   760  	}()
   761  
   762  	err = executeChannelClose(ctxc, client, req, txidChan, ctx.Bool("block"))
   763  	if err != nil {
   764  		return err
   765  	}
   766  
   767  	// In the case that the user did not provide the `block` flag, then we
   768  	// need to wait for the goroutine to be done to prevent it from being
   769  	// destroyed when exiting before printing the closing transaction ID.
   770  	wg.Wait()
   771  
   772  	return nil
   773  }
   774  
   775  // executeChannelClose attempts to close the channel from a request. The closing
   776  // transaction ID is sent through `txidChan` as soon as it is broadcasted to the
   777  // network. The block boolean is used to determine if we should block until the
   778  // closing transaction receives all of its required confirmations.
   779  func executeChannelClose(ctxc context.Context, client lnrpc.LightningClient,
   780  	req *lnrpc.CloseChannelRequest, txidChan chan<- string, block bool) error {
   781  
   782  	stream, err := client.CloseChannel(ctxc, req)
   783  	if err != nil {
   784  		return err
   785  	}
   786  
   787  	for {
   788  		resp, err := stream.Recv()
   789  		if err == io.EOF {
   790  			return nil
   791  		} else if err != nil {
   792  			return err
   793  		}
   794  
   795  		switch update := resp.Update.(type) {
   796  		case *lnrpc.CloseStatusUpdate_ClosePending:
   797  			closingHash := update.ClosePending.Txid
   798  			txid, err := chainhash.NewHash(closingHash)
   799  			if err != nil {
   800  				return err
   801  			}
   802  
   803  			txidChan <- txid.String()
   804  
   805  			if !block {
   806  				return nil
   807  			}
   808  		case *lnrpc.CloseStatusUpdate_ChanClose:
   809  			return nil
   810  		}
   811  	}
   812  }
   813  
   814  var closeAllChannelsCommand = cli.Command{
   815  	Name:     "closeallchannels",
   816  	Category: "Channels",
   817  	Usage:    "Close all existing channels.",
   818  	Description: `
   819  	Close all existing channels.
   820  
   821  	Channels will be closed either cooperatively or unilaterally, depending
   822  	on whether the channel is active or not. If the channel is inactive, any
   823  	settled funds within it will be time locked for a few blocks before they
   824  	can be spent.
   825  
   826  	You can request to close inactive channels only by using the
   827  	'--inactive_only' flag.
   828  
   829  	By default, confirmation is prompted each time an inactive
   830  	channel is requested to be closed. To avoid this, one can set the
   831  	'--force' flag, which will only prompt for confirmation once for all
   832  	inactive channels and proceed to close them.
   833  
   834  	In the case of cooperative closures, one can manually set the fee to
   835  	be used for the closing transactions via either the '--conf_target' or
   836  	'--atoms_per_byte' arguments. This will be the starting value used during
   837  	fee negotiation. This is optional.`,
   838  	Flags: []cli.Flag{
   839  		cli.BoolFlag{
   840  			Name:  "inactive_only",
   841  			Usage: "Close inactive channels only",
   842  		},
   843  		cli.BoolFlag{
   844  			Name: "force",
   845  			Usage: "Ask for confirmation once before attempting " +
   846  				"to close existing channels",
   847  		},
   848  		cli.Int64Flag{
   849  			Name: "conf_target",
   850  			Usage: "The number of blocks that the " +
   851  				"closing transactions *should* confirm in, will be " +
   852  				"used for fee estimation (optional)",
   853  		},
   854  		cli.Int64Flag{
   855  			Name: "atoms_per_byte",
   856  			Usage: "A manual fee expressed in " +
   857  				"atom/byte that should be used when crafting " +
   858  				"the closing transactions (optional)",
   859  		},
   860  	},
   861  	Action: actionDecorator(closeAllChannels),
   862  }
   863  
   864  func closeAllChannels(ctx *cli.Context) error {
   865  	ctxc := getContext()
   866  	client, cleanUp := getClient(ctx)
   867  	defer cleanUp()
   868  
   869  	listReq := &lnrpc.ListChannelsRequest{}
   870  	openChannels, err := client.ListChannels(ctxc, listReq)
   871  	if err != nil {
   872  		return fmt.Errorf("unable to fetch open channels: %v", err)
   873  	}
   874  
   875  	if len(openChannels.Channels) == 0 {
   876  		return errors.New("no open channels to close")
   877  	}
   878  
   879  	var channelsToClose []*lnrpc.Channel
   880  
   881  	switch {
   882  	case ctx.Bool("force") && ctx.Bool("inactive_only"):
   883  		msg := "Unilaterally close all inactive channels? The funds " +
   884  			"within these channels will be locked for some blocks " +
   885  			"(CSV delay) before they can be spent. (yes/no): "
   886  
   887  		confirmed := promptForConfirmation(msg)
   888  
   889  		// We can safely exit if the user did not confirm.
   890  		if !confirmed {
   891  			return nil
   892  		}
   893  
   894  		// Go through the list of open channels and only add inactive
   895  		// channels to the closing list.
   896  		for _, channel := range openChannels.Channels {
   897  			if !channel.GetActive() {
   898  				channelsToClose = append(
   899  					channelsToClose, channel,
   900  				)
   901  			}
   902  		}
   903  	case ctx.Bool("force"):
   904  		msg := "Close all active and inactive channels? Inactive " +
   905  			"channels will be closed unilaterally, so funds " +
   906  			"within them will be locked for a few blocks (CSV " +
   907  			"delay) before they can be spent. (yes/no): "
   908  
   909  		confirmed := promptForConfirmation(msg)
   910  
   911  		// We can safely exit if the user did not confirm.
   912  		if !confirmed {
   913  			return nil
   914  		}
   915  
   916  		channelsToClose = openChannels.Channels
   917  	default:
   918  		// Go through the list of open channels and determine which
   919  		// should be added to the closing list.
   920  		for _, channel := range openChannels.Channels {
   921  			// If the channel is inactive, we'll attempt to
   922  			// unilaterally close the channel, so we should prompt
   923  			// the user for confirmation beforehand.
   924  			if !channel.GetActive() {
   925  				msg := fmt.Sprintf("Unilaterally close channel "+
   926  					"with node %s and channel point %s? "+
   927  					"The closing transaction will need %d "+
   928  					"confirmations before the funds can be "+
   929  					"spent. (yes/no): ", channel.RemotePubkey,
   930  					channel.ChannelPoint, channel.LocalConstraints.CsvDelay)
   931  
   932  				confirmed := promptForConfirmation(msg)
   933  
   934  				if confirmed {
   935  					channelsToClose = append(
   936  						channelsToClose, channel,
   937  					)
   938  				}
   939  			} else if !ctx.Bool("inactive_only") {
   940  				// Otherwise, we'll only add active channels if
   941  				// we were not requested to close inactive
   942  				// channels only.
   943  				channelsToClose = append(
   944  					channelsToClose, channel,
   945  				)
   946  			}
   947  		}
   948  	}
   949  
   950  	// result defines the result of closing a channel. The closing
   951  	// transaction ID is populated if a channel is successfully closed.
   952  	// Otherwise, the error that prevented closing the channel is populated.
   953  	type result struct {
   954  		RemotePubKey string `json:"remote_pub_key"`
   955  		ChannelPoint string `json:"channel_point"`
   956  		ClosingTxid  string `json:"closing_txid"`
   957  		FailErr      string `json:"error"`
   958  	}
   959  
   960  	// Launch each channel closure in a goroutine in order to execute them
   961  	// in parallel. Once they're all executed, we will print the results as
   962  	// they come.
   963  	resultChan := make(chan result, len(channelsToClose))
   964  	for _, channel := range channelsToClose {
   965  		go func(channel *lnrpc.Channel) {
   966  			res := result{}
   967  			res.RemotePubKey = channel.RemotePubkey
   968  			res.ChannelPoint = channel.ChannelPoint
   969  			defer func() {
   970  				resultChan <- res
   971  			}()
   972  
   973  			// Parse the channel point in order to create the close
   974  			// channel request.
   975  			s := strings.Split(res.ChannelPoint, ":")
   976  			if len(s) != 2 {
   977  				res.FailErr = "expected channel point with " +
   978  					"format txid:index"
   979  				return
   980  			}
   981  			index, err := strconv.ParseUint(s[1], 10, 32)
   982  			if err != nil {
   983  				res.FailErr = fmt.Sprintf("unable to parse "+
   984  					"channel point output index: %v", err)
   985  				return
   986  			}
   987  
   988  			req := &lnrpc.CloseChannelRequest{
   989  				ChannelPoint: &lnrpc.ChannelPoint{
   990  					FundingTxid: &lnrpc.ChannelPoint_FundingTxidStr{
   991  						FundingTxidStr: s[0],
   992  					},
   993  					OutputIndex: uint32(index),
   994  				},
   995  				Force:        !channel.GetActive(),
   996  				TargetConf:   int32(ctx.Int64("conf_target")),
   997  				AtomsPerByte: ctx.Int64("atoms_per_byte"),
   998  			}
   999  
  1000  			txidChan := make(chan string, 1)
  1001  			err = executeChannelClose(ctxc, client, req, txidChan, false)
  1002  			if err != nil {
  1003  				res.FailErr = fmt.Sprintf("unable to close "+
  1004  					"channel: %v", err)
  1005  				return
  1006  			}
  1007  
  1008  			res.ClosingTxid = <-txidChan
  1009  		}(channel)
  1010  	}
  1011  
  1012  	for range channelsToClose {
  1013  		res := <-resultChan
  1014  		printJSON(res)
  1015  	}
  1016  
  1017  	return nil
  1018  }
  1019  
  1020  // promptForConfirmation continuously prompts the user for the message until
  1021  // receiving a response of "yes" or "no" and returns their answer as a bool.
  1022  func promptForConfirmation(msg string) bool {
  1023  	reader := bufio.NewReader(os.Stdin)
  1024  
  1025  	for {
  1026  		fmt.Print(msg)
  1027  
  1028  		answer, err := reader.ReadString('\n')
  1029  		if err != nil {
  1030  			return false
  1031  		}
  1032  
  1033  		answer = strings.ToLower(strings.TrimSpace(answer))
  1034  
  1035  		switch {
  1036  		case answer == "yes":
  1037  			return true
  1038  		case answer == "no":
  1039  			return false
  1040  		default:
  1041  			continue
  1042  		}
  1043  	}
  1044  }
  1045  
  1046  var abandonChannelCommand = cli.Command{
  1047  	Name:     "abandonchannel",
  1048  	Category: "Channels",
  1049  	Usage:    "Abandon an existing channel.",
  1050  	Description: `
  1051  	Removes all channel state from the database except for a close
  1052  	summary. This method can be used to get rid of permanently unusable
  1053  	channels due to bugs fixed in newer versions of dcrlnd.
  1054  
  1055  	Only available when dcrlnd is built in debug mode. The flag
  1056  	--i_know_what_im_doing can be set to override the debug/dev mode
  1057  	requirement.
  1058  
  1059  	To view which 'funding_txids' or 'output_indexes' can be used for this command,
  1060  	see the 'channel_point' values within the 'listchannels' command output.
  1061  	The format for a 'channel_point' is 'funding_txid:output_index'.`,
  1062  	ArgsUsage: "funding_txid [output_index]",
  1063  	Flags: []cli.Flag{
  1064  		cli.StringFlag{
  1065  			Name:  "funding_txid",
  1066  			Usage: "The txid of the channel's funding transaction",
  1067  		},
  1068  		cli.IntFlag{
  1069  			Name: "output_index",
  1070  			Usage: "The output index for the funding output of the funding " +
  1071  				"transaction",
  1072  		},
  1073  		cli.BoolFlag{
  1074  			Name: "i_know_what_i_am_doing",
  1075  			Usage: "override the requirement for lnd needing to " +
  1076  				"be in dev/debug mode to use this command; " +
  1077  				"when setting this the user attests that " +
  1078  				"they know the danger of using this command " +
  1079  				"on channels and that doing so can lead to " +
  1080  				"loss of funds if the channel funding TX " +
  1081  				"ever confirms (or was confirmed)",
  1082  		},
  1083  	},
  1084  	Action: actionDecorator(abandonChannel),
  1085  }
  1086  
  1087  func abandonChannel(ctx *cli.Context) error {
  1088  	ctxc := getContext()
  1089  	client, cleanUp := getClient(ctx)
  1090  	defer cleanUp()
  1091  
  1092  	// Show command help if no arguments and flags were provided.
  1093  	if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
  1094  		cli.ShowCommandHelp(ctx, "abandonchannel")
  1095  		return nil
  1096  	}
  1097  
  1098  	channelPoint, err := parseChannelPoint(ctx)
  1099  	if err != nil {
  1100  		return err
  1101  	}
  1102  
  1103  	req := &lnrpc.AbandonChannelRequest{
  1104  		ChannelPoint:      channelPoint,
  1105  		IKnowWhatIAmDoing: ctx.Bool("i_know_what_i_am_doing"),
  1106  	}
  1107  
  1108  	resp, err := client.AbandonChannel(ctxc, req)
  1109  	if err != nil {
  1110  		return err
  1111  	}
  1112  
  1113  	printRespJSON(resp)
  1114  	return nil
  1115  }
  1116  
  1117  // parseChannelPoint parses a funding txid and output index from the command
  1118  // line. Both named options as well as unnamed parameters are supported.
  1119  func parseChannelPoint(ctx *cli.Context) (*lnrpc.ChannelPoint, error) {
  1120  	channelPoint := &lnrpc.ChannelPoint{}
  1121  
  1122  	args := ctx.Args()
  1123  
  1124  	switch {
  1125  	case ctx.IsSet("funding_txid"):
  1126  		channelPoint.FundingTxid = &lnrpc.ChannelPoint_FundingTxidStr{
  1127  			FundingTxidStr: ctx.String("funding_txid"),
  1128  		}
  1129  	case args.Present():
  1130  		channelPoint.FundingTxid = &lnrpc.ChannelPoint_FundingTxidStr{
  1131  			FundingTxidStr: args.First(),
  1132  		}
  1133  		args = args.Tail()
  1134  	default:
  1135  		return nil, fmt.Errorf("funding txid argument missing")
  1136  	}
  1137  
  1138  	switch {
  1139  	case ctx.IsSet("output_index"):
  1140  		channelPoint.OutputIndex = uint32(ctx.Int("output_index"))
  1141  	case args.Present():
  1142  		index, err := strconv.ParseUint(args.First(), 10, 32)
  1143  		if err != nil {
  1144  			return nil, fmt.Errorf("unable to decode output index: %v", err)
  1145  		}
  1146  		channelPoint.OutputIndex = uint32(index)
  1147  	default:
  1148  		channelPoint.OutputIndex = 0
  1149  	}
  1150  
  1151  	return channelPoint, nil
  1152  }
  1153  
  1154  var listPeersCommand = cli.Command{
  1155  	Name:     "listpeers",
  1156  	Category: "Peers",
  1157  	Usage:    "List all active, currently connected peers.",
  1158  	Flags: []cli.Flag{
  1159  		cli.BoolFlag{
  1160  			Name:  "list_errors",
  1161  			Usage: "list a full set of most recent errors for the peer",
  1162  		},
  1163  	},
  1164  	Action: actionDecorator(listPeers),
  1165  }
  1166  
  1167  func listPeers(ctx *cli.Context) error {
  1168  	ctxc := getContext()
  1169  	client, cleanUp := getClient(ctx)
  1170  	defer cleanUp()
  1171  
  1172  	// By default, we display a single error on the cli. If the user
  1173  	// specifically requests a full error set, then we will provide it.
  1174  	req := &lnrpc.ListPeersRequest{
  1175  		LatestError: !ctx.IsSet("list_errors"),
  1176  	}
  1177  	resp, err := client.ListPeers(ctxc, req)
  1178  	if err != nil {
  1179  		return err
  1180  	}
  1181  
  1182  	printRespJSON(resp)
  1183  	return nil
  1184  }
  1185  
  1186  var walletBalanceCommand = cli.Command{
  1187  	Name:     "walletbalance",
  1188  	Category: "Wallet",
  1189  	Usage:    "Compute and display the wallet's current balance.",
  1190  	Action:   actionDecorator(walletBalance),
  1191  }
  1192  
  1193  func walletBalance(ctx *cli.Context) error {
  1194  	ctxc := getContext()
  1195  	client, cleanUp := getClient(ctx)
  1196  	defer cleanUp()
  1197  
  1198  	req := &lnrpc.WalletBalanceRequest{}
  1199  	resp, err := client.WalletBalance(ctxc, req)
  1200  	if err != nil {
  1201  		return err
  1202  	}
  1203  
  1204  	printRespJSON(resp)
  1205  	return nil
  1206  }
  1207  
  1208  var channelBalanceCommand = cli.Command{
  1209  	Name:     "channelbalance",
  1210  	Category: "Channels",
  1211  	Usage: "Returns the sum of the total available channel balance across " +
  1212  		"all open channels.",
  1213  	Action: actionDecorator(channelBalance),
  1214  }
  1215  
  1216  func channelBalance(ctx *cli.Context) error {
  1217  	ctxc := getContext()
  1218  	client, cleanUp := getClient(ctx)
  1219  	defer cleanUp()
  1220  
  1221  	req := &lnrpc.ChannelBalanceRequest{}
  1222  	resp, err := client.ChannelBalance(ctxc, req)
  1223  	if err != nil {
  1224  		return err
  1225  	}
  1226  
  1227  	printRespJSON(resp)
  1228  	return nil
  1229  }
  1230  
  1231  var getInfoCommand = cli.Command{
  1232  	Name:   "getinfo",
  1233  	Usage:  "Returns basic information related to the active daemon.",
  1234  	Action: actionDecorator(getInfo),
  1235  }
  1236  
  1237  func getInfo(ctx *cli.Context) error {
  1238  	ctxc := getContext()
  1239  	client, cleanUp := getClient(ctx)
  1240  	defer cleanUp()
  1241  
  1242  	req := &lnrpc.GetInfoRequest{}
  1243  	resp, err := client.GetInfo(ctxc, req)
  1244  	if err != nil {
  1245  		return err
  1246  	}
  1247  
  1248  	printRespJSON(resp)
  1249  	return nil
  1250  }
  1251  
  1252  var getRecoveryInfoCommand = cli.Command{
  1253  	Name:   "getrecoveryinfo",
  1254  	Usage:  "Display information about an ongoing recovery attempt.",
  1255  	Action: actionDecorator(getRecoveryInfo),
  1256  }
  1257  
  1258  func getRecoveryInfo(ctx *cli.Context) error {
  1259  	ctxc := getContext()
  1260  	client, cleanUp := getClient(ctx)
  1261  	defer cleanUp()
  1262  
  1263  	req := &lnrpc.GetRecoveryInfoRequest{}
  1264  	resp, err := client.GetRecoveryInfo(ctxc, req)
  1265  	if err != nil {
  1266  		return err
  1267  	}
  1268  
  1269  	printRespJSON(resp)
  1270  	return nil
  1271  }
  1272  
  1273  var pendingChannelsCommand = cli.Command{
  1274  	Name:     "pendingchannels",
  1275  	Category: "Channels",
  1276  	Usage:    "Display information pertaining to pending channels.",
  1277  	Action:   actionDecorator(pendingChannels),
  1278  }
  1279  
  1280  func pendingChannels(ctx *cli.Context) error {
  1281  	ctxc := getContext()
  1282  	client, cleanUp := getClient(ctx)
  1283  	defer cleanUp()
  1284  
  1285  	req := &lnrpc.PendingChannelsRequest{}
  1286  	resp, err := client.PendingChannels(ctxc, req)
  1287  	if err != nil {
  1288  		return err
  1289  	}
  1290  
  1291  	printRespJSON(resp)
  1292  
  1293  	return nil
  1294  }
  1295  
  1296  var listChannelsCommand = cli.Command{
  1297  	Name:     "listchannels",
  1298  	Category: "Channels",
  1299  	Usage:    "List all open channels.",
  1300  	Flags: []cli.Flag{
  1301  		cli.BoolFlag{
  1302  			Name:  "active_only",
  1303  			Usage: "Only list channels which are currently active",
  1304  		},
  1305  		cli.BoolFlag{
  1306  			Name:  "inactive_only",
  1307  			Usage: "Only list channels which are currently inactive",
  1308  		},
  1309  		cli.BoolFlag{
  1310  			Name:  "public_only",
  1311  			Usage: "Only list channels which are currently public",
  1312  		},
  1313  		cli.BoolFlag{
  1314  			Name:  "private_only",
  1315  			Usage: "Only list channels which are currently private",
  1316  		},
  1317  		cli.StringFlag{
  1318  			Name: "peer",
  1319  			Usage: "(optional) only display channels with a " +
  1320  				"particular peer, accepts 66-byte, " +
  1321  				"hex-encoded pubkeys",
  1322  		},
  1323  	},
  1324  	Action: actionDecorator(listChannels),
  1325  }
  1326  
  1327  func listChannels(ctx *cli.Context) error {
  1328  	ctxc := getContext()
  1329  	client, cleanUp := getClient(ctx)
  1330  	defer cleanUp()
  1331  
  1332  	peer := ctx.String("peer")
  1333  
  1334  	// If the user requested channels with a particular key, parse the
  1335  	// provided pubkey.
  1336  	var peerKey []byte
  1337  	if len(peer) > 0 {
  1338  		pk, err := route.NewVertexFromStr(peer)
  1339  		if err != nil {
  1340  			return fmt.Errorf("invalid --peer pubkey: %v", err)
  1341  		}
  1342  
  1343  		peerKey = pk[:]
  1344  	}
  1345  
  1346  	req := &lnrpc.ListChannelsRequest{
  1347  		ActiveOnly:   ctx.Bool("active_only"),
  1348  		InactiveOnly: ctx.Bool("inactive_only"),
  1349  		PublicOnly:   ctx.Bool("public_only"),
  1350  		PrivateOnly:  ctx.Bool("private_only"),
  1351  		Peer:         peerKey,
  1352  	}
  1353  
  1354  	resp, err := client.ListChannels(ctxc, req)
  1355  	if err != nil {
  1356  		return err
  1357  	}
  1358  
  1359  	printRespJSON(resp)
  1360  
  1361  	return nil
  1362  }
  1363  
  1364  var closedChannelsCommand = cli.Command{
  1365  	Name:     "closedchannels",
  1366  	Category: "Channels",
  1367  	Usage:    "List all closed channels.",
  1368  	Flags: []cli.Flag{
  1369  		cli.BoolFlag{
  1370  			Name:  "cooperative",
  1371  			Usage: "List channels that were closed cooperatively",
  1372  		},
  1373  		cli.BoolFlag{
  1374  			Name: "local_force",
  1375  			Usage: "List channels that were force-closed " +
  1376  				"by the local node",
  1377  		},
  1378  		cli.BoolFlag{
  1379  			Name: "remote_force",
  1380  			Usage: "List channels that were force-closed " +
  1381  				"by the remote node",
  1382  		},
  1383  		cli.BoolFlag{
  1384  			Name: "breach",
  1385  			Usage: "List channels for which the remote node " +
  1386  				"attempted to broadcast a prior " +
  1387  				"revoked channel state",
  1388  		},
  1389  		cli.BoolFlag{
  1390  			Name:  "funding_canceled",
  1391  			Usage: "List channels that were never fully opened",
  1392  		},
  1393  		cli.BoolFlag{
  1394  			Name: "abandoned",
  1395  			Usage: "List channels that were abandoned by " +
  1396  				"the local node",
  1397  		},
  1398  	},
  1399  	Action: actionDecorator(closedChannels),
  1400  }
  1401  
  1402  func closedChannels(ctx *cli.Context) error {
  1403  	ctxc := getContext()
  1404  	client, cleanUp := getClient(ctx)
  1405  	defer cleanUp()
  1406  
  1407  	req := &lnrpc.ClosedChannelsRequest{
  1408  		Cooperative:     ctx.Bool("cooperative"),
  1409  		LocalForce:      ctx.Bool("local_force"),
  1410  		RemoteForce:     ctx.Bool("remote_force"),
  1411  		Breach:          ctx.Bool("breach"),
  1412  		FundingCanceled: ctx.Bool("funding_canceled"),
  1413  		Abandoned:       ctx.Bool("abandoned"),
  1414  	}
  1415  
  1416  	resp, err := client.ClosedChannels(ctxc, req)
  1417  	if err != nil {
  1418  		return err
  1419  	}
  1420  
  1421  	printRespJSON(resp)
  1422  
  1423  	return nil
  1424  }
  1425  
  1426  var describeGraphCommand = cli.Command{
  1427  	Name:     "describegraph",
  1428  	Category: "Graph",
  1429  	Description: "Prints a human readable version of the known channel " +
  1430  		"graph from the PoV of the node",
  1431  	Usage: "Describe the network graph.",
  1432  	Flags: []cli.Flag{
  1433  		cli.BoolFlag{
  1434  			Name: "include_unannounced",
  1435  			Usage: "If set, unannounced channels will be included in the " +
  1436  				"graph. Unannounced channels are both private channels, and " +
  1437  				"public channels that are not yet announced to the network.",
  1438  		},
  1439  	},
  1440  	Action: actionDecorator(describeGraph),
  1441  }
  1442  
  1443  func describeGraph(ctx *cli.Context) error {
  1444  	ctxc := getContext()
  1445  	client, cleanUp := getClient(ctx)
  1446  	defer cleanUp()
  1447  
  1448  	req := &lnrpc.ChannelGraphRequest{
  1449  		IncludeUnannounced: ctx.Bool("include_unannounced"),
  1450  	}
  1451  
  1452  	graph, err := client.DescribeGraph(ctxc, req)
  1453  	if err != nil {
  1454  		return err
  1455  	}
  1456  
  1457  	printRespJSON(graph)
  1458  	return nil
  1459  }
  1460  
  1461  var getNodeMetricsCommand = cli.Command{
  1462  	Name:        "getnodemetrics",
  1463  	Category:    "Graph",
  1464  	Description: "Prints out node metrics calculated from the current graph",
  1465  	Usage:       "Get node metrics.",
  1466  	Action:      actionDecorator(getNodeMetrics),
  1467  }
  1468  
  1469  func getNodeMetrics(ctx *cli.Context) error {
  1470  	ctxc := getContext()
  1471  	client, cleanUp := getClient(ctx)
  1472  	defer cleanUp()
  1473  
  1474  	req := &lnrpc.NodeMetricsRequest{
  1475  		Types: []lnrpc.NodeMetricType{lnrpc.NodeMetricType_BETWEENNESS_CENTRALITY},
  1476  	}
  1477  
  1478  	nodeMetrics, err := client.GetNodeMetrics(ctxc, req)
  1479  	if err != nil {
  1480  		return err
  1481  	}
  1482  
  1483  	printRespJSON(nodeMetrics)
  1484  	return nil
  1485  }
  1486  
  1487  var getChanInfoCommand = cli.Command{
  1488  	Name:     "getchaninfo",
  1489  	Category: "Graph",
  1490  	Usage:    "Get the state of a channel.",
  1491  	Description: "Prints out the latest authenticated state for a " +
  1492  		"particular channel",
  1493  	ArgsUsage: "chan_id",
  1494  	Flags: []cli.Flag{
  1495  		cli.Uint64Flag{
  1496  			Name:  "chan_id",
  1497  			Usage: "The 8-byte compact channel ID to query for",
  1498  		},
  1499  	},
  1500  	Action: actionDecorator(getChanInfo),
  1501  }
  1502  
  1503  func getChanInfo(ctx *cli.Context) error {
  1504  	ctxc := getContext()
  1505  	client, cleanUp := getClient(ctx)
  1506  	defer cleanUp()
  1507  
  1508  	var (
  1509  		chanID uint64
  1510  		err    error
  1511  	)
  1512  
  1513  	switch {
  1514  	case ctx.IsSet("chan_id"):
  1515  		chanID = ctx.Uint64("chan_id")
  1516  	case ctx.Args().Present():
  1517  		chanID, err = strconv.ParseUint(ctx.Args().First(), 10, 64)
  1518  		if err != nil {
  1519  			return err
  1520  		}
  1521  	default:
  1522  		return fmt.Errorf("chan_id argument missing")
  1523  	}
  1524  
  1525  	req := &lnrpc.ChanInfoRequest{
  1526  		ChanId: chanID,
  1527  	}
  1528  
  1529  	chanInfo, err := client.GetChanInfo(ctxc, req)
  1530  	if err != nil {
  1531  		return err
  1532  	}
  1533  
  1534  	printRespJSON(chanInfo)
  1535  	return nil
  1536  }
  1537  
  1538  var getNodeInfoCommand = cli.Command{
  1539  	Name:     "getnodeinfo",
  1540  	Category: "Graph",
  1541  	Usage:    "Get information on a specific node.",
  1542  	Description: "Prints out the latest authenticated node state for an " +
  1543  		"advertised node",
  1544  	Flags: []cli.Flag{
  1545  		cli.StringFlag{
  1546  			Name: "pub_key",
  1547  			Usage: "The 33-byte hex-encoded compressed public of the target " +
  1548  				"node",
  1549  		},
  1550  		cli.BoolFlag{
  1551  			Name: "include_channels",
  1552  			Usage: "If true, will return all known channels " +
  1553  				"associated with the node",
  1554  		},
  1555  	},
  1556  	Action: actionDecorator(getNodeInfo),
  1557  }
  1558  
  1559  func getNodeInfo(ctx *cli.Context) error {
  1560  	ctxc := getContext()
  1561  	client, cleanUp := getClient(ctx)
  1562  	defer cleanUp()
  1563  
  1564  	args := ctx.Args()
  1565  
  1566  	var pubKey string
  1567  	switch {
  1568  	case ctx.IsSet("pub_key"):
  1569  		pubKey = ctx.String("pub_key")
  1570  	case args.Present():
  1571  		pubKey = args.First()
  1572  	default:
  1573  		return fmt.Errorf("pub_key argument missing")
  1574  	}
  1575  
  1576  	req := &lnrpc.NodeInfoRequest{
  1577  		PubKey:          pubKey,
  1578  		IncludeChannels: ctx.Bool("include_channels"),
  1579  	}
  1580  
  1581  	nodeInfo, err := client.GetNodeInfo(ctxc, req)
  1582  	if err != nil {
  1583  		return err
  1584  	}
  1585  
  1586  	printRespJSON(nodeInfo)
  1587  	return nil
  1588  }
  1589  
  1590  var getNetworkInfoCommand = cli.Command{
  1591  	Name:     "getnetworkinfo",
  1592  	Category: "Channels",
  1593  	Usage: "Get statistical information about the current " +
  1594  		"state of the network.",
  1595  	Description: "Returns a set of statistics pertaining to the known " +
  1596  		"channel graph",
  1597  	Action: actionDecorator(getNetworkInfo),
  1598  }
  1599  
  1600  func getNetworkInfo(ctx *cli.Context) error {
  1601  	ctxc := getContext()
  1602  	client, cleanUp := getClient(ctx)
  1603  	defer cleanUp()
  1604  
  1605  	req := &lnrpc.NetworkInfoRequest{}
  1606  
  1607  	netInfo, err := client.GetNetworkInfo(ctxc, req)
  1608  	if err != nil {
  1609  		return err
  1610  	}
  1611  
  1612  	printRespJSON(netInfo)
  1613  	return nil
  1614  }
  1615  
  1616  var debugLevelCommand = cli.Command{
  1617  	Name:  "debuglevel",
  1618  	Usage: "Set the debug level.",
  1619  	Description: `Logging level for all subsystems {trace, debug, info, warn, error, critical, off}
  1620  	You may also specify <subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems
  1621  
  1622  	Use show to list available subsystems`,
  1623  	Flags: []cli.Flag{
  1624  		cli.BoolFlag{
  1625  			Name:  "show",
  1626  			Usage: "if true, then the list of available sub-systems will be printed out",
  1627  		},
  1628  		cli.StringFlag{
  1629  			Name:  "level",
  1630  			Usage: "the level specification to target either a coarse logging level, or granular set of specific sub-systems with logging levels for each",
  1631  		},
  1632  	},
  1633  	Action: actionDecorator(debugLevel),
  1634  }
  1635  
  1636  func debugLevel(ctx *cli.Context) error {
  1637  	ctxc := getContext()
  1638  	client, cleanUp := getClient(ctx)
  1639  	defer cleanUp()
  1640  	req := &lnrpc.DebugLevelRequest{
  1641  		Show:      ctx.Bool("show"),
  1642  		LevelSpec: ctx.String("level"),
  1643  	}
  1644  
  1645  	resp, err := client.DebugLevel(ctxc, req)
  1646  	if err != nil {
  1647  		return err
  1648  	}
  1649  
  1650  	printRespJSON(resp)
  1651  	return nil
  1652  }
  1653  
  1654  var listChainTxnsCommand = cli.Command{
  1655  	Name:     "listchaintxns",
  1656  	Category: "On-chain",
  1657  	Usage:    "List transactions from the wallet.",
  1658  	Flags: []cli.Flag{
  1659  		cli.Int64Flag{
  1660  			Name: "start_height",
  1661  			Usage: "the block height from which to list " +
  1662  				"transactions, inclusive",
  1663  		},
  1664  		cli.Int64Flag{
  1665  			Name: "end_height",
  1666  			Usage: "the block height until which to list " +
  1667  				"transactions, inclusive, to get transactions " +
  1668  				"until the chain tip, including unconfirmed, " +
  1669  				"set this value to -1",
  1670  		},
  1671  	},
  1672  	Description: `
  1673  	List all transactions an address of the wallet was involved in.
  1674  
  1675  	This call will return a list of wallet related transactions that paid
  1676  	to an address our wallet controls, or spent utxos that we held. The
  1677  	start_height and end_height flags can be used to specify an inclusive
  1678  	block range over which to query for transactions. If the end_height is
  1679  	less than the start_height, transactions will be queried in reverse.
  1680  	To get all transactions until the chain tip, including unconfirmed
  1681  	transactions (identifiable with BlockHeight=0), set end_height to -1.
  1682  	By default, this call will get all transactions our wallet was involved
  1683  	in, including unconfirmed transactions.
  1684  `,
  1685  	Action: actionDecorator(listChainTxns),
  1686  }
  1687  
  1688  func listChainTxns(ctx *cli.Context) error {
  1689  	ctxc := getContext()
  1690  	client, cleanUp := getClient(ctx)
  1691  	defer cleanUp()
  1692  
  1693  	req := &lnrpc.GetTransactionsRequest{}
  1694  
  1695  	if ctx.IsSet("start_height") {
  1696  		req.StartHeight = int32(ctx.Int64("start_height"))
  1697  	}
  1698  	if ctx.IsSet("end_height") {
  1699  		req.EndHeight = int32(ctx.Int64("end_height"))
  1700  	}
  1701  
  1702  	resp, err := client.GetTransactions(ctxc, req)
  1703  	if err != nil {
  1704  		return err
  1705  	}
  1706  
  1707  	printRespJSON(resp)
  1708  	return nil
  1709  }
  1710  
  1711  var stopCommand = cli.Command{
  1712  	Name:  "stop",
  1713  	Usage: "Stop and shutdown the daemon.",
  1714  	Description: `
  1715  	Gracefully stop all daemon subsystems before stopping the daemon itself.
  1716  	This is equivalent to stopping it using CTRL-C.`,
  1717  	Action: actionDecorator(stopDaemon),
  1718  }
  1719  
  1720  func stopDaemon(ctx *cli.Context) error {
  1721  	ctxc := getContext()
  1722  	client, cleanUp := getClient(ctx)
  1723  	defer cleanUp()
  1724  
  1725  	_, err := client.StopDaemon(ctxc, &lnrpc.StopRequest{})
  1726  	if err != nil {
  1727  		return err
  1728  	}
  1729  
  1730  	return nil
  1731  }
  1732  
  1733  var signMessageCommand = cli.Command{
  1734  	Name:      "signmessage",
  1735  	Category:  "Wallet",
  1736  	Usage:     "Sign a message with the node's private key.",
  1737  	ArgsUsage: "msg",
  1738  	Description: `
  1739  	Sign msg with the resident node's private key.
  1740  	Returns the signature as a zbase32 string.
  1741  
  1742  	Positional arguments and flags can be used interchangeably but not at the same time!`,
  1743  	Flags: []cli.Flag{
  1744  		cli.StringFlag{
  1745  			Name:  "msg",
  1746  			Usage: "The message to sign",
  1747  		},
  1748  	},
  1749  	Action: actionDecorator(signMessage),
  1750  }
  1751  
  1752  func signMessage(ctx *cli.Context) error {
  1753  	ctxc := getContext()
  1754  	client, cleanUp := getClient(ctx)
  1755  	defer cleanUp()
  1756  
  1757  	var msg []byte
  1758  
  1759  	switch {
  1760  	case ctx.IsSet("msg"):
  1761  		msg = []byte(ctx.String("msg"))
  1762  	case ctx.Args().Present():
  1763  		msg = []byte(ctx.Args().First())
  1764  	default:
  1765  		return fmt.Errorf("msg argument missing")
  1766  	}
  1767  
  1768  	resp, err := client.SignMessage(ctxc, &lnrpc.SignMessageRequest{Msg: msg})
  1769  	if err != nil {
  1770  		return err
  1771  	}
  1772  
  1773  	printRespJSON(resp)
  1774  	return nil
  1775  }
  1776  
  1777  var verifyMessageCommand = cli.Command{
  1778  	Name:      "verifymessage",
  1779  	Category:  "Wallet",
  1780  	Usage:     "Verify a message signed with the signature.",
  1781  	ArgsUsage: "msg signature",
  1782  	Description: `
  1783  	Verify that the message was signed with a properly-formed signature
  1784  	The signature must be zbase32 encoded and signed with the private key of
  1785  	an active node in the resident node's channel database.
  1786  
  1787  	Positional arguments and flags can be used interchangeably but not at the same time!`,
  1788  	Flags: []cli.Flag{
  1789  		cli.StringFlag{
  1790  			Name:  "msg",
  1791  			Usage: "The message to verify",
  1792  		},
  1793  		cli.StringFlag{
  1794  			Name:  "sig",
  1795  			Usage: "The zbase32 encoded signature of the message",
  1796  		},
  1797  	},
  1798  	Action: actionDecorator(verifyMessage),
  1799  }
  1800  
  1801  func verifyMessage(ctx *cli.Context) error {
  1802  	ctxc := getContext()
  1803  	client, cleanUp := getClient(ctx)
  1804  	defer cleanUp()
  1805  
  1806  	var (
  1807  		msg []byte
  1808  		sig string
  1809  	)
  1810  
  1811  	args := ctx.Args()
  1812  
  1813  	switch {
  1814  	case ctx.IsSet("msg"):
  1815  		msg = []byte(ctx.String("msg"))
  1816  	case args.Present():
  1817  		msg = []byte(ctx.Args().First())
  1818  		args = args.Tail()
  1819  	default:
  1820  		return fmt.Errorf("msg argument missing")
  1821  	}
  1822  
  1823  	switch {
  1824  	case ctx.IsSet("sig"):
  1825  		sig = ctx.String("sig")
  1826  	case args.Present():
  1827  		sig = args.First()
  1828  	default:
  1829  		return fmt.Errorf("signature argument missing")
  1830  	}
  1831  
  1832  	req := &lnrpc.VerifyMessageRequest{Msg: msg, Signature: sig}
  1833  	resp, err := client.VerifyMessage(ctxc, req)
  1834  	if err != nil {
  1835  		return err
  1836  	}
  1837  
  1838  	printRespJSON(resp)
  1839  	return nil
  1840  }
  1841  
  1842  var feeReportCommand = cli.Command{
  1843  	Name:     "feereport",
  1844  	Category: "Channels",
  1845  	Usage:    "Display the current fee policies of all active channels.",
  1846  	Description: `
  1847  	Returns the current fee policies of all active channels.
  1848  	Fee policies can be updated using the 'updatechanpolicy' command.`,
  1849  	Action: actionDecorator(feeReport),
  1850  }
  1851  
  1852  func feeReport(ctx *cli.Context) error {
  1853  	ctxc := getContext()
  1854  	client, cleanUp := getClient(ctx)
  1855  	defer cleanUp()
  1856  
  1857  	req := &lnrpc.FeeReportRequest{}
  1858  	resp, err := client.FeeReport(ctxc, req)
  1859  	if err != nil {
  1860  		return err
  1861  	}
  1862  
  1863  	printRespJSON(resp)
  1864  	return nil
  1865  }
  1866  
  1867  var updateChannelPolicyCommand = cli.Command{
  1868  	Name:     "updatechanpolicy",
  1869  	Category: "Channels",
  1870  	Usage: "Update the channel policy for all channels, or a single " +
  1871  		"channel.",
  1872  	ArgsUsage: "base_fee_m_atoms fee_rate time_lock_delta " +
  1873  		"[--max_htlc_m_atoms=N] [channel_point]",
  1874  	Description: `
  1875  	Updates the channel policy for all channels, or just a particular channel
  1876  	identified by its channel point. The update will be committed, and
  1877  	broadcast to the rest of the network within the next batch.
  1878  	Channel points are encoded as 'funding_txid:output_index'`,
  1879  	Flags: []cli.Flag{
  1880  		cli.Int64Flag{
  1881  			Name: "base_fee_m_atoms",
  1882  			Usage: "the base fee in milli-atoms that will " +
  1883  				"Be charged for each forwarded HTLC, regardless " +
  1884  				"of payment size",
  1885  		},
  1886  		cli.StringFlag{
  1887  			Name: "fee_rate",
  1888  			Usage: "the fee rate that will be charged " +
  1889  				"Proportionally based on the value of each " +
  1890  				"forwarded HTLC, the lowest possible rate is 0 " +
  1891  				"with a granularity of 0.000001 (millionths). Can not " +
  1892  				"be set at the same time as fee_rate_ppm.",
  1893  		},
  1894  		cli.Uint64Flag{
  1895  			Name: "fee_rate_ppm",
  1896  			Usage: "the fee rate ppm (parts per million) that " +
  1897  				"will be charged proportionally based on the value of each " +
  1898  				"forwarded HTLC, the lowest possible rate is 0 " +
  1899  				"with a granularity of 0.000001 (millionths). Can not " +
  1900  				"be set at the same time as fee_rate.",
  1901  		},
  1902  		cli.Int64Flag{
  1903  			Name: "time_lock_delta",
  1904  			Usage: "The CLTV delta that will be applied to all " +
  1905  				"forwarded HTLCs",
  1906  		},
  1907  		cli.Uint64Flag{
  1908  			Name: "min_htlc_m_atoms",
  1909  			Usage: "If set, the min HTLC size that will be applied " +
  1910  				"to all forwarded HTLCs. If unset, the min HTLC " +
  1911  				"is left unchanged.",
  1912  		},
  1913  		cli.Uint64Flag{
  1914  			Name: "max_htlc_m_atoms",
  1915  			Usage: "If set, the max HTLC size that will be applied " +
  1916  				"to all forwarded HTLCs. If unset, the max HTLC " +
  1917  				"is left unchanged.",
  1918  		},
  1919  		cli.StringFlag{
  1920  			Name: "chan_point",
  1921  			Usage: "The channel whose fee policy should be " +
  1922  				"updated, if nil the policies for all channels " +
  1923  				"will be updated. Takes the form of 'txid:output_index'",
  1924  		},
  1925  	},
  1926  	Action: actionDecorator(updateChannelPolicy),
  1927  }
  1928  
  1929  func parseChanPoint(s string) (*lnrpc.ChannelPoint, error) {
  1930  	split := strings.Split(s, ":")
  1931  	if len(split) != 2 {
  1932  		return nil, fmt.Errorf("expecting chan_point to be in format of: " +
  1933  			"txid:index")
  1934  	}
  1935  
  1936  	index, err := strconv.ParseInt(split[1], 10, 32)
  1937  	if err != nil {
  1938  		return nil, fmt.Errorf("unable to decode output index: %v", err)
  1939  	}
  1940  
  1941  	txid, err := chainhash.NewHashFromStr(split[0])
  1942  	if err != nil {
  1943  		return nil, fmt.Errorf("unable to parse hex string: %v", err)
  1944  	}
  1945  
  1946  	return &lnrpc.ChannelPoint{
  1947  		FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
  1948  			FundingTxidBytes: txid[:],
  1949  		},
  1950  		OutputIndex: uint32(index),
  1951  	}, nil
  1952  }
  1953  
  1954  func updateChannelPolicy(ctx *cli.Context) error {
  1955  	ctxc := getContext()
  1956  	client, cleanUp := getClient(ctx)
  1957  	defer cleanUp()
  1958  
  1959  	var (
  1960  		baseFee       int64
  1961  		feeRate       float64
  1962  		feeRatePpm    uint64
  1963  		timeLockDelta int64
  1964  		err           error
  1965  	)
  1966  	args := ctx.Args()
  1967  
  1968  	switch {
  1969  	case ctx.IsSet("base_fee_m_atoms"):
  1970  		baseFee = ctx.Int64("base_fee_m_atoms")
  1971  	case args.Present():
  1972  		baseFee, err = strconv.ParseInt(args.First(), 10, 64)
  1973  		if err != nil {
  1974  			return fmt.Errorf("unable to decode base_fee_m_atoms: %v", err)
  1975  		}
  1976  		args = args.Tail()
  1977  	default:
  1978  		return fmt.Errorf("base_fee_m_atoms argument missing")
  1979  	}
  1980  
  1981  	switch {
  1982  	case ctx.IsSet("fee_rate") && ctx.IsSet("fee_rate_ppm"):
  1983  		return fmt.Errorf("fee_rate or fee_rate_ppm can not both be set")
  1984  	case ctx.IsSet("fee_rate"):
  1985  		feeRate = ctx.Float64("fee_rate")
  1986  	case ctx.IsSet("fee_rate_ppm"):
  1987  		feeRatePpm = ctx.Uint64("fee_rate_ppm")
  1988  	case args.Present():
  1989  		feeRate, err = strconv.ParseFloat(args.First(), 64)
  1990  		if err != nil {
  1991  			return fmt.Errorf("unable to decode fee_rate: %v", err)
  1992  		}
  1993  
  1994  		args = args.Tail()
  1995  	default:
  1996  		return fmt.Errorf("fee_rate or fee_rate_ppm argument missing")
  1997  	}
  1998  
  1999  	switch {
  2000  	case ctx.IsSet("time_lock_delta"):
  2001  		timeLockDelta = ctx.Int64("time_lock_delta")
  2002  	case args.Present():
  2003  		timeLockDelta, err = strconv.ParseInt(args.First(), 10, 64)
  2004  		if err != nil {
  2005  			return fmt.Errorf("unable to decode time_lock_delta: %v",
  2006  				err)
  2007  		}
  2008  
  2009  		args = args.Tail()
  2010  	default:
  2011  		return fmt.Errorf("time_lock_delta argument missing")
  2012  	}
  2013  
  2014  	var (
  2015  		chanPoint    *lnrpc.ChannelPoint
  2016  		chanPointStr string
  2017  	)
  2018  
  2019  	switch {
  2020  	case ctx.IsSet("chan_point"):
  2021  		chanPointStr = ctx.String("chan_point")
  2022  	case args.Present():
  2023  		chanPointStr = args.First()
  2024  	}
  2025  
  2026  	if chanPointStr != "" {
  2027  		chanPoint, err = parseChanPoint(chanPointStr)
  2028  		if err != nil {
  2029  			return fmt.Errorf("unable to parse chan point: %v", err)
  2030  		}
  2031  	}
  2032  
  2033  	req := &lnrpc.PolicyUpdateRequest{
  2034  		BaseFeeMAtoms: baseFee,
  2035  		TimeLockDelta: uint32(timeLockDelta),
  2036  		MaxHtlcMAtoms: ctx.Uint64("max_htlc_m_atoms"),
  2037  	}
  2038  
  2039  	if ctx.IsSet("min_htlc_m_atoms") {
  2040  		req.MinHtlcMAtoms = ctx.Uint64("min_htlc_m_atoms")
  2041  		req.MinHtlcMAtomsSpecified = true
  2042  	}
  2043  
  2044  	if chanPoint != nil {
  2045  		req.Scope = &lnrpc.PolicyUpdateRequest_ChanPoint{
  2046  			ChanPoint: chanPoint,
  2047  		}
  2048  	} else {
  2049  		req.Scope = &lnrpc.PolicyUpdateRequest_Global{
  2050  			Global: true,
  2051  		}
  2052  	}
  2053  
  2054  	if feeRate != 0 {
  2055  		req.FeeRate = feeRate
  2056  	} else if feeRatePpm != 0 {
  2057  		req.FeeRatePpm = uint32(feeRatePpm)
  2058  	}
  2059  
  2060  	resp, err := client.UpdateChannelPolicy(ctxc, req)
  2061  	if err != nil {
  2062  		return err
  2063  	}
  2064  
  2065  	// Parse the response into the final json object that will be printed
  2066  	// to stdout. At the moment, this filters out the raw txid bytes from
  2067  	// each failed update's outpoint and only prints the txid string.
  2068  	var listFailedUpdateResp = struct {
  2069  		FailedUpdates []*FailedUpdate `json:"failed_updates"`
  2070  	}{
  2071  		FailedUpdates: make([]*FailedUpdate, 0, len(resp.FailedUpdates)),
  2072  	}
  2073  	for _, protoUpdate := range resp.FailedUpdates {
  2074  		failedUpdate := NewFailedUpdateFromProto(protoUpdate)
  2075  		listFailedUpdateResp.FailedUpdates = append(
  2076  			listFailedUpdateResp.FailedUpdates, failedUpdate)
  2077  	}
  2078  
  2079  	printJSON(listFailedUpdateResp)
  2080  
  2081  	return nil
  2082  }
  2083  
  2084  var exportChanBackupCommand = cli.Command{
  2085  	Name:     "exportchanbackup",
  2086  	Category: "Channels",
  2087  	Usage: "Obtain a static channel back up for a selected channels, " +
  2088  		"or all known channels.",
  2089  	ArgsUsage: "[chan_point] [--all] [--output_file]",
  2090  	Description: `
  2091  	This command allows a user to export a Static Channel Backup (SCB) for
  2092  	a selected channel. SCB's are encrypted backups of a channel's initial
  2093  	state that are encrypted with a key derived from the seed of a user. In
  2094  	the case of partial or complete data loss, the SCB will allow the user
  2095  	to reclaim settled funds in the channel at its final state. The
  2096  	exported channel backups can be restored at a later time using the
  2097  	restorechanbackup command.
  2098  
  2099  	This command will return one of two types of channel backups depending
  2100  	on the set of passed arguments:
  2101  
  2102  	   * If a target channel point is specified, then a single channel
  2103  	     backup containing only the information for that channel will be
  2104  	     returned.
  2105  
  2106  	   * If the '--all' flag is passed, then a multi-channel backup will be
  2107  	     returned. A multi backup is a single encrypted blob (displayed in
  2108  	     hex encoding) that contains several channels in a single cipher
  2109  	     text.
  2110  
  2111  	Both of the backup types can be restored using the 'restorechanbackup'
  2112  	command.
  2113  	`,
  2114  	Flags: []cli.Flag{
  2115  		cli.StringFlag{
  2116  			Name:  "chan_point",
  2117  			Usage: "The target channel to obtain an SCB for",
  2118  		},
  2119  		cli.BoolFlag{
  2120  			Name: "all",
  2121  			Usage: "If specified, then a multi backup of all " +
  2122  				"active channels will be returned",
  2123  		},
  2124  		cli.StringFlag{
  2125  			Name: "output_file",
  2126  			Usage: `
  2127  			If specified, then rather than printing a JSON output
  2128  			of the static channel backup, a serialized version of
  2129  			the backup (either Single or Multi) will be written to
  2130  			the target file, this is the same format used by dcrlnd in
  2131  			its 'channel.backup' file `,
  2132  		},
  2133  	},
  2134  	Action: actionDecorator(exportChanBackup),
  2135  }
  2136  
  2137  func exportChanBackup(ctx *cli.Context) error {
  2138  	ctxc := getContext()
  2139  	client, cleanUp := getClient(ctx)
  2140  	defer cleanUp()
  2141  
  2142  	// Show command help if no arguments provided
  2143  	if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
  2144  		cli.ShowCommandHelp(ctx, "exportchanbackup")
  2145  		return nil
  2146  	}
  2147  
  2148  	var (
  2149  		err          error
  2150  		chanPointStr string
  2151  	)
  2152  	args := ctx.Args()
  2153  
  2154  	switch {
  2155  	case ctx.IsSet("chan_point"):
  2156  		chanPointStr = ctx.String("chan_point")
  2157  
  2158  	case args.Present():
  2159  		chanPointStr = args.First()
  2160  
  2161  	case !ctx.IsSet("all"):
  2162  		return fmt.Errorf("must specify chan_point if --all isn't set")
  2163  	}
  2164  
  2165  	if chanPointStr != "" {
  2166  		chanPointRPC, err := parseChanPoint(chanPointStr)
  2167  		if err != nil {
  2168  			return err
  2169  		}
  2170  
  2171  		chanBackup, err := client.ExportChannelBackup(
  2172  			ctxc, &lnrpc.ExportChannelBackupRequest{
  2173  				ChanPoint: chanPointRPC,
  2174  			},
  2175  		)
  2176  		if err != nil {
  2177  			return err
  2178  		}
  2179  
  2180  		txid, err := chainhash.NewHash(
  2181  			chanPointRPC.GetFundingTxidBytes(),
  2182  		)
  2183  		if err != nil {
  2184  			return err
  2185  		}
  2186  
  2187  		chanPoint := wire.OutPoint{
  2188  			Hash:  *txid,
  2189  			Index: chanPointRPC.OutputIndex,
  2190  		}
  2191  
  2192  		printJSON(struct {
  2193  			ChanPoint  string `json:"chan_point"`
  2194  			ChanBackup []byte `json:"chan_backup"`
  2195  		}{
  2196  			ChanPoint:  chanPoint.String(),
  2197  			ChanBackup: chanBackup.ChanBackup,
  2198  		})
  2199  		return nil
  2200  	}
  2201  
  2202  	if !ctx.IsSet("all") {
  2203  		return fmt.Errorf("if a channel isn't specified, --all must be")
  2204  	}
  2205  
  2206  	chanBackup, err := client.ExportAllChannelBackups(
  2207  		ctxc, &lnrpc.ChanBackupExportRequest{},
  2208  	)
  2209  	if err != nil {
  2210  		return err
  2211  	}
  2212  
  2213  	if ctx.IsSet("output_file") {
  2214  		return ioutil.WriteFile(
  2215  			ctx.String("output_file"),
  2216  			chanBackup.MultiChanBackup.MultiChanBackup,
  2217  			0666,
  2218  		)
  2219  	}
  2220  
  2221  	// TODO(roasbeef): support for export | restore ?
  2222  
  2223  	var chanPoints []string
  2224  	for _, chanPoint := range chanBackup.MultiChanBackup.ChanPoints {
  2225  		txid, err := chainhash.NewHash(chanPoint.GetFundingTxidBytes())
  2226  		if err != nil {
  2227  			return err
  2228  		}
  2229  
  2230  		chanPoints = append(chanPoints, wire.OutPoint{
  2231  			Hash:  *txid,
  2232  			Index: chanPoint.OutputIndex,
  2233  		}.String())
  2234  	}
  2235  
  2236  	printRespJSON(chanBackup)
  2237  
  2238  	return nil
  2239  }
  2240  
  2241  var verifyChanBackupCommand = cli.Command{
  2242  	Name:      "verifychanbackup",
  2243  	Category:  "Channels",
  2244  	Usage:     "Verify an existing channel backup.",
  2245  	ArgsUsage: "[--single_backup] [--multi_backup] [--multi_file]",
  2246  	Description: `
  2247      This command allows a user to verify an existing Single or Multi channel
  2248      backup for integrity. This is useful when a user has a backup, but is
  2249      unsure as to if it's valid or for the target node.
  2250  
  2251      The command will accept backups in one of three forms:
  2252  
  2253         * A single channel packed SCB, which can be obtained from
  2254  	 exportchanbackup. This should be passed in hex encoded format.
  2255  
  2256         * A packed multi-channel SCB, which couples several individual
  2257  	 static channel backups in single blob.
  2258  
  2259         * A file path which points to a packed multi-channel backup within a
  2260  	 file, using the same format that dcrlnd does in its 'channel.backup'
  2261  	 file.
  2262      `,
  2263  	Flags: []cli.Flag{
  2264  		cli.StringFlag{
  2265  			Name: "single_backup",
  2266  			Usage: "A hex encoded single channel backup obtained " +
  2267  				"from 'exportchanbackup'",
  2268  		},
  2269  		cli.StringFlag{
  2270  			Name: "multi_backup",
  2271  			Usage: "A hex encoded multi-channel backup obtained " +
  2272  				"from 'exportchanbackup'",
  2273  		},
  2274  		cli.StringFlag{
  2275  			Name:  "multi_file",
  2276  			Usage: "The path to a multi-channel back up file",
  2277  		},
  2278  	},
  2279  	Action: actionDecorator(verifyChanBackup),
  2280  }
  2281  
  2282  func verifyChanBackup(ctx *cli.Context) error {
  2283  	ctxc := getContext()
  2284  	client, cleanUp := getClient(ctx)
  2285  	defer cleanUp()
  2286  
  2287  	// Show command help if no arguments provided
  2288  	if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
  2289  		cli.ShowCommandHelp(ctx, "verifychanbackup")
  2290  		return nil
  2291  	}
  2292  
  2293  	backups, err := parseChanBackups(ctx)
  2294  	if err != nil {
  2295  		return err
  2296  	}
  2297  
  2298  	verifyReq := lnrpc.ChanBackupSnapshot{}
  2299  
  2300  	if backups.GetChanBackups() != nil {
  2301  		verifyReq.SingleChanBackups = backups.GetChanBackups()
  2302  	}
  2303  	if backups.GetMultiChanBackup() != nil {
  2304  		verifyReq.MultiChanBackup = &lnrpc.MultiChanBackup{
  2305  			MultiChanBackup: backups.GetMultiChanBackup(),
  2306  		}
  2307  	}
  2308  
  2309  	resp, err := client.VerifyChanBackup(ctxc, &verifyReq)
  2310  	if err != nil {
  2311  		return err
  2312  	}
  2313  
  2314  	printRespJSON(resp)
  2315  	return nil
  2316  }
  2317  
  2318  var restoreChanBackupCommand = cli.Command{
  2319  	Name:     "restorechanbackup",
  2320  	Category: "Channels",
  2321  	Usage: "Restore an existing single or multi-channel static channel " +
  2322  		"backup.",
  2323  	ArgsUsage: "[--single_backup] [--multi_backup] [--multi_file=",
  2324  	Description: `
  2325  	Allows a user to restore a Static Channel Backup (SCB) that was
  2326  	obtained either via the 'exportchanbackup' command, or from lnd's
  2327  	automatically managed 'channel.backup' file. This command should be used
  2328  	if a user is attempting to restore a channel due to data loss on a
  2329  	running node restored with the same seed as the node that created the
  2330  	channel. If successful, this command will allows the user to recover
  2331  	the settled funds stored in the recovered channels.
  2332  
  2333  	The command will accept backups in one of three forms:
  2334  
  2335  	   * A single channel packed SCB, which can be obtained from
  2336  	     'exportchanbackup'. This should be passed in hex encoded format.
  2337  
  2338  	   * A packed multi-channel SCB, which couples several individual
  2339  	     static channel backups in single blob.
  2340  
  2341  	   * A file path which points to a packed multi-channel backup within a
  2342  	     file, using the same format that dcrlnd does in its 'channel.backup'
  2343  	     file.
  2344  	`,
  2345  	Flags: []cli.Flag{
  2346  		cli.StringFlag{
  2347  			Name: "single_backup",
  2348  			Usage: "A hex encoded single channel backup obtained " +
  2349  				"from exportchanbackup",
  2350  		},
  2351  		cli.StringFlag{
  2352  			Name: "multi_backup",
  2353  			Usage: "A hex encoded multi-channel backup obtained " +
  2354  				"from exportchanbackup",
  2355  		},
  2356  		cli.StringFlag{
  2357  			Name:  "multi_file",
  2358  			Usage: "The path to a multi-channel back up file",
  2359  		},
  2360  	},
  2361  	Action: actionDecorator(restoreChanBackup),
  2362  }
  2363  
  2364  // errMissingChanBackup is an error returned when we attempt to parse a channel
  2365  // backup from a CLI command and it is missing.
  2366  var errMissingChanBackup = errors.New("missing channel backup")
  2367  
  2368  func parseChanBackups(ctx *cli.Context) (*lnrpc.RestoreChanBackupRequest, error) {
  2369  	switch {
  2370  	case ctx.IsSet("single_backup"):
  2371  		packedBackup, err := hex.DecodeString(
  2372  			ctx.String("single_backup"),
  2373  		)
  2374  		if err != nil {
  2375  			return nil, fmt.Errorf("unable to decode single packed "+
  2376  				"backup: %v", err)
  2377  		}
  2378  
  2379  		return &lnrpc.RestoreChanBackupRequest{
  2380  			Backup: &lnrpc.RestoreChanBackupRequest_ChanBackups{
  2381  				ChanBackups: &lnrpc.ChannelBackups{
  2382  					ChanBackups: []*lnrpc.ChannelBackup{
  2383  						{
  2384  							ChanBackup: packedBackup,
  2385  						},
  2386  					},
  2387  				},
  2388  			},
  2389  		}, nil
  2390  
  2391  	case ctx.IsSet("multi_backup"):
  2392  		packedMulti, err := hex.DecodeString(
  2393  			ctx.String("multi_backup"),
  2394  		)
  2395  		if err != nil {
  2396  			return nil, fmt.Errorf("unable to decode multi packed "+
  2397  				"backup: %v", err)
  2398  		}
  2399  
  2400  		return &lnrpc.RestoreChanBackupRequest{
  2401  			Backup: &lnrpc.RestoreChanBackupRequest_MultiChanBackup{
  2402  				MultiChanBackup: packedMulti,
  2403  			},
  2404  		}, nil
  2405  
  2406  	case ctx.IsSet("multi_file"):
  2407  		packedMulti, err := ioutil.ReadFile(ctx.String("multi_file"))
  2408  		if err != nil {
  2409  			return nil, fmt.Errorf("unable to decode multi packed "+
  2410  				"backup: %v", err)
  2411  		}
  2412  
  2413  		return &lnrpc.RestoreChanBackupRequest{
  2414  			Backup: &lnrpc.RestoreChanBackupRequest_MultiChanBackup{
  2415  				MultiChanBackup: packedMulti,
  2416  			},
  2417  		}, nil
  2418  
  2419  	default:
  2420  		return nil, errMissingChanBackup
  2421  	}
  2422  }
  2423  
  2424  func restoreChanBackup(ctx *cli.Context) error {
  2425  	ctxc := getContext()
  2426  	client, cleanUp := getClient(ctx)
  2427  	defer cleanUp()
  2428  
  2429  	// Show command help if no arguments provided
  2430  	if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
  2431  		cli.ShowCommandHelp(ctx, "restorechanbackup")
  2432  		return nil
  2433  	}
  2434  
  2435  	var req lnrpc.RestoreChanBackupRequest
  2436  
  2437  	backups, err := parseChanBackups(ctx)
  2438  	if err != nil {
  2439  		return err
  2440  	}
  2441  
  2442  	req.Backup = backups.Backup
  2443  
  2444  	_, err = client.RestoreChannelBackups(ctxc, &req)
  2445  	if err != nil {
  2446  		return fmt.Errorf("unable to restore chan backups: %v", err)
  2447  	}
  2448  
  2449  	return nil
  2450  }