chainfee: create new chainfee package extracting fees from lnwallet

In this commit, we create a new chainfee package, that houses all fee
related functionality used within the codebase. The creation of this new
package furthers our long-term goal of extracting functionality from the
bloated `lnwallet` package into new distinct packages. Additionally,
this new packages resolves a class of import cycle that could arise if a
new package that was imported by something in `lnwallet` wanted to use
the existing fee related functions in the prior `lnwallet` package.
This commit is contained in:
Olaoluwa Osuntokun 2019-10-30 19:43:05 -07:00
parent fcf81ed8ff
commit 777ed104a3
No known key found for this signature in database
GPG Key ID: BC13F65E2DC84465
47 changed files with 536 additions and 400 deletions

@ -21,6 +21,7 @@ import (
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
var ( var (
@ -78,7 +79,7 @@ type BreachConfig struct {
// Estimator is used by the breach arbiter to determine an appropriate // Estimator is used by the breach arbiter to determine an appropriate
// fee level when generating, signing, and broadcasting sweep // fee level when generating, signing, and broadcasting sweep
// transactions. // transactions.
Estimator lnwallet.FeeEstimator Estimator chainfee.Estimator
// GenSweepScript generates the receiving scripts for swept outputs. // GenSweepScript generates the receiving scripts for swept outputs.
GenSweepScript func() ([]byte, error) GenSweepScript func() ([]byte, error)

@ -31,6 +31,7 @@ import (
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntest/wait"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
) )
@ -1675,7 +1676,7 @@ func createTestArbiter(t *testing.T, contractBreaches chan *ContractBreachEvent,
ba := newBreachArbiter(&BreachConfig{ ba := newBreachArbiter(&BreachConfig{
CloseLink: func(_ *wire.OutPoint, _ htlcswitch.ChannelCloseType) {}, CloseLink: func(_ *wire.OutPoint, _ htlcswitch.ChannelCloseType) {},
DB: db, DB: db,
Estimator: lnwallet.NewStaticFeeEstimator(12500, 0), Estimator: chainfee.NewStaticEstimator(12500, 0),
GenSweepScript: func() ([]byte, error) { return nil, nil }, GenSweepScript: func() ([]byte, error) { return nil, nil },
ContractBreaches: contractBreaches, ContractBreaches: contractBreaches,
Signer: signer, Signer: signer,
@ -1824,7 +1825,7 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
return nil, nil, nil, err return nil, nil, nil, err
} }
estimator := lnwallet.NewStaticFeeEstimator(12500, 0) estimator := chainfee.NewStaticEstimator(12500, 0)
feePerKw, err := estimator.EstimateFeePerKW(1) feePerKw, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err

@ -30,6 +30,7 @@ import (
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/chainview" "github.com/lightningnetwork/lnd/routing/chainview"
) )
@ -55,11 +56,11 @@ const (
// defaultBitcoinStaticFeePerKW is the fee rate of 50 sat/vbyte // defaultBitcoinStaticFeePerKW is the fee rate of 50 sat/vbyte
// expressed in sat/kw. // expressed in sat/kw.
defaultBitcoinStaticFeePerKW = lnwallet.SatPerKWeight(12500) defaultBitcoinStaticFeePerKW = chainfee.SatPerKWeight(12500)
// defaultLitecoinStaticFeePerKW is the fee rate of 200 sat/vbyte // defaultLitecoinStaticFeePerKW is the fee rate of 200 sat/vbyte
// expressed in sat/kw. // expressed in sat/kw.
defaultLitecoinStaticFeePerKW = lnwallet.SatPerKWeight(50000) defaultLitecoinStaticFeePerKW = chainfee.SatPerKWeight(50000)
// btcToLtcConversionRate is a fixed ratio used in order to scale up // btcToLtcConversionRate is a fixed ratio used in order to scale up
// payments when running on the Litecoin chain. // payments when running on the Litecoin chain.
@ -112,7 +113,7 @@ func (c chainCode) String() string {
type chainControl struct { type chainControl struct {
chainIO lnwallet.BlockChainIO chainIO lnwallet.BlockChainIO
feeEstimator lnwallet.FeeEstimator feeEstimator chainfee.Estimator
signer input.Signer signer input.Signer
@ -161,7 +162,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
FeeRate: cfg.Bitcoin.FeeRate, FeeRate: cfg.Bitcoin.FeeRate,
TimeLockDelta: cfg.Bitcoin.TimeLockDelta, TimeLockDelta: cfg.Bitcoin.TimeLockDelta,
} }
cc.feeEstimator = lnwallet.NewStaticFeeEstimator( cc.feeEstimator = chainfee.NewStaticEstimator(
defaultBitcoinStaticFeePerKW, 0, defaultBitcoinStaticFeePerKW, 0,
) )
case litecoinChain: case litecoinChain:
@ -171,7 +172,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
FeeRate: cfg.Litecoin.FeeRate, FeeRate: cfg.Litecoin.FeeRate,
TimeLockDelta: cfg.Litecoin.TimeLockDelta, TimeLockDelta: cfg.Litecoin.TimeLockDelta,
} }
cc.feeEstimator = lnwallet.NewStaticFeeEstimator( cc.feeEstimator = chainfee.NewStaticEstimator(
defaultLitecoinStaticFeePerKW, 0, defaultLitecoinStaticFeePerKW, 0,
) )
default: default:
@ -219,8 +220,8 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
if cfg.NeutrinoMode.FeeURL != "" { if cfg.NeutrinoMode.FeeURL != "" {
ltndLog.Infof("Using API fee estimator!") ltndLog.Infof("Using API fee estimator!")
estimator := lnwallet.NewWebAPIFeeEstimator( estimator := chainfee.NewWebAPIEstimator(
lnwallet.SparseConfFeeSource{ chainfee.SparseConfFeeSource{
URL: cfg.NeutrinoMode.FeeURL, URL: cfg.NeutrinoMode.FeeURL,
}, },
defaultBitcoinStaticFeePerKW, defaultBitcoinStaticFeePerKW,
@ -323,8 +324,8 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// if we're using bitcoind as a backend, then we can // if we're using bitcoind as a backend, then we can
// use live fee estimates, rather than a statically // use live fee estimates, rather than a statically
// coded value. // coded value.
fallBackFeeRate := lnwallet.SatPerKVByte(25 * 1000) fallBackFeeRate := chainfee.SatPerKVByte(25 * 1000)
cc.feeEstimator, err = lnwallet.NewBitcoindFeeEstimator( cc.feeEstimator, err = chainfee.NewBitcoindEstimator(
*rpcConfig, fallBackFeeRate.FeePerKWeight(), *rpcConfig, fallBackFeeRate.FeePerKWeight(),
) )
if err != nil { if err != nil {
@ -340,8 +341,8 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// if we're using litecoind as a backend, then we can // if we're using litecoind as a backend, then we can
// use live fee estimates, rather than a statically // use live fee estimates, rather than a statically
// coded value. // coded value.
fallBackFeeRate := lnwallet.SatPerKVByte(25 * 1000) fallBackFeeRate := chainfee.SatPerKVByte(25 * 1000)
cc.feeEstimator, err = lnwallet.NewBitcoindFeeEstimator( cc.feeEstimator, err = chainfee.NewBitcoindEstimator(
*rpcConfig, fallBackFeeRate.FeePerKWeight(), *rpcConfig, fallBackFeeRate.FeePerKWeight(),
) )
if err != nil { if err != nil {
@ -445,8 +446,8 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// if we're using btcd as a backend, then we can use // if we're using btcd as a backend, then we can use
// live fee estimates, rather than a statically coded // live fee estimates, rather than a statically coded
// value. // value.
fallBackFeeRate := lnwallet.SatPerKVByte(25 * 1000) fallBackFeeRate := chainfee.SatPerKVByte(25 * 1000)
cc.feeEstimator, err = lnwallet.NewBtcdFeeEstimator( cc.feeEstimator, err = chainfee.NewBtcdEstimator(
*rpcConfig, fallBackFeeRate.FeePerKWeight(), *rpcConfig, fallBackFeeRate.FeePerKWeight(),
) )
if err != nil { if err != nil {

@ -9,6 +9,7 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -150,7 +151,7 @@ type channelCloser struct {
// passed configuration, and delivery+fee preference. The final argument should // passed configuration, and delivery+fee preference. The final argument should
// only be populated iff, we're the initiator of this closing request. // only be populated iff, we're the initiator of this closing request.
func newChannelCloser(cfg chanCloseCfg, deliveryScript []byte, func newChannelCloser(cfg chanCloseCfg, deliveryScript []byte,
idealFeePerKw lnwallet.SatPerKWeight, negotiationHeight uint32, idealFeePerKw chainfee.SatPerKWeight, negotiationHeight uint32,
closeReq *htlcswitch.ChanClose) *channelCloser { closeReq *htlcswitch.ChanClose) *channelCloser {
// Given the target fee-per-kw, we'll compute what our ideal _total_ // Given the target fee-per-kw, we'll compute what our ideal _total_

@ -13,6 +13,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/sweep"
) )
@ -131,7 +132,7 @@ type ChainArbitratorConfig struct {
Signer input.Signer Signer input.Signer
// FeeEstimator will be used to return fee estimates. // FeeEstimator will be used to return fee estimates.
FeeEstimator lnwallet.FeeEstimator FeeEstimator chainfee.Estimator
// ChainIO allows us to query the state of the current main chain. // ChainIO allows us to query the state of the current main chain.
ChainIO lnwallet.BlockChainIO ChainIO lnwallet.BlockChainIO

@ -24,6 +24,7 @@ import (
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"golang.org/x/crypto/salsa20" "golang.org/x/crypto/salsa20"
@ -230,7 +231,7 @@ type fundingConfig struct {
// FeeEstimator calculates appropriate fee rates based on historical // FeeEstimator calculates appropriate fee rates based on historical
// transaction information. // transaction information.
FeeEstimator lnwallet.FeeEstimator FeeEstimator chainfee.Estimator
// Notifier is used by the FundingManager to determine when the // Notifier is used by the FundingManager to determine when the
// channel's funding transaction has been confirmed on the blockchain // channel's funding transaction has been confirmed on the blockchain
@ -1218,7 +1219,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
NodeAddr: fmsg.peer.Address(), NodeAddr: fmsg.peer.Address(),
LocalFundingAmt: 0, LocalFundingAmt: 0,
RemoteFundingAmt: amt, RemoteFundingAmt: amt,
CommitFeePerKw: lnwallet.SatPerKWeight(msg.FeePerKiloWeight), CommitFeePerKw: chainfee.SatPerKWeight(msg.FeePerKiloWeight),
FundingFeePerKw: 0, FundingFeePerKw: 0,
PushMSat: msg.PushAmount, PushMSat: msg.PushAmount,
Flags: msg.ChannelFlags, Flags: msg.ChannelFlags,

@ -32,6 +32,7 @@ import (
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -218,7 +219,7 @@ func createTestWallet(cdb *channeldb.DB, netParams *chaincfg.Params,
notifier chainntnfs.ChainNotifier, wc lnwallet.WalletController, notifier chainntnfs.ChainNotifier, wc lnwallet.WalletController,
signer input.Signer, keyRing keychain.SecretKeyRing, signer input.Signer, keyRing keychain.SecretKeyRing,
bio lnwallet.BlockChainIO, bio lnwallet.BlockChainIO,
estimator lnwallet.FeeEstimator) (*lnwallet.LightningWallet, error) { estimator chainfee.Estimator) (*lnwallet.LightningWallet, error) {
wallet, err := lnwallet.NewLightningWallet(lnwallet.Config{ wallet, err := lnwallet.NewLightningWallet(lnwallet.Config{
Database: cdb, Database: cdb,
@ -247,7 +248,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
options ...cfgOption) (*testNode, error) { options ...cfgOption) (*testNode, error) {
netParams := activeNetParams.Params netParams := activeNetParams.Params
estimator := lnwallet.NewStaticFeeEstimator(62500, 0) estimator := chainfee.NewStaticEstimator(62500, 0)
chainNotifier := &mockNotifier{ chainNotifier := &mockNotifier{
oneConfChannel: make(chan *chainntnfs.TxConfirmation, 1), oneConfChannel: make(chan *chainntnfs.TxConfirmation, 1),

@ -24,6 +24,7 @@ import (
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/queue" "github.com/lightningnetwork/lnd/queue"
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
@ -199,7 +200,7 @@ type ChannelLinkConfig struct {
// FeeEstimator is an instance of a live fee estimator which will be // FeeEstimator is an instance of a live fee estimator which will be
// used to dynamically regulate the current fee of the commitment // used to dynamically regulate the current fee of the commitment
// transaction to ensure timely confirmation. // transaction to ensure timely confirmation.
FeeEstimator lnwallet.FeeEstimator FeeEstimator chainfee.Estimator
// hodl.Mask is a bitvector composed of hodl.Flags, specifying breakpoints // hodl.Mask is a bitvector composed of hodl.Flags, specifying breakpoints
// for HTLC forwarding internal to the switch. // for HTLC forwarding internal to the switch.
@ -570,7 +571,7 @@ func (l *channelLink) markReestablished() {
// chain in a timely manner. The returned value is expressed in fee-per-kw, as // chain in a timely manner. The returned value is expressed in fee-per-kw, as
// this is the native rate used when computing the fee for commitment // this is the native rate used when computing the fee for commitment
// transactions, and the second-level HTLC transactions. // transactions, and the second-level HTLC transactions.
func (l *channelLink) sampleNetworkFee() (lnwallet.SatPerKWeight, error) { func (l *channelLink) sampleNetworkFee() (chainfee.SatPerKWeight, error) {
// We'll first query for the sat/kw recommended to be confirmed within 3 // We'll first query for the sat/kw recommended to be confirmed within 3
// blocks. // blocks.
feePerKw, err := l.cfg.FeeEstimator.EstimateFeePerKW(3) feePerKw, err := l.cfg.FeeEstimator.EstimateFeePerKW(3)
@ -587,7 +588,7 @@ func (l *channelLink) sampleNetworkFee() (lnwallet.SatPerKWeight, error) {
// shouldAdjustCommitFee returns true if we should update our commitment fee to // shouldAdjustCommitFee returns true if we should update our commitment fee to
// match that of the network fee. We'll only update our commitment fee if the // match that of the network fee. We'll only update our commitment fee if the
// network fee is +/- 10% to our network fee. // network fee is +/- 10% to our network fee.
func shouldAdjustCommitFee(netFee, chanFee lnwallet.SatPerKWeight) bool { func shouldAdjustCommitFee(netFee, chanFee chainfee.SatPerKWeight) bool {
switch { switch {
// If the network fee is greater than the commitment fee, then we'll // If the network fee is greater than the commitment fee, then we'll
// switch to it if it's at least 10% greater than the commit fee. // switch to it if it's at least 10% greater than the commit fee.
@ -1061,7 +1062,7 @@ out:
// fee rate to our max fee allocation. // fee rate to our max fee allocation.
commitFee := l.channel.CommitFeeRate() commitFee := l.channel.CommitFeeRate()
maxFee := l.channel.MaxFeeRate(l.cfg.MaxFeeAllocation) maxFee := l.channel.MaxFeeRate(l.cfg.MaxFeeAllocation)
newCommitFee := lnwallet.SatPerKWeight( newCommitFee := chainfee.SatPerKWeight(
math.Min(float64(netFee), float64(maxFee)), math.Min(float64(netFee), float64(maxFee)),
) )
if !shouldAdjustCommitFee(newCommitFee, commitFee) { if !shouldAdjustCommitFee(newCommitFee, commitFee) {
@ -1894,7 +1895,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
case *lnwire.UpdateFee: case *lnwire.UpdateFee:
// We received fee update from peer. If we are the initiator we // We received fee update from peer. If we are the initiator we
// will fail the channel, if not we will apply the update. // will fail the channel, if not we will apply the update.
fee := lnwallet.SatPerKWeight(msg.FeePerKw) fee := chainfee.SatPerKWeight(msg.FeePerKw)
if err := l.channel.ReceiveUpdateFee(fee); err != nil { if err := l.channel.ReceiveUpdateFee(fee); err != nil {
l.fail(LinkFailureError{code: ErrInvalidUpdate}, l.fail(LinkFailureError{code: ErrInvalidUpdate},
"error receiving fee update: %v", err) "error receiving fee update: %v", err)
@ -2394,7 +2395,7 @@ func (l *channelLink) HandleChannelUpdate(message lnwire.Message) {
// updateChannelFee updates the commitment fee-per-kw on this channel by // updateChannelFee updates the commitment fee-per-kw on this channel by
// committing to an update_fee message. // committing to an update_fee message.
func (l *channelLink) updateChannelFee(feePerKw lnwallet.SatPerKWeight) error { func (l *channelLink) updateChannelFee(feePerKw chainfee.SatPerKWeight) error {
l.log.Infof("updating commit fee to %v sat/kw", feePerKw) l.log.Infof("updating commit fee to %v sat/kw", feePerKw)

@ -31,6 +31,7 @@ import (
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
) )
@ -1959,7 +1960,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
// incoming HTLCs automatically. // incoming HTLCs automatically.
coreLink.cfg.HodlMask = hodl.MaskFromFlags(hodl.ExitSettle) coreLink.cfg.HodlMask = hodl.MaskFromFlags(hodl.ExitSettle)
estimator := lnwallet.NewStaticFeeEstimator(6000, 0) estimator := chainfee.NewStaticEstimator(6000, 0)
feePerKw, err := estimator.EstimateFeePerKW(1) feePerKw, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
t.Fatalf("unable to query fee estimator: %v", err) t.Fatalf("unable to query fee estimator: %v", err)
@ -2379,7 +2380,7 @@ func TestChannelLinkBandwidthConsistencyOverflow(t *testing.T) {
aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
) )
estimator := lnwallet.NewStaticFeeEstimator(6000, 0) estimator := chainfee.NewStaticEstimator(6000, 0)
feePerKw, err := estimator.EstimateFeePerKW(1) feePerKw, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
t.Fatalf("unable to query fee estimator: %v", err) t.Fatalf("unable to query fee estimator: %v", err)
@ -2630,7 +2631,7 @@ func TestChannelLinkTrimCircuitsPending(t *testing.T) {
// Compute the static fees that will be used to determine the // Compute the static fees that will be used to determine the
// correctness of Alice's bandwidth when forwarding HTLCs. // correctness of Alice's bandwidth when forwarding HTLCs.
estimator := lnwallet.NewStaticFeeEstimator(6000, 0) estimator := chainfee.NewStaticEstimator(6000, 0)
feePerKw, err := estimator.EstimateFeePerKW(1) feePerKw, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
t.Fatalf("unable to query fee estimator: %v", err) t.Fatalf("unable to query fee estimator: %v", err)
@ -2909,7 +2910,7 @@ func TestChannelLinkTrimCircuitsNoCommit(t *testing.T) {
// Compute the static fees that will be used to determine the // Compute the static fees that will be used to determine the
// correctness of Alice's bandwidth when forwarding HTLCs. // correctness of Alice's bandwidth when forwarding HTLCs.
estimator := lnwallet.NewStaticFeeEstimator(6000, 0) estimator := chainfee.NewStaticEstimator(6000, 0)
feePerKw, err := estimator.EstimateFeePerKW(1) feePerKw, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
t.Fatalf("unable to query fee estimator: %v", err) t.Fatalf("unable to query fee estimator: %v", err)
@ -3167,7 +3168,7 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
) )
estimator := lnwallet.NewStaticFeeEstimator(6000, 0) estimator := chainfee.NewStaticEstimator(6000, 0)
feePerKw, err := estimator.EstimateFeePerKW(1) feePerKw, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
t.Fatalf("unable to query fee estimator: %v", err) t.Fatalf("unable to query fee estimator: %v", err)
@ -3554,8 +3555,8 @@ func TestChannelRetransmission(t *testing.T) {
// deviates from our current fee by more 10% or more. // deviates from our current fee by more 10% or more.
func TestShouldAdjustCommitFee(t *testing.T) { func TestShouldAdjustCommitFee(t *testing.T) {
tests := []struct { tests := []struct {
netFee lnwallet.SatPerKWeight netFee chainfee.SatPerKWeight
chanFee lnwallet.SatPerKWeight chanFee chainfee.SatPerKWeight
shouldAdjust bool shouldAdjust bool
}{ }{
@ -3837,7 +3838,7 @@ func TestChannelLinkUpdateCommitFee(t *testing.T) {
// triggerFeeUpdate is a helper closure to determine whether a fee // triggerFeeUpdate is a helper closure to determine whether a fee
// update was triggered and completed properly. // update was triggered and completed properly.
triggerFeeUpdate := func(feeEstimate, newFeeRate lnwallet.SatPerKWeight, triggerFeeUpdate := func(feeEstimate, newFeeRate chainfee.SatPerKWeight,
shouldUpdate bool) { shouldUpdate bool) {
t.Helper() t.Helper()
@ -3898,7 +3899,7 @@ func TestChannelLinkUpdateCommitFee(t *testing.T) {
// Triggering the link to update the fee of the channel with a fee rate // Triggering the link to update the fee of the channel with a fee rate
// that exceeds its maximum fee allocation should result in a fee rate // that exceeds its maximum fee allocation should result in a fee rate
// corresponding to the maximum fee allocation. // corresponding to the maximum fee allocation.
const maxFeeRate lnwallet.SatPerKWeight = 207182320 const maxFeeRate chainfee.SatPerKWeight = 207182320
triggerFeeUpdate(maxFeeRate+1, maxFeeRate, true) triggerFeeUpdate(maxFeeRate+1, maxFeeRate, true)
} }

@ -28,7 +28,7 @@ import (
"github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
) )
@ -70,13 +70,13 @@ func (m *mockPreimageCache) SubscribeUpdates() *contractcourt.WitnessSubscriptio
} }
type mockFeeEstimator struct { type mockFeeEstimator struct {
byteFeeIn chan lnwallet.SatPerKWeight byteFeeIn chan chainfee.SatPerKWeight
quit chan struct{} quit chan struct{}
} }
func (m *mockFeeEstimator) EstimateFeePerKW( func (m *mockFeeEstimator) EstimateFeePerKW(
numBlocks uint32) (lnwallet.SatPerKWeight, error) { numBlocks uint32) (chainfee.SatPerKWeight, error) {
select { select {
case feeRate := <-m.byteFeeIn: case feeRate := <-m.byteFeeIn:
@ -86,7 +86,7 @@ func (m *mockFeeEstimator) EstimateFeePerKW(
} }
} }
func (m *mockFeeEstimator) RelayFeePerKW() lnwallet.SatPerKWeight { func (m *mockFeeEstimator) RelayFeePerKW() chainfee.SatPerKWeight {
return 1e3 return 1e3
} }
@ -98,7 +98,7 @@ func (m *mockFeeEstimator) Stop() error {
return nil return nil
} }
var _ lnwallet.FeeEstimator = (*mockFeeEstimator)(nil) var _ chainfee.Estimator = (*mockFeeEstimator)(nil)
type mockForwardingLog struct { type mockForwardingLog struct {
sync.Mutex sync.Mutex

@ -18,6 +18,7 @@ import (
"github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
) )
@ -102,7 +103,7 @@ type ChanClose struct {
// This value is only utilized if the closure type is CloseRegular. // This value is only utilized if the closure type is CloseRegular.
// This will be the starting offered fee when the fee negotiation // This will be the starting offered fee when the fee negotiation
// process for the cooperative closure transaction kicks off. // process for the cooperative closure transaction kicks off.
TargetFeePerKw lnwallet.SatPerKWeight TargetFeePerKw chainfee.SatPerKWeight
// Updates is used by request creator to receive the notifications about // Updates is used by request creator to receive the notifications about
// execution of the close channel request. // execution of the close channel request.
@ -1368,7 +1369,7 @@ func (s *Switch) teardownCircuit(pkt *htlcPacket) error {
// then the last parameter should be the ideal fee-per-kw that will be used as // then the last parameter should be the ideal fee-per-kw that will be used as
// a starting point for close negotiation. // a starting point for close negotiation.
func (s *Switch) CloseLink(chanPoint *wire.OutPoint, closeType ChannelCloseType, func (s *Switch) CloseLink(chanPoint *wire.OutPoint, closeType ChannelCloseType,
targetFeePerKw lnwallet.SatPerKWeight) (chan interface{}, targetFeePerKw chainfee.SatPerKWeight) (chan interface{},
chan error) { chan error) {
// TODO(roasbeef) abstract out the close updates. // TODO(roasbeef) abstract out the close updates.

@ -32,6 +32,7 @@ import (
"github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntest/wait"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
@ -286,7 +287,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
return nil, nil, nil, err return nil, nil, nil, err
} }
estimator := lnwallet.NewStaticFeeEstimator(6000, 0) estimator := chainfee.NewStaticEstimator(6000, 0)
feePerKw, err := estimator.EstimateFeePerKW(1) feePerKw, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
@ -1078,7 +1079,7 @@ func newHopNetwork() *hopNetwork {
obfuscator := NewMockObfuscator() obfuscator := NewMockObfuscator()
feeEstimator := &mockFeeEstimator{ feeEstimator := &mockFeeEstimator{
byteFeeIn: make(chan lnwallet.SatPerKWeight), byteFeeIn: make(chan chainfee.SatPerKWeight),
quit: make(chan struct{}), quit: make(chan struct{}),
} }

@ -5,6 +5,7 @@ package walletrpc
import ( import (
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/sweep"
) )
@ -30,7 +31,7 @@ type Config struct {
// FeeEstimator is an instance of the primary fee estimator instance // FeeEstimator is an instance of the primary fee estimator instance
// the WalletKit will use to respond to fee estimation requests. // the WalletKit will use to respond to fee estimation requests.
FeeEstimator lnwallet.FeeEstimator FeeEstimator chainfee.Estimator
// Wallet is the primary wallet that the WalletKit will use to proxy // Wallet is the primary wallet that the WalletKit will use to proxy
// any relevant requests to. // any relevant requests to.

@ -19,6 +19,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/sweep"
"google.golang.org/grpc" "google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery" "gopkg.in/macaroon-bakery.v2/bakery"
@ -303,7 +304,7 @@ func (w *WalletKit) SendOutputs(ctx context.Context,
// Now that we have the outputs mapped, we can request that the wallet // Now that we have the outputs mapped, we can request that the wallet
// attempt to create this transaction. // attempt to create this transaction.
tx, err := w.cfg.Wallet.SendOutputs( tx, err := w.cfg.Wallet.SendOutputs(
outputsToCreate, lnwallet.SatPerKWeight(req.SatPerKw), outputsToCreate, chainfee.SatPerKWeight(req.SatPerKw),
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -468,7 +469,7 @@ func (w *WalletKit) BumpFee(ctx context.Context,
} }
// Construct the request's fee preference. // Construct the request's fee preference.
satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() satPerKw := chainfee.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight()
feePreference := sweep.FeePreference{ feePreference := sweep.FeePreference{
ConfTarget: uint32(in.TargetConf), ConfTarget: uint32(in.TargetConf),
FeeRate: satPerKw, FeeRate: satPerKw,

@ -21,6 +21,7 @@ import (
"github.com/btcsuite/btcwallet/walletdb" "github.com/btcsuite/btcwallet/walletdb"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
const ( const (
@ -289,7 +290,7 @@ func (b *BtcWallet) IsOurAddress(a btcutil.Address) bool {
// //
// This is a part of the WalletController interface. // This is a part of the WalletController interface.
func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut, func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut,
feeRate lnwallet.SatPerKWeight) (*wire.MsgTx, error) { feeRate chainfee.SatPerKWeight) (*wire.MsgTx, error) {
// Convert our fee rate from sat/kw to sat/kb since it's required by // Convert our fee rate from sat/kw to sat/kb since it's required by
// SendOutputs. // SendOutputs.
@ -314,7 +315,7 @@ func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut,
// //
// This is a part of the WalletController interface. // This is a part of the WalletController interface.
func (b *BtcWallet) CreateSimpleTx(outputs []*wire.TxOut, func (b *BtcWallet) CreateSimpleTx(outputs []*wire.TxOut,
feeRate lnwallet.SatPerKWeight, dryRun bool) (*txauthor.AuthoredTx, error) { feeRate chainfee.SatPerKWeight, dryRun bool) (*txauthor.AuthoredTx, error) {
// The fee rate is passed in using units of sat/kw, so we'll convert // The fee rate is passed in using units of sat/kw, so we'll convert
// this to sat/KB as the CreateSimpleTx method requires this unit. // this to sat/KB as the CreateSimpleTx method requires this unit.

@ -1,4 +1,4 @@
package lnwallet package chainfee
import ( import (
"encoding/json" "encoding/json"
@ -10,85 +10,41 @@ import (
"sync" "sync"
"time" "time"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
) )
const ( const (
// FeePerKwFloor is the lowest fee rate in sat/kw that we should use for
// determining transaction fees.
FeePerKwFloor SatPerKWeight = 253
// maxBlockTarget is the highest number of blocks confirmations that // maxBlockTarget is the highest number of blocks confirmations that
// a WebAPIFeeEstimator will cache fees for. This number is chosen // a WebAPIEstimator will cache fees for. This number is chosen
// because it's the highest number of confs bitcoind will return a fee // because it's the highest number of confs bitcoind will return a fee
// estimate for. // estimate for.
maxBlockTarget uint32 = 1009 maxBlockTarget uint32 = 1009
// minBlockTarget is the lowest number of blocks confirmations that // minBlockTarget is the lowest number of blocks confirmations that
// a WebAPIFeeEstimator will cache fees for. Requesting an estimate for // a WebAPIEstimator will cache fees for. Requesting an estimate for
// less than this will result in an error. // less than this will result in an error.
minBlockTarget uint32 = 2 minBlockTarget uint32 = 2
// minFeeUpdateTimeout represents the minimum interval in which a // minFeeUpdateTimeout represents the minimum interval in which a
// WebAPIFeeEstimator will request fresh fees from its API. // WebAPIEstimator will request fresh fees from its API.
minFeeUpdateTimeout = 5 * time.Minute minFeeUpdateTimeout = 5 * time.Minute
// maxFeeUpdateTimeout represents the maximum interval in which a // maxFeeUpdateTimeout represents the maximum interval in which a
// WebAPIFeeEstimator will request fresh fees from its API. // WebAPIEstimator will request fresh fees from its API.
maxFeeUpdateTimeout = 20 * time.Minute maxFeeUpdateTimeout = 20 * time.Minute
) )
// SatPerKVByte represents a fee rate in sat/kb. // Estimator provides the ability to estimate on-chain transaction fees for
type SatPerKVByte btcutil.Amount
// FeeForVSize calculates the fee resulting from this fee rate and the given
// vsize in vbytes.
func (s SatPerKVByte) FeeForVSize(vbytes int64) btcutil.Amount {
return btcutil.Amount(s) * btcutil.Amount(vbytes) / 1000
}
// FeePerKWeight converts the current fee rate from sat/kb to sat/kw.
func (s SatPerKVByte) FeePerKWeight() SatPerKWeight {
return SatPerKWeight(s / blockchain.WitnessScaleFactor)
}
// String returns a human-readable string of the fee rate.
func (s SatPerKVByte) String() string {
return fmt.Sprintf("%v sat/kb", int64(s))
}
// SatPerKWeight represents a fee rate in sat/kw.
type SatPerKWeight btcutil.Amount
// FeeForWeight calculates the fee resulting from this fee rate and the given
// weight in weight units (wu).
func (s SatPerKWeight) FeeForWeight(wu int64) btcutil.Amount {
// The resulting fee is rounded down, as specified in BOLT#03.
return btcutil.Amount(s) * btcutil.Amount(wu) / 1000
}
// FeePerKVByte converts the current fee rate from sat/kw to sat/kb.
func (s SatPerKWeight) FeePerKVByte() SatPerKVByte {
return SatPerKVByte(s * blockchain.WitnessScaleFactor)
}
// String returns a human-readable string of the fee rate.
func (s SatPerKWeight) String() string {
return fmt.Sprintf("%v sat/kw", int64(s))
}
// FeeEstimator provides the ability to estimate on-chain transaction fees for
// various combinations of transaction sizes and desired confirmation time // various combinations of transaction sizes and desired confirmation time
// (measured by number of blocks). // (measured by number of blocks).
type FeeEstimator interface { type Estimator interface {
// EstimateFeePerKW takes in a target for the number of blocks until an // EstimateFeePerKW takes in a target for the number of blocks until an
// initial confirmation and returns the estimated fee expressed in // initial confirmation and returns the estimated fee expressed in
// sat/kw. // sat/kw.
EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error)
// Start signals the FeeEstimator to start any processes or goroutines // Start signals the Estimator to start any processes or goroutines
// it needs to perform its duty. // it needs to perform its duty.
Start() error Start() error
@ -102,11 +58,11 @@ type FeeEstimator interface {
RelayFeePerKW() SatPerKWeight RelayFeePerKW() SatPerKWeight
} }
// StaticFeeEstimator will return a static value for all fee calculation // StaticEstimator will return a static value for all fee calculation requests.
// requests. It is designed to be replaced by a proper fee calculation // It is designed to be replaced by a proper fee calculation implementation.
// implementation. The fees are not accessible directly, because changing them // The fees are not accessible directly, because changing them would not be
// would not be thread safe. // thread safe.
type StaticFeeEstimator struct { type StaticEstimator struct {
// feePerKW is the static fee rate in satoshis-per-vbyte that will be // feePerKW is the static fee rate in satoshis-per-vbyte that will be
// returned by this fee estimator. // returned by this fee estimator.
feePerKW SatPerKWeight feePerKW SatPerKWeight
@ -116,11 +72,10 @@ type StaticFeeEstimator struct {
relayFee SatPerKWeight relayFee SatPerKWeight
} }
// NewStaticFeeEstimator returns a new static fee estimator instance. // NewStaticEstimator returns a new static fee estimator instance.
func NewStaticFeeEstimator(feePerKW, func NewStaticEstimator(feePerKW, relayFee SatPerKWeight) *StaticEstimator {
relayFee SatPerKWeight) *StaticFeeEstimator {
return &StaticFeeEstimator{ return &StaticEstimator{
feePerKW: feePerKW, feePerKW: feePerKW,
relayFee: relayFee, relayFee: relayFee,
} }
@ -128,43 +83,43 @@ func NewStaticFeeEstimator(feePerKW,
// EstimateFeePerKW will return a static value for fee calculations. // EstimateFeePerKW will return a static value for fee calculations.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (e StaticFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { func (e StaticEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) {
return e.feePerKW, nil return e.feePerKW, nil
} }
// RelayFeePerKW returns the minimum fee rate required for transactions to be // RelayFeePerKW returns the minimum fee rate required for transactions to be
// relayed. // relayed.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (e StaticFeeEstimator) RelayFeePerKW() SatPerKWeight { func (e StaticEstimator) RelayFeePerKW() SatPerKWeight {
return e.relayFee return e.relayFee
} }
// Start signals the FeeEstimator to start any processes or goroutines // Start signals the Estimator to start any processes or goroutines
// it needs to perform its duty. // it needs to perform its duty.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (e StaticFeeEstimator) Start() error { func (e StaticEstimator) Start() error {
return nil return nil
} }
// Stop stops any spawned goroutines and cleans up the resources used // Stop stops any spawned goroutines and cleans up the resources used
// by the fee estimator. // by the fee estimator.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (e StaticFeeEstimator) Stop() error { func (e StaticEstimator) Stop() error {
return nil return nil
} }
// A compile-time assertion to ensure that StaticFeeEstimator implements the // A compile-time assertion to ensure that StaticFeeEstimator implements the
// FeeEstimator interface. // Estimator interface.
var _ FeeEstimator = (*StaticFeeEstimator)(nil) var _ Estimator = (*StaticEstimator)(nil)
// BtcdFeeEstimator is an implementation of the FeeEstimator interface backed // BtcdEstimator is an implementation of the Estimator interface backed
// by the RPC interface of an active btcd node. This implementation will proxy // by the RPC interface of an active btcd node. This implementation will proxy
// any fee estimation requests to btcd's RPC interface. // any fee estimation requests to btcd's RPC interface.
type BtcdFeeEstimator struct { type BtcdEstimator struct {
// fallbackFeePerKW is the fall back fee rate in sat/kw that is returned // fallbackFeePerKW is the fall back fee rate in sat/kw that is returned
// if the fee estimator does not yet have enough data to actually // if the fee estimator does not yet have enough data to actually
// produce fee estimates. // produce fee estimates.
@ -179,13 +134,13 @@ type BtcdFeeEstimator struct {
btcdConn *rpcclient.Client btcdConn *rpcclient.Client
} }
// NewBtcdFeeEstimator creates a new BtcdFeeEstimator given a fully populated // NewBtcdEstimator creates a new BtcdEstimator given a fully populated
// rpc config that is able to successfully connect and authenticate with the // rpc config that is able to successfully connect and authenticate with the
// btcd node, and also a fall back fee rate. The fallback fee rate is used in // btcd node, and also a fall back fee rate. The fallback fee rate is used in
// the occasion that the estimator has insufficient data, or returns zero for a // the occasion that the estimator has insufficient data, or returns zero for a
// fee estimate. // fee estimate.
func NewBtcdFeeEstimator(rpcConfig rpcclient.ConnConfig, func NewBtcdEstimator(rpcConfig rpcclient.ConnConfig,
fallBackFeeRate SatPerKWeight) (*BtcdFeeEstimator, error) { fallBackFeeRate SatPerKWeight) (*BtcdEstimator, error) {
rpcConfig.DisableConnectOnNew = true rpcConfig.DisableConnectOnNew = true
rpcConfig.DisableAutoReconnect = false rpcConfig.DisableAutoReconnect = false
@ -194,17 +149,17 @@ func NewBtcdFeeEstimator(rpcConfig rpcclient.ConnConfig,
return nil, err return nil, err
} }
return &BtcdFeeEstimator{ return &BtcdEstimator{
fallbackFeePerKW: fallBackFeeRate, fallbackFeePerKW: fallBackFeeRate,
btcdConn: chainConn, btcdConn: chainConn,
}, nil }, nil
} }
// Start signals the FeeEstimator to start any processes or goroutines // Start signals the Estimator to start any processes or goroutines
// it needs to perform its duty. // it needs to perform its duty.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (b *BtcdFeeEstimator) Start() error { func (b *BtcdEstimator) Start() error {
if err := b.btcdConn.Connect(20); err != nil { if err := b.btcdConn.Connect(20); err != nil {
return err return err
} }
@ -233,7 +188,7 @@ func (b *BtcdFeeEstimator) Start() error {
b.minFeePerKW = FeePerKwFloor b.minFeePerKW = FeePerKwFloor
} }
walletLog.Debugf("Using minimum fee rate of %v sat/kw", log.Debugf("Using minimum fee rate of %v sat/kw",
int64(b.minFeePerKW)) int64(b.minFeePerKW))
return nil return nil
@ -242,8 +197,8 @@ func (b *BtcdFeeEstimator) Start() error {
// Stop stops any spawned goroutines and cleans up the resources used // Stop stops any spawned goroutines and cleans up the resources used
// by the fee estimator. // by the fee estimator.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (b *BtcdFeeEstimator) Stop() error { func (b *BtcdEstimator) Stop() error {
b.btcdConn.Shutdown() b.btcdConn.Shutdown()
return nil return nil
@ -252,15 +207,15 @@ func (b *BtcdFeeEstimator) Stop() error {
// EstimateFeePerKW takes in a target for the number of blocks until an initial // EstimateFeePerKW takes in a target for the number of blocks until an initial
// confirmation and returns the estimated fee expressed in sat/kw. // confirmation and returns the estimated fee expressed in sat/kw.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (b *BtcdFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { func (b *BtcdEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) {
feeEstimate, err := b.fetchEstimate(numBlocks) feeEstimate, err := b.fetchEstimate(numBlocks)
switch { switch {
// If the estimator doesn't have enough data, or returns an error, then // If the estimator doesn't have enough data, or returns an error, then
// to return a proper value, then we'll return the default fall back // to return a proper value, then we'll return the default fall back
// fee rate. // fee rate.
case err != nil: case err != nil:
walletLog.Errorf("unable to query estimator: %v", err) log.Errorf("unable to query estimator: %v", err)
fallthrough fallthrough
case feeEstimate == 0: case feeEstimate == 0:
@ -273,14 +228,14 @@ func (b *BtcdFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, er
// RelayFeePerKW returns the minimum fee rate required for transactions to be // RelayFeePerKW returns the minimum fee rate required for transactions to be
// relayed. // relayed.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (b *BtcdFeeEstimator) RelayFeePerKW() SatPerKWeight { func (b *BtcdEstimator) RelayFeePerKW() SatPerKWeight {
return b.minFeePerKW return b.minFeePerKW
} }
// fetchEstimate returns a fee estimate for a transaction to be confirmed in // fetchEstimate returns a fee estimate for a transaction to be confirmed in
// confTarget blocks. The estimate is returned in sat/kw. // confTarget blocks. The estimate is returned in sat/kw.
func (b *BtcdFeeEstimator) fetchEstimate(confTarget uint32) (SatPerKWeight, error) { func (b *BtcdEstimator) fetchEstimate(confTarget uint32) (SatPerKWeight, error) {
// First, we'll fetch the estimate for our confirmation target. // First, we'll fetch the estimate for our confirmation target.
btcPerKB, err := b.btcdConn.EstimateFee(int64(confTarget)) btcPerKB, err := b.btcdConn.EstimateFee(int64(confTarget))
if err != nil { if err != nil {
@ -300,26 +255,26 @@ func (b *BtcdFeeEstimator) fetchEstimate(confTarget uint32) (SatPerKWeight, erro
// Finally, we'll enforce our fee floor. // Finally, we'll enforce our fee floor.
if satPerKw < b.minFeePerKW { if satPerKw < b.minFeePerKW {
walletLog.Debugf("Estimated fee rate of %v sat/kw is too low, "+ log.Debugf("Estimated fee rate of %v sat/kw is too low, "+
"using fee floor of %v sat/kw instead", satPerKw, "using fee floor of %v sat/kw instead", satPerKw,
b.minFeePerKW) b.minFeePerKW)
satPerKw = b.minFeePerKW satPerKw = b.minFeePerKW
} }
walletLog.Debugf("Returning %v sat/kw for conf target of %v", log.Debugf("Returning %v sat/kw for conf target of %v",
int64(satPerKw), confTarget) int64(satPerKw), confTarget)
return satPerKw, nil return satPerKw, nil
} }
// A compile-time assertion to ensure that BtcdFeeEstimator implements the // A compile-time assertion to ensure that BtcdEstimator implements the
// FeeEstimator interface. // Estimator interface.
var _ FeeEstimator = (*BtcdFeeEstimator)(nil) var _ Estimator = (*BtcdEstimator)(nil)
// BitcoindFeeEstimator is an implementation of the FeeEstimator interface // BitcoindEstimator is an implementation of the Estimator interface backed by
// backed by the RPC interface of an active bitcoind node. This implementation // the RPC interface of an active bitcoind node. This implementation will proxy
// will proxy any fee estimation requests to bitcoind's RPC interface. // any fee estimation requests to bitcoind's RPC interface.
type BitcoindFeeEstimator struct { type BitcoindEstimator struct {
// fallbackFeePerKW is the fallback fee rate in sat/kw that is returned // fallbackFeePerKW is the fallback fee rate in sat/kw that is returned
// if the fee estimator does not yet have enough data to actually // if the fee estimator does not yet have enough data to actually
// produce fee estimates. // produce fee estimates.
@ -334,13 +289,13 @@ type BitcoindFeeEstimator struct {
bitcoindConn *rpcclient.Client bitcoindConn *rpcclient.Client
} }
// NewBitcoindFeeEstimator creates a new BitcoindFeeEstimator given a fully // NewBitcoindEstimator creates a new BitcoindEstimator given a fully populated
// populated rpc config that is able to successfully connect and authenticate // rpc config that is able to successfully connect and authenticate with the
// with the bitcoind node, and also a fall back fee rate. The fallback fee rate // bitcoind node, and also a fall back fee rate. The fallback fee rate is used
// is used in the occasion that the estimator has insufficient data, or returns // in the occasion that the estimator has insufficient data, or returns zero
// zero for a fee estimate. // for a fee estimate.
func NewBitcoindFeeEstimator(rpcConfig rpcclient.ConnConfig, func NewBitcoindEstimator(rpcConfig rpcclient.ConnConfig,
fallBackFeeRate SatPerKWeight) (*BitcoindFeeEstimator, error) { fallBackFeeRate SatPerKWeight) (*BitcoindEstimator, error) {
rpcConfig.DisableConnectOnNew = true rpcConfig.DisableConnectOnNew = true
rpcConfig.DisableAutoReconnect = false rpcConfig.DisableAutoReconnect = false
@ -351,17 +306,17 @@ func NewBitcoindFeeEstimator(rpcConfig rpcclient.ConnConfig,
return nil, err return nil, err
} }
return &BitcoindFeeEstimator{ return &BitcoindEstimator{
fallbackFeePerKW: fallBackFeeRate, fallbackFeePerKW: fallBackFeeRate,
bitcoindConn: chainConn, bitcoindConn: chainConn,
}, nil }, nil
} }
// Start signals the FeeEstimator to start any processes or goroutines // Start signals the Estimator to start any processes or goroutines
// it needs to perform its duty. // it needs to perform its duty.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (b *BitcoindFeeEstimator) Start() error { func (b *BitcoindEstimator) Start() error {
// Once the connection to the backend node has been established, we'll // Once the connection to the backend node has been established, we'll
// query it for its minimum relay fee. Since the `getinfo` RPC has been // query it for its minimum relay fee. Since the `getinfo` RPC has been
// deprecated for `bitcoind`, we'll need to send a `getnetworkinfo` // deprecated for `bitcoind`, we'll need to send a `getnetworkinfo`
@ -396,7 +351,7 @@ func (b *BitcoindFeeEstimator) Start() error {
b.minFeePerKW = FeePerKwFloor b.minFeePerKW = FeePerKwFloor
} }
walletLog.Debugf("Using minimum fee rate of %v sat/kw", log.Debugf("Using minimum fee rate of %v sat/kw",
int64(b.minFeePerKW)) int64(b.minFeePerKW))
return nil return nil
@ -405,23 +360,23 @@ func (b *BitcoindFeeEstimator) Start() error {
// Stop stops any spawned goroutines and cleans up the resources used // Stop stops any spawned goroutines and cleans up the resources used
// by the fee estimator. // by the fee estimator.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (b *BitcoindFeeEstimator) Stop() error { func (b *BitcoindEstimator) Stop() error {
return nil return nil
} }
// EstimateFeePerKW takes in a target for the number of blocks until an initial // EstimateFeePerKW takes in a target for the number of blocks until an initial
// confirmation and returns the estimated fee expressed in sat/kw. // confirmation and returns the estimated fee expressed in sat/kw.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (b *BitcoindFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { func (b *BitcoindEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) {
feeEstimate, err := b.fetchEstimate(numBlocks) feeEstimate, err := b.fetchEstimate(numBlocks)
switch { switch {
// If the estimator doesn't have enough data, or returns an error, then // If the estimator doesn't have enough data, or returns an error, then
// to return a proper value, then we'll return the default fall back // to return a proper value, then we'll return the default fall back
// fee rate. // fee rate.
case err != nil: case err != nil:
walletLog.Errorf("unable to query estimator: %v", err) log.Errorf("unable to query estimator: %v", err)
fallthrough fallthrough
case feeEstimate == 0: case feeEstimate == 0:
@ -434,14 +389,14 @@ func (b *BitcoindFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight
// RelayFeePerKW returns the minimum fee rate required for transactions to be // RelayFeePerKW returns the minimum fee rate required for transactions to be
// relayed. // relayed.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (b *BitcoindFeeEstimator) RelayFeePerKW() SatPerKWeight { func (b *BitcoindEstimator) RelayFeePerKW() SatPerKWeight {
return b.minFeePerKW return b.minFeePerKW
} }
// fetchEstimate returns a fee estimate for a transaction to be confirmed in // fetchEstimate returns a fee estimate for a transaction to be confirmed in
// confTarget blocks. The estimate is returned in sat/kw. // confTarget blocks. The estimate is returned in sat/kw.
func (b *BitcoindFeeEstimator) fetchEstimate(confTarget uint32) (SatPerKWeight, error) { func (b *BitcoindEstimator) fetchEstimate(confTarget uint32) (SatPerKWeight, error) {
// First, we'll send an "estimatesmartfee" command as a raw request, // First, we'll send an "estimatesmartfee" command as a raw request,
// since it isn't supported by btcd but is available in bitcoind. // since it isn't supported by btcd but is available in bitcoind.
target, err := json.Marshal(uint64(confTarget)) target, err := json.Marshal(uint64(confTarget))
@ -478,26 +433,26 @@ func (b *BitcoindFeeEstimator) fetchEstimate(confTarget uint32) (SatPerKWeight,
// Finally, we'll enforce our fee floor. // Finally, we'll enforce our fee floor.
if satPerKw < b.minFeePerKW { if satPerKw < b.minFeePerKW {
walletLog.Debugf("Estimated fee rate of %v sat/kw is too low, "+ log.Debugf("Estimated fee rate of %v sat/kw is too low, "+
"using fee floor of %v sat/kw instead", satPerKw, "using fee floor of %v sat/kw instead", satPerKw,
b.minFeePerKW) b.minFeePerKW)
satPerKw = b.minFeePerKW satPerKw = b.minFeePerKW
} }
walletLog.Debugf("Returning %v sat/kw for conf target of %v", log.Debugf("Returning %v sat/kw for conf target of %v",
int64(satPerKw), confTarget) int64(satPerKw), confTarget)
return satPerKw, nil return satPerKw, nil
} }
// A compile-time assertion to ensure that BitcoindFeeEstimator implements the // A compile-time assertion to ensure that BitcoindEstimator implements the
// FeeEstimator interface. // Estimator interface.
var _ FeeEstimator = (*BitcoindFeeEstimator)(nil) var _ Estimator = (*BitcoindEstimator)(nil)
// WebAPIFeeSource is an interface allows the WebAPIFeeEstimator to query an // WebAPIFeeSource is an interface allows the WebAPIEstimator to query an
// arbitrary HTTP-based fee estimator. Each new set/network will gain an // arbitrary HTTP-based fee estimator. Each new set/network will gain an
// implementation of this interface in order to allow the WebAPIFeeEstimator to // implementation of this interface in order to allow the WebAPIEstimator to
// be fully generic in its logic. // be fully generic in its logic.
type WebAPIFeeSource interface { type WebAPIFeeSource interface {
// GenQueryURL generates the full query URL. The value returned by this // GenQueryURL generates the full query URL. The value returned by this
@ -554,9 +509,9 @@ func (s SparseConfFeeSource) ParseResponse(r io.Reader) (map[uint32]uint32, erro
// WebAPIFeeSource interface. // WebAPIFeeSource interface.
var _ WebAPIFeeSource = (*SparseConfFeeSource)(nil) var _ WebAPIFeeSource = (*SparseConfFeeSource)(nil)
// WebAPIFeeEstimator is an implementation of the FeeEstimator interface that // WebAPIEstimator is an implementation of the Estimator interface that
// queries an HTTP-based fee estimation from an existing web API. // queries an HTTP-based fee estimation from an existing web API.
type WebAPIFeeEstimator struct { type WebAPIEstimator struct {
started sync.Once started sync.Once
stopped sync.Once stopped sync.Once
@ -581,12 +536,12 @@ type WebAPIFeeEstimator struct {
wg sync.WaitGroup wg sync.WaitGroup
} }
// NewWebAPIFeeEstimator creates a new WebAPIFeeEstimator from a given URL and a // NewWebAPIEstimator creates a new WebAPIEstimator from a given URL and a
// fallback default fee. The fees are updated whenever a new block is mined. // fallback default fee. The fees are updated whenever a new block is mined.
func NewWebAPIFeeEstimator( func NewWebAPIEstimator(
api WebAPIFeeSource, defaultFee SatPerKWeight) *WebAPIFeeEstimator { api WebAPIFeeSource, defaultFee SatPerKWeight) *WebAPIEstimator {
return &WebAPIFeeEstimator{ return &WebAPIEstimator{
apiSource: api, apiSource: api,
feeByBlockTarget: make(map[uint32]uint32), feeByBlockTarget: make(map[uint32]uint32),
defaultFeePerKw: defaultFee, defaultFeePerKw: defaultFee,
@ -597,8 +552,8 @@ func NewWebAPIFeeEstimator(
// EstimateFeePerKW takes in a target for the number of blocks until an initial // EstimateFeePerKW takes in a target for the number of blocks until an initial
// confirmation and returns the estimated fee expressed in sat/kw. // confirmation and returns the estimated fee expressed in sat/kw.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (w *WebAPIFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { func (w *WebAPIEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) {
if numBlocks > maxBlockTarget { if numBlocks > maxBlockTarget {
numBlocks = maxBlockTarget numBlocks = maxBlockTarget
} else if numBlocks < minBlockTarget { } else if numBlocks < minBlockTarget {
@ -618,20 +573,20 @@ func (w *WebAPIFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight,
satPerKw = FeePerKwFloor satPerKw = FeePerKwFloor
} }
walletLog.Debugf("Web API returning %v sat/kw for conf target of %v", log.Debugf("Web API returning %v sat/kw for conf target of %v",
int64(satPerKw), numBlocks) int64(satPerKw), numBlocks)
return satPerKw, nil return satPerKw, nil
} }
// Start signals the FeeEstimator to start any processes or goroutines it needs // Start signals the Estimator to start any processes or goroutines it needs
// to perform its duty. // to perform its duty.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (w *WebAPIFeeEstimator) Start() error { func (w *WebAPIEstimator) Start() error {
var err error var err error
w.started.Do(func() { w.started.Do(func() {
walletLog.Infof("Starting web API fee estimator") log.Infof("Starting web API fee estimator")
w.updateFeeTicker = time.NewTicker(w.randomFeeUpdateTimeout()) w.updateFeeTicker = time.NewTicker(w.randomFeeUpdateTimeout())
w.updateFeeEstimates() w.updateFeeEstimates()
@ -646,10 +601,10 @@ func (w *WebAPIFeeEstimator) Start() error {
// Stop stops any spawned goroutines and cleans up the resources used by the // Stop stops any spawned goroutines and cleans up the resources used by the
// fee estimator. // fee estimator.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (w *WebAPIFeeEstimator) Stop() error { func (w *WebAPIEstimator) Stop() error {
w.stopped.Do(func() { w.stopped.Do(func() {
walletLog.Infof("Stopping web API fee estimator") log.Infof("Stopping web API fee estimator")
w.updateFeeTicker.Stop() w.updateFeeTicker.Stop()
@ -662,15 +617,15 @@ func (w *WebAPIFeeEstimator) Stop() error {
// RelayFeePerKW returns the minimum fee rate required for transactions to be // RelayFeePerKW returns the minimum fee rate required for transactions to be
// relayed. // relayed.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the Estimator interface.
func (w *WebAPIFeeEstimator) RelayFeePerKW() SatPerKWeight { func (w *WebAPIEstimator) RelayFeePerKW() SatPerKWeight {
return FeePerKwFloor return FeePerKwFloor
} }
// randomFeeUpdateTimeout returns a random timeout between minFeeUpdateTimeout // randomFeeUpdateTimeout returns a random timeout between minFeeUpdateTimeout
// and maxFeeUpdateTimeout that will be used to determine how often the Estimator // and maxFeeUpdateTimeout that will be used to determine how often the Estimator
// should retrieve fresh fees from its API. // should retrieve fresh fees from its API.
func (w *WebAPIFeeEstimator) randomFeeUpdateTimeout() time.Duration { func (w *WebAPIEstimator) randomFeeUpdateTimeout() time.Duration {
lower := int64(minFeeUpdateTimeout) lower := int64(minFeeUpdateTimeout)
upper := int64(maxFeeUpdateTimeout) upper := int64(maxFeeUpdateTimeout)
return time.Duration(prand.Int63n(upper-lower) + lower) return time.Duration(prand.Int63n(upper-lower) + lower)
@ -679,7 +634,7 @@ func (w *WebAPIFeeEstimator) randomFeeUpdateTimeout() time.Duration {
// getCachedFee takes in a target for the number of blocks until an initial // getCachedFee takes in a target for the number of blocks until an initial
// confirmation and returns an estimated fee (if one was returned by the API). If // confirmation and returns an estimated fee (if one was returned by the API). If
// the fee was not previously cached, we cache it here. // the fee was not previously cached, we cache it here.
func (w *WebAPIFeeEstimator) getCachedFee(numBlocks uint32) (uint32, error) { func (w *WebAPIEstimator) getCachedFee(numBlocks uint32) (uint32, error) {
w.feesMtx.Lock() w.feesMtx.Lock()
defer w.feesMtx.Unlock() defer w.feesMtx.Unlock()
@ -704,7 +659,7 @@ func (w *WebAPIFeeEstimator) getCachedFee(numBlocks uint32) (uint32, error) {
} }
// updateFeeEstimates re-queries the API for fresh fees and caches them. // updateFeeEstimates re-queries the API for fresh fees and caches them.
func (w *WebAPIFeeEstimator) updateFeeEstimates() { func (w *WebAPIEstimator) updateFeeEstimates() {
// Rather than use the default http.Client, we'll make a custom one // Rather than use the default http.Client, we'll make a custom one
// which will allow us to control how long we'll wait to read the // which will allow us to control how long we'll wait to read the
// response from the service. This way, if the service is down or // response from the service. This way, if the service is down or
@ -725,7 +680,7 @@ func (w *WebAPIFeeEstimator) updateFeeEstimates() {
targetURL := w.apiSource.GenQueryURL() targetURL := w.apiSource.GenQueryURL()
resp, err := netClient.Get(targetURL) resp, err := netClient.Get(targetURL)
if err != nil { if err != nil {
walletLog.Errorf("unable to query web api for fee response: %v", log.Errorf("unable to query web api for fee response: %v",
err) err)
return return
} }
@ -735,7 +690,7 @@ func (w *WebAPIFeeEstimator) updateFeeEstimates() {
// to parse out the body to obtain our final result. // to parse out the body to obtain our final result.
feesByBlockTarget, err := w.apiSource.ParseResponse(resp.Body) feesByBlockTarget, err := w.apiSource.ParseResponse(resp.Body)
if err != nil { if err != nil {
walletLog.Errorf("unable to query web api for fee response: %v", log.Errorf("unable to query web api for fee response: %v",
err) err)
return return
} }
@ -746,7 +701,7 @@ func (w *WebAPIFeeEstimator) updateFeeEstimates() {
} }
// feeUpdateManager updates the fee estimates whenever a new block comes in. // feeUpdateManager updates the fee estimates whenever a new block comes in.
func (w *WebAPIFeeEstimator) feeUpdateManager() { func (w *WebAPIEstimator) feeUpdateManager() {
defer w.wg.Done() defer w.wg.Done()
for { for {
@ -759,6 +714,6 @@ func (w *WebAPIFeeEstimator) feeUpdateManager() {
} }
} }
// A compile-time assertion to ensure that WebAPIFeeEstimator implements the // A compile-time assertion to ensure that WebAPIEstimator implements the
// FeeEstimator interface. // Estimator interface.
var _ FeeEstimator = (*WebAPIFeeEstimator)(nil) var _ Estimator = (*WebAPIEstimator)(nil)

@ -1,4 +1,4 @@
package lnwallet_test package chainfee
import ( import (
"bytes" "bytes"
@ -9,8 +9,6 @@ import (
"testing" "testing"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lnwallet"
) )
type mockSparseConfFeeSource struct { type mockSparseConfFeeSource struct {
@ -38,9 +36,9 @@ func TestFeeRateTypes(t *testing.T) {
const weight = vsize * 4 const weight = vsize * 4
// Test the conversion from sat/kw to sat/kb. // Test the conversion from sat/kw to sat/kb.
for feePerKw := lnwallet.SatPerKWeight(250); feePerKw < 10000; feePerKw += 50 { for feePerKw := SatPerKWeight(250); feePerKw < 10000; feePerKw += 50 {
feePerKB := feePerKw.FeePerKVByte() feePerKB := feePerKw.FeePerKVByte()
if feePerKB != lnwallet.SatPerKVByte(feePerKw*4) { if feePerKB != SatPerKVByte(feePerKw*4) {
t.Fatalf("expected %d sat/kb, got %d sat/kb when "+ t.Fatalf("expected %d sat/kb, got %d sat/kb when "+
"converting from %d sat/kw", feePerKw*4, "converting from %d sat/kw", feePerKw*4,
feePerKB, feePerKw) feePerKB, feePerKw)
@ -62,9 +60,9 @@ func TestFeeRateTypes(t *testing.T) {
} }
// Test the conversion from sat/kb to sat/kw. // Test the conversion from sat/kb to sat/kw.
for feePerKB := lnwallet.SatPerKVByte(1000); feePerKB < 40000; feePerKB += 1000 { for feePerKB := SatPerKVByte(1000); feePerKB < 40000; feePerKB += 1000 {
feePerKw := feePerKB.FeePerKWeight() feePerKw := feePerKB.FeePerKWeight()
if feePerKw != lnwallet.SatPerKWeight(feePerKB/4) { if feePerKw != SatPerKWeight(feePerKB/4) {
t.Fatalf("expected %d sat/kw, got %d sat/kw when "+ t.Fatalf("expected %d sat/kw, got %d sat/kw when "+
"converting from %d sat/kb", feePerKB/4, "converting from %d sat/kb", feePerKB/4,
feePerKw, feePerKB) feePerKw, feePerKB)
@ -91,9 +89,9 @@ func TestFeeRateTypes(t *testing.T) {
func TestStaticFeeEstimator(t *testing.T) { func TestStaticFeeEstimator(t *testing.T) {
t.Parallel() t.Parallel()
const feePerKw = lnwallet.FeePerKwFloor const feePerKw = FeePerKwFloor
feeEstimator := lnwallet.NewStaticFeeEstimator(feePerKw, 0) feeEstimator := NewStaticEstimator(feePerKw, 0)
if err := feeEstimator.Start(); err != nil { if err := feeEstimator.Start(); err != nil {
t.Fatalf("unable to start fee estimator: %v", err) t.Fatalf("unable to start fee estimator: %v", err)
} }
@ -116,7 +114,7 @@ func TestSparseConfFeeSource(t *testing.T) {
// Test that GenQueryURL returns the URL as is. // Test that GenQueryURL returns the URL as is.
url := "test" url := "test"
feeSource := lnwallet.SparseConfFeeSource{URL: url} feeSource := SparseConfFeeSource{URL: url}
queryURL := feeSource.GenQueryURL() queryURL := feeSource.GenQueryURL()
if queryURL != url { if queryURL != url {
t.Fatalf("expected query URL of %v, got %v", url, queryURL) t.Fatalf("expected query URL of %v, got %v", url, queryURL)
@ -166,7 +164,7 @@ func TestSparseConfFeeSource(t *testing.T) {
func TestWebAPIFeeEstimator(t *testing.T) { func TestWebAPIFeeEstimator(t *testing.T) {
t.Parallel() t.Parallel()
feeFloor := uint32(lnwallet.FeePerKwFloor.FeePerKVByte()) feeFloor := uint32(FeePerKwFloor.FeePerKVByte())
testCases := []struct { testCases := []struct {
name string name string
target uint32 target uint32
@ -194,7 +192,7 @@ func TestWebAPIFeeEstimator(t *testing.T) {
fees: testFees, fees: testFees,
} }
estimator := lnwallet.NewWebAPIFeeEstimator(feeSource, 10) estimator := NewWebAPIEstimator(feeSource, 10)
// Test that requesting a fee when no fees have been cached fails. // Test that requesting a fee when no fees have been cached fails.
_, err := estimator.EstimateFeePerKW(5) _, err := estimator.EstimateFeePerKW(5)
@ -210,6 +208,7 @@ func TestWebAPIFeeEstimator(t *testing.T) {
defer estimator.Stop() defer estimator.Stop()
for _, tc := range testCases { for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
est, err := estimator.EstimateFeePerKW(tc.target) est, err := estimator.EstimateFeePerKW(tc.target)
if tc.err != "" { if tc.err != "" {
@ -220,7 +219,7 @@ func TestWebAPIFeeEstimator(t *testing.T) {
"fail, instead got: %v", err) "fail, instead got: %v", err)
} }
} else { } else {
exp := lnwallet.SatPerKVByte(tc.est).FeePerKWeight() exp := SatPerKVByte(tc.est).FeePerKWeight()
if err != nil { if err != nil {
t.Fatalf("unable to estimate fee for "+ t.Fatalf("unable to estimate fee for "+
"%v block target, got: %v", "%v block target, got: %v",

29
lnwallet/chainfee/log.go Normal file

@ -0,0 +1,29 @@
package chainfee
import (
"github.com/btcsuite/btclog"
"github.com/lightningnetwork/lnd/build"
)
// log is a logger that is initialized with no output filters. This means the
// package will not perform any logging by default until the caller requests
// it.
var log btclog.Logger
// The default amount of logging is none.
func init() {
UseLogger(build.NewSubLogger("CFEE", nil))
}
// DisableLog disables all library log output. Logging output is disabled by
// default until UseLogger is called.
func DisableLog() {
UseLogger(btclog.Disabled)
}
// UseLogger uses a specified Logger to output package logging info. This
// should be used in preference to SetLogWriter if the caller is also using
// btclog.
func UseLogger(logger btclog.Logger) {
log = logger
}

@ -0,0 +1,53 @@
package chainfee
import (
"fmt"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcutil"
)
const (
// FeePerKwFloor is the lowest fee rate in sat/kw that we should use for
// determining transaction fees.
FeePerKwFloor SatPerKWeight = 253
)
// SatPerKVByte represents a fee rate in sat/kb.
type SatPerKVByte btcutil.Amount
// FeeForVSize calculates the fee resulting from this fee rate and the given
// vsize in vbytes.
func (s SatPerKVByte) FeeForVSize(vbytes int64) btcutil.Amount {
return btcutil.Amount(s) * btcutil.Amount(vbytes) / 1000
}
// FeePerKWeight converts the current fee rate from sat/kb to sat/kw.
func (s SatPerKVByte) FeePerKWeight() SatPerKWeight {
return SatPerKWeight(s / blockchain.WitnessScaleFactor)
}
// String returns a human-readable string of the fee rate.
func (s SatPerKVByte) String() string {
return fmt.Sprintf("%v sat/kb", int64(s))
}
// SatPerKWeight represents a fee rate in sat/kw.
type SatPerKWeight btcutil.Amount
// FeeForWeight calculates the fee resulting from this fee rate and the given
// weight in weight units (wu).
func (s SatPerKWeight) FeeForWeight(wu int64) btcutil.Amount {
// The resulting fee is rounded down, as specified in BOLT#03.
return btcutil.Amount(s) * btcutil.Amount(wu) / 1000
}
// FeePerKVByte converts the current fee rate from sat/kw to sat/kb.
func (s SatPerKWeight) FeePerKVByte() SatPerKVByte {
return SatPerKVByte(s * blockchain.WitnessScaleFactor)
}
// String returns a human-readable string of the fee rate.
func (s SatPerKWeight) String() string {
return fmt.Sprintf("%v sat/kw", int64(s))
}

@ -22,6 +22,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -512,7 +513,7 @@ type commitment struct {
// feePerKw is the fee per kw used to calculate this commitment // feePerKw is the fee per kw used to calculate this commitment
// transaction's fee. // transaction's fee.
feePerKw SatPerKWeight feePerKw chainfee.SatPerKWeight
// dustLimit is the limit on the commitment transaction such that no // dustLimit is the limit on the commitment transaction such that no
// output values should be below this amount. // output values should be below this amount.
@ -759,7 +760,7 @@ func (c *commitment) toDiskCommit(ourCommit bool) *channeldb.ChannelCommitment {
// commitment struct and updateLog. This function is used when we need to // commitment struct and updateLog. This function is used when we need to
// restore commitment state written do disk back into memory once we need to // restore commitment state written do disk back into memory once we need to
// restart a channel session. // restart a channel session.
func (lc *LightningChannel) diskHtlcToPayDesc(feeRate SatPerKWeight, func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight,
commitHeight uint64, htlc *channeldb.HTLC, localCommitKeys, commitHeight uint64, htlc *channeldb.HTLC, localCommitKeys,
remoteCommitKeys *CommitmentKeyRing) (PaymentDescriptor, error) { remoteCommitKeys *CommitmentKeyRing) (PaymentDescriptor, error) {
@ -824,7 +825,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate SatPerKWeight,
// these payment descriptors can be re-inserted into the in-memory updateLog // these payment descriptors can be re-inserted into the in-memory updateLog
// for each side. // for each side.
func (lc *LightningChannel) extractPayDescs(commitHeight uint64, func (lc *LightningChannel) extractPayDescs(commitHeight uint64,
feeRate SatPerKWeight, htlcs []channeldb.HTLC, localCommitKeys, feeRate chainfee.SatPerKWeight, htlcs []channeldb.HTLC, localCommitKeys,
remoteCommitKeys *CommitmentKeyRing) ([]PaymentDescriptor, []PaymentDescriptor, error) { remoteCommitKeys *CommitmentKeyRing) ([]PaymentDescriptor, []PaymentDescriptor, error) {
var ( var (
@ -894,7 +895,8 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool,
// HTLC"s into PaymentDescriptor's so we can re-insert them into our // HTLC"s into PaymentDescriptor's so we can re-insert them into our
// update log. // update log.
incomingHtlcs, outgoingHtlcs, err := lc.extractPayDescs( incomingHtlcs, outgoingHtlcs, err := lc.extractPayDescs(
diskCommit.CommitHeight, SatPerKWeight(diskCommit.FeePerKw), diskCommit.CommitHeight,
chainfee.SatPerKWeight(diskCommit.FeePerKw),
diskCommit.Htlcs, localCommitKeys, remoteCommitKeys, diskCommit.Htlcs, localCommitKeys, remoteCommitKeys,
) )
if err != nil { if err != nil {
@ -915,7 +917,7 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool,
txn: diskCommit.CommitTx, txn: diskCommit.CommitTx,
sig: diskCommit.CommitSig, sig: diskCommit.CommitSig,
fee: diskCommit.CommitFee, fee: diskCommit.CommitFee,
feePerKw: SatPerKWeight(diskCommit.FeePerKw), feePerKw: chainfee.SatPerKWeight(diskCommit.FeePerKw),
incomingHTLCs: incomingHtlcs, incomingHTLCs: incomingHtlcs,
outgoingHTLCs: outgoingHtlcs, outgoingHTLCs: outgoingHtlcs,
} }
@ -1509,7 +1511,7 @@ func (lc *LightningChannel) ResetState() {
// if nothing happened. // if nothing happened.
func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate,
remoteUpdateLog *updateLog, commitHeight uint64, remoteUpdateLog *updateLog, commitHeight uint64,
feeRate SatPerKWeight, remoteCommitKeys *CommitmentKeyRing, feeRate chainfee.SatPerKWeight, remoteCommitKeys *CommitmentKeyRing,
remoteDustLimit btcutil.Amount) (*PaymentDescriptor, error) { remoteDustLimit btcutil.Amount) (*PaymentDescriptor, error) {
// Depending on the type of update message we'll map that to a distinct // Depending on the type of update message we'll map that to a distinct
@ -1826,7 +1828,8 @@ func (lc *LightningChannel) restoreStateLogs(
for _, logUpdate := range pendingRemoteCommitDiff.LogUpdates { for _, logUpdate := range pendingRemoteCommitDiff.LogUpdates {
payDesc, err := lc.logUpdateToPayDesc( payDesc, err := lc.logUpdateToPayDesc(
&logUpdate, lc.remoteUpdateLog, pendingHeight, &logUpdate, lc.remoteUpdateLog, pendingHeight,
SatPerKWeight(pendingCommit.FeePerKw), pendingRemoteKeys, chainfee.SatPerKWeight(pendingCommit.FeePerKw),
pendingRemoteKeys,
lc.channelState.RemoteChanCfg.DustLimit, lc.channelState.RemoteChanCfg.DustLimit,
) )
if err != nil { if err != nil {
@ -2109,7 +2112,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// an output on the commitment transaction. // an output on the commitment transaction.
if htlcIsDust( if htlcIsDust(
htlc.Incoming, false, htlc.Incoming, false,
SatPerKWeight(revokedSnapshot.FeePerKw), chainfee.SatPerKWeight(revokedSnapshot.FeePerKw),
htlc.Amt.ToSatoshis(), chanState.RemoteChanCfg.DustLimit, htlc.Amt.ToSatoshis(), chanState.RemoteChanCfg.DustLimit,
) { ) {
continue continue
@ -2198,13 +2201,13 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// htlcTimeoutFee returns the fee in satoshis required for an HTLC timeout // htlcTimeoutFee returns the fee in satoshis required for an HTLC timeout
// transaction based on the current fee rate. // transaction based on the current fee rate.
func htlcTimeoutFee(feePerKw SatPerKWeight) btcutil.Amount { func htlcTimeoutFee(feePerKw chainfee.SatPerKWeight) btcutil.Amount {
return feePerKw.FeeForWeight(input.HtlcTimeoutWeight) return feePerKw.FeeForWeight(input.HtlcTimeoutWeight)
} }
// htlcSuccessFee returns the fee in satoshis required for an HTLC success // htlcSuccessFee returns the fee in satoshis required for an HTLC success
// transaction based on the current fee rate. // transaction based on the current fee rate.
func htlcSuccessFee(feePerKw SatPerKWeight) btcutil.Amount { func htlcSuccessFee(feePerKw chainfee.SatPerKWeight) btcutil.Amount {
return feePerKw.FeeForWeight(input.HtlcSuccessWeight) return feePerKw.FeeForWeight(input.HtlcSuccessWeight)
} }
@ -2214,7 +2217,7 @@ func htlcSuccessFee(feePerKw SatPerKWeight) btcutil.Amount {
// require as we currently used second-level HTLC transactions as off-chain // require as we currently used second-level HTLC transactions as off-chain
// covenants. Depending on the two bits, we'll either be using a timeout or // covenants. Depending on the two bits, we'll either be using a timeout or
// success transaction which have different weights. // success transaction which have different weights.
func htlcIsDust(incoming, ourCommit bool, feePerKw SatPerKWeight, func htlcIsDust(incoming, ourCommit bool, feePerKw chainfee.SatPerKWeight,
htlcAmt, dustLimit btcutil.Amount) bool { htlcAmt, dustLimit btcutil.Amount) bool {
// First we'll determine the fee required for this HTLC based on if this is // First we'll determine the fee required for this HTLC based on if this is
@ -2255,7 +2258,7 @@ func htlcIsDust(incoming, ourCommit bool, feePerKw SatPerKWeight,
type htlcView struct { type htlcView struct {
ourUpdates []*PaymentDescriptor ourUpdates []*PaymentDescriptor
theirUpdates []*PaymentDescriptor theirUpdates []*PaymentDescriptor
feePerKw SatPerKWeight feePerKw chainfee.SatPerKWeight
} }
// fetchHTLCView returns all the candidate HTLC updates which should be // fetchHTLCView returns all the candidate HTLC updates which should be
@ -2786,7 +2789,7 @@ func processFeeUpdate(feeUpdate *PaymentDescriptor, nextHeight uint64,
// If the update wasn't already locked in, update the current fee rate // If the update wasn't already locked in, update the current fee rate
// to reflect this update. // to reflect this update.
view.feePerKw = SatPerKWeight(feeUpdate.Amount.ToSatoshis()) view.feePerKw = chainfee.SatPerKWeight(feeUpdate.Amount.ToSatoshis())
if mutateState { if mutateState {
*addHeight = nextHeight *addHeight = nextHeight
@ -3131,9 +3134,9 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
// Ensure that the fee being applied is enough to be relayed across the // Ensure that the fee being applied is enough to be relayed across the
// network in a reasonable time frame. // network in a reasonable time frame.
if feePerKw < FeePerKwFloor { if feePerKw < chainfee.FeePerKwFloor {
return fmt.Errorf("commitment fee per kw %v below fee floor %v", return fmt.Errorf("commitment fee per kw %v below fee floor %v",
feePerKw, FeePerKwFloor) feePerKw, chainfee.FeePerKwFloor)
} }
// If the added HTLCs will decrease the balance, make sure they won't // If the added HTLCs will decrease the balance, make sure they won't
@ -5049,7 +5052,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
// Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we // Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we
// had on their commitment transaction. // had on their commitment transaction.
htlcResolutions, err := extractHtlcResolutions( htlcResolutions, err := extractHtlcResolutions(
SatPerKWeight(remoteCommit.FeePerKw), false, signer, chainfee.SatPerKWeight(remoteCommit.FeePerKw), false, signer,
remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
&chanState.RemoteChanCfg, *commitSpend.SpenderTxHash, &chanState.RemoteChanCfg, *commitSpend.SpenderTxHash,
) )
@ -5246,10 +5249,11 @@ type HtlcResolutions struct {
// newOutgoingHtlcResolution generates a new HTLC resolution capable of // newOutgoingHtlcResolution generates a new HTLC resolution capable of
// allowing the caller to sweep an outgoing HTLC present on either their, or // allowing the caller to sweep an outgoing HTLC present on either their, or
// the remote party's commitment transaction. // the remote party's commitment transaction.
func newOutgoingHtlcResolution(signer input.Signer, localChanCfg *channeldb.ChannelConfig, func newOutgoingHtlcResolution(signer input.Signer,
commitHash chainhash.Hash, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, localChanCfg *channeldb.ChannelConfig, commitHash chainhash.Hash,
feePerKw SatPerKWeight, dustLimit btcutil.Amount, csvDelay uint32, localCommit bool, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
) (*OutgoingHtlcResolution, error) { feePerKw chainfee.SatPerKWeight, csvDelay uint32,
localCommit bool) (*OutgoingHtlcResolution, error) {
op := wire.OutPoint{ op := wire.OutPoint{
Hash: commitHash, Hash: commitHash,
@ -5388,7 +5392,7 @@ func newOutgoingHtlcResolution(signer input.Signer, localChanCfg *channeldb.Chan
// TODO(roasbeef) consolidate code with above func // TODO(roasbeef) consolidate code with above func
func newIncomingHtlcResolution(signer input.Signer, localChanCfg *channeldb.ChannelConfig, func newIncomingHtlcResolution(signer input.Signer, localChanCfg *channeldb.ChannelConfig,
commitHash chainhash.Hash, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, commitHash chainhash.Hash, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
feePerKw SatPerKWeight, dustLimit btcutil.Amount, csvDelay uint32, feePerKw chainfee.SatPerKWeight, csvDelay uint32,
localCommit bool) (*IncomingHtlcResolution, error) { localCommit bool) (*IncomingHtlcResolution, error) {
op := wire.OutPoint{ op := wire.OutPoint{
@ -5542,7 +5546,7 @@ func (r *OutgoingHtlcResolution) HtlcPoint() wire.OutPoint {
// extractHtlcResolutions creates a series of outgoing HTLC resolutions, and // extractHtlcResolutions creates a series of outgoing HTLC resolutions, and
// the local key used when generating the HTLC scrips. This function is to be // the local key used when generating the HTLC scrips. This function is to be
// used in two cases: force close, or a unilateral close. // used in two cases: force close, or a unilateral close.
func extractHtlcResolutions(feePerKw SatPerKWeight, ourCommit bool, func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ourCommit bool,
signer input.Signer, htlcs []channeldb.HTLC, keyRing *CommitmentKeyRing, signer input.Signer, htlcs []channeldb.HTLC, keyRing *CommitmentKeyRing,
localChanCfg, remoteChanCfg *channeldb.ChannelConfig, localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
commitHash chainhash.Hash) (*HtlcResolutions, error) { commitHash chainhash.Hash) (*HtlcResolutions, error) {
@ -5573,7 +5577,7 @@ func extractHtlcResolutions(feePerKw SatPerKWeight, ourCommit bool,
// as we can satisfy the contract. // as we can satisfy the contract.
ihr, err := newIncomingHtlcResolution( ihr, err := newIncomingHtlcResolution(
signer, localChanCfg, commitHash, &htlc, keyRing, signer, localChanCfg, commitHash, &htlc, keyRing,
feePerKw, dustLimit, uint32(csvDelay), ourCommit, feePerKw, uint32(csvDelay), ourCommit,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -5585,7 +5589,7 @@ func extractHtlcResolutions(feePerKw SatPerKWeight, ourCommit bool,
ohr, err := newOutgoingHtlcResolution( ohr, err := newOutgoingHtlcResolution(
signer, localChanCfg, commitHash, &htlc, keyRing, signer, localChanCfg, commitHash, &htlc, keyRing,
feePerKw, dustLimit, uint32(csvDelay), ourCommit, feePerKw, uint32(csvDelay), ourCommit,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -5763,7 +5767,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
// outgoing HTLC's that we'll need to claim as well. // outgoing HTLC's that we'll need to claim as well.
txHash := commitTx.TxHash() txHash := commitTx.TxHash()
htlcResolutions, err := extractHtlcResolutions( htlcResolutions, err := extractHtlcResolutions(
SatPerKWeight(localCommit.FeePerKw), true, signer, chainfee.SatPerKWeight(localCommit.FeePerKw), true, signer,
localCommit.Htlcs, keyRing, &chanState.LocalChanCfg, localCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
&chanState.RemoteChanCfg, txHash, &chanState.RemoteChanCfg, txHash,
) )
@ -5981,7 +5985,7 @@ func (lc *LightningChannel) StateSnapshot() *channeldb.ChannelSnapshot {
// validateFeeRate ensures that if the passed fee is applied to the channel, // validateFeeRate ensures that if the passed fee is applied to the channel,
// and a new commitment is created (which evaluates this fee), then the // and a new commitment is created (which evaluates this fee), then the
// initiator of the channel does not dip below their reserve. // initiator of the channel does not dip below their reserve.
func (lc *LightningChannel) validateFeeRate(feePerKw SatPerKWeight) error { func (lc *LightningChannel) validateFeeRate(feePerKw chainfee.SatPerKWeight) error {
// We'll ensure that we can accommodate this new fee change, yet still // We'll ensure that we can accommodate this new fee change, yet still
// be above our reserve balance. Otherwise, we'll reject the fee // be above our reserve balance. Otherwise, we'll reject the fee
// update. // update.
@ -6029,7 +6033,7 @@ func (lc *LightningChannel) validateFeeRate(feePerKw SatPerKWeight) error {
// UpdateFee initiates a fee update for this channel. Must only be called by // UpdateFee initiates a fee update for this channel. Must only be called by
// the channel initiator, and must be called before sending update_fee to // the channel initiator, and must be called before sending update_fee to
// the remote. // the remote.
func (lc *LightningChannel) UpdateFee(feePerKw SatPerKWeight) error { func (lc *LightningChannel) UpdateFee(feePerKw chainfee.SatPerKWeight) error {
lc.Lock() lc.Lock()
defer lc.Unlock() defer lc.Unlock()
@ -6057,7 +6061,7 @@ func (lc *LightningChannel) UpdateFee(feePerKw SatPerKWeight) error {
// ReceiveUpdateFee handles an updated fee sent from remote. This method will // ReceiveUpdateFee handles an updated fee sent from remote. This method will
// return an error if called as channel initiator. // return an error if called as channel initiator.
func (lc *LightningChannel) ReceiveUpdateFee(feePerKw SatPerKWeight) error { func (lc *LightningChannel) ReceiveUpdateFee(feePerKw chainfee.SatPerKWeight) error {
lc.Lock() lc.Lock()
defer lc.Unlock() defer lc.Unlock()
@ -6213,7 +6217,7 @@ func CreateCooperativeCloseTx(fundingTxIn wire.TxIn,
// CalcFee returns the commitment fee to use for the given // CalcFee returns the commitment fee to use for the given
// fee rate (fee-per-kw). // fee rate (fee-per-kw).
func (lc *LightningChannel) CalcFee(feeRate SatPerKWeight) btcutil.Amount { func (lc *LightningChannel) CalcFee(feeRate chainfee.SatPerKWeight) btcutil.Amount {
return feeRate.FeeForWeight(input.CommitWeight) return feeRate.FeeForWeight(input.CommitWeight)
} }
@ -6223,7 +6227,7 @@ func (lc *LightningChannel) CalcFee(feeRate SatPerKWeight) btcutil.Amount {
// //
// NOTE: This should only be used for channels in which the local commitment is // NOTE: This should only be used for channels in which the local commitment is
// the initiator. // the initiator.
func (lc *LightningChannel) MaxFeeRate(maxAllocation float64) SatPerKWeight { func (lc *LightningChannel) MaxFeeRate(maxAllocation float64) chainfee.SatPerKWeight {
lc.RLock() lc.RLock()
defer lc.RUnlock() defer lc.RUnlock()
@ -6237,7 +6241,9 @@ func (lc *LightningChannel) MaxFeeRate(maxAllocation float64) SatPerKWeight {
// Ensure the fee rate doesn't dip below the fee floor. // Ensure the fee rate doesn't dip below the fee floor.
maxFeeRate := maxFee / (float64(weight) / 1000) maxFeeRate := maxFee / (float64(weight) / 1000)
return SatPerKWeight(math.Max(maxFeeRate, float64(FeePerKwFloor))) return chainfee.SatPerKWeight(
math.Max(maxFeeRate, float64(chainfee.FeePerKwFloor)),
)
} }
// RemoteNextRevocation returns the channelState's RemoteNextRevocation. // RemoteNextRevocation returns the channelState's RemoteNextRevocation.
@ -6260,11 +6266,11 @@ func (lc *LightningChannel) IsInitiator() bool {
// CommitFeeRate returns the current fee rate of the commitment transaction in // CommitFeeRate returns the current fee rate of the commitment transaction in
// units of sat-per-kw. // units of sat-per-kw.
func (lc *LightningChannel) CommitFeeRate() SatPerKWeight { func (lc *LightningChannel) CommitFeeRate() chainfee.SatPerKWeight {
lc.RLock() lc.RLock()
defer lc.RUnlock() defer lc.RUnlock()
return SatPerKWeight(lc.channelState.LocalCommitment.FeePerKw) return chainfee.SatPerKWeight(lc.channelState.LocalCommitment.FeePerKw)
} }
// IsPending returns true if the channel's funding transaction has been fully // IsPending returns true if the channel's funding transaction has been fully

@ -19,6 +19,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -456,8 +457,12 @@ func TestCooperativeChannelClosure(t *testing.T) {
aliceDeliveryScript := bobsPrivKey[:] aliceDeliveryScript := bobsPrivKey[:]
bobDeliveryScript := testHdSeed[:] bobDeliveryScript := testHdSeed[:]
aliceFeeRate := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) aliceFeeRate := chainfee.SatPerKWeight(
bobFeeRate := SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) aliceChannel.channelState.LocalCommitment.FeePerKw,
)
bobFeeRate := chainfee.SatPerKWeight(
bobChannel.channelState.LocalCommitment.FeePerKw,
)
// We'll store with both Alice and Bob creating a new close proposal // We'll store with both Alice and Bob creating a new close proposal
// with the same fee. // with the same fee.
@ -596,7 +601,9 @@ func TestForceClose(t *testing.T) {
// Factoring in the fee rate, Alice's amount should properly reflect // Factoring in the fee rate, Alice's amount should properly reflect
// that we've added two additional HTLC to the commitment transaction. // that we've added two additional HTLC to the commitment transaction.
totalCommitWeight := input.CommitWeight + (input.HtlcWeight * 2) totalCommitWeight := input.CommitWeight + (input.HtlcWeight * 2)
feePerKw := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) feePerKw := chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
)
commitFee := feePerKw.FeeForWeight(totalCommitWeight) commitFee := feePerKw.FeeForWeight(totalCommitWeight)
expectedAmount := (aliceChannel.Capacity / 2) - htlcAmount.ToSatoshis() - commitFee expectedAmount := (aliceChannel.Capacity / 2) - htlcAmount.ToSatoshis() - commitFee
if aliceCommitResolution.SelfOutputSignDesc.Output.Value != int64(expectedAmount) { if aliceCommitResolution.SelfOutputSignDesc.Output.Value != int64(expectedAmount) {
@ -1004,7 +1011,10 @@ func TestHTLCDustLimit(t *testing.T) {
// The amount of the HTLC should be above Alice's dust limit and below // The amount of the HTLC should be above Alice's dust limit and below
// Bob's dust limit. // Bob's dust limit.
htlcSat := (btcutil.Amount(500) + htlcTimeoutFee( htlcSat := (btcutil.Amount(500) + htlcTimeoutFee(
SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw))) chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
),
))
htlcAmount := lnwire.NewMSatFromSatoshis(htlcSat) htlcAmount := lnwire.NewMSatFromSatoshis(htlcSat)
htlc, preimage := createHTLC(0, htlcAmount) htlc, preimage := createHTLC(0, htlcAmount)
@ -1102,7 +1112,7 @@ func TestHTLCSigNumber(t *testing.T) {
} }
// Calculate two values that will be below and above Bob's dust limit. // Calculate two values that will be below and above Bob's dust limit.
estimator := NewStaticFeeEstimator(6000, 0) estimator := chainfee.NewStaticEstimator(6000, 0)
feePerKw, err := estimator.EstimateFeePerKW(1) feePerKw, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
t.Fatalf("unable to get fee: %v", err) t.Fatalf("unable to get fee: %v", err)
@ -1268,7 +1278,9 @@ func TestChannelBalanceDustLimit(t *testing.T) {
aliceBalance := aliceChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis() aliceBalance := aliceChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis()
htlcSat := aliceBalance - defaultFee htlcSat := aliceBalance - defaultFee
htlcSat += htlcSuccessFee( htlcSat += htlcSuccessFee(
SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw), chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
),
) )
htlcAmount := lnwire.NewMSatFromSatoshis(htlcSat) htlcAmount := lnwire.NewMSatFromSatoshis(htlcSat)
@ -1366,7 +1378,7 @@ func TestStateUpdatePersistence(t *testing.T) {
} }
// Also add a fee update to the update logs. // Also add a fee update to the update logs.
fee := SatPerKWeight(333) fee := chainfee.SatPerKWeight(333)
if err := aliceChannel.UpdateFee(fee); err != nil { if err := aliceChannel.UpdateFee(fee); err != nil {
t.Fatalf("unable to send fee update") t.Fatalf("unable to send fee update")
} }
@ -1779,8 +1791,12 @@ func TestCooperativeCloseDustAdherence(t *testing.T) {
} }
defer cleanUp() defer cleanUp()
aliceFeeRate := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) aliceFeeRate := chainfee.SatPerKWeight(
bobFeeRate := SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) aliceChannel.channelState.LocalCommitment.FeePerKw,
)
bobFeeRate := chainfee.SatPerKWeight(
bobChannel.channelState.LocalCommitment.FeePerKw,
)
setDustLimit := func(dustVal btcutil.Amount) { setDustLimit := func(dustVal btcutil.Amount) {
aliceChannel.channelState.LocalChanCfg.DustLimit = dustVal aliceChannel.channelState.LocalChanCfg.DustLimit = dustVal
@ -1946,7 +1962,7 @@ func TestUpdateFeeAdjustments(t *testing.T) {
// We'll first try to increase the fee rate 5x, this should be able to // We'll first try to increase the fee rate 5x, this should be able to
// be committed without any issue. // be committed without any issue.
newFee := SatPerKWeight(baseFeeRate * 5) newFee := chainfee.SatPerKWeight(baseFeeRate * 5)
if err := aliceChannel.UpdateFee(newFee); err != nil { if err := aliceChannel.UpdateFee(newFee); err != nil {
t.Fatalf("unable to alice update fee: %v", err) t.Fatalf("unable to alice update fee: %v", err)
@ -1964,7 +1980,7 @@ func TestUpdateFeeAdjustments(t *testing.T) {
// We'll now attempt to increase the fee rate 1,000,000x of the base // We'll now attempt to increase the fee rate 1,000,000x of the base
// fee. This should result in an error as Alice won't be able to pay // fee. This should result in an error as Alice won't be able to pay
// this new fee rate. // this new fee rate.
newFee = SatPerKWeight(baseFeeRate * 1000000) newFee = chainfee.SatPerKWeight(baseFeeRate * 1000000)
if err := aliceChannel.UpdateFee(newFee); err == nil { if err := aliceChannel.UpdateFee(newFee); err == nil {
t.Fatalf("alice should reject the fee rate") t.Fatalf("alice should reject the fee rate")
} }
@ -1972,7 +1988,7 @@ func TestUpdateFeeAdjustments(t *testing.T) {
// Finally, we'll attempt to adjust the fee down and use a fee which is // Finally, we'll attempt to adjust the fee down and use a fee which is
// smaller than the initial base fee rate. The fee application and // smaller than the initial base fee rate. The fee application and
// state transition should proceed without issue. // state transition should proceed without issue.
newFee = SatPerKWeight(baseFeeRate / 10) newFee = chainfee.SatPerKWeight(baseFeeRate / 10)
if err := aliceChannel.UpdateFee(newFee); err != nil { if err := aliceChannel.UpdateFee(newFee); err != nil {
t.Fatalf("unable to alice update fee: %v", err) t.Fatalf("unable to alice update fee: %v", err)
} }
@ -2048,7 +2064,7 @@ func TestUpdateFeeConcurrentSig(t *testing.T) {
} }
// Simulate Alice sending update fee message to bob. // Simulate Alice sending update fee message to bob.
fee := SatPerKWeight(333) fee := chainfee.SatPerKWeight(333)
if err := aliceChannel.UpdateFee(fee); err != nil { if err := aliceChannel.UpdateFee(fee); err != nil {
t.Fatalf("unable to send fee update") t.Fatalf("unable to send fee update")
} }
@ -2084,7 +2100,7 @@ func TestUpdateFeeConcurrentSig(t *testing.T) {
t.Fatalf("bob unable to process alice's new commitment: %v", err) t.Fatalf("bob unable to process alice's new commitment: %v", err)
} }
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee { if chainfee.SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee {
t.Fatalf("bob's feePerKw was unexpectedly locked in") t.Fatalf("bob's feePerKw was unexpectedly locked in")
} }
@ -2095,7 +2111,7 @@ func TestUpdateFeeConcurrentSig(t *testing.T) {
t.Fatalf("unable to generate bob revocation: %v", err) t.Fatalf("unable to generate bob revocation: %v", err)
} }
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee { if chainfee.SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee {
t.Fatalf("bob's feePerKw was not locked in") t.Fatalf("bob's feePerKw was not locked in")
} }
} }
@ -2134,7 +2150,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
} }
// Simulate Alice sending update fee message to bob. // Simulate Alice sending update fee message to bob.
fee := SatPerKWeight(333) fee := chainfee.SatPerKWeight(333)
aliceChannel.UpdateFee(fee) aliceChannel.UpdateFee(fee)
bobChannel.ReceiveUpdateFee(fee) bobChannel.ReceiveUpdateFee(fee)
@ -2154,7 +2170,9 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
t.Fatalf("bob unable to process alice's new commitment: %v", err) t.Fatalf("bob unable to process alice's new commitment: %v", err)
} }
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee { if chainfee.SatPerKWeight(
bobChannel.channelState.LocalCommitment.FeePerKw,
) == fee {
t.Fatalf("bob's feePerKw was unexpectedly locked in") t.Fatalf("bob's feePerKw was unexpectedly locked in")
} }
@ -2165,7 +2183,9 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
t.Fatalf("unable to generate bob revocation: %v", err) t.Fatalf("unable to generate bob revocation: %v", err)
} }
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee { if chainfee.SatPerKWeight(
bobChannel.channelState.LocalCommitment.FeePerKw,
) != fee {
t.Fatalf("bob's feePerKw was not locked in") t.Fatalf("bob's feePerKw was not locked in")
} }
@ -2191,7 +2211,9 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
t.Fatalf("alice unable to process bob's new commitment: %v", err) t.Fatalf("alice unable to process bob's new commitment: %v", err)
} }
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) == fee { if chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
) == fee {
t.Fatalf("alice's feePerKw was unexpectedly locked in") t.Fatalf("alice's feePerKw was unexpectedly locked in")
} }
@ -2202,7 +2224,9 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
t.Fatalf("unable to revoke alice channel: %v", err) t.Fatalf("unable to revoke alice channel: %v", err)
} }
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != fee { if chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
) != fee {
t.Fatalf("alice's feePerKw was not locked in") t.Fatalf("alice's feePerKw was not locked in")
} }
@ -2248,7 +2272,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
} }
// Simulate Alice sending update fee message to bob // Simulate Alice sending update fee message to bob
fee := SatPerKWeight(333) fee := chainfee.SatPerKWeight(333)
aliceChannel.UpdateFee(fee) aliceChannel.UpdateFee(fee)
bobChannel.ReceiveUpdateFee(fee) bobChannel.ReceiveUpdateFee(fee)
@ -2296,7 +2320,9 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
t.Fatalf("alice unable to process bob's new commitment: %v", err) t.Fatalf("alice unable to process bob's new commitment: %v", err)
} }
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee { if chainfee.SatPerKWeight(
bobChannel.channelState.LocalCommitment.FeePerKw,
) == fee {
t.Fatalf("bob's feePerKw was unexpectedly locked in") t.Fatalf("bob's feePerKw was unexpectedly locked in")
} }
@ -2308,7 +2334,9 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
t.Fatalf("unable to revoke alice channel: %v", err) t.Fatalf("unable to revoke alice channel: %v", err)
} }
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee { if chainfee.SatPerKWeight(
bobChannel.channelState.LocalCommitment.FeePerKw,
) != fee {
t.Fatalf("bob's feePerKw was not locked in") t.Fatalf("bob's feePerKw was not locked in")
} }
@ -2333,7 +2361,9 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
t.Fatalf("alice unable to process bob's new commitment: %v", err) t.Fatalf("alice unable to process bob's new commitment: %v", err)
} }
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) == fee { if chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
) == fee {
t.Fatalf("alice's feePerKw was unexpectedly locked in") t.Fatalf("alice's feePerKw was unexpectedly locked in")
} }
@ -2344,7 +2374,9 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
t.Fatalf("unable to generate bob revocation: %v", err) t.Fatalf("unable to generate bob revocation: %v", err)
} }
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != fee { if chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
) != fee {
t.Fatalf("Alice's feePerKw was not locked in") t.Fatalf("Alice's feePerKw was not locked in")
} }
@ -2372,7 +2404,7 @@ func TestUpdateFeeReceiverSendsUpdate(t *testing.T) {
// Since Alice is the channel initiator, she should fail when receiving // Since Alice is the channel initiator, she should fail when receiving
// fee update // fee update
fee := SatPerKWeight(333) fee := chainfee.SatPerKWeight(333)
err = aliceChannel.ReceiveUpdateFee(fee) err = aliceChannel.ReceiveUpdateFee(fee)
if err == nil { if err == nil {
t.Fatalf("expected alice to fail receiving fee update") t.Fatalf("expected alice to fail receiving fee update")
@ -2400,9 +2432,9 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
defer cleanUp() defer cleanUp()
// Simulate Alice sending update fee message to bob. // Simulate Alice sending update fee message to bob.
fee1 := SatPerKWeight(333) fee1 := chainfee.SatPerKWeight(333)
fee2 := SatPerKWeight(333) fee2 := chainfee.SatPerKWeight(333)
fee := SatPerKWeight(333) fee := chainfee.SatPerKWeight(333)
aliceChannel.UpdateFee(fee1) aliceChannel.UpdateFee(fee1)
aliceChannel.UpdateFee(fee2) aliceChannel.UpdateFee(fee2)
aliceChannel.UpdateFee(fee) aliceChannel.UpdateFee(fee)
@ -2427,15 +2459,17 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
t.Fatalf("bob unable to process alice's new commitment: %v", err) t.Fatalf("bob unable to process alice's new commitment: %v", err)
} }
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee { if chainfee.SatPerKWeight(
bobChannel.channelState.LocalCommitment.FeePerKw,
) == fee {
t.Fatalf("bob's feePerKw was unexpectedly locked in") t.Fatalf("bob's feePerKw was unexpectedly locked in")
} }
// Alice sending more fee updates now should not mess up the old fee // Alice sending more fee updates now should not mess up the old fee
// they both committed to. // they both committed to.
fee3 := SatPerKWeight(444) fee3 := chainfee.SatPerKWeight(444)
fee4 := SatPerKWeight(555) fee4 := chainfee.SatPerKWeight(555)
fee5 := SatPerKWeight(666) fee5 := chainfee.SatPerKWeight(666)
aliceChannel.UpdateFee(fee3) aliceChannel.UpdateFee(fee3)
aliceChannel.UpdateFee(fee4) aliceChannel.UpdateFee(fee4)
aliceChannel.UpdateFee(fee5) aliceChannel.UpdateFee(fee5)
@ -2450,7 +2484,9 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
t.Fatalf("unable to generate bob revocation: %v", err) t.Fatalf("unable to generate bob revocation: %v", err)
} }
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee { if chainfee.SatPerKWeight(
bobChannel.channelState.LocalCommitment.FeePerKw,
) != fee {
t.Fatalf("bob's feePerKw was not locked in") t.Fatalf("bob's feePerKw was not locked in")
} }
@ -2475,7 +2511,9 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
t.Fatalf("alice unable to process bob's new commitment: %v", err) t.Fatalf("alice unable to process bob's new commitment: %v", err)
} }
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) == fee { if chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
) == fee {
t.Fatalf("alice's feePerKw was unexpectedly locked in") t.Fatalf("alice's feePerKw was unexpectedly locked in")
} }
@ -2486,7 +2524,9 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
t.Fatalf("unable to revoke alice channel: %v", err) t.Fatalf("unable to revoke alice channel: %v", err)
} }
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != fee { if chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
) != fee {
t.Fatalf("alice's feePerKw was not locked in") t.Fatalf("alice's feePerKw was not locked in")
} }
@ -3869,7 +3909,9 @@ func TestFeeUpdateRejectInsaneFee(t *testing.T) {
// Next, we'll try to add a fee rate to Alice which is 1,000,000x her // Next, we'll try to add a fee rate to Alice which is 1,000,000x her
// starting fee rate. // starting fee rate.
startingFeeRate := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) startingFeeRate := chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
)
newFeeRate := startingFeeRate * 1000000 newFeeRate := startingFeeRate * 1000000
// Both Alice and Bob should reject this new fee rate as it is far too // Both Alice and Bob should reject this new fee rate as it is far too
@ -3895,7 +3937,9 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
// First, we'll fetch the current fee rate present within the // First, we'll fetch the current fee rate present within the
// commitment transactions. // commitment transactions.
startingFeeRate := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) startingFeeRate := chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
)
// Next, we'll start a commitment update, with Alice sending a new // Next, we'll start a commitment update, with Alice sending a new
// update to double the fee rate of the commitment. // update to double the fee rate of the commitment.
@ -4034,10 +4078,14 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
} }
// Both parties should now have the latest fee rate locked-in. // Both parties should now have the latest fee rate locked-in.
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != newFeeRate { if chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
) != newFeeRate {
t.Fatalf("alice's feePerKw was not locked in") t.Fatalf("alice's feePerKw was not locked in")
} }
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != newFeeRate { if chainfee.SatPerKWeight(
bobChannel.channelState.LocalCommitment.FeePerKw,
) != newFeeRate {
t.Fatalf("bob's feePerKw was not locked in") t.Fatalf("bob's feePerKw was not locked in")
} }
@ -4118,7 +4166,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) {
// First, we'll fetch the current fee rate present within the // First, we'll fetch the current fee rate present within the
// commitment transactions. // commitment transactions.
startingFeeRate := SatPerKWeight( startingFeeRate := chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw, aliceChannel.channelState.LocalCommitment.FeePerKw,
) )
newFeeRate := startingFeeRate newFeeRate := startingFeeRate
@ -4247,10 +4295,14 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) {
} }
// Both parties should now have the latest fee rate locked-in. // Both parties should now have the latest fee rate locked-in.
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != newFeeRate { if chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
) != newFeeRate {
t.Fatalf("alice's feePerKw was not locked in") t.Fatalf("alice's feePerKw was not locked in")
} }
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != newFeeRate { if chainfee.SatPerKWeight(
bobChannel.channelState.LocalCommitment.FeePerKw,
) != newFeeRate {
t.Fatalf("bob's feePerKw was not locked in") t.Fatalf("bob's feePerKw was not locked in")
} }
@ -4272,10 +4324,14 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) {
assertLogItems(0, numHTLCs+1) assertLogItems(0, numHTLCs+1)
// ...and the final fee rate locked in. // ...and the final fee rate locked in.
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != newFeeRate { if chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
) != newFeeRate {
t.Fatalf("alice's feePerKw was not locked in") t.Fatalf("alice's feePerKw was not locked in")
} }
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != newFeeRate { if chainfee.SatPerKWeight(
bobChannel.channelState.LocalCommitment.FeePerKw,
) != newFeeRate {
t.Fatalf("bob's feePerKw was not locked in") t.Fatalf("bob's feePerKw was not locked in")
} }
} }
@ -6624,7 +6680,9 @@ func TestChannelMaxFeeRate(t *testing.T) {
} }
defer cleanUp() defer cleanUp()
assertMaxFeeRate := func(maxAlloc float64, expFeeRate SatPerKWeight) { assertMaxFeeRate := func(maxAlloc float64,
expFeeRate chainfee.SatPerKWeight) {
maxFeeRate := aliceChannel.MaxFeeRate(maxAlloc) maxFeeRate := aliceChannel.MaxFeeRate(maxAlloc)
if maxFeeRate != expFeeRate { if maxFeeRate != expFeeRate {
t.Fatalf("expected max fee rate of %v with max "+ t.Fatalf("expected max fee rate of %v with max "+
@ -6636,5 +6694,5 @@ func TestChannelMaxFeeRate(t *testing.T) {
assertMaxFeeRate(1.0, 690607734) assertMaxFeeRate(1.0, 690607734)
assertMaxFeeRate(0.001, 690607) assertMaxFeeRate(0.001, 690607)
assertMaxFeeRate(0.000001, 690) assertMaxFeeRate(0.000001, 690)
assertMaxFeeRate(0.0000001, FeePerKwFloor) assertMaxFeeRate(0.0000001, chainfee.FeePerKwFloor)
} }

@ -6,6 +6,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
// Config is a struct which houses configuration parameters which modify the // Config is a struct which houses configuration parameters which modify the
@ -42,7 +43,7 @@ type Config struct {
// FeeEstimator is the implementation that the wallet will use for the // FeeEstimator is the implementation that the wallet will use for the
// calculation of on-chain transaction fees. // calculation of on-chain transaction fees.
FeeEstimator FeeEstimator FeeEstimator chainfee.Estimator
// ChainIO is an instance of the BlockChainIO interface. ChainIO is // ChainIO is an instance of the BlockChainIO interface. ChainIO is
// used to lookup the existence of outputs within the UTXO set. // used to lookup the existence of outputs within the UTXO set.

@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/wallet/txauthor" "github.com/btcsuite/btcwallet/wallet/txauthor"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
// AddressType is an enum-like type which denotes the possible address types // AddressType is an enum-like type which denotes the possible address types
@ -172,7 +173,7 @@ type WalletController interface {
// This method also takes the target fee expressed in sat/kw that should // This method also takes the target fee expressed in sat/kw that should
// be used when crafting the transaction. // be used when crafting the transaction.
SendOutputs(outputs []*wire.TxOut, SendOutputs(outputs []*wire.TxOut,
feeRate SatPerKWeight) (*wire.MsgTx, error) feeRate chainfee.SatPerKWeight) (*wire.MsgTx, error)
// CreateSimpleTx creates a Bitcoin transaction paying to the specified // CreateSimpleTx creates a Bitcoin transaction paying to the specified
// outputs. The transaction is not broadcasted to the network. In the // outputs. The transaction is not broadcasted to the network. In the
@ -184,7 +185,7 @@ type WalletController interface {
// NOTE: The dryRun argument can be set true to create a tx that // NOTE: The dryRun argument can be set true to create a tx that
// doesn't alter the database. A tx created with this set to true // doesn't alter the database. A tx created with this set to true
// SHOULD NOT be broadcasted. // SHOULD NOT be broadcasted.
CreateSimpleTx(outputs []*wire.TxOut, feeRate SatPerKWeight, CreateSimpleTx(outputs []*wire.TxOut, feeRate chainfee.SatPerKWeight,
dryRun bool) (*txauthor.AuthoredTx, error) dryRun bool) (*txauthor.AuthoredTx, error)
// ListUnspentWitness returns all unspent outputs which are version 0 // ListUnspentWitness returns all unspent outputs which are version 0

@ -40,6 +40,7 @@ import (
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -166,7 +167,7 @@ func newPkScript(t *testing.T, w *lnwallet.LightningWallet,
// parties to send on-chain funds to each other. // parties to send on-chain funds to each other.
func sendCoins(t *testing.T, miner *rpctest.Harness, func sendCoins(t *testing.T, miner *rpctest.Harness,
sender, receiver *lnwallet.LightningWallet, output *wire.TxOut, sender, receiver *lnwallet.LightningWallet, output *wire.TxOut,
feeRate lnwallet.SatPerKWeight) *wire.MsgTx { feeRate chainfee.SatPerKWeight) *wire.MsgTx { //nolint:unparam
t.Helper() t.Helper()
@ -330,7 +331,7 @@ func createTestWallet(tempTestDir string, miningNode *rpctest.Harness,
WalletController: wc, WalletController: wc,
Signer: signer, Signer: signer,
ChainIO: bio, ChainIO: bio,
FeeEstimator: lnwallet.NewStaticFeeEstimator(2500, 0), FeeEstimator: chainfee.NewStaticEstimator(2500, 0),
DefaultConstraints: channeldb.ChannelConstraints{ DefaultConstraints: channeldb.ChannelConstraints{
DustLimit: 500, DustLimit: 500,
MaxPendingAmount: lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin) * 100, MaxPendingAmount: lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin) * 100,
@ -723,7 +724,7 @@ func testReservationInitiatorBalanceBelowDustCancel(miner *rpctest.Harness,
t.Fatalf("unable to create amt: %v", err) t.Fatalf("unable to create amt: %v", err)
} }
feePerKw := lnwallet.SatPerKWeight( feePerKw := chainfee.SatPerKWeight(
numBTC * numBTC * btcutil.SatoshiPerBitcoin, numBTC * numBTC * btcutil.SatoshiPerBitcoin,
) )
req := &lnwallet.InitFundingReserveMsg{ req := &lnwallet.InitFundingReserveMsg{
@ -2151,7 +2152,7 @@ func testChangeOutputSpendConfirmation(r *rpctest.Harness,
// //
// TODO(wilmer): replace this once SendOutputs easily supports sending // TODO(wilmer): replace this once SendOutputs easily supports sending
// all funds in one transaction. // all funds in one transaction.
txFeeRate := lnwallet.SatPerKWeight(2500) txFeeRate := chainfee.SatPerKWeight(2500)
txFee := btcutil.Amount(14380) txFee := btcutil.Amount(14380)
output := &wire.TxOut{ output := &wire.TxOut{
Value: int64(aliceBalance - txFee), Value: int64(aliceBalance - txFee),
@ -2247,7 +2248,7 @@ func testLastUnusedAddr(miner *rpctest.Harness,
if err != nil { if err != nil {
t.Fatalf("unable to convert addr to script: %v", err) t.Fatalf("unable to convert addr to script: %v", err)
} }
feeRate := lnwallet.SatPerKWeight(2500) feeRate := chainfee.SatPerKWeight(2500)
output := &wire.TxOut{ output := &wire.TxOut{
Value: 1000000, Value: 1000000,
PkScript: addrScript, PkScript: addrScript,
@ -2281,7 +2282,7 @@ func testCreateSimpleTx(r *rpctest.Harness, w *lnwallet.LightningWallet,
// The test cases we will run through for all backends. // The test cases we will run through for all backends.
testCases := []struct { testCases := []struct {
outVals []int64 outVals []int64
feeRate lnwallet.SatPerKWeight feeRate chainfee.SatPerKWeight
valid bool valid bool
}{ }{
{ {

@ -7,6 +7,7 @@ import (
"github.com/btcsuite/btcwallet/wtxmgr" "github.com/btcsuite/btcwallet/wtxmgr"
"github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
// walletLog is a logger that is initialized with no output filters. This // walletLog is a logger that is initialized with no output filters. This
@ -34,6 +35,7 @@ func UseLogger(logger btclog.Logger) {
btcwallet.UseLogger(logger) btcwallet.UseLogger(logger)
wtxmgr.UseLogger(logger) wtxmgr.UseLogger(logger)
chain.UseLogger(logger) chain.UseLogger(logger)
chainfee.UseLogger(logger)
} }
// logClosure is used to provide a closure over expensive logging operations // logClosure is used to provide a closure over expensive logging operations

@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -128,7 +129,7 @@ type ChannelReservation struct {
// creation of all channel reservations should be carried out via the // creation of all channel reservations should be carried out via the
// lnwallet.InitChannelReservation interface. // lnwallet.InitChannelReservation interface.
func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
commitFeePerKw SatPerKWeight, wallet *LightningWallet, commitFeePerKw chainfee.SatPerKWeight, wallet *LightningWallet,
id uint64, pushMSat lnwire.MilliSatoshi, chainHash *chainhash.Hash, id uint64, pushMSat lnwire.MilliSatoshi, chainHash *chainhash.Hash,
flags lnwire.FundingFlag, flags lnwire.FundingFlag,
tweaklessCommit bool) (*ChannelReservation, error) { tweaklessCommit bool) (*ChannelReservation, error) {

@ -18,6 +18,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
) )
@ -233,7 +234,7 @@ func CreateTestChannels(tweaklessCommits bool) (
return nil, nil, nil, err return nil, nil, nil, err
} }
estimator := NewStaticFeeEstimator(6000, 0) estimator := chainfee.NewStaticEstimator(6000, 0)
feePerKw, err := estimator.EstimateFeePerKW(1) feePerKw, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err

@ -17,6 +17,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
) )
@ -800,7 +801,7 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
height: test.commitment.CommitHeight, height: test.commitment.CommitHeight,
ourBalance: test.commitment.LocalBalance, ourBalance: test.commitment.LocalBalance,
theirBalance: test.commitment.RemoteBalance, theirBalance: test.commitment.RemoteBalance,
feePerKw: SatPerKWeight(test.commitment.FeePerKw), feePerKw: chainfee.SatPerKWeight(test.commitment.FeePerKw),
dustLimit: tc.dustLimit, dustLimit: tc.dustLimit,
isOurs: true, isOurs: true,
} }
@ -843,7 +844,7 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
// Generate second-level HTLC transactions for HTLCs in // Generate second-level HTLC transactions for HTLCs in
// commitment tx. // commitment tx.
htlcResolutions, err := extractHtlcResolutions( htlcResolutions, err := extractHtlcResolutions(
SatPerKWeight(test.commitment.FeePerKw), true, signer, chainfee.SatPerKWeight(test.commitment.FeePerKw), true, signer,
htlcs, keys, channel.localChanCfg, channel.remoteChanCfg, htlcs, keys, channel.localChanCfg, channel.remoteChanCfg,
commitTx.TxHash(), commitTx.TxHash(),
) )

@ -21,6 +21,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwallet/chanvalidate" "github.com/lightningnetwork/lnd/lnwallet/chanvalidate"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
@ -89,11 +90,11 @@ type InitFundingReserveMsg struct {
// of initial commitment transactions. In order to ensure timely // of initial commitment transactions. In order to ensure timely
// confirmation, it is recommended that this fee should be generous, // confirmation, it is recommended that this fee should be generous,
// paying some multiple of the accepted base fee rate of the network. // paying some multiple of the accepted base fee rate of the network.
CommitFeePerKw SatPerKWeight CommitFeePerKw chainfee.SatPerKWeight
// FundingFeePerKw is the fee rate in sat/kw to use for the initial // FundingFeePerKw is the fee rate in sat/kw to use for the initial
// funding transaction. // funding transaction.
FundingFeePerKw SatPerKWeight FundingFeePerKw chainfee.SatPerKWeight
// PushMSat is the number of milli-satoshis that should be pushed over // PushMSat is the number of milli-satoshis that should be pushed over
// the responder as part of the initial channel creation. // the responder as part of the initial channel creation.
@ -1323,7 +1324,7 @@ type coinSelection struct {
// returned, and the value of the resulting funding output. This method locks // returned, and the value of the resulting funding output. This method locks
// the selected outputs, and a function closure to unlock them in case of an // the selected outputs, and a function closure to unlock them in case of an
// error is returned. // error is returned.
func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerKWeight, func (l *LightningWallet) selectCoinsAndChange(feeRate chainfee.SatPerKWeight,
amt btcutil.Amount, minConfs int32, subtractFees bool) ( amt btcutil.Amount, minConfs int32, subtractFees bool) (
*coinSelection, error) { *coinSelection, error) {
@ -1485,7 +1486,7 @@ func selectInputs(amt btcutil.Amount, coins []*Utxo) (btcutil.Amount, []*Utxo, e
// change output to fund amt satoshis, adhering to the specified fee rate. The // change output to fund amt satoshis, adhering to the specified fee rate. The
// specified fee rate should be expressed in sat/kw for coin selection to // specified fee rate should be expressed in sat/kw for coin selection to
// function properly. // function properly.
func coinSelect(feeRate SatPerKWeight, amt btcutil.Amount, func coinSelect(feeRate chainfee.SatPerKWeight, amt btcutil.Amount,
coins []*Utxo) ([]*Utxo, btcutil.Amount, error) { coins []*Utxo) ([]*Utxo, btcutil.Amount, error) {
amtNeeded := amt amtNeeded := amt
@ -1549,7 +1550,7 @@ func coinSelect(feeRate SatPerKWeight, amt btcutil.Amount,
// coinSelectSubtractFees attempts to select coins such that we'll spend up to // coinSelectSubtractFees attempts to select coins such that we'll spend up to
// amt in total after fees, adhering to the specified fee rate. The selected // amt in total after fees, adhering to the specified fee rate. The selected
// coins, the final output and change values are returned. // coins, the final output and change values are returned.
func coinSelectSubtractFees(feeRate SatPerKWeight, amt, func coinSelectSubtractFees(feeRate chainfee.SatPerKWeight, amt,
dustLimit btcutil.Amount, coins []*Utxo) ([]*Utxo, btcutil.Amount, dustLimit btcutil.Amount, coins []*Utxo) ([]*Utxo, btcutil.Amount,
btcutil.Amount, error) { btcutil.Amount, error) {

@ -5,12 +5,15 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
// fundingFee is a helper method that returns the fee estimate used for a tx // fundingFee is a helper method that returns the fee estimate used for a tx
// with the given number of inputs and the optional change output. This matches // with the given number of inputs and the optional change output. This matches
// the estimate done by the wallet. // the estimate done by the wallet.
func fundingFee(feeRate SatPerKWeight, numInput int, change bool) btcutil.Amount { func fundingFee(feeRate chainfee.SatPerKWeight, numInput int, // nolint:unparam
change bool) btcutil.Amount {
var weightEstimate input.TxWeightEstimator var weightEstimate input.TxWeightEstimator
// All inputs. // All inputs.
@ -39,7 +42,7 @@ func fundingFee(feeRate SatPerKWeight, numInput int, change bool) btcutil.Amount
func TestCoinSelect(t *testing.T) { func TestCoinSelect(t *testing.T) {
t.Parallel() t.Parallel()
const feeRate = SatPerKWeight(100) const feeRate = chainfee.SatPerKWeight(100)
const dust = btcutil.Amount(100) const dust = btcutil.Amount(100)
type testCase struct { type testCase struct {
@ -185,7 +188,7 @@ func TestCoinSelect(t *testing.T) {
func TestCoinSelectSubtractFees(t *testing.T) { func TestCoinSelectSubtractFees(t *testing.T) {
t.Parallel() t.Parallel()
const feeRate = SatPerKWeight(100) const feeRate = chainfee.SatPerKWeight(100)
const dustLimit = btcutil.Amount(1000) const dustLimit = btcutil.Amount(1000)
const dust = btcutil.Amount(100) const dust = btcutil.Amount(100)

@ -17,6 +17,7 @@ import (
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
// The block height returned by the mock BlockChainIO's GetBestBlock. // The block height returned by the mock BlockChainIO's GetBestBlock.
@ -271,13 +272,13 @@ func (*mockWalletController) IsOurAddress(a btcutil.Address) bool {
} }
func (*mockWalletController) SendOutputs(outputs []*wire.TxOut, func (*mockWalletController) SendOutputs(outputs []*wire.TxOut,
_ lnwallet.SatPerKWeight) (*wire.MsgTx, error) { _ chainfee.SatPerKWeight) (*wire.MsgTx, error) {
return nil, nil return nil, nil
} }
func (*mockWalletController) CreateSimpleTx(outputs []*wire.TxOut, func (*mockWalletController) CreateSimpleTx(outputs []*wire.TxOut,
_ lnwallet.SatPerKWeight, _ bool) (*txauthor.AuthoredTx, error) { _ chainfee.SatPerKWeight, _ bool) (*txauthor.AuthoredTx, error) {
return nil, nil return nil, nil
} }

@ -10,7 +10,7 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -156,7 +156,7 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) {
dummyDeliveryScript), dummyDeliveryScript),
} }
estimator := lnwallet.NewStaticFeeEstimator(12500, 0) estimator := chainfee.NewStaticEstimator(12500, 0)
feePerKw, err := estimator.EstimateFeePerKW(1) feePerKw, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
t.Fatalf("unable to query fee estimator: %v", err) t.Fatalf("unable to query fee estimator: %v", err)
@ -446,7 +446,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
msg: respShutdown, msg: respShutdown,
} }
estimator := lnwallet.NewStaticFeeEstimator(12500, 0) estimator := chainfee.NewStaticEstimator(12500, 0)
initiatorIdealFeeRate, err := estimator.EstimateFeePerKW(1) initiatorIdealFeeRate, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
t.Fatalf("unable to query fee estimator: %v", err) t.Fatalf("unable to query fee estimator: %v", err)

@ -44,6 +44,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/monitoring" "github.com/lightningnetwork/lnd/monitoring"
@ -815,7 +816,7 @@ func addrPairsToOutputs(addrPairs map[string]int64) ([]*wire.TxOut, error) {
// more addresses specified in the passed payment map. The payment map maps an // more addresses specified in the passed payment map. The payment map maps an
// address to a specified output value to be sent to that address. // address to a specified output value to be sent to that address.
func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64, func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64,
feeRate lnwallet.SatPerKWeight) (*chainhash.Hash, error) { feeRate chainfee.SatPerKWeight) (*chainhash.Hash, error) {
outputs, err := addrPairsToOutputs(paymentMap) outputs, err := addrPairsToOutputs(paymentMap)
if err != nil { if err != nil {
@ -1000,7 +1001,7 @@ func (r *rpcServer) SendCoins(ctx context.Context,
// Based on the passed fee related parameters, we'll determine an // Based on the passed fee related parameters, we'll determine an
// appropriate fee rate for this transaction. // appropriate fee rate for this transaction.
satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() satPerKw := chainfee.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight()
feePerKw, err := sweep.DetermineFeePerKw( feePerKw, err := sweep.DetermineFeePerKw(
r.server.cc.feeEstimator, sweep.FeePreference{ r.server.cc.feeEstimator, sweep.FeePreference{
ConfTarget: uint32(in.TargetConf), ConfTarget: uint32(in.TargetConf),
@ -1122,7 +1123,7 @@ func (r *rpcServer) SendMany(ctx context.Context,
// Based on the passed fee related parameters, we'll determine an // Based on the passed fee related parameters, we'll determine an
// appropriate fee rate for this transaction. // appropriate fee rate for this transaction.
satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() satPerKw := chainfee.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight()
feePerKw, err := sweep.DetermineFeePerKw( feePerKw, err := sweep.DetermineFeePerKw(
r.server.cc.feeEstimator, sweep.FeePreference{ r.server.cc.feeEstimator, sweep.FeePreference{
ConfTarget: uint32(in.TargetConf), ConfTarget: uint32(in.TargetConf),
@ -1504,7 +1505,7 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
// Based on the passed fee related parameters, we'll determine an // Based on the passed fee related parameters, we'll determine an
// appropriate fee rate for the funding transaction. // appropriate fee rate for the funding transaction.
satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() satPerKw := chainfee.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight()
feeRate, err := sweep.DetermineFeePerKw( feeRate, err := sweep.DetermineFeePerKw(
r.server.cc.feeEstimator, sweep.FeePreference{ r.server.cc.feeEstimator, sweep.FeePreference{
ConfTarget: uint32(in.TargetConf), ConfTarget: uint32(in.TargetConf),
@ -1649,7 +1650,7 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context,
// Based on the passed fee related parameters, we'll determine an // Based on the passed fee related parameters, we'll determine an
// appropriate fee rate for the funding transaction. // appropriate fee rate for the funding transaction.
satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() satPerKw := chainfee.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight()
feeRate, err := sweep.DetermineFeePerKw( feeRate, err := sweep.DetermineFeePerKw(
r.server.cc.feeEstimator, sweep.FeePreference{ r.server.cc.feeEstimator, sweep.FeePreference{
ConfTarget: uint32(in.TargetConf), ConfTarget: uint32(in.TargetConf),
@ -1847,7 +1848,7 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
// Based on the passed fee related parameters, we'll determine // Based on the passed fee related parameters, we'll determine
// an appropriate fee rate for the cooperative closure // an appropriate fee rate for the cooperative closure
// transaction. // transaction.
satPerKw := lnwallet.SatPerKVByte( satPerKw := chainfee.SatPerKVByte(
in.SatPerByte * 1000, in.SatPerByte * 1000,
).FeePerKWeight() ).FeePerKWeight()
feeRate, err := sweep.DetermineFeePerKw( feeRate, err := sweep.DetermineFeePerKw(

@ -44,6 +44,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/nat" "github.com/lightningnetwork/lnd/nat"
"github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/netann"
@ -1136,7 +1137,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
if cfg.WtClient.SweepFeeRate != 0 { if cfg.WtClient.SweepFeeRate != 0 {
// We expose the sweep fee rate in sat/byte, but the // We expose the sweep fee rate in sat/byte, but the
// tower protocol operations on sat/kw. // tower protocol operations on sat/kw.
sweepRateSatPerByte := lnwallet.SatPerKVByte( sweepRateSatPerByte := chainfee.SatPerKVByte(
1000 * cfg.WtClient.SweepFeeRate, 1000 * cfg.WtClient.SweepFeeRate,
) )
policy.SweepFeeRate = sweepRateSatPerByte.FeePerKWeight() policy.SweepFeeRate = sweepRateSatPerByte.FeePerKWeight()
@ -3071,7 +3072,7 @@ type openChanReq struct {
pushAmt lnwire.MilliSatoshi pushAmt lnwire.MilliSatoshi
fundingFeePerKw lnwallet.SatPerKWeight fundingFeePerKw chainfee.SatPerKWeight
private bool private bool

@ -3,38 +3,38 @@ package sweep
import ( import (
"sync" "sync"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
// mockFeeEstimator implements a mock fee estimator. It closely resembles // mockFeeEstimator implements a mock fee estimator. It closely resembles
// lnwallet.StaticFeeEstimator with the addition that fees can be changed for // lnwallet.StaticFeeEstimator with the addition that fees can be changed for
// testing purposes in a thread safe manner. // testing purposes in a thread safe manner.
type mockFeeEstimator struct { type mockFeeEstimator struct {
feePerKW lnwallet.SatPerKWeight feePerKW chainfee.SatPerKWeight
relayFee lnwallet.SatPerKWeight relayFee chainfee.SatPerKWeight
blocksToFee map[uint32]lnwallet.SatPerKWeight blocksToFee map[uint32]chainfee.SatPerKWeight
// A closure that when set is used instead of the // A closure that when set is used instead of the
// mockFeeEstimator.EstimateFeePerKW method. // mockFeeEstimator.EstimateFeePerKW method.
estimateFeePerKW func(numBlocks uint32) (lnwallet.SatPerKWeight, error) estimateFeePerKW func(numBlocks uint32) (chainfee.SatPerKWeight, error)
lock sync.Mutex lock sync.Mutex
} }
func newMockFeeEstimator(feePerKW, func newMockFeeEstimator(feePerKW,
relayFee lnwallet.SatPerKWeight) *mockFeeEstimator { relayFee chainfee.SatPerKWeight) *mockFeeEstimator {
return &mockFeeEstimator{ return &mockFeeEstimator{
feePerKW: feePerKW, feePerKW: feePerKW,
relayFee: relayFee, relayFee: relayFee,
blocksToFee: make(map[uint32]lnwallet.SatPerKWeight), blocksToFee: make(map[uint32]chainfee.SatPerKWeight),
} }
} }
func (e *mockFeeEstimator) updateFees(feePerKW, func (e *mockFeeEstimator) updateFees(feePerKW,
relayFee lnwallet.SatPerKWeight) { relayFee chainfee.SatPerKWeight) {
e.lock.Lock() e.lock.Lock()
defer e.lock.Unlock() defer e.lock.Unlock()
@ -44,7 +44,7 @@ func (e *mockFeeEstimator) updateFees(feePerKW,
} }
func (e *mockFeeEstimator) EstimateFeePerKW(numBlocks uint32) ( func (e *mockFeeEstimator) EstimateFeePerKW(numBlocks uint32) (
lnwallet.SatPerKWeight, error) { chainfee.SatPerKWeight, error) {
e.lock.Lock() e.lock.Lock()
defer e.lock.Unlock() defer e.lock.Unlock()
@ -60,7 +60,7 @@ func (e *mockFeeEstimator) EstimateFeePerKW(numBlocks uint32) (
return e.feePerKW, nil return e.feePerKW, nil
} }
func (e *mockFeeEstimator) RelayFeePerKW() lnwallet.SatPerKWeight { func (e *mockFeeEstimator) RelayFeePerKW() chainfee.SatPerKWeight {
e.lock.Lock() e.lock.Lock()
defer e.lock.Unlock() defer e.lock.Unlock()
@ -75,4 +75,4 @@ func (e *mockFeeEstimator) Stop() error {
return nil return nil
} }
var _ lnwallet.FeeEstimator = (*mockFeeEstimator)(nil) var _ chainfee.Estimator = (*mockFeeEstimator)(nil)

@ -16,13 +16,14 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
const ( const (
// DefaultMaxFeeRate is the default maximum fee rate allowed within the // DefaultMaxFeeRate is the default maximum fee rate allowed within the
// UtxoSweeper. The current value is equivalent to a fee rate of 10,000 // UtxoSweeper. The current value is equivalent to a fee rate of 10,000
// sat/vbyte. // sat/vbyte.
DefaultMaxFeeRate = lnwallet.FeePerKwFloor * 1e4 DefaultMaxFeeRate = chainfee.FeePerKwFloor * 1e4
// DefaultFeeRateBucketSize is the default size of fee rate buckets // DefaultFeeRateBucketSize is the default size of fee rate buckets
// we'll use when clustering inputs into buckets with similar fee rates // we'll use when clustering inputs into buckets with similar fee rates
@ -92,7 +93,7 @@ type pendingInput struct {
// lastFeeRate is the most recent fee rate used for this input within a // lastFeeRate is the most recent fee rate used for this input within a
// transaction broadcast to the network. // transaction broadcast to the network.
lastFeeRate lnwallet.SatPerKWeight lastFeeRate chainfee.SatPerKWeight
} }
// pendingInputs is a type alias for a set of pending inputs. // pendingInputs is a type alias for a set of pending inputs.
@ -101,7 +102,7 @@ type pendingInputs = map[wire.OutPoint]*pendingInput
// inputCluster is a helper struct to gather a set of pending inputs that should // inputCluster is a helper struct to gather a set of pending inputs that should
// be swept with the specified fee rate. // be swept with the specified fee rate.
type inputCluster struct { type inputCluster struct {
sweepFeeRate lnwallet.SatPerKWeight sweepFeeRate chainfee.SatPerKWeight
inputs pendingInputs inputs pendingInputs
} }
@ -126,7 +127,7 @@ type PendingInput struct {
// LastFeeRate is the most recent fee rate used for the input being // LastFeeRate is the most recent fee rate used for the input being
// swept within a transaction broadcast to the network. // swept within a transaction broadcast to the network.
LastFeeRate lnwallet.SatPerKWeight LastFeeRate chainfee.SatPerKWeight
// BroadcastAttempts is the number of attempts we've made to sweept the // BroadcastAttempts is the number of attempts we've made to sweept the
// input. // input.
@ -182,7 +183,7 @@ type UtxoSweeper struct {
currentOutputScript []byte currentOutputScript []byte
relayFeeRate lnwallet.SatPerKWeight relayFeeRate chainfee.SatPerKWeight
quit chan struct{} quit chan struct{}
wg sync.WaitGroup wg sync.WaitGroup
@ -197,7 +198,7 @@ type UtxoSweeperConfig struct {
// FeeEstimator is used when crafting sweep transactions to estimate // FeeEstimator is used when crafting sweep transactions to estimate
// the necessary fee relative to the expected size of the sweep // the necessary fee relative to the expected size of the sweep
// transaction. // transaction.
FeeEstimator lnwallet.FeeEstimator FeeEstimator chainfee.Estimator
// PublishTransaction facilitates the process of broadcasting a signed // PublishTransaction facilitates the process of broadcasting a signed
// transaction to the appropriate network. // transaction to the appropriate network.
@ -235,7 +236,7 @@ type UtxoSweeperConfig struct {
// MaxFeeRate is the the maximum fee rate allowed within the // MaxFeeRate is the the maximum fee rate allowed within the
// UtxoSweeper. // UtxoSweeper.
MaxFeeRate lnwallet.SatPerKWeight MaxFeeRate chainfee.SatPerKWeight
// FeeRateBucketSize is the default size of fee rate buckets we'll use // FeeRateBucketSize is the default size of fee rate buckets we'll use
// when clustering inputs into buckets with similar fee rates within the // when clustering inputs into buckets with similar fee rates within the
@ -403,7 +404,7 @@ func (s *UtxoSweeper) SweepInput(input input.Input,
// feeRateForPreference returns a fee rate for the given fee preference. It // feeRateForPreference returns a fee rate for the given fee preference. It
// ensures that the fee rate respects the bounds of the UtxoSweeper. // ensures that the fee rate respects the bounds of the UtxoSweeper.
func (s *UtxoSweeper) feeRateForPreference( func (s *UtxoSweeper) feeRateForPreference(
feePreference FeePreference) (lnwallet.SatPerKWeight, error) { feePreference FeePreference) (chainfee.SatPerKWeight, error) {
// Ensure a type of fee preference is specified to prevent using a // Ensure a type of fee preference is specified to prevent using a
// default below. // default below.
@ -637,10 +638,10 @@ func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch) {
// bucketForFeeReate determines the proper bucket for a fee rate. This is done // bucketForFeeReate determines the proper bucket for a fee rate. This is done
// in order to batch inputs with similar fee rates together. // in order to batch inputs with similar fee rates together.
func (s *UtxoSweeper) bucketForFeeRate( func (s *UtxoSweeper) bucketForFeeRate(
feeRate lnwallet.SatPerKWeight) lnwallet.SatPerKWeight { feeRate chainfee.SatPerKWeight) chainfee.SatPerKWeight {
minBucket := s.relayFeeRate + lnwallet.SatPerKWeight(s.cfg.FeeRateBucketSize) minBucket := s.relayFeeRate + chainfee.SatPerKWeight(s.cfg.FeeRateBucketSize)
return lnwallet.SatPerKWeight( return chainfee.SatPerKWeight(
math.Ceil(float64(feeRate) / float64(minBucket)), math.Ceil(float64(feeRate) / float64(minBucket)),
) )
} }
@ -650,8 +651,8 @@ func (s *UtxoSweeper) bucketForFeeRate(
// sweep fee rate, which is determined by calculating the average fee rate of // sweep fee rate, which is determined by calculating the average fee rate of
// all inputs within that cluster. // all inputs within that cluster.
func (s *UtxoSweeper) clusterBySweepFeeRate() []inputCluster { func (s *UtxoSweeper) clusterBySweepFeeRate() []inputCluster {
bucketInputs := make(map[lnwallet.SatPerKWeight]pendingInputs) bucketInputs := make(map[chainfee.SatPerKWeight]pendingInputs)
inputFeeRates := make(map[wire.OutPoint]lnwallet.SatPerKWeight) inputFeeRates := make(map[wire.OutPoint]chainfee.SatPerKWeight)
// First, we'll group together all inputs with similar fee rates. This // First, we'll group together all inputs with similar fee rates. This
// is done by determining the fee rate bucket they should belong in. // is done by determining the fee rate bucket they should belong in.
@ -678,11 +679,11 @@ func (s *UtxoSweeper) clusterBySweepFeeRate() []inputCluster {
// calculating the average fee rate of the inputs within each set. // calculating the average fee rate of the inputs within each set.
inputClusters := make([]inputCluster, 0, len(bucketInputs)) inputClusters := make([]inputCluster, 0, len(bucketInputs))
for _, inputs := range bucketInputs { for _, inputs := range bucketInputs {
var sweepFeeRate lnwallet.SatPerKWeight var sweepFeeRate chainfee.SatPerKWeight
for op := range inputs { for op := range inputs {
sweepFeeRate += inputFeeRates[op] sweepFeeRate += inputFeeRates[op]
} }
sweepFeeRate /= lnwallet.SatPerKWeight(len(inputs)) sweepFeeRate /= chainfee.SatPerKWeight(len(inputs))
inputClusters = append(inputClusters, inputCluster{ inputClusters = append(inputClusters, inputCluster{
sweepFeeRate: sweepFeeRate, sweepFeeRate: sweepFeeRate,
inputs: inputs, inputs: inputs,
@ -836,7 +837,7 @@ func (s *UtxoSweeper) getInputLists(cluster inputCluster,
// sweep takes a set of preselected inputs, creates a sweep tx and publishes the // sweep takes a set of preselected inputs, creates a sweep tx and publishes the
// tx. The output address is only marked as used if the publish succeeds. // tx. The output address is only marked as used if the publish succeeds.
func (s *UtxoSweeper) sweep(inputs inputSet, feeRate lnwallet.SatPerKWeight, func (s *UtxoSweeper) sweep(inputs inputSet, feeRate chainfee.SatPerKWeight,
currentHeight int32) error { currentHeight int32) error {
// Generate an output script if there isn't an unused script available. // Generate an output script if there isn't an unused script available.

@ -15,6 +15,7 @@ import (
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
var ( var (
@ -99,7 +100,7 @@ func createSweeperTestContext(t *testing.T) *sweeperTestContext {
backend := newMockBackend(notifier) backend := newMockBackend(notifier)
estimator := newMockFeeEstimator(10000, lnwallet.FeePerKwFloor) estimator := newMockFeeEstimator(10000, chainfee.FeePerKwFloor)
publishChan := make(chan wire.MsgTx, 2) publishChan := make(chan wire.MsgTx, 2)
ctx := &sweeperTestContext{ ctx := &sweeperTestContext{
@ -314,7 +315,7 @@ func assertTxSweepsInputs(t *testing.T, sweepTx *wire.MsgTx,
// NOTE: This assumes that transactions only have one output, as this is the // NOTE: This assumes that transactions only have one output, as this is the
// only type of transaction the UtxoSweeper can create at the moment. // only type of transaction the UtxoSweeper can create at the moment.
func assertTxFeeRate(t *testing.T, tx *wire.MsgTx, func assertTxFeeRate(t *testing.T, tx *wire.MsgTx,
expectedFeeRate lnwallet.SatPerKWeight, inputs ...input.Input) { expectedFeeRate chainfee.SatPerKWeight, inputs ...input.Input) {
t.Helper() t.Helper()
@ -994,11 +995,11 @@ func TestDifferentFeePreferences(t *testing.T) {
// this to ensure the sweeper can broadcast distinct transactions for // this to ensure the sweeper can broadcast distinct transactions for
// each sweep with a different fee preference. // each sweep with a different fee preference.
lowFeePref := FeePreference{ConfTarget: 12} lowFeePref := FeePreference{ConfTarget: 12}
lowFeeRate := lnwallet.SatPerKWeight(5000) lowFeeRate := chainfee.SatPerKWeight(5000)
ctx.estimator.blocksToFee[lowFeePref.ConfTarget] = lowFeeRate ctx.estimator.blocksToFee[lowFeePref.ConfTarget] = lowFeeRate
highFeePref := FeePreference{ConfTarget: 6} highFeePref := FeePreference{ConfTarget: 6}
highFeeRate := lnwallet.SatPerKWeight(10000) highFeeRate := chainfee.SatPerKWeight(10000)
ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate
input1 := spendableInputs[0] input1 := spendableInputs[0]
@ -1116,7 +1117,7 @@ func TestBumpFeeRBF(t *testing.T) {
ctx := createSweeperTestContext(t) ctx := createSweeperTestContext(t)
lowFeePref := FeePreference{ConfTarget: 144} lowFeePref := FeePreference{ConfTarget: 144}
lowFeeRate := lnwallet.FeePerKwFloor lowFeeRate := chainfee.FeePerKwFloor
ctx.estimator.blocksToFee[lowFeePref.ConfTarget] = lowFeeRate ctx.estimator.blocksToFee[lowFeePref.ConfTarget] = lowFeeRate
// We'll first try to bump the fee of an output currently unknown to the // We'll first try to bump the fee of an output currently unknown to the

@ -11,7 +11,7 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/wallet/txrules" "github.com/btcsuite/btcwallet/wallet/txrules"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
var ( var (
@ -31,7 +31,7 @@ type inputSet []input.Input
// inputs are skipped. No input sets with a total value after fees below the // inputs are skipped. No input sets with a total value after fees below the
// dust limit are returned. // dust limit are returned.
func generateInputPartitionings(sweepableInputs []input.Input, func generateInputPartitionings(sweepableInputs []input.Input,
relayFeePerKW, feePerKW lnwallet.SatPerKWeight, relayFeePerKW, feePerKW chainfee.SatPerKWeight,
maxInputsPerTx int) ([]inputSet, error) { maxInputsPerTx int) ([]inputSet, error) {
// Calculate dust limit based on the P2WPKH output script of the sweep // Calculate dust limit based on the P2WPKH output script of the sweep
@ -116,7 +116,7 @@ func generateInputPartitionings(sweepableInputs []input.Input,
// minimizing any negative externalities we cause for the Bitcoin system as a // minimizing any negative externalities we cause for the Bitcoin system as a
// whole. // whole.
func getPositiveYieldInputs(sweepableInputs []input.Input, maxInputs int, func getPositiveYieldInputs(sweepableInputs []input.Input, maxInputs int,
feePerKW lnwallet.SatPerKWeight) (int, btcutil.Amount) { feePerKW chainfee.SatPerKWeight) (int, btcutil.Amount) {
var weightEstimate input.TxWeightEstimator var weightEstimate input.TxWeightEstimator
@ -169,7 +169,7 @@ func getPositiveYieldInputs(sweepableInputs []input.Input, maxInputs int,
// createSweepTx builds a signed tx spending the inputs to a the output script. // createSweepTx builds a signed tx spending the inputs to a the output script.
func createSweepTx(inputs []input.Input, outputPkScript []byte, func createSweepTx(inputs []input.Input, outputPkScript []byte,
currentBlockHeight uint32, feePerKw lnwallet.SatPerKWeight, currentBlockHeight uint32, feePerKw chainfee.SatPerKWeight,
signer input.Signer) (*wire.MsgTx, error) { signer input.Signer) (*wire.MsgTx, error) {
inputs, txWeight := getWeightEstimate(inputs) inputs, txWeight := getWeightEstimate(inputs)

@ -9,6 +9,7 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
const ( const (
@ -27,7 +28,7 @@ type FeePreference struct {
// FeeRate if non-zero, signals a fee pre fence expressed in the fee // FeeRate if non-zero, signals a fee pre fence expressed in the fee
// rate expressed in sat/kw for a particular transaction. // rate expressed in sat/kw for a particular transaction.
FeeRate lnwallet.SatPerKWeight FeeRate chainfee.SatPerKWeight
} }
// String returns a human-readable string of the fee preference. // String returns a human-readable string of the fee preference.
@ -42,8 +43,8 @@ func (p FeePreference) String() string {
// an estimator, a confirmation target, and a manual value for sat/byte. A // 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 // value is chosen based on the two free parameters as one, or both of them can
// be zero. // be zero.
func DetermineFeePerKw(feeEstimator lnwallet.FeeEstimator, func DetermineFeePerKw(feeEstimator chainfee.Estimator,
feePref FeePreference) (lnwallet.SatPerKWeight, error) { feePref FeePreference) (chainfee.SatPerKWeight, error) {
switch { switch {
// If both values are set, then we'll return an error as we require a // If both values are set, then we'll return an error as we require a
@ -70,12 +71,12 @@ func DetermineFeePerKw(feeEstimator lnwallet.FeeEstimator,
// internally. // internally.
case feePref.FeeRate != 0: case feePref.FeeRate != 0:
feePerKW := feePref.FeeRate feePerKW := feePref.FeeRate
if feePerKW < lnwallet.FeePerKwFloor { if feePerKW < chainfee.FeePerKwFloor {
log.Infof("Manual fee rate input of %d sat/kw is "+ log.Infof("Manual fee rate input of %d sat/kw is "+
"too low, using %d sat/kw instead", feePerKW, "too low, using %d sat/kw instead", feePerKW,
lnwallet.FeePerKwFloor) chainfee.FeePerKwFloor)
feePerKW = lnwallet.FeePerKwFloor feePerKW = chainfee.FeePerKwFloor
} }
return feePerKW, nil return feePerKW, nil
@ -152,10 +153,10 @@ type WalletSweepPackage struct {
// by the delivery address. The sweep transaction will be crafted with the // by the delivery address. The sweep transaction will be crafted with the
// target fee rate, and will use the utxoSource and outpointLocker as sources // target fee rate, and will use the utxoSource and outpointLocker as sources
// for wallet funds. // for wallet funds.
func CraftSweepAllTx(feeRate lnwallet.SatPerKWeight, blockHeight uint32, func CraftSweepAllTx(feeRate chainfee.SatPerKWeight, blockHeight uint32,
deliveryAddr btcutil.Address, coinSelectLocker CoinSelectionLocker, deliveryAddr btcutil.Address, coinSelectLocker CoinSelectionLocker,
utxoSource UtxoSource, outpointLocker OutpointLocker, utxoSource UtxoSource, outpointLocker OutpointLocker,
feeEstimator lnwallet.FeeEstimator, feeEstimator chainfee.Estimator,
signer input.Signer) (*WalletSweepPackage, error) { signer input.Signer) (*WalletSweepPackage, error) {
// TODO(roasbeef): turn off ATPL as well when available? // TODO(roasbeef): turn off ATPL as well when available?

@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
) )
// TestDetermineFeePerKw tests that given a fee preference, the // TestDetermineFeePerKw tests that given a fee preference, the
@ -17,8 +18,8 @@ import (
func TestDetermineFeePerKw(t *testing.T) { func TestDetermineFeePerKw(t *testing.T) {
t.Parallel() t.Parallel()
defaultFee := lnwallet.SatPerKWeight(999) defaultFee := chainfee.SatPerKWeight(999)
relayFee := lnwallet.SatPerKWeight(300) relayFee := chainfee.SatPerKWeight(300)
feeEstimator := newMockFeeEstimator(defaultFee, relayFee) feeEstimator := newMockFeeEstimator(defaultFee, relayFee)
@ -35,7 +36,7 @@ func TestDetermineFeePerKw(t *testing.T) {
// fee is the value the DetermineFeePerKw should return given // fee is the value the DetermineFeePerKw should return given
// the FeePreference above // the FeePreference above
fee lnwallet.SatPerKWeight fee chainfee.SatPerKWeight
// fail determines if this test case should fail or not. // fail determines if this test case should fail or not.
fail bool fail bool
@ -43,9 +44,9 @@ func TestDetermineFeePerKw(t *testing.T) {
// A fee rate below the fee rate floor should output the floor. // A fee rate below the fee rate floor should output the floor.
{ {
feePref: FeePreference{ feePref: FeePreference{
FeeRate: lnwallet.SatPerKWeight(99), FeeRate: chainfee.SatPerKWeight(99),
}, },
fee: lnwallet.FeePerKwFloor, fee: chainfee.FeePerKwFloor,
}, },
// A fee rate above the floor, should pass through and return // A fee rate above the floor, should pass through and return

@ -22,6 +22,7 @@ import (
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/netann"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
@ -214,7 +215,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
estimator := lnwallet.NewStaticFeeEstimator(12500, 0) estimator := chainfee.NewStaticEstimator(12500, 0)
feePerKw, err := estimator.EstimateFeePerKW(1) feePerKw, err := estimator.EstimateFeePerKW(1)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err

@ -6,7 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"testing" "testing"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/tlv" "github.com/lightningnetwork/lnd/tlv"
"github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/blob"
"github.com/lightningnetwork/lnd/watchtower/wtwire" "github.com/lightningnetwork/lnd/watchtower/wtwire"
@ -19,7 +19,7 @@ type CreateSessionTLV struct {
MaxUpdates uint16 MaxUpdates uint16
RewardBase uint32 RewardBase uint32
RewardRate uint32 RewardRate uint32
SweepFeeRate lnwallet.SatPerKWeight SweepFeeRate chainfee.SatPerKWeight
tlvStream *tlv.Stream tlvStream *tlv.Stream
} }
@ -48,24 +48,24 @@ func DBlobType(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
// ESatPerKW is an encoder for lnwallet.SatPerKWeight. // ESatPerKW is an encoder for lnwallet.SatPerKWeight.
func ESatPerKW(w io.Writer, val interface{}, buf *[8]byte) error { func ESatPerKW(w io.Writer, val interface{}, buf *[8]byte) error {
if v, ok := val.(*lnwallet.SatPerKWeight); ok { if v, ok := val.(*chainfee.SatPerKWeight); ok {
return tlv.EUint64(w, uint64(*v), buf) return tlv.EUint64(w, uint64(*v), buf)
} }
return tlv.NewTypeForEncodingErr(val, "lnwallet.SatPerKWeight") return tlv.NewTypeForEncodingErr(val, "chainfee.SatPerKWeight")
} }
// DSatPerKW is an decoder for lnwallet.SatPerKWeight. // DSatPerKW is an decoder for lnwallet.SatPerKWeight.
func DSatPerKW(r io.Reader, val interface{}, buf *[8]byte, l uint64) error { func DSatPerKW(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
if v, ok := val.(*lnwallet.SatPerKWeight); ok { if v, ok := val.(*chainfee.SatPerKWeight); ok {
var sat uint64 var sat uint64
err := tlv.DUint64(r, &sat, buf, l) err := tlv.DUint64(r, &sat, buf, l)
if err != nil { if err != nil {
return err return err
} }
*v = lnwallet.SatPerKWeight(sat) *v = chainfee.SatPerKWeight(sat)
return nil return nil
} }
return tlv.NewTypeForDecodingErr(val, "lnwallet.SatPerKWeight", l, 8) return tlv.NewTypeForDecodingErr(val, "chainfee.SatPerKWeight", l, 8)
} }
// NewCreateSessionTLV initializes a new CreateSessionTLV message. // NewCreateSessionTLV initializes a new CreateSessionTLV message.

@ -16,6 +16,7 @@ import (
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/blob"
"github.com/lightningnetwork/lnd/watchtower/wtdb" "github.com/lightningnetwork/lnd/watchtower/wtdb"
@ -86,7 +87,7 @@ func genTaskTest(
toLocalAmt int64, toLocalAmt int64,
toRemoteAmt int64, toRemoteAmt int64,
blobType blob.Type, blobType blob.Type,
sweepFeeRate lnwallet.SatPerKWeight, sweepFeeRate chainfee.SatPerKWeight,
rewardScript []byte, rewardScript []byte,
expSweepAmt int64, expSweepAmt int64,
expRewardAmt int64, expRewardAmt int64,

@ -4,7 +4,7 @@ import (
"io" "io"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/blob"
"github.com/lightningnetwork/lnd/watchtower/wtpolicy" "github.com/lightningnetwork/lnd/watchtower/wtpolicy"
) )
@ -58,7 +58,7 @@ func ReadElement(r io.Reader, element interface{}) error {
} }
e.BlobType = blob.Type(blobType) e.BlobType = blob.Type(blobType)
e.SweepFeeRate = lnwallet.SatPerKWeight(sweepFeeRate) e.SweepFeeRate = chainfee.SatPerKWeight(sweepFeeRate)
// Type is still unknown to wtdb extensions, fail. // Type is still unknown to wtdb extensions, fail.
default: default:

@ -7,6 +7,7 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/blob"
) )
@ -27,11 +28,11 @@ const (
// DefaultSweepFeeRate specifies the fee rate used to construct justice // DefaultSweepFeeRate specifies the fee rate used to construct justice
// transactions. The value is expressed in satoshis per kilo-weight. // transactions. The value is expressed in satoshis per kilo-weight.
DefaultSweepFeeRate = lnwallet.SatPerKWeight(2500) DefaultSweepFeeRate = chainfee.SatPerKWeight(2500)
// MinSweepFeeRate is the minimum sweep fee rate a client may use in its // MinSweepFeeRate is the minimum sweep fee rate a client may use in its
// policy, the current value is 4 sat/vbyte. // policy, the current value is 4 sat/vbyte.
MinSweepFeeRate = lnwallet.SatPerKWeight(1000) MinSweepFeeRate = chainfee.SatPerKWeight(1000)
) )
var ( var (
@ -97,7 +98,7 @@ type TxPolicy struct {
// constructing the justice transaction. All sweep transactions created // constructing the justice transaction. All sweep transactions created
// for this session must use this value during construction, and the // for this session must use this value during construction, and the
// signatures must implicitly commit to the resulting output values. // signatures must implicitly commit to the resulting output values.
SweepFeeRate lnwallet.SatPerKWeight SweepFeeRate chainfee.SatPerKWeight
} }
// Policy defines the negotiated parameters for a session between a client and // Policy defines the negotiated parameters for a session between a client and

@ -3,7 +3,7 @@ package wtwire
import ( import (
"io" "io"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/blob"
) )
@ -34,7 +34,7 @@ type CreateSession struct {
// constructing the justice transaction. All sweep transactions created // constructing the justice transaction. All sweep transactions created
// for this session must use this value during construction, and the // for this session must use this value during construction, and the
// signatures must implicitly commit to the resulting output values. // signatures must implicitly commit to the resulting output values.
SweepFeeRate lnwallet.SatPerKWeight SweepFeeRate chainfee.SatPerKWeight
} }
// A compile time check to ensure CreateSession implements the wtwire.Message // A compile time check to ensure CreateSession implements the wtwire.Message

@ -8,7 +8,7 @@ import (
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/blob"
) )
@ -74,7 +74,7 @@ func WriteElement(w io.Writer, element interface{}) error {
return err return err
} }
case lnwallet.SatPerKWeight: case chainfee.SatPerKWeight:
var b [8]byte var b [8]byte
binary.BigEndian.PutUint64(b[:], uint64(e)) binary.BigEndian.PutUint64(b[:], uint64(e))
if _, err := w.Write(b[:]); err != nil { if _, err := w.Write(b[:]); err != nil {
@ -194,12 +194,12 @@ func ReadElement(r io.Reader, element interface{}) error {
} }
*e = bytes *e = bytes
case *lnwallet.SatPerKWeight: case *chainfee.SatPerKWeight:
var b [8]byte var b [8]byte
if _, err := io.ReadFull(r, b[:]); err != nil { if _, err := io.ReadFull(r, b[:]); err != nil {
return err return err
} }
*e = lnwallet.SatPerKWeight(binary.BigEndian.Uint64(b[:])) *e = chainfee.SatPerKWeight(binary.BigEndian.Uint64(b[:]))
case *ErrorCode: case *ErrorCode:
var b [2]byte var b [2]byte