diff --git a/contractcourt/contract_resolvers.go b/contractcourt/contract_resolvers.go index 6d4609da4..3cc5e4a96 100644 --- a/contractcourt/contract_resolvers.go +++ b/contractcourt/contract_resolvers.go @@ -476,7 +476,10 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) { // TODO: Use time-based sweeper and result chan. var err error h.sweepTx, err = h.Sweeper.CreateSweepTx( - []sweep.Input{&input}, sweepConfTarget, 0, + []sweep.Input{&input}, + sweep.FeePreference{ + ConfTarget: sweepConfTarget, + }, 0, ) if err != nil { return nil, err @@ -1270,7 +1273,10 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) { // // TODO: Use time-based sweeper and result chan. c.sweepTx, err = c.Sweeper.CreateSweepTx( - []sweep.Input{&input}, sweepConfTarget, 0, + []sweep.Input{&input}, + sweep.FeePreference{ + ConfTarget: sweepConfTarget, + }, 0, ) if err != nil { return nil, err diff --git a/rpcserver.go b/rpcserver.go index 4254ae5da..dfd3e8905 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -39,6 +39,7 @@ import ( "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/signal" + "github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/zpay32" "github.com/tv42/zbase32" "golang.org/x/net/context" @@ -637,53 +638,7 @@ func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64, } txHash := tx.TxHash() - return &txHash, err -} - -// determineFeePerKw will determine the fee in sat/kw that should be paid given -// an estimator, a confirmation target, and a manual value for sat/byte. A value -// is chosen based on the two free parameters as one, or both of them can be -// zero. -func determineFeePerKw(feeEstimator lnwallet.FeeEstimator, targetConf int32, - feePerByte int64) (lnwallet.SatPerKWeight, error) { - - switch { - // If the target number of confirmations is set, then we'll use that to - // consult our fee estimator for an adequate fee. - case targetConf != 0: - feePerKw, err := feeEstimator.EstimateFeePerKW( - uint32(targetConf), - ) - if err != nil { - return 0, fmt.Errorf("unable to query fee "+ - "estimator: %v", err) - } - - return feePerKw, nil - - // If a manual sat/byte fee rate is set, then we'll use that directly. - // We'll need to convert it to sat/kw as this is what we use internally. - case feePerByte != 0: - feePerKW := lnwallet.SatPerKVByte(feePerByte * 1000).FeePerKWeight() - if feePerKW < lnwallet.FeePerKwFloor { - rpcsLog.Infof("Manual fee rate input of %d sat/kw is "+ - "too low, using %d sat/kw instead", feePerKW, - lnwallet.FeePerKwFloor) - feePerKW = lnwallet.FeePerKwFloor - } - return feePerKW, nil - - // Otherwise, we'll attempt a relaxed confirmation target for the - // transaction - default: - feePerKw, err := feeEstimator.EstimateFeePerKW(6) - if err != nil { - return 0, fmt.Errorf("unable to query fee estimator: "+ - "%v", err) - } - - return feePerKw, nil - } + return &txHash, nil } // ListUnspent returns useful information about each unspent output owned by @@ -803,8 +758,12 @@ func (r *rpcServer) SendCoins(ctx context.Context, // Based on the passed fee related parameters, we'll determine an // appropriate fee rate for this transaction. - feePerKw, err := determineFeePerKw( - r.server.cc.feeEstimator, in.TargetConf, in.SatPerByte, + satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() + feePerKw, err := sweep.DetermineFeePerKw( + r.server.cc.feeEstimator, sweep.FeePreference{ + ConfTarget: uint32(in.TargetConf), + FeeRate: satPerKw, + }, ) if err != nil { return nil, err @@ -831,8 +790,12 @@ func (r *rpcServer) SendMany(ctx context.Context, // Based on the passed fee related parameters, we'll determine an // appropriate fee rate for this transaction. - feePerKw, err := determineFeePerKw( - r.server.cc.feeEstimator, in.TargetConf, in.SatPerByte, + satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() + feePerKw, err := sweep.DetermineFeePerKw( + r.server.cc.feeEstimator, sweep.FeePreference{ + ConfTarget: uint32(in.TargetConf), + FeeRate: satPerKw, + }, ) if err != nil { return nil, err @@ -1167,8 +1130,12 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, // Based on the passed fee related parameters, we'll determine an // appropriate fee rate for the funding transaction. - feeRate, err := determineFeePerKw( - r.server.cc.feeEstimator, in.TargetConf, in.SatPerByte, + satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() + feeRate, err := sweep.DetermineFeePerKw( + r.server.cc.feeEstimator, sweep.FeePreference{ + ConfTarget: uint32(in.TargetConf), + FeeRate: satPerKw, + }, ) if err != nil { return err @@ -1314,8 +1281,12 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context, // Based on the passed fee related parameters, we'll determine an // appropriate fee rate for the funding transaction. - feeRate, err := determineFeePerKw( - r.server.cc.feeEstimator, in.TargetConf, in.SatPerByte, + satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() + feeRate, err := sweep.DetermineFeePerKw( + r.server.cc.feeEstimator, sweep.FeePreference{ + ConfTarget: uint32(in.TargetConf), + FeeRate: satPerKw, + }, ) if err != nil { return nil, err @@ -1503,8 +1474,14 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest, // Based on the passed fee related parameters, we'll determine // an appropriate fee rate for the cooperative closure // transaction. - feeRate, err := determineFeePerKw( - r.server.cc.feeEstimator, in.TargetConf, in.SatPerByte, + satPerKw := lnwallet.SatPerKVByte( + in.SatPerByte * 1000, + ).FeePerKWeight() + feeRate, err := sweep.DetermineFeePerKw( + r.server.cc.feeEstimator, sweep.FeePreference{ + ConfTarget: uint32(in.TargetConf), + FeeRate: satPerKw, + }, ) if err != nil { return err diff --git a/sweep/sweeper.go b/sweep/sweeper.go index 2c87ba719..61cb1a222 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -750,10 +750,10 @@ func (s *UtxoSweeper) waitForSpend(outpoint wire.OutPoint, // - Make handling re-orgs easier. // - Thwart future possible fee sniping attempts. // - Make us blend in with the bitcoind wallet. -func (s *UtxoSweeper) CreateSweepTx(inputs []Input, confTarget uint32, +func (s *UtxoSweeper) CreateSweepTx(inputs []Input, feePref FeePreference, currentBlockHeight uint32) (*wire.MsgTx, error) { - feePerKw, err := s.cfg.FeeEstimator.EstimateFeePerKW(confTarget) + feePerKw, err := DetermineFeePerKw(s.cfg.FeeEstimator, feePref) if err != nil { return nil, err } diff --git a/sweep/walletsweep.go b/sweep/walletsweep.go new file mode 100644 index 000000000..24417cbec --- /dev/null +++ b/sweep/walletsweep.go @@ -0,0 +1,68 @@ +package sweep + +import ( + "fmt" + + "github.com/lightningnetwork/lnd/lnwallet" +) + +// FeePreference allows callers to express their time value for inclusion of a +// transaction into a block via either a confirmation target, or a fee rate. +type FeePreference struct { + // ConfTarget if non-zero, signals a fee preference expressed in the + // number of desired blocks between first broadcast, and confirmation. + ConfTarget uint32 + + // FeeRate if non-zero, signals a fee pre fence expressed in the fee + // rate expressed in sat/kw for a particular transaction. + FeeRate lnwallet.SatPerKWeight +} + +// DetermineFeePerKw will determine the fee in sat/kw that should be paid given +// an estimator, a confirmation target, and a manual value for sat/byte. A +// value is chosen based on the two free parameters as one, or both of them can +// be zero. +func DetermineFeePerKw(feeEstimator lnwallet.FeeEstimator, + feePref FeePreference) (lnwallet.SatPerKWeight, error) { + + switch { + // If the target number of confirmations is set, then we'll use that to + // consult our fee estimator for an adequate fee. + case feePref.ConfTarget != 0: + feePerKw, err := feeEstimator.EstimateFeePerKW( + uint32(feePref.ConfTarget), + ) + if err != nil { + return 0, fmt.Errorf("unable to query fee "+ + "estimator: %v", err) + } + + return feePerKw, nil + + // If a manual sat/byte fee rate is set, then we'll use that directly. + // We'll need to convert it to sat/kw as this is what we use + // internally. + case feePref.FeeRate != 0: + feePerKW := feePref.FeeRate + if feePerKW < lnwallet.FeePerKwFloor { + log.Infof("Manual fee rate input of %d sat/kw is "+ + "too low, using %d sat/kw instead", feePerKW, + lnwallet.FeePerKwFloor) + + feePerKW = lnwallet.FeePerKwFloor + } + + return feePerKW, nil + + // Otherwise, we'll attempt a relaxed confirmation target for the + // transaction + default: + feePerKw, err := feeEstimator.EstimateFeePerKW(6) + if err != nil { + return 0, fmt.Errorf("unable to query fee estimator: "+ + "%v", err) + } + + return feePerKw, nil + } +}