lnrpc/walletrpc: expose bumping fee of inputs/transactions over RPC

This RPC exposes the recently added BumpFee functionality to the
UtxoSweeper in order to allow users of the RPC to manually bump fees of
low fee inputs/transactions.
This commit is contained in:
Wilmer Paulino committed 2019-06-11 15:06:40 -07:00
1 parent e69d93949c
commit a4675063b7
3 files changed
+396 -59

No files matched your search

+110
View File
@@ -9,12 +9,15 @@ import (
"os"
"path/filepath"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/sweep"
"golang.org/x/net/context"
"google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery"
@@ -79,6 +82,10 @@ var (
Entity: "onchain",
Action: "read",
}},
"/walletrpc.WalletKit/BumpFee": {{
Entity: "onchain",
Action: "write",
}},
}
// DefaultWalletKitMacFilename is the default name of the wallet kit
@@ -409,3 +416,106 @@ func (w *WalletKit) PendingSweeps(ctx context.Context,
PendingSweeps: rpcPendingSweeps,
}, nil
}
// unmarshallOutPoint converts an outpoint from its lnrpc type to its canonical
// type.
func unmarshallOutPoint(op *lnrpc.OutPoint) (*wire.OutPoint, error) {
var hash chainhash.Hash
switch {
case len(op.TxidBytes) == 0 && len(op.TxidStr) == 0:
fallthrough
case len(op.TxidBytes) != 0 && len(op.TxidStr) != 0:
return nil, fmt.Errorf("either TxidBytes or TxidStr must be " +
"specified, but not both")
// The hash was provided as raw bytes.
case len(op.TxidBytes) != 0:
copy(hash[:], op.TxidBytes)
// The hash was provided as a hex-encoded string.
case len(op.TxidStr) != 0:
h, err := chainhash.NewHashFromStr(op.TxidStr)
if err != nil {
return nil, err
}
hash = *h
}
return &wire.OutPoint{
Hash: hash,
Index: op.OutputIndex,
}, nil
}
// BumpFee allows bumping the fee rate of an arbitrary input. A fee preference
// can be expressed either as a specific fee rate or a delta of blocks in which
// the output should be swept on-chain within. If a fee preference is not
// explicitly specified, then an error is returned. The status of the input
// sweep can be checked through the PendingSweeps RPC.
func (w *WalletKit) BumpFee(ctx context.Context,
in *BumpFeeRequest) (*BumpFeeResponse, error) {
// Parse the outpoint from the request.
op, err := unmarshallOutPoint(in.Outpoint)
if err != nil {
return nil, err
}
// Construct the request's fee preference.
satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight()
feePreference := sweep.FeePreference{
ConfTarget: uint32(in.TargetConf),
FeeRate: satPerKw,
}
// We'll attempt to bump the fee of the input through the UtxoSweeper.
// If it is currently attempting to sweep the input, then it'll simply
// bump its fee, which will result in a replacement transaction (RBF)
// being broadcast. If it is not aware of the input however,
// lnwallet.ErrNotMine is returned.
_, err = w.cfg.Sweeper.BumpFee(*op, feePreference)
switch err {
case nil:
return &BumpFeeResponse{}, nil
case lnwallet.ErrNotMine:
break
default:
return nil, err
}
// Since we're unable to perform a bump through RBF, we'll assume the
// user is attempting to bump an unconfirmed transaction's fee rate by
// sweeping an output within it under control of the wallet with a
// higher fee rate, essentially performing a Child-Pays-For-Parent
// (CPFP).
//
// We'll gather all of the information required by the UtxoSweeper in
// order to sweep the output.
txOut, err := w.cfg.Wallet.FetchInputInfo(op)
if err != nil {
return nil, err
}
var witnessType input.WitnessType
switch {
case txscript.IsPayToWitnessPubKeyHash(txOut.PkScript):
witnessType = input.WitnessKeyHash
case txscript.IsPayToScriptHash(txOut.PkScript):
witnessType = input.NestedWitnessKeyHash
default:
return nil, fmt.Errorf("unknown input witness %v", op)
}
signDesc := &input.SignDescriptor{
Output: txOut,
HashType: txscript.SigHashAll,
}
input := input.NewBaseInput(op, witnessType, signDesc, 0)
if _, err = w.cfg.Sweeper.SweepInput(input, feePreference); err != nil {
return nil, err
}
return &BumpFeeResponse{}, nil
}