diff --git a/cmd/lncli/main.go b/cmd/lncli/main.go index 73a6ce75..51917e4e 100644 --- a/cmd/lncli/main.go +++ b/cmd/lncli/main.go @@ -304,6 +304,7 @@ func main() { app.Commands = append(app.Commands, autopilotCommands()...) app.Commands = append(app.Commands, invoicesCommands()...) app.Commands = append(app.Commands, routerCommands()...) + app.Commands = append(app.Commands, walletCommands()...) if err := app.Run(os.Args); err != nil { fatal(err) diff --git a/cmd/lncli/types.go b/cmd/lncli/types.go index 80f7cdf6..78d640f9 100644 --- a/cmd/lncli/types.go +++ b/cmd/lncli/types.go @@ -3,6 +3,7 @@ package main import ( "fmt" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/lightningnetwork/lnd/lnrpc" ) @@ -11,7 +12,9 @@ type OutPoint string // NewOutPointFromProto formats the lnrpc.OutPoint into an OutPoint for display. func NewOutPointFromProto(op *lnrpc.OutPoint) OutPoint { - return OutPoint(fmt.Sprintf("%s:%d", op.TxidStr, op.OutputIndex)) + var hash chainhash.Hash + copy(hash[:], op.TxidBytes) + return OutPoint(fmt.Sprintf("%v:%d", hash, op.OutputIndex)) } // Utxo displays information about an unspent output, including its address, diff --git a/cmd/lncli/walletrpc_active.go b/cmd/lncli/walletrpc_active.go new file mode 100644 index 00000000..e86819bb --- /dev/null +++ b/cmd/lncli/walletrpc_active.go @@ -0,0 +1,83 @@ +// +build walletrpc + +package main + +import ( + "context" + "sort" + + "github.com/lightningnetwork/lnd/lnrpc/walletrpc" + "github.com/urfave/cli" +) + +// walletCommands will return the set of commands to enable for walletrpc +// builds. +func walletCommands() []cli.Command { + return []cli.Command{ + { + Name: "wallet", + Category: "Wallet", + Usage: "Interact with the wallet.", + Description: "", + Subcommands: []cli.Command{ + pendingSweepsCommand, + }, + }, + } +} + +func getWalletClient(ctx *cli.Context) (walletrpc.WalletKitClient, func()) { + conn := getClientConn(ctx, false) + cleanUp := func() { + conn.Close() + } + return walletrpc.NewWalletKitClient(conn), cleanUp +} + +var pendingSweepsCommand = cli.Command{ + Name: "pendingsweeps", + Usage: "List all outputs that are pending to be swept within lnd.", + ArgsUsage: "", + Description: ` + List all on-chain outputs that lnd is currently attempting to sweep + within its central batching engine. Outputs with similar fee rates are + batched together in order to sweep them within a single transaction. + `, + Flags: []cli.Flag{}, + Action: actionDecorator(pendingSweeps), +} + +func pendingSweeps(ctx *cli.Context) error { + ctxb := context.Background() + client, cleanUp := getWalletClient(ctx) + defer cleanUp() + + req := &walletrpc.PendingSweepsRequest{} + resp, err := client.PendingSweeps(ctxb, req) + if err != nil { + return err + } + + // Sort them in ascending fee rate order for display purposes. + sort.Slice(resp.PendingSweeps, func(i, j int) bool { + return resp.PendingSweeps[i].SatPerByte < + resp.PendingSweeps[j].SatPerByte + }) + + var pendingSweepsResp = struct { + PendingSweeps []*PendingSweep `json:"pending_sweeps"` + }{ + PendingSweeps: make([]*PendingSweep, 0, len(resp.PendingSweeps)), + } + + for _, protoPendingSweep := range resp.PendingSweeps { + pendingSweep := NewPendingSweepFromProto(protoPendingSweep) + pendingSweepsResp.PendingSweeps = append( + pendingSweepsResp.PendingSweeps, pendingSweep, + ) + } + + printJSON(pendingSweepsResp) + + return nil +} diff --git a/cmd/lncli/walletrpc_default.go b/cmd/lncli/walletrpc_default.go new file mode 100644 index 00000000..f919a993 --- /dev/null +++ b/cmd/lncli/walletrpc_default.go @@ -0,0 +1,10 @@ +// +build !walletrpc + +package main + +import "github.com/urfave/cli" + +// walletCommands will return nil for non-walletrpc builds. +func walletCommands() []cli.Command { + return nil +} diff --git a/cmd/lncli/walletrpc_types.go b/cmd/lncli/walletrpc_types.go new file mode 100644 index 00000000..f336e02e --- /dev/null +++ b/cmd/lncli/walletrpc_types.go @@ -0,0 +1,27 @@ +package main + +import "github.com/lightningnetwork/lnd/lnrpc/walletrpc" + +// PendingSweep is a CLI-friendly type of the walletrpc.PendingSweep proto. We +// use this to show more useful string versions of byte slices and enums. +type PendingSweep struct { + OutPoint OutPoint `json:"outpoint"` + WitnessType string `json:"witness_type"` + AmountSat uint32 `json:"amount_sat"` + SatPerByte uint32 `json:"sat_per_byte"` + BroadcastAttempts uint32 `json:"broadcast_attempts"` + NextBroadcastHeight uint32 `json:"next_broadcast_height"` +} + +// NewPendingSweepFromProto converts the walletrpc.PendingSweep proto type into +// its corresponding CLI-friendly type. +func NewPendingSweepFromProto(pendingSweep *walletrpc.PendingSweep) *PendingSweep { + return &PendingSweep{ + OutPoint: NewOutPointFromProto(pendingSweep.Outpoint), + WitnessType: pendingSweep.WitnessType.String(), + AmountSat: pendingSweep.AmountSat, + SatPerByte: pendingSweep.SatPerByte, + BroadcastAttempts: pendingSweep.BroadcastAttempts, + NextBroadcastHeight: pendingSweep.NextBroadcastHeight, + } +}