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

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/rand"
     7  	"encoding/hex"
     8  	"errors"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"os"
    12  	"runtime"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/decred/dcrd/dcrutil/v4"
    18  	"github.com/decred/dcrlnd/chainreg"
    19  	"github.com/decred/dcrlnd/lnrpc"
    20  	"github.com/decred/dcrlnd/lnrpc/routerrpc"
    21  	"github.com/decred/dcrlnd/lntypes"
    22  	"github.com/decred/dcrlnd/lnwallet"
    23  	"github.com/decred/dcrlnd/lnwire"
    24  	"github.com/decred/dcrlnd/record"
    25  	"github.com/decred/dcrlnd/routing/route"
    26  	"github.com/jedib0t/go-pretty/table"
    27  	"github.com/jedib0t/go-pretty/text"
    28  	"github.com/matheusd/protobuf-hex-display/jsonpb"
    29  	"github.com/urfave/cli"
    30  )
    31  
    32  const (
    33  	// paymentTimeout is the default timeout for the payment loop in lnd.
    34  	// No new attempts will be started after the timeout.
    35  	paymentTimeout = time.Second * 60
    36  )
    37  
    38  var (
    39  	cltvLimitFlag = cli.UintFlag{
    40  		Name: "cltv_limit",
    41  		Usage: "the maximum time lock that may be used for " +
    42  			"this payment",
    43  	}
    44  
    45  	lastHopFlag = cli.StringFlag{
    46  		Name: "last_hop",
    47  		Usage: "pubkey of the last hop (penultimate node in the path) " +
    48  			"to route through for this payment",
    49  	}
    50  
    51  	dataFlag = cli.StringFlag{
    52  		Name: "data",
    53  		Usage: "attach custom data to the payment. The required " +
    54  			"format is: <record_id>=<hex_value>,<record_id>=" +
    55  			"<hex_value>,.. For example: --data 3438382=0a21ff. " +
    56  			"Custom record ids start from 65536.",
    57  	}
    58  
    59  	inflightUpdatesFlag = cli.BoolFlag{
    60  		Name: "inflight_updates",
    61  		Usage: "if set, intermediate payment state updates will be " +
    62  			"displayed. Only valid in combination with --json.",
    63  	}
    64  
    65  	maxPartsFlag = cli.UintFlag{
    66  		Name: "max_parts",
    67  		Usage: "the maximum number of partial payments that may be " +
    68  			"used",
    69  		Value: routerrpc.DefaultMaxParts,
    70  	}
    71  
    72  	jsonFlag = cli.BoolFlag{
    73  		Name: "json",
    74  		Usage: "if set, payment updates are printed as json " +
    75  			"messages. Set by default on Windows because table " +
    76  			"formatting is unsupported.",
    77  	}
    78  
    79  	maxShardSizeAtomsFlag = cli.UintFlag{
    80  		Name: "max_shard_size_atoms",
    81  		Usage: "the largest payment split that should be attempted if " +
    82  			"payment splitting is required to attempt a payment, " +
    83  			"specified in atoms",
    84  	}
    85  
    86  	maxShardSizeMatomsFlag = cli.UintFlag{
    87  		Name: "max_shard_size_matoms",
    88  		Usage: "the largest payment split that should be attempted if " +
    89  			"payment splitting is required to attempt a payment, " +
    90  			"specified in milli-atoms",
    91  	}
    92  
    93  	ampFlag = cli.BoolFlag{
    94  		Name: "amp",
    95  		Usage: "if set to true, then AMP will be used to complete the " +
    96  			"payment",
    97  	}
    98  )
    99  
   100  // paymentFlags returns common flags for sendpayment and payinvoice.
   101  func paymentFlags() []cli.Flag {
   102  	return []cli.Flag{
   103  		cli.StringFlag{
   104  			Name:  "pay_req",
   105  			Usage: "A zpay32 encoded payment request to fulfill",
   106  		},
   107  		cli.Int64Flag{
   108  			Name: "fee_limit",
   109  			Usage: "Maximum fee allowed in atoms when " +
   110  				"sending the payment",
   111  		},
   112  		cli.Int64Flag{
   113  			Name: "fee_limit_percent",
   114  			Usage: "Percentage of the payment's amount used as " +
   115  				"the maximum fee allowed when sending the " +
   116  				"payment",
   117  		},
   118  		cli.DurationFlag{
   119  			Name: "timeout",
   120  			Usage: "the maximum amount of time we should spend " +
   121  				"trying to fulfill the payment, failing " +
   122  				"after the timeout has elapsed",
   123  			Value: paymentTimeout,
   124  		},
   125  		cltvLimitFlag,
   126  		lastHopFlag,
   127  		cli.Uint64Flag{
   128  			Name: "outgoing_chan_id",
   129  			Usage: "Short channel id of the outgoing channel to " +
   130  				"use for the first hop of the payment",
   131  			Value: 0,
   132  		},
   133  		cli.BoolFlag{
   134  			Name:  "force, f",
   135  			Usage: "Will skip payment request confirmation",
   136  		},
   137  		cli.BoolFlag{
   138  			Name:  "allow_self_payment",
   139  			Usage: "Allow sending a circular payment to self",
   140  		},
   141  		dataFlag, inflightUpdatesFlag, maxPartsFlag, jsonFlag,
   142  		maxShardSizeAtomsFlag, maxShardSizeMatomsFlag, ampFlag,
   143  	}
   144  }
   145  
   146  var sendPaymentCommand = cli.Command{
   147  	Name:     "sendpayment",
   148  	Category: "Payments",
   149  	Usage:    "Send a payment over lightning.",
   150  	Description: `
   151  	Send a payment over Lightning. One can either specify the full
   152  	parameters of the payment, or just use a payment request which encodes
   153  	all the payment details.
   154  
   155  	If payment isn't manually specified, then only a payment request needs
   156  	to be passed using the '--pay_req' argument.
   157  
   158  	If the payment *is* manually specified, then the following arguments
   159  	need to be specified in order to complete the payment:
   160  
   161  	For invoice with keysend,
   162  	    --dest=N --amt=A --final_cltv_delta=T --keysend
   163  	For invoice without payment address:
   164  	    --dest=N --amt=A --payment_hash=H --final_cltv_delta=T
   165  	For invoice with payment address:
   166  	    --dest=N --amt=A --payment_hash=H --final_cltv_delta=T --pay_addr=H
   167  	`,
   168  	ArgsUsage: "dest amt payment_hash final_cltv_delta pay_addr | --pay_req=[payment request]",
   169  	Flags: append(paymentFlags(),
   170  		cli.StringFlag{
   171  			Name: "dest, d",
   172  			Usage: "The compressed identity pubkey of the " +
   173  				"payment recipient",
   174  		},
   175  		cli.Int64Flag{
   176  			Name:  "amt, a",
   177  			Usage: "Number of atoms to send",
   178  		},
   179  		cli.StringFlag{
   180  			Name:  "payment_hash, r",
   181  			Usage: "The hash to use within the payment's HTLC",
   182  		},
   183  		cli.Int64Flag{
   184  			Name:  "final_cltv_delta",
   185  			Usage: "The number of blocks the last hop has to reveal the preimage",
   186  		},
   187  		cli.StringFlag{
   188  			Name:  "pay_addr",
   189  			Usage: "the payment address of the generated invoice",
   190  		},
   191  		cli.BoolFlag{
   192  			Name:  "keysend",
   193  			Usage: "Will generate a pre-image and encode it in the sphinx packet, a dest must be set [experimental]",
   194  		},
   195  	),
   196  	Action: sendPayment,
   197  }
   198  
   199  // retrieveFeeLimit retrieves the fee limit based on the different fee limit
   200  // flags passed. It always returns a value and doesn't rely on lnd applying a
   201  // default.
   202  func retrieveFeeLimit(ctx *cli.Context, amt int64) (int64, error) {
   203  	switch {
   204  
   205  	case ctx.IsSet("fee_limit") && ctx.IsSet("fee_limit_percent"):
   206  		return 0, fmt.Errorf("either fee_limit or fee_limit_percent " +
   207  			"can be set, but not both")
   208  
   209  	case ctx.IsSet("fee_limit"):
   210  		return ctx.Int64("fee_limit"), nil
   211  
   212  	case ctx.IsSet("fee_limit_percent"):
   213  		// Round up the fee limit to prevent hitting zero on small
   214  		// amounts.
   215  		feeLimitRoundedUp :=
   216  			(amt*ctx.Int64("fee_limit_percent") + 99) / 100
   217  
   218  		return feeLimitRoundedUp, nil
   219  	}
   220  
   221  	// If no fee limit is set, use a default value based on the amount.
   222  	amtMAtoms := lnwire.NewMAtomsFromAtoms(dcrutil.Amount(amt))
   223  	limitMsat := lnwallet.DefaultRoutingFeeLimitForAmount(amtMAtoms)
   224  	return int64(limitMsat.ToAtoms()), nil
   225  }
   226  
   227  func confirmPayReq(resp *lnrpc.PayReq, amt, feeLimit int64) error {
   228  	fmt.Printf("Payment hash: %v\n", resp.GetPaymentHash())
   229  	fmt.Printf("Description: %v\n", resp.GetDescription())
   230  	fmt.Printf("Amount (in atoms): %v\n", amt)
   231  	fmt.Printf("Fee limit (in atoms): %v\n", feeLimit)
   232  	fmt.Printf("Destination: %v\n", resp.GetDestination())
   233  
   234  	confirm := promptForConfirmation("Confirm payment (yes/no): ")
   235  	if !confirm {
   236  		return fmt.Errorf("payment not confirmed")
   237  	}
   238  
   239  	return nil
   240  }
   241  
   242  func parsePayAddr(ctx *cli.Context) ([]byte, error) {
   243  	var (
   244  		payAddr []byte
   245  		err     error
   246  	)
   247  	switch {
   248  	case ctx.IsSet("pay_addr"):
   249  		payAddr, err = hex.DecodeString(ctx.String("pay_addr"))
   250  
   251  	case ctx.Args().Present():
   252  		payAddr, err = hex.DecodeString(ctx.Args().First())
   253  	}
   254  
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	// payAddr may be not required if it's a legacy invoice.
   260  	if len(payAddr) != 0 && len(payAddr) != 32 {
   261  		return nil, fmt.Errorf("payment addr must be exactly 32 "+
   262  			"bytes, is instead %v", len(payAddr))
   263  	}
   264  
   265  	return payAddr, nil
   266  }
   267  
   268  func sendPayment(ctx *cli.Context) error {
   269  	// Show command help if no arguments provided
   270  	if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
   271  		cli.ShowCommandHelp(ctx, "sendpayment")
   272  		return nil
   273  	}
   274  
   275  	args := ctx.Args()
   276  
   277  	// If a payment request was provided, we can exit early since all of the
   278  	// details of the payment are encoded within the request.
   279  	if ctx.IsSet("pay_req") {
   280  		req := &routerrpc.SendPaymentRequest{
   281  			PaymentRequest:    ctx.String("pay_req"),
   282  			Amt:               ctx.Int64("amt"),
   283  			DestCustomRecords: make(map[uint64][]byte),
   284  		}
   285  
   286  		// We'll attempt to parse a payment address as well, given that
   287  		// if the user is using an AMP invoice, then they may be trying
   288  		// to specify that value manually.
   289  		payAddr, err := parsePayAddr(ctx)
   290  		if err != nil {
   291  			return err
   292  		}
   293  
   294  		req.PaymentAddr = payAddr
   295  
   296  		return sendPaymentRequest(ctx, req)
   297  	}
   298  
   299  	var (
   300  		destNode []byte
   301  		amount   int64
   302  		err      error
   303  	)
   304  
   305  	switch {
   306  	case ctx.IsSet("dest"):
   307  		destNode, err = hex.DecodeString(ctx.String("dest"))
   308  	case args.Present():
   309  		destNode, err = hex.DecodeString(args.First())
   310  		args = args.Tail()
   311  	default:
   312  		return fmt.Errorf("destination txid argument missing")
   313  	}
   314  	if err != nil {
   315  		return err
   316  	}
   317  
   318  	if len(destNode) != 33 {
   319  		return fmt.Errorf("dest node pubkey must be exactly 33 bytes, is "+
   320  			"instead: %v", len(destNode))
   321  	}
   322  
   323  	if ctx.IsSet("amt") {
   324  		amount = ctx.Int64("amt")
   325  	} else if args.Present() {
   326  		amount, err = strconv.ParseInt(args.First(), 10, 64)
   327  		args = args.Tail()
   328  		if err != nil {
   329  			return fmt.Errorf("unable to decode payment amount: %v", err)
   330  		}
   331  	}
   332  
   333  	if ctx.Bool("ignore_max_outbound_amt") {
   334  		return fmt.Errorf("plz re-implement")
   335  	}
   336  
   337  	req := &routerrpc.SendPaymentRequest{
   338  		Dest:              destNode,
   339  		Amt:               amount,
   340  		DestCustomRecords: make(map[uint64][]byte),
   341  		Amp:               ctx.Bool(ampFlag.Name),
   342  	}
   343  
   344  	var rHash []byte
   345  
   346  	switch {
   347  	case ctx.Bool("keysend") && ctx.Bool(ampFlag.Name):
   348  		return errors.New("either keysend or amp may be set, but not both")
   349  
   350  	case ctx.Bool("keysend"):
   351  		if ctx.IsSet("payment_hash") {
   352  			return errors.New("cannot set payment hash when using " +
   353  				"keysend")
   354  		}
   355  		var preimage lntypes.Preimage
   356  		if _, err := rand.Read(preimage[:]); err != nil {
   357  			return err
   358  		}
   359  
   360  		// Set the preimage. If the user supplied a preimage with the
   361  		// data flag, the preimage that is set here will be overwritten
   362  		// later.
   363  		req.DestCustomRecords[record.KeySendType] = preimage[:]
   364  
   365  		hash := preimage.Hash()
   366  		rHash = hash[:]
   367  	case !ctx.Bool(ampFlag.Name):
   368  		switch {
   369  		case ctx.IsSet("payment_hash"):
   370  			rHash, err = hex.DecodeString(ctx.String("payment_hash"))
   371  		case args.Present():
   372  			rHash, err = hex.DecodeString(args.First())
   373  			args = args.Tail()
   374  		default:
   375  			return fmt.Errorf("payment hash argument missing")
   376  		}
   377  	}
   378  
   379  	if err != nil {
   380  		return err
   381  	}
   382  	if !req.Amp && len(rHash) != 32 {
   383  		return fmt.Errorf("payment hash must be exactly 32 "+
   384  			"bytes, is instead %v", len(rHash))
   385  	}
   386  	req.PaymentHash = rHash
   387  
   388  	switch {
   389  	case ctx.IsSet("final_cltv_delta"):
   390  		req.FinalCltvDelta = int32(ctx.Int64("final_cltv_delta"))
   391  	case args.Present():
   392  		delta, err := strconv.ParseInt(args.First(), 10, 64)
   393  		if err != nil {
   394  			return err
   395  		}
   396  		args = args.Tail()
   397  		req.FinalCltvDelta = int32(delta)
   398  	}
   399  
   400  	payAddr, err := parsePayAddr(ctx)
   401  	if err != nil {
   402  		return err
   403  	}
   404  
   405  	req.PaymentAddr = payAddr
   406  
   407  	return sendPaymentRequest(ctx, req)
   408  }
   409  
   410  func sendPaymentRequest(ctx *cli.Context,
   411  	req *routerrpc.SendPaymentRequest) error {
   412  	ctxc := getContext()
   413  
   414  	conn := getClientConn(ctx, false)
   415  	defer conn.Close()
   416  
   417  	client := lnrpc.NewLightningClient(conn)
   418  	routerClient := routerrpc.NewRouterClient(conn)
   419  
   420  	outChan := ctx.Uint64("outgoing_chan_id")
   421  	if outChan != 0 {
   422  		req.OutgoingChanIds = []uint64{outChan}
   423  	}
   424  	if ctx.IsSet(lastHopFlag.Name) {
   425  		lastHop, err := route.NewVertexFromStr(
   426  			ctx.String(lastHopFlag.Name),
   427  		)
   428  		if err != nil {
   429  			return err
   430  		}
   431  		req.LastHopPubkey = lastHop[:]
   432  	}
   433  
   434  	req.CltvLimit = int32(ctx.Int(cltvLimitFlag.Name))
   435  
   436  	pmtTimeout := ctx.Duration("timeout")
   437  	if pmtTimeout <= 0 {
   438  		return errors.New("payment timeout must be greater than zero")
   439  	}
   440  	req.TimeoutSeconds = int32(pmtTimeout.Seconds())
   441  
   442  	req.AllowSelfPayment = ctx.Bool("allow_self_payment")
   443  
   444  	req.MaxParts = uint32(ctx.Uint(maxPartsFlag.Name))
   445  
   446  	switch {
   447  	// If the max shard size is specified, then it should either be in sat
   448  	// or msat, but not both.
   449  	case ctx.Uint64(maxShardSizeMatomsFlag.Name) != 0 &&
   450  		ctx.Uint64(maxShardSizeAtomsFlag.Name) != 0:
   451  		return fmt.Errorf("only --max_split_size_matoms or " +
   452  			"--max_split_size_atoms should be set, but not both")
   453  
   454  	case ctx.Uint64(maxShardSizeMatomsFlag.Name) != 0:
   455  		req.MaxShardSizeMatoms = ctx.Uint64(maxShardSizeMatomsFlag.Name)
   456  
   457  	case ctx.Uint64(maxShardSizeAtomsFlag.Name) != 0:
   458  		req.MaxShardSizeMatoms = uint64(lnwire.NewMAtomsFromAtoms(
   459  			dcrutil.Amount(ctx.Uint64(maxShardSizeAtomsFlag.Name)),
   460  		))
   461  	}
   462  
   463  	// Parse custom data records.
   464  	data := ctx.String(dataFlag.Name)
   465  	if data != "" {
   466  		records := strings.Split(data, ",")
   467  		for _, r := range records {
   468  			kv := strings.Split(r, "=")
   469  			if len(kv) != 2 {
   470  				return errors.New("invalid data format: " +
   471  					"multiple equal signs in record")
   472  			}
   473  
   474  			recordID, err := strconv.ParseUint(kv[0], 10, 64)
   475  			if err != nil {
   476  				return fmt.Errorf("invalid data format: %v",
   477  					err)
   478  			}
   479  
   480  			hexValue, err := hex.DecodeString(kv[1])
   481  			if err != nil {
   482  				return fmt.Errorf("invalid data format: %v",
   483  					err)
   484  			}
   485  
   486  			req.DestCustomRecords[recordID] = hexValue
   487  		}
   488  	}
   489  
   490  	var feeLimit int64
   491  	if req.PaymentRequest != "" {
   492  		// Decode payment request to find out the amount.
   493  		decodeReq := &lnrpc.PayReqString{PayReq: req.PaymentRequest}
   494  		decodeResp, err := client.DecodePayReq(ctxc, decodeReq)
   495  		if err != nil {
   496  			return err
   497  		}
   498  
   499  		// If amount is present in the request, override the request
   500  		// amount.
   501  		amt := req.Amt
   502  		invoiceAmt := decodeResp.GetNumAtoms()
   503  		if invoiceAmt != 0 {
   504  			amt = invoiceAmt
   505  		}
   506  
   507  		// Calculate fee limit based on the determined amount.
   508  		feeLimit, err = retrieveFeeLimit(ctx, amt)
   509  		if err != nil {
   510  			return err
   511  		}
   512  
   513  		// Ask for confirmation of amount and fee limit if payment is
   514  		// forced.
   515  		if !ctx.Bool("force") {
   516  			err := confirmPayReq(decodeResp, amt, feeLimit)
   517  			if err != nil {
   518  				return err
   519  			}
   520  		}
   521  	} else {
   522  		var err error
   523  		feeLimit, err = retrieveFeeLimit(ctx, req.Amt)
   524  		if err != nil {
   525  			return err
   526  		}
   527  	}
   528  
   529  	req.FeeLimitAtoms = feeLimit
   530  
   531  	// Always print in-flight updates for the table output.
   532  	printJSON := ctx.Bool(jsonFlag.Name)
   533  	req.NoInflightUpdates = !ctx.Bool(inflightUpdatesFlag.Name) && printJSON
   534  
   535  	stream, err := routerClient.SendPaymentV2(ctxc, req)
   536  	if err != nil {
   537  		return err
   538  	}
   539  
   540  	finalState, err := printLivePayment(
   541  		ctxc, stream, client, printJSON,
   542  	)
   543  	if err != nil {
   544  		return err
   545  	}
   546  
   547  	// If we get a payment error back, we pass an error up
   548  	// to main which eventually calls fatal() and returns
   549  	// with a non-zero exit code.
   550  	if finalState.Status != lnrpc.Payment_SUCCEEDED {
   551  		return errors.New(finalState.Status.String())
   552  	}
   553  
   554  	return nil
   555  }
   556  
   557  var trackPaymentCommand = cli.Command{
   558  	Name:     "trackpayment",
   559  	Category: "Payments",
   560  	Usage:    "Track progress of an existing payment.",
   561  	Description: `
   562  	Pick up monitoring the progression of a previously initiated payment
   563  	specified by the hash argument.
   564  	`,
   565  	ArgsUsage: "hash",
   566  	Flags: []cli.Flag{
   567  		jsonFlag,
   568  	},
   569  	Action: actionDecorator(trackPayment),
   570  }
   571  
   572  func trackPayment(ctx *cli.Context) error {
   573  	ctxc := getContext()
   574  	args := ctx.Args()
   575  
   576  	conn := getClientConn(ctx, false)
   577  	defer conn.Close()
   578  
   579  	routerClient := routerrpc.NewRouterClient(conn)
   580  
   581  	if !args.Present() {
   582  		return fmt.Errorf("hash argument missing")
   583  	}
   584  
   585  	hash, err := hex.DecodeString(args.First())
   586  	if err != nil {
   587  		return err
   588  	}
   589  
   590  	req := &routerrpc.TrackPaymentRequest{
   591  		PaymentHash: hash,
   592  	}
   593  
   594  	stream, err := routerClient.TrackPaymentV2(ctxc, req)
   595  	if err != nil {
   596  		return err
   597  	}
   598  
   599  	client := lnrpc.NewLightningClient(conn)
   600  	_, err = printLivePayment(ctxc, stream, client, ctx.Bool(jsonFlag.Name))
   601  	return err
   602  }
   603  
   604  // printLivePayment receives payment updates from the given stream and either
   605  // outputs them as json or as a more user-friendly formatted table. The table
   606  // option uses terminal control codes to rewrite the output. This call
   607  // terminates when the payment reaches a final state.
   608  func printLivePayment(ctxc context.Context,
   609  	stream routerrpc.Router_TrackPaymentV2Client,
   610  	client lnrpc.LightningClient, json bool) (*lnrpc.Payment, error) {
   611  
   612  	// Terminal escape codes aren't supported on Windows, fall back to json.
   613  	if !json && runtime.GOOS == "windows" {
   614  		json = true
   615  	}
   616  
   617  	aliases := newAliasCache(client)
   618  
   619  	first := true
   620  	var lastLineCount int
   621  	for {
   622  		payment, err := stream.Recv()
   623  		if err != nil {
   624  			return nil, err
   625  		}
   626  
   627  		if json {
   628  			// Delimit json messages by newlines (inspired by
   629  			// grpc over rest chunking).
   630  			if first {
   631  				first = false
   632  			} else {
   633  				fmt.Println()
   634  			}
   635  
   636  			// Write raw json to stdout.
   637  			printRespJSON(payment)
   638  		} else {
   639  			table := formatPayment(ctxc, payment, aliases)
   640  
   641  			// Clear all previously written lines and print the
   642  			// updated table.
   643  			clearLines(lastLineCount)
   644  			fmt.Print(table)
   645  
   646  			// Store the number of lines written for the next update
   647  			// pass.
   648  			lastLineCount = 0
   649  			for _, b := range table {
   650  				if b == '\n' {
   651  					lastLineCount++
   652  				}
   653  			}
   654  		}
   655  
   656  		// Terminate loop if payments state is final.
   657  		if payment.Status != lnrpc.Payment_IN_FLIGHT {
   658  			return payment, nil
   659  		}
   660  	}
   661  }
   662  
   663  // aliasCache allows cached retrieval of node aliases.
   664  type aliasCache struct {
   665  	cache  map[string]string
   666  	client lnrpc.LightningClient
   667  }
   668  
   669  func newAliasCache(client lnrpc.LightningClient) *aliasCache {
   670  	return &aliasCache{
   671  		client: client,
   672  		cache:  make(map[string]string),
   673  	}
   674  }
   675  
   676  // get returns a node alias either from cache or freshly requested from lnd.
   677  func (a *aliasCache) get(ctxc context.Context, pubkey string) string {
   678  	alias, ok := a.cache[pubkey]
   679  	if ok {
   680  		return alias
   681  	}
   682  
   683  	// Request node info.
   684  	resp, err := a.client.GetNodeInfo(
   685  		ctxc,
   686  		&lnrpc.NodeInfoRequest{
   687  			PubKey: pubkey,
   688  		},
   689  	)
   690  	if err != nil {
   691  		// If no info is available, use the
   692  		// pubkey as identifier.
   693  		alias = pubkey[:6]
   694  	} else {
   695  		alias = resp.Node.Alias
   696  	}
   697  	a.cache[pubkey] = alias
   698  
   699  	return alias
   700  }
   701  
   702  // formatMAtoms formats msat amounts as fractional sats.
   703  func formatMAtoms(amt int64) string {
   704  	return strconv.FormatFloat(float64(amt)/1000.0, 'f', -1, 64)
   705  }
   706  
   707  // formatPayment formats the payment state as an ascii table.
   708  func formatPayment(ctxc context.Context, payment *lnrpc.Payment,
   709  	aliases *aliasCache) string {
   710  	t := table.NewWriter()
   711  
   712  	// Build table header.
   713  	t.AppendHeader(table.Row{
   714  		"HTLC_STATE", "ATTEMPT_TIME", "RESOLVE_TIME", "RECEIVER_AMT",
   715  		"FEE", "TIMELOCK", "CHAN_OUT", "ROUTE",
   716  	})
   717  	t.SetColumnConfigs([]table.ColumnConfig{
   718  		{Name: "ATTEMPT_TIME", Align: text.AlignRight},
   719  		{Name: "RESOLVE_TIME", Align: text.AlignRight},
   720  		{Name: "CHAN_OUT", Align: text.AlignLeft,
   721  			AlignHeader: text.AlignLeft},
   722  	})
   723  
   724  	// Add all htlcs as rows.
   725  	createTime := time.Unix(0, payment.CreationTimeNs)
   726  	var totalPaid, totalFees int64
   727  	for _, htlc := range payment.Htlcs {
   728  		formatTime := func(timeNs int64) string {
   729  			if timeNs == 0 {
   730  				return "-"
   731  			}
   732  			resolveTime := time.Unix(0, timeNs)
   733  			resolveTimeDiff := resolveTime.Sub(createTime)
   734  			resolveTimeMs := resolveTimeDiff / time.Millisecond
   735  			return fmt.Sprintf(
   736  				"%.3f", float64(resolveTimeMs)/1000.0,
   737  			)
   738  		}
   739  
   740  		attemptTime := formatTime(htlc.AttemptTimeNs)
   741  		resolveTime := formatTime(htlc.ResolveTimeNs)
   742  
   743  		route := htlc.Route
   744  		lastHop := route.Hops[len(route.Hops)-1]
   745  
   746  		hops := []string{}
   747  		for _, h := range route.Hops {
   748  			alias := aliases.get(ctxc, h.PubKey)
   749  			hops = append(hops, alias)
   750  		}
   751  
   752  		state := htlc.Status.String()
   753  		if htlc.Failure != nil {
   754  			state = fmt.Sprintf(
   755  				"%v @ %v",
   756  				htlc.Failure.Code,
   757  				htlc.Failure.FailureSourceIndex,
   758  			)
   759  		}
   760  
   761  		t.AppendRow([]interface{}{
   762  			state, attemptTime, resolveTime,
   763  			formatMAtoms(lastHop.AmtToForwardMAtoms),
   764  			formatMAtoms(route.TotalFeesMAtoms),
   765  			route.TotalTimeLock, route.Hops[0].ChanId,
   766  			strings.Join(hops, "->")},
   767  		)
   768  
   769  		if htlc.Status == lnrpc.HTLCAttempt_SUCCEEDED {
   770  			totalPaid += lastHop.AmtToForwardMAtoms
   771  			totalFees += route.TotalFeesMAtoms
   772  		}
   773  	}
   774  
   775  	// Render table.
   776  	b := &bytes.Buffer{}
   777  	t.SetOutputMirror(b)
   778  	t.Render()
   779  
   780  	// Add additional payment-level data.
   781  	fmt.Fprintf(b, "Amount + fee:   %v + %v atoms\n",
   782  		formatMAtoms(totalPaid), formatMAtoms(totalFees))
   783  	fmt.Fprintf(b, "Payment hash:   %v\n", payment.PaymentHash)
   784  	fmt.Fprintf(b, "Payment status: %v", payment.Status)
   785  	switch payment.Status {
   786  	case lnrpc.Payment_SUCCEEDED:
   787  		fmt.Fprintf(b, ", preimage: %v", payment.PaymentPreimage)
   788  	case lnrpc.Payment_FAILED:
   789  		fmt.Fprintf(b, ", reason: %v", payment.FailureReason)
   790  	}
   791  	fmt.Fprintf(b, "\n")
   792  
   793  	return b.String()
   794  }
   795  
   796  var payInvoiceCommand = cli.Command{
   797  	Name:      "payinvoice",
   798  	Category:  "Payments",
   799  	Usage:     "Pay an invoice over lightning.",
   800  	ArgsUsage: "pay_req",
   801  	Flags: append(paymentFlags(),
   802  		cli.Int64Flag{
   803  			Name: "amt",
   804  			Usage: "Number of atoms to fulfill the " +
   805  				"invoice (optional)",
   806  		},
   807  	),
   808  	Action: actionDecorator(payInvoice),
   809  }
   810  
   811  func payInvoice(ctx *cli.Context) error {
   812  	args := ctx.Args()
   813  
   814  	var payReq string
   815  	switch {
   816  	case ctx.IsSet("pay_req"):
   817  		payReq = ctx.String("pay_req")
   818  	case args.Present():
   819  		payReq = args.First()
   820  	default:
   821  		return fmt.Errorf("pay_req argument missing")
   822  	}
   823  
   824  	req := &routerrpc.SendPaymentRequest{
   825  		PaymentRequest:    payReq,
   826  		Amt:               ctx.Int64("amt"),
   827  		DestCustomRecords: make(map[uint64][]byte),
   828  	}
   829  
   830  	return sendPaymentRequest(ctx, req)
   831  }
   832  
   833  var sendToRouteCommand = cli.Command{
   834  	Name:     "sendtoroute",
   835  	Category: "Payments",
   836  	Usage:    "Send a payment over a predefined route.",
   837  	Description: `
   838  	Send a payment over Lightning using a specific route. One must specify
   839  	the route to attempt and the payment hash. This command can even
   840  	be chained with the response to 'queryroutes' or 'buildroute'. This command
   841  	can be used to implement channel rebalancing by crafting a self-route,
   842  	or even atomic swaps using a self-route that crosses multiple chains.
   843  
   844  	There are three ways to specify a route:
   845  	   * using the '--routes' parameter to manually specify a JSON encoded
   846  	     route in the format of the return value of queryroutes or
   847  	     buildroute:
   848  	         'dcrlncli sendtoroute --payment_hash=<pay_hash> --routes=<route>'
   849  
   850  	   * passing the route as a positional argument:
   851  	         ('dcrlncli sendtoroute --payment_hash=pay_hash <route>'
   852  
   853  	   * or reading in the route from stdin, which can allow chaining the
   854  	     response from 'queryroutes' or 'buildroute', or even read in a file
   855  	     with a pre-computed route:
   856  	         'dcrlncli queryroutes --args.. | dcrlncli sendtoroute --payment_hash= -'
   857  
   858  	     notice the '-' at the end, which signals that dcrlncli should read
   859  	     the route in from stdin
   860  	`,
   861  	Flags: []cli.Flag{
   862  		cli.StringFlag{
   863  			Name:  "payment_hash, pay_hash",
   864  			Usage: "The hash to use within the payment's HTLC",
   865  		},
   866  		cli.StringFlag{
   867  			Name: "routes, r",
   868  			Usage: "A json array string in the format of the response " +
   869  				"of 'queryroutes' that denotes which routes to use",
   870  		},
   871  	},
   872  	Action: sendToRoute,
   873  }
   874  
   875  func sendToRoute(ctx *cli.Context) error {
   876  	// Show command help if no arguments provided.
   877  	if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
   878  		cli.ShowCommandHelp(ctx, "sendtoroute")
   879  		return nil
   880  	}
   881  
   882  	args := ctx.Args()
   883  
   884  	var (
   885  		rHash []byte
   886  		err   error
   887  	)
   888  	switch {
   889  	case ctx.IsSet("payment_hash"):
   890  		rHash, err = hex.DecodeString(ctx.String("payment_hash"))
   891  	case args.Present():
   892  		rHash, err = hex.DecodeString(args.First())
   893  
   894  		args = args.Tail()
   895  	default:
   896  		return fmt.Errorf("payment hash argument missing")
   897  	}
   898  
   899  	if err != nil {
   900  		return err
   901  	}
   902  
   903  	if len(rHash) != 32 {
   904  		return fmt.Errorf("payment hash must be exactly 32 "+
   905  			"bytes, is instead %d", len(rHash))
   906  	}
   907  
   908  	var jsonRoutes string
   909  	switch {
   910  	// The user is specifying the routes explicitly via the key word
   911  	// argument.
   912  	case ctx.IsSet("routes"):
   913  		jsonRoutes = ctx.String("routes")
   914  
   915  	// The user is specifying the routes as a positional argument.
   916  	case args.Present() && args.First() != "-":
   917  		jsonRoutes = args.First()
   918  
   919  	// The user is signalling that we should read stdin in order to parse
   920  	// the set of target routes.
   921  	case args.Present() && args.First() == "-":
   922  		b, err := ioutil.ReadAll(os.Stdin)
   923  		if err != nil {
   924  			return err
   925  		}
   926  		if len(b) == 0 {
   927  			return fmt.Errorf("queryroutes output is empty")
   928  		}
   929  
   930  		jsonRoutes = string(b)
   931  	}
   932  
   933  	// Try to parse the provided json both in the legacy QueryRoutes format
   934  	// that contains a list of routes and the single route BuildRoute
   935  	// format.
   936  	var route *lnrpc.Route
   937  	routes := &lnrpc.QueryRoutesResponse{}
   938  	err = jsonpb.UnmarshalString(jsonRoutes, routes)
   939  	if err == nil {
   940  		if len(routes.Routes) == 0 {
   941  			return fmt.Errorf("no routes provided")
   942  		}
   943  
   944  		if len(routes.Routes) != 1 {
   945  			return fmt.Errorf("expected a single route, but got %v",
   946  				len(routes.Routes))
   947  		}
   948  
   949  		route = routes.Routes[0]
   950  	} else {
   951  		routes := &routerrpc.BuildRouteResponse{}
   952  		err = jsonpb.UnmarshalString(jsonRoutes, routes)
   953  		if err != nil {
   954  			return fmt.Errorf("unable to unmarshal json string "+
   955  				"from incoming array of routes: %v", err)
   956  		}
   957  
   958  		route = routes.Route
   959  	}
   960  
   961  	req := &routerrpc.SendToRouteRequest{
   962  		PaymentHash: rHash,
   963  		Route:       route,
   964  	}
   965  
   966  	return sendToRouteRequest(ctx, req)
   967  }
   968  
   969  func sendToRouteRequest(ctx *cli.Context, req *routerrpc.SendToRouteRequest) error {
   970  	ctxc := getContext()
   971  	conn := getClientConn(ctx, false)
   972  	defer conn.Close()
   973  
   974  	client := routerrpc.NewRouterClient(conn)
   975  
   976  	resp, err := client.SendToRouteV2(ctxc, req)
   977  	if err != nil {
   978  		return err
   979  	}
   980  
   981  	printRespJSON(resp)
   982  
   983  	return nil
   984  }
   985  
   986  var queryRoutesCommand = cli.Command{
   987  	Name:        "queryroutes",
   988  	Category:    "Payments",
   989  	Usage:       "Query a route to a destination.",
   990  	Description: "Queries the channel router for a potential path to the destination that has sufficient flow for the amount including fees",
   991  	ArgsUsage:   "dest amt",
   992  	Flags: []cli.Flag{
   993  		cli.StringFlag{
   994  			Name: "dest",
   995  			Usage: "The 33-byte hex-encoded public key for the payment " +
   996  				"destination",
   997  		},
   998  		cli.Int64Flag{
   999  			Name:  "amt",
  1000  			Usage: "The amount to send expressed in atoms",
  1001  		},
  1002  		cli.Int64Flag{
  1003  			Name: "fee_limit",
  1004  			Usage: "Maximum fee allowed in atoms when sending " +
  1005  				"the payment",
  1006  		},
  1007  		cli.Int64Flag{
  1008  			Name: "fee_limit_percent",
  1009  			Usage: "Percentage of the payment's amount used as the " +
  1010  				"maximum fee allowed when sending the payment",
  1011  		},
  1012  		cli.Int64Flag{
  1013  			Name: "final_cltv_delta",
  1014  			Usage: "Number of blocks the last hop has to reveal " +
  1015  				"the preimage (optional)",
  1016  		},
  1017  		cli.BoolFlag{
  1018  			Name:  "use_mc",
  1019  			Usage: "Use mission control probabilities",
  1020  		},
  1021  		cli.Uint64Flag{
  1022  			Name: "outgoing_chanid",
  1023  			Usage: "(optional) the channel id of the channel " +
  1024  				"that must be taken to the first hop",
  1025  		},
  1026  		cltvLimitFlag,
  1027  	},
  1028  	Action: actionDecorator(queryRoutes),
  1029  }
  1030  
  1031  func queryRoutes(ctx *cli.Context) error {
  1032  	ctxc := getContext()
  1033  	client, cleanUp := getClient(ctx)
  1034  	defer cleanUp()
  1035  
  1036  	var (
  1037  		dest string
  1038  		amt  int64
  1039  		err  error
  1040  	)
  1041  
  1042  	args := ctx.Args()
  1043  
  1044  	switch {
  1045  	case ctx.IsSet("dest"):
  1046  		dest = ctx.String("dest")
  1047  	case args.Present():
  1048  		dest = args.First()
  1049  		args = args.Tail()
  1050  	default:
  1051  		return fmt.Errorf("dest argument missing")
  1052  	}
  1053  
  1054  	switch {
  1055  	case ctx.IsSet("amt"):
  1056  		amt = ctx.Int64("amt")
  1057  	case args.Present():
  1058  		amt, err = strconv.ParseInt(args.First(), 10, 64)
  1059  		if err != nil {
  1060  			return fmt.Errorf("unable to decode amt argument: %v", err)
  1061  		}
  1062  	default:
  1063  		return fmt.Errorf("amt argument missing")
  1064  	}
  1065  
  1066  	feeLimit, err := retrieveFeeLimitLegacy(ctx)
  1067  	if err != nil {
  1068  		return err
  1069  	}
  1070  
  1071  	req := &lnrpc.QueryRoutesRequest{
  1072  		PubKey:            dest,
  1073  		Amt:               amt,
  1074  		FeeLimit:          feeLimit,
  1075  		FinalCltvDelta:    int32(ctx.Int("final_cltv_delta")),
  1076  		UseMissionControl: ctx.Bool("use_mc"),
  1077  		CltvLimit:         uint32(ctx.Uint64(cltvLimitFlag.Name)),
  1078  		OutgoingChanId:    ctx.Uint64("outgoing_chanid"),
  1079  	}
  1080  
  1081  	route, err := client.QueryRoutes(ctxc, req)
  1082  	if err != nil {
  1083  		return err
  1084  	}
  1085  
  1086  	printRespJSON(route)
  1087  	return nil
  1088  }
  1089  
  1090  // retrieveFeeLimitLegacy retrieves the fee limit based on the different fee
  1091  // limit flags passed. This function will eventually disappear in favor of
  1092  // retrieveFeeLimit and the new payment rpc.
  1093  func retrieveFeeLimitLegacy(ctx *cli.Context) (*lnrpc.FeeLimit, error) {
  1094  	switch {
  1095  	case ctx.IsSet("fee_limit") && ctx.IsSet("fee_limit_percent"):
  1096  		return nil, fmt.Errorf("either fee_limit or fee_limit_percent " +
  1097  			"can be set, but not both")
  1098  	case ctx.IsSet("fee_limit"):
  1099  		return &lnrpc.FeeLimit{
  1100  			Limit: &lnrpc.FeeLimit_Fixed{
  1101  				Fixed: ctx.Int64("fee_limit"),
  1102  			},
  1103  		}, nil
  1104  	case ctx.IsSet("fee_limit_percent"):
  1105  		feeLimitPercent := ctx.Int64("fee_limit_percent")
  1106  		if feeLimitPercent < 0 {
  1107  			return nil, errors.New("negative fee limit percentage " +
  1108  				"provided")
  1109  		}
  1110  		return &lnrpc.FeeLimit{
  1111  			Limit: &lnrpc.FeeLimit_Percent{
  1112  				Percent: feeLimitPercent,
  1113  			},
  1114  		}, nil
  1115  	}
  1116  
  1117  	// Since the fee limit flags aren't required, we don't return an error
  1118  	// if they're not set.
  1119  	return nil, nil
  1120  }
  1121  
  1122  var listPaymentsCommand = cli.Command{
  1123  	Name:     "listpayments",
  1124  	Category: "Payments",
  1125  	Usage:    "List all outgoing payments.",
  1126  	Description: "This command enables the retrieval of payments stored " +
  1127  		"in the database. Pagination is supported by the usage of " +
  1128  		"index_offset in combination with the paginate_forwards flag. " +
  1129  		"Reversed pagination is enabled by default to receive " +
  1130  		"current payments first. Pagination can be resumed by using " +
  1131  		"the returned last_index_offset (for forwards order), or " +
  1132  		"first_index_offset (for reversed order) as the offset_index. ",
  1133  	Flags: []cli.Flag{
  1134  		cli.BoolFlag{
  1135  			Name: "include_incomplete",
  1136  			Usage: "If set to true, payments still in flight (or " +
  1137  				"failed) will be returned as well, keeping" +
  1138  				"indices for payments the same as without " +
  1139  				"the flag.",
  1140  		},
  1141  		cli.UintFlag{
  1142  			Name: "index_offset",
  1143  			Usage: "The index of a payment that will be used as " +
  1144  				"either the start (in forwards mode) or end " +
  1145  				"(in reverse mode) of a query to determine " +
  1146  				"which payments should be returned in the " +
  1147  				"response, where the index_offset is " +
  1148  				"excluded. If index_offset is set to zero in " +
  1149  				"reversed mode, the query will end with the " +
  1150  				"last payment made.",
  1151  		},
  1152  		cli.UintFlag{
  1153  			Name: "max_payments",
  1154  			Usage: "the max number of payments to return, by " +
  1155  				"default, all completed payments are returned",
  1156  		},
  1157  		cli.BoolFlag{
  1158  			Name: "paginate_forwards",
  1159  			Usage: "if set, payments succeeding the " +
  1160  				"index_offset will be returned, allowing " +
  1161  				"forwards pagination",
  1162  		},
  1163  	},
  1164  	Action: actionDecorator(listPayments),
  1165  }
  1166  
  1167  func listPayments(ctx *cli.Context) error {
  1168  	ctxc := getContext()
  1169  	client, cleanUp := getClient(ctx)
  1170  	defer cleanUp()
  1171  
  1172  	req := &lnrpc.ListPaymentsRequest{
  1173  		IncludeIncomplete: ctx.Bool("include_incomplete"),
  1174  		IndexOffset:       uint64(ctx.Uint("index_offset")),
  1175  		MaxPayments:       uint64(ctx.Uint("max_payments")),
  1176  		Reversed:          !ctx.Bool("paginate_forwards"),
  1177  	}
  1178  
  1179  	payments, err := client.ListPayments(ctxc, req)
  1180  	if err != nil {
  1181  		return err
  1182  	}
  1183  
  1184  	printRespJSON(payments)
  1185  	return nil
  1186  }
  1187  
  1188  var forwardingHistoryCommand = cli.Command{
  1189  	Name:      "fwdinghistory",
  1190  	Category:  "Payments",
  1191  	Usage:     "Query the history of all forwarded HTLCs.",
  1192  	ArgsUsage: "start_time [end_time] [index_offset] [max_events]",
  1193  	Description: `
  1194  	Query the HTLC switch's internal forwarding log for all completed
  1195  	payment circuits (HTLCs) over a particular time range '--start_time' and
  1196  	'--end_time'. The start and end times are meant to be expressed in
  1197  	seconds since the Unix epoch.
  1198  	Alternatively negative time ranges can be used, e.g. '-3d'. Supports
  1199  	s(seconds), m(minutes), h(ours), d(ays), w(eeks), M(onths), y(ears).
  1200  	Month equals 30.44 days, year equals 365.25 days.
  1201  	If '--start_time' isn't provided, then 24 hours ago is used. If
  1202  	'--end_time' isn't provided, then the current time is used.
  1203  
  1204  	The max number of events returned is 50k. The default number is 100,
  1205  	callers can use the '--max_events' param to modify this value.
  1206  
  1207  	Finally, callers can skip a series of events using the '--index_offset'
  1208  	parameter. Each response will contain the offset index of the last
  1209  	entry. Using this callers can manually paginate within a time slice.
  1210  	`,
  1211  	Flags: []cli.Flag{
  1212  		cli.StringFlag{
  1213  			Name: "start_time",
  1214  			Usage: "The starting time for the query " +
  1215  				`as unix timestamp or relative e.g. "-1w"`,
  1216  		},
  1217  		cli.StringFlag{
  1218  			Name: "end_time",
  1219  			Usage: "The end time for the query " +
  1220  				`as unix timestamp or relative e.g. "-1w"`,
  1221  		},
  1222  		cli.Int64Flag{
  1223  			Name:  "index_offset",
  1224  			Usage: "The number of events to skip",
  1225  		},
  1226  		cli.Int64Flag{
  1227  			Name:  "max_events",
  1228  			Usage: "The max number of events to return",
  1229  		},
  1230  	},
  1231  	Action: actionDecorator(forwardingHistory),
  1232  }
  1233  
  1234  func forwardingHistory(ctx *cli.Context) error {
  1235  	ctxc := getContext()
  1236  	client, cleanUp := getClient(ctx)
  1237  	defer cleanUp()
  1238  
  1239  	var (
  1240  		startTime, endTime     uint64
  1241  		indexOffset, maxEvents uint32
  1242  		err                    error
  1243  	)
  1244  	args := ctx.Args()
  1245  	now := time.Now()
  1246  
  1247  	switch {
  1248  	case ctx.IsSet("start_time"):
  1249  		startTime, err = parseTime(ctx.String("start_time"), now)
  1250  	case args.Present():
  1251  		startTime, err = parseTime(args.First(), now)
  1252  		args = args.Tail()
  1253  	default:
  1254  		now := time.Now()
  1255  		startTime = uint64(now.Add(-time.Hour * 24).Unix())
  1256  	}
  1257  	if err != nil {
  1258  		return fmt.Errorf("unable to decode start_time: %v", err)
  1259  	}
  1260  
  1261  	switch {
  1262  	case ctx.IsSet("end_time"):
  1263  		endTime, err = parseTime(ctx.String("end_time"), now)
  1264  	case args.Present():
  1265  		endTime, err = parseTime(args.First(), now)
  1266  		args = args.Tail()
  1267  	default:
  1268  		endTime = uint64(now.Unix())
  1269  	}
  1270  	if err != nil {
  1271  		return fmt.Errorf("unable to decode end_time: %v", err)
  1272  	}
  1273  
  1274  	switch {
  1275  	case ctx.IsSet("index_offset"):
  1276  		indexOffset = uint32(ctx.Int64("index_offset"))
  1277  	case args.Present():
  1278  		i, err := strconv.ParseInt(args.First(), 10, 64)
  1279  		if err != nil {
  1280  			return fmt.Errorf("unable to decode index_offset: %v", err)
  1281  		}
  1282  		indexOffset = uint32(i)
  1283  		args = args.Tail()
  1284  	}
  1285  
  1286  	switch {
  1287  	case ctx.IsSet("max_events"):
  1288  		maxEvents = uint32(ctx.Int64("max_events"))
  1289  	case args.Present():
  1290  		m, err := strconv.ParseInt(args.First(), 10, 64)
  1291  		if err != nil {
  1292  			return fmt.Errorf("unable to decode max_events: %v", err)
  1293  		}
  1294  		maxEvents = uint32(m)
  1295  		args = args.Tail()
  1296  	}
  1297  
  1298  	req := &lnrpc.ForwardingHistoryRequest{
  1299  		StartTime:    startTime,
  1300  		EndTime:      endTime,
  1301  		IndexOffset:  indexOffset,
  1302  		NumMaxEvents: maxEvents,
  1303  	}
  1304  	resp, err := client.ForwardingHistory(ctxc, req)
  1305  	if err != nil {
  1306  		return err
  1307  	}
  1308  
  1309  	printRespJSON(resp)
  1310  	return nil
  1311  }
  1312  
  1313  var buildRouteCommand = cli.Command{
  1314  	Name:     "buildroute",
  1315  	Category: "Payments",
  1316  	Usage:    "Build a route from a list of hop pubkeys.",
  1317  	Action:   actionDecorator(buildRoute),
  1318  	Flags: []cli.Flag{
  1319  		cli.Int64Flag{
  1320  			Name: "amt",
  1321  			Usage: "the amount to send expressed in satoshis. If" +
  1322  				"not set, the minimum routable amount is used",
  1323  		},
  1324  		cli.Int64Flag{
  1325  			Name: "final_cltv_delta",
  1326  			Usage: "number of blocks the last hop has to reveal " +
  1327  				"the preimage",
  1328  			Value: chainreg.DefaultDecredTimeLockDelta,
  1329  		},
  1330  		cli.StringFlag{
  1331  			Name:  "hops",
  1332  			Usage: "comma separated hex pubkeys",
  1333  		},
  1334  		cli.Uint64Flag{
  1335  			Name: "outgoing_chan_id",
  1336  			Usage: "short channel id of the outgoing channel to " +
  1337  				"use for the first hop of the payment",
  1338  			Value: 0,
  1339  		},
  1340  	},
  1341  }
  1342  
  1343  func buildRoute(ctx *cli.Context) error {
  1344  	ctxc := getContext()
  1345  	conn := getClientConn(ctx, false)
  1346  	defer conn.Close()
  1347  
  1348  	client := routerrpc.NewRouterClient(conn)
  1349  
  1350  	if !ctx.IsSet("hops") {
  1351  		return errors.New("hops required")
  1352  	}
  1353  
  1354  	// Build list of hop addresses for the rpc.
  1355  	hops := strings.Split(ctx.String("hops"), ",")
  1356  	rpcHops := make([][]byte, 0, len(hops))
  1357  	for _, k := range hops {
  1358  		pubkey, err := route.NewVertexFromStr(k)
  1359  		if err != nil {
  1360  			return fmt.Errorf("error parsing %v: %v", k, err)
  1361  		}
  1362  		rpcHops = append(rpcHops, pubkey[:])
  1363  	}
  1364  
  1365  	var amtMAtoms int64
  1366  	hasAmt := ctx.IsSet("amt")
  1367  	if hasAmt {
  1368  		amtMAtoms = ctx.Int64("amt") * 1000
  1369  		if amtMAtoms == 0 {
  1370  			return fmt.Errorf("non-zero amount required")
  1371  		}
  1372  	}
  1373  
  1374  	// Call BuildRoute rpc.
  1375  	req := &routerrpc.BuildRouteRequest{
  1376  		AmtMAtoms:      amtMAtoms,
  1377  		FinalCltvDelta: int32(ctx.Int64("final_cltv_delta")),
  1378  		HopPubkeys:     rpcHops,
  1379  		OutgoingChanId: ctx.Uint64("outgoing_chan_id"),
  1380  	}
  1381  
  1382  	route, err := client.BuildRoute(ctxc, req)
  1383  	if err != nil {
  1384  		return err
  1385  	}
  1386  
  1387  	printRespJSON(route)
  1388  
  1389  	return nil
  1390  }
  1391  
  1392  var deletePaymentsCommand = cli.Command{
  1393  	Name:     "deletepayments",
  1394  	Category: "Payments",
  1395  	Usage:    "Delete a single or multiple payments from the database.",
  1396  	ArgsUsage: "--all [--failed_htlcs_only --include_non_failed] | " +
  1397  		"--payment_hash hash [--failed_htlcs_only]",
  1398  	Description: `
  1399  	This command either deletes all failed payments or a single payment from
  1400  	the database to reclaim disk space.
  1401  
  1402  	If the --all flag is used, then all failed payments are removed. If so
  1403  	desired, _ALL_ payments (even the successful ones) can be deleted
  1404  	by additionally specifying --include_non_failed.
  1405  
  1406  	If a --payment_hash is specified, that single payment is deleted,
  1407  	independent of its state.
  1408  
  1409  	If --failed_htlcs_only is specified then the payments themselves (or the
  1410  	single payment itself if used with --payment_hash) is not deleted, only
  1411  	the information about any failed HTLC attempts during the payment.
  1412  
  1413  	NOTE: Removing payments from the database does free up disk space within
  1414  	the internal bbolt database. But that disk space is only reclaimed after
  1415  	compacting the database. Users might want to turn on auto compaction
  1416  	(db.bolt.auto-compact=true in the config file or --db.bolt.auto-compact
  1417  	as a command line flag) and restart lnd after deleting a large number of
  1418  	payments to see a reduction in the file size of the channel.db file.
  1419  	`,
  1420  	Action: actionDecorator(deletePayments),
  1421  	Flags: []cli.Flag{
  1422  		cli.BoolFlag{
  1423  			Name:  "all",
  1424  			Usage: "delete all failed payments",
  1425  		},
  1426  		cli.StringFlag{
  1427  			Name: "payment_hash",
  1428  			Usage: "delete a specific payment identified by its " +
  1429  				"payment hash",
  1430  		},
  1431  		cli.BoolFlag{
  1432  			Name: "failed_htlcs_only",
  1433  			Usage: "only delete failed HTLCs from payments, not " +
  1434  				"the payment itself",
  1435  		},
  1436  		cli.BoolFlag{
  1437  			Name:  "include_non_failed",
  1438  			Usage: "delete ALL payments, not just the failed ones",
  1439  		},
  1440  	},
  1441  }
  1442  
  1443  func deletePayments(ctx *cli.Context) error {
  1444  	ctxc := getContext()
  1445  	client, cleanUp := getClient(ctx)
  1446  	defer cleanUp()
  1447  
  1448  	// Show command help if arguments or no flags are provided.
  1449  	if ctx.NArg() > 0 || ctx.NumFlags() == 0 {
  1450  		_ = cli.ShowCommandHelp(ctx, "deletepayments")
  1451  		return nil
  1452  	}
  1453  
  1454  	var (
  1455  		paymentHash      []byte
  1456  		all              = ctx.Bool("all")
  1457  		singlePayment    = ctx.IsSet("payment_hash")
  1458  		failedHTLCsOnly  = ctx.Bool("failed_htlcs_only")
  1459  		includeNonFailed = ctx.Bool("include_non_failed")
  1460  		err              error
  1461  		okMsg            = struct {
  1462  			OK bool `json:"ok"`
  1463  		}{
  1464  			OK: true,
  1465  		}
  1466  	)
  1467  
  1468  	// We pack two RPCs into the same CLI so there are a few non-valid
  1469  	// combinations of the flags we need to filter out.
  1470  	switch {
  1471  	case all && singlePayment:
  1472  		return fmt.Errorf("cannot use --all and --payment_hash at " +
  1473  			"the same time")
  1474  
  1475  	case singlePayment && includeNonFailed:
  1476  		return fmt.Errorf("cannot use --payment_hash and " +
  1477  			"--include_non_failed at the same time, when using " +
  1478  			"a payment hash the payment is deleted independent " +
  1479  			"of its state")
  1480  	}
  1481  
  1482  	// Deleting a single payment is implemented in a different RPC than
  1483  	// removing all/multiple payments.
  1484  	switch {
  1485  	case singlePayment:
  1486  		paymentHash, err = hex.DecodeString(ctx.String("payment_hash"))
  1487  		if err != nil {
  1488  			return fmt.Errorf("error decoding payment_hash: %v",
  1489  				err)
  1490  		}
  1491  
  1492  		_, err = client.DeletePayment(ctxc, &lnrpc.DeletePaymentRequest{
  1493  			PaymentHash:     paymentHash,
  1494  			FailedHtlcsOnly: failedHTLCsOnly,
  1495  		})
  1496  		if err != nil {
  1497  			return fmt.Errorf("error deleting single payment: %v",
  1498  				err)
  1499  		}
  1500  
  1501  	case all:
  1502  		what := "failed"
  1503  		if includeNonFailed {
  1504  			what = "all"
  1505  		}
  1506  		if failedHTLCsOnly {
  1507  			what = fmt.Sprintf("failed HTLCs from %s", what)
  1508  		}
  1509  
  1510  		fmt.Printf("Removing %s payments, this might take a while...\n",
  1511  			what)
  1512  		_, err = client.DeleteAllPayments(
  1513  			ctxc, &lnrpc.DeleteAllPaymentsRequest{
  1514  				FailedPaymentsOnly: !includeNonFailed,
  1515  				FailedHtlcsOnly:    failedHTLCsOnly,
  1516  			},
  1517  		)
  1518  		if err != nil {
  1519  			return fmt.Errorf("error deleting payments: %v", err)
  1520  		}
  1521  	}
  1522  
  1523  	// Users are confused by empty JSON outputs so let's return a simple OK
  1524  	// instead of just printing the empty response RPC message.
  1525  	printJSON(okMsg)
  1526  
  1527  	return nil
  1528  }
  1529  
  1530  // ESC is the ASCII code for escape character
  1531  const ESC = 27
  1532  
  1533  // clearCode defines a terminal escape code to clear the currently line and move
  1534  // the cursor up.
  1535  var clearCode = fmt.Sprintf("%c[%dA%c[2K", ESC, 1, ESC)
  1536  
  1537  // clearLines erases the last count lines in the terminal window.
  1538  func clearLines(count int) {
  1539  	_, _ = fmt.Print(strings.Repeat(clearCode, count))
  1540  }