cmd/lncli: expose bumping fee of inputs/transactions over lncli

This commit is contained in:
Wilmer Paulino 2019-05-29 14:00:41 -07:00
parent a4675063b7
commit d41af9a65f
No known key found for this signature in database
GPG Key ID: 6DF57B9F9514972F
2 changed files with 113 additions and 0 deletions

@ -1,7 +1,11 @@
package main
import (
"encoding/hex"
"errors"
"fmt"
"strconv"
"strings"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightningnetwork/lnd/lnrpc"
@ -17,6 +21,27 @@ func NewOutPointFromProto(op *lnrpc.OutPoint) OutPoint {
return OutPoint(fmt.Sprintf("%v:%d", hash, op.OutputIndex))
}
// NewProtoOutPoint parses an OutPoint into its corresponding lnrpc.OutPoint
// type.
func NewProtoOutPoint(op string) (*lnrpc.OutPoint, error) {
parts := strings.Split(op, ":")
if len(parts) != 2 {
return nil, errors.New("outpoint should be of the form txid:index")
}
txid := parts[0]
if hex.DecodedLen(len(txid)) != chainhash.HashSize {
return nil, fmt.Errorf("invalid hex-encoded txid %v", txid)
}
outputIndex, err := strconv.Atoi(parts[1])
if err != nil {
return nil, fmt.Errorf("invalid output index: %v", err)
}
return &lnrpc.OutPoint{
TxidStr: txid,
OutputIndex: uint32(outputIndex),
}, nil
}
// Utxo displays information about an unspent output, including its address,
// amount, pkscript, and confirmations.
type Utxo struct {

@ -4,6 +4,7 @@ package main
import (
"context"
"fmt"
"sort"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
@ -21,6 +22,7 @@ func walletCommands() []cli.Command {
Description: "",
Subcommands: []cli.Command{
pendingSweepsCommand,
bumpFeeCommand,
},
},
}
@ -81,3 +83,89 @@ func pendingSweeps(ctx *cli.Context) error {
return nil
}
var bumpFeeCommand = cli.Command{
Name: "bumpfee",
Usage: "Bumps the fee of an arbitrary input/transaction.",
ArgsUsage: "outpoint",
Description: `
This command takes a different approach than bitcoind's bumpfee command.
lnd has a central batching engine in which inputs with similar fee rates
are batched together to save on transaction fees. Due to this, we cannot
rely on bumping the fee on a specific transaction, since transactions
can change at any point with the addition of new inputs. The list of
inputs that currently exist within lnd's central batching engine can be
retrieved through lncli pendingsweeps.
When bumping the fee of an input that currently exists within lnd's
central batching engine, a higher fee transaction will be created that
replaces the lower fee transaction through the Replace-By-Fee (RBF)
policy.
This command also serves useful when wanting to perform a
Child-Pays-For-Parent (CPFP), where the child transaction pays for its
parent's fee. This can be done by specifying an outpoint within the low
fee transaction that is under the control of the wallet.
A fee preference must be provided, either through the conf_target or
sat_per_byte parameters.
Note that this command currently doesn't perform any validation checks
on the fee preference being provided. For now, the responsibility of
ensuring that the new fee preference is sufficient is delegated to the
user.`,
Flags: []cli.Flag{
cli.Uint64Flag{
Name: "conf_target",
Usage: "the number of blocks that the output should " +
"be swept on-chain within",
},
cli.Uint64Flag{
Name: "sat_per_byte",
Usage: "a manual fee expressed in sat/byte that " +
"should be used when sweeping the output",
},
},
Action: actionDecorator(bumpFee),
}
func bumpFee(ctx *cli.Context) error {
// Display the command's help message if we do not have the expected
// number of arguments/flags.
if ctx.NArg() != 1 || ctx.NumFlags() != 1 {
return cli.ShowCommandHelp(ctx, "bumpfee")
}
// Validate and parse the relevant arguments/flags.
protoOutPoint, err := NewProtoOutPoint(ctx.Args().Get(0))
if err != nil {
return err
}
var confTarget, satPerByte uint32
switch {
case ctx.IsSet("conf_target") && ctx.IsSet("sat_per_byte"):
return fmt.Errorf("either conf_target or sat_per_byte should " +
"be set, but not both")
case ctx.IsSet("conf_target"):
confTarget = uint32(ctx.Uint64("conf_target"))
case ctx.IsSet("sat_per_byte"):
satPerByte = uint32(ctx.Uint64("sat_per_byte"))
}
client, cleanUp := getWalletClient(ctx)
defer cleanUp()
resp, err := client.BumpFee(context.Background(), &walletrpc.BumpFeeRequest{
Outpoint: protoOutPoint,
TargetConf: confTarget,
SatPerByte: satPerByte,
})
if err != nil {
return err
}
printRespJSON(resp)
return nil
}