From 777ed104a35ad27aed14e41c610db88efe5e62a2 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 Oct 2019 19:43:05 -0700 Subject: [PATCH] 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. --- breacharbiter.go | 3 +- breacharbiter_test.go | 5 +- chainregistry.go | 27 +- chancloser.go | 3 +- contractcourt/chain_arbitrator.go | 3 +- fundingmanager.go | 5 +- fundingmanager_test.go | 5 +- htlcswitch/link.go | 13 +- htlcswitch/link_test.go | 19 +- htlcswitch/mock.go | 10 +- htlcswitch/switch.go | 5 +- htlcswitch/test_utils.go | 5 +- lnrpc/walletrpc/config_active.go | 3 +- lnrpc/walletrpc/walletkit_server.go | 5 +- lnwallet/btcwallet/btcwallet.go | 5 +- .../estimator.go} | 261 ++++++++---------- .../estimator_test.go} | 25 +- lnwallet/chainfee/log.go | 29 ++ lnwallet/chainfee/rates.go | 53 ++++ lnwallet/channel.go | 72 ++--- lnwallet/channel_test.go | 152 ++++++---- lnwallet/config.go | 3 +- lnwallet/interface.go | 5 +- lnwallet/interface_test.go | 13 +- lnwallet/log.go | 2 + lnwallet/reservation.go | 3 +- lnwallet/test_utils.go | 3 +- lnwallet/transactions_test.go | 5 +- lnwallet/wallet.go | 11 +- lnwallet/wallet_test.go | 9 +- mock.go | 5 +- peer_test.go | 6 +- rpcserver.go | 13 +- server.go | 5 +- sweep/fee_estimator_mock_test.go | 22 +- sweep/sweeper.go | 33 +-- sweep/sweeper_test.go | 11 +- sweep/txgenerator.go | 8 +- sweep/walletsweep.go | 17 +- sweep/walletsweep_test.go | 11 +- test_utils.go | 3 +- tlv/bench_test.go | 14 +- .../wtclient/backup_task_internal_test.go | 3 +- watchtower/wtdb/codec.go | 4 +- watchtower/wtpolicy/policy.go | 7 +- watchtower/wtwire/create_session.go | 4 +- watchtower/wtwire/wtwire.go | 8 +- 47 files changed, 536 insertions(+), 400 deletions(-) rename lnwallet/{fee_estimator.go => chainfee/estimator.go} (68%) rename lnwallet/{fee_estimator_test.go => chainfee/estimator_test.go} (89%) create mode 100644 lnwallet/chainfee/log.go create mode 100644 lnwallet/chainfee/rates.go diff --git a/breacharbiter.go b/breacharbiter.go index 3bb33453..3706f3bd 100644 --- a/breacharbiter.go +++ b/breacharbiter.go @@ -21,6 +21,7 @@ import ( "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) var ( @@ -78,7 +79,7 @@ type BreachConfig struct { // Estimator is used by the breach arbiter to determine an appropriate // fee level when generating, signing, and broadcasting sweep // transactions. - Estimator lnwallet.FeeEstimator + Estimator chainfee.Estimator // GenSweepScript generates the receiving scripts for swept outputs. GenSweepScript func() ([]byte, error) diff --git a/breacharbiter_test.go b/breacharbiter_test.go index 7f471ea2..0b0a9c3f 100644 --- a/breacharbiter_test.go +++ b/breacharbiter_test.go @@ -31,6 +31,7 @@ import ( "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" ) @@ -1675,7 +1676,7 @@ func createTestArbiter(t *testing.T, contractBreaches chan *ContractBreachEvent, ba := newBreachArbiter(&BreachConfig{ CloseLink: func(_ *wire.OutPoint, _ htlcswitch.ChannelCloseType) {}, DB: db, - Estimator: lnwallet.NewStaticFeeEstimator(12500, 0), + Estimator: chainfee.NewStaticEstimator(12500, 0), GenSweepScript: func() ([]byte, error) { return nil, nil }, ContractBreaches: contractBreaches, Signer: signer, @@ -1824,7 +1825,7 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa return nil, nil, nil, err } - estimator := lnwallet.NewStaticFeeEstimator(12500, 0) + estimator := chainfee.NewStaticEstimator(12500, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { return nil, nil, nil, err diff --git a/chainregistry.go b/chainregistry.go index 7be92afd..ee07caaa 100644 --- a/chainregistry.go +++ b/chainregistry.go @@ -30,6 +30,7 @@ import ( "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/chainview" ) @@ -55,11 +56,11 @@ const ( // defaultBitcoinStaticFeePerKW is the fee rate of 50 sat/vbyte // expressed in sat/kw. - defaultBitcoinStaticFeePerKW = lnwallet.SatPerKWeight(12500) + defaultBitcoinStaticFeePerKW = chainfee.SatPerKWeight(12500) // defaultLitecoinStaticFeePerKW is the fee rate of 200 sat/vbyte // expressed in sat/kw. - defaultLitecoinStaticFeePerKW = lnwallet.SatPerKWeight(50000) + defaultLitecoinStaticFeePerKW = chainfee.SatPerKWeight(50000) // btcToLtcConversionRate is a fixed ratio used in order to scale up // payments when running on the Litecoin chain. @@ -112,7 +113,7 @@ func (c chainCode) String() string { type chainControl struct { chainIO lnwallet.BlockChainIO - feeEstimator lnwallet.FeeEstimator + feeEstimator chainfee.Estimator signer input.Signer @@ -161,7 +162,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, FeeRate: cfg.Bitcoin.FeeRate, TimeLockDelta: cfg.Bitcoin.TimeLockDelta, } - cc.feeEstimator = lnwallet.NewStaticFeeEstimator( + cc.feeEstimator = chainfee.NewStaticEstimator( defaultBitcoinStaticFeePerKW, 0, ) case litecoinChain: @@ -171,7 +172,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, FeeRate: cfg.Litecoin.FeeRate, TimeLockDelta: cfg.Litecoin.TimeLockDelta, } - cc.feeEstimator = lnwallet.NewStaticFeeEstimator( + cc.feeEstimator = chainfee.NewStaticEstimator( defaultLitecoinStaticFeePerKW, 0, ) default: @@ -219,8 +220,8 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, if cfg.NeutrinoMode.FeeURL != "" { ltndLog.Infof("Using API fee estimator!") - estimator := lnwallet.NewWebAPIFeeEstimator( - lnwallet.SparseConfFeeSource{ + estimator := chainfee.NewWebAPIEstimator( + chainfee.SparseConfFeeSource{ URL: cfg.NeutrinoMode.FeeURL, }, defaultBitcoinStaticFeePerKW, @@ -323,8 +324,8 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, // if we're using bitcoind as a backend, then we can // use live fee estimates, rather than a statically // coded value. - fallBackFeeRate := lnwallet.SatPerKVByte(25 * 1000) - cc.feeEstimator, err = lnwallet.NewBitcoindFeeEstimator( + fallBackFeeRate := chainfee.SatPerKVByte(25 * 1000) + cc.feeEstimator, err = chainfee.NewBitcoindEstimator( *rpcConfig, fallBackFeeRate.FeePerKWeight(), ) 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 // use live fee estimates, rather than a statically // coded value. - fallBackFeeRate := lnwallet.SatPerKVByte(25 * 1000) - cc.feeEstimator, err = lnwallet.NewBitcoindFeeEstimator( + fallBackFeeRate := chainfee.SatPerKVByte(25 * 1000) + cc.feeEstimator, err = chainfee.NewBitcoindEstimator( *rpcConfig, fallBackFeeRate.FeePerKWeight(), ) 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 // live fee estimates, rather than a statically coded // value. - fallBackFeeRate := lnwallet.SatPerKVByte(25 * 1000) - cc.feeEstimator, err = lnwallet.NewBtcdFeeEstimator( + fallBackFeeRate := chainfee.SatPerKVByte(25 * 1000) + cc.feeEstimator, err = chainfee.NewBtcdEstimator( *rpcConfig, fallBackFeeRate.FeePerKWeight(), ) if err != nil { diff --git a/chancloser.go b/chancloser.go index 3c015a5b..12bd7560 100644 --- a/chancloser.go +++ b/chancloser.go @@ -9,6 +9,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" ) @@ -150,7 +151,7 @@ type channelCloser struct { // passed configuration, and delivery+fee preference. The final argument should // only be populated iff, we're the initiator of this closing request. func newChannelCloser(cfg chanCloseCfg, deliveryScript []byte, - idealFeePerKw lnwallet.SatPerKWeight, negotiationHeight uint32, + idealFeePerKw chainfee.SatPerKWeight, negotiationHeight uint32, closeReq *htlcswitch.ChanClose) *channelCloser { // Given the target fee-per-kw, we'll compute what our ideal _total_ diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index 96759f7e..098bc5f3 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -13,6 +13,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/sweep" ) @@ -131,7 +132,7 @@ type ChainArbitratorConfig struct { Signer input.Signer // 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 lnwallet.BlockChainIO diff --git a/fundingmanager.go b/fundingmanager.go index c18df5ab..769c5a5e 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -24,6 +24,7 @@ import ( "github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" "golang.org/x/crypto/salsa20" @@ -230,7 +231,7 @@ type fundingConfig struct { // FeeEstimator calculates appropriate fee rates based on historical // transaction information. - FeeEstimator lnwallet.FeeEstimator + FeeEstimator chainfee.Estimator // Notifier is used by the FundingManager to determine when the // channel's funding transaction has been confirmed on the blockchain @@ -1218,7 +1219,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { NodeAddr: fmsg.peer.Address(), LocalFundingAmt: 0, RemoteFundingAmt: amt, - CommitFeePerKw: lnwallet.SatPerKWeight(msg.FeePerKiloWeight), + CommitFeePerKw: chainfee.SatPerKWeight(msg.FeePerKiloWeight), FundingFeePerKw: 0, PushMSat: msg.PushAmount, Flags: msg.ChannelFlags, diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 759f783b..024339d9 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -32,6 +32,7 @@ import ( "github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" ) @@ -218,7 +219,7 @@ func createTestWallet(cdb *channeldb.DB, netParams *chaincfg.Params, notifier chainntnfs.ChainNotifier, wc lnwallet.WalletController, signer input.Signer, keyRing keychain.SecretKeyRing, bio lnwallet.BlockChainIO, - estimator lnwallet.FeeEstimator) (*lnwallet.LightningWallet, error) { + estimator chainfee.Estimator) (*lnwallet.LightningWallet, error) { wallet, err := lnwallet.NewLightningWallet(lnwallet.Config{ Database: cdb, @@ -247,7 +248,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, options ...cfgOption) (*testNode, error) { netParams := activeNetParams.Params - estimator := lnwallet.NewStaticFeeEstimator(62500, 0) + estimator := chainfee.NewStaticEstimator(62500, 0) chainNotifier := &mockNotifier{ oneConfChannel: make(chan *chainntnfs.TxConfirmation, 1), diff --git a/htlcswitch/link.go b/htlcswitch/link.go index c0b62633..4f059186 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -24,6 +24,7 @@ import ( "github.com/lightningnetwork/lnd/lnpeer" "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/queue" "github.com/lightningnetwork/lnd/ticker" @@ -199,7 +200,7 @@ type ChannelLinkConfig struct { // FeeEstimator is an instance of a live fee estimator which will be // used to dynamically regulate the current fee of the commitment // transaction to ensure timely confirmation. - FeeEstimator lnwallet.FeeEstimator + FeeEstimator chainfee.Estimator // hodl.Mask is a bitvector composed of hodl.Flags, specifying breakpoints // 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 // this is the native rate used when computing the fee for commitment // 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 // blocks. 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 // match that of the network fee. We'll only update our commitment fee if the // network fee is +/- 10% to our network fee. -func shouldAdjustCommitFee(netFee, chanFee lnwallet.SatPerKWeight) bool { +func shouldAdjustCommitFee(netFee, chanFee chainfee.SatPerKWeight) bool { switch { // 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. @@ -1061,7 +1062,7 @@ out: // fee rate to our max fee allocation. commitFee := l.channel.CommitFeeRate() maxFee := l.channel.MaxFeeRate(l.cfg.MaxFeeAllocation) - newCommitFee := lnwallet.SatPerKWeight( + newCommitFee := chainfee.SatPerKWeight( math.Min(float64(netFee), float64(maxFee)), ) if !shouldAdjustCommitFee(newCommitFee, commitFee) { @@ -1894,7 +1895,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { case *lnwire.UpdateFee: // We received fee update from peer. If we are the initiator we // 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 { l.fail(LinkFailureError{code: ErrInvalidUpdate}, "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 // 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) diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 86463ee4..13dd7869 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -31,6 +31,7 @@ import ( "github.com/lightningnetwork/lnd/lnpeer" "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/ticker" ) @@ -1959,7 +1960,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { // incoming HTLCs automatically. coreLink.cfg.HodlMask = hodl.MaskFromFlags(hodl.ExitSettle) - estimator := lnwallet.NewStaticFeeEstimator(6000, 0) + estimator := chainfee.NewStaticEstimator(6000, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { t.Fatalf("unable to query fee estimator: %v", err) @@ -2379,7 +2380,7 @@ func TestChannelLinkBandwidthConsistencyOverflow(t *testing.T) { aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs ) - estimator := lnwallet.NewStaticFeeEstimator(6000, 0) + estimator := chainfee.NewStaticEstimator(6000, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { 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 // correctness of Alice's bandwidth when forwarding HTLCs. - estimator := lnwallet.NewStaticFeeEstimator(6000, 0) + estimator := chainfee.NewStaticEstimator(6000, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { 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 // correctness of Alice's bandwidth when forwarding HTLCs. - estimator := lnwallet.NewStaticFeeEstimator(6000, 0) + estimator := chainfee.NewStaticEstimator(6000, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { t.Fatalf("unable to query fee estimator: %v", err) @@ -3167,7 +3168,7 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) { aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs ) - estimator := lnwallet.NewStaticFeeEstimator(6000, 0) + estimator := chainfee.NewStaticEstimator(6000, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { 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. func TestShouldAdjustCommitFee(t *testing.T) { tests := []struct { - netFee lnwallet.SatPerKWeight - chanFee lnwallet.SatPerKWeight + netFee chainfee.SatPerKWeight + chanFee chainfee.SatPerKWeight shouldAdjust bool }{ @@ -3837,7 +3838,7 @@ func TestChannelLinkUpdateCommitFee(t *testing.T) { // triggerFeeUpdate is a helper closure to determine whether a fee // update was triggered and completed properly. - triggerFeeUpdate := func(feeEstimate, newFeeRate lnwallet.SatPerKWeight, + triggerFeeUpdate := func(feeEstimate, newFeeRate chainfee.SatPerKWeight, shouldUpdate bool) { 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 // that exceeds its maximum fee allocation should result in a fee rate // corresponding to the maximum fee allocation. - const maxFeeRate lnwallet.SatPerKWeight = 207182320 + const maxFeeRate chainfee.SatPerKWeight = 207182320 triggerFeeUpdate(maxFeeRate+1, maxFeeRate, true) } diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 53d95720..69bbec90 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -28,7 +28,7 @@ import ( "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lnpeer" "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/ticker" ) @@ -70,13 +70,13 @@ func (m *mockPreimageCache) SubscribeUpdates() *contractcourt.WitnessSubscriptio } type mockFeeEstimator struct { - byteFeeIn chan lnwallet.SatPerKWeight + byteFeeIn chan chainfee.SatPerKWeight quit chan struct{} } func (m *mockFeeEstimator) EstimateFeePerKW( - numBlocks uint32) (lnwallet.SatPerKWeight, error) { + numBlocks uint32) (chainfee.SatPerKWeight, error) { select { 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 } @@ -98,7 +98,7 @@ func (m *mockFeeEstimator) Stop() error { return nil } -var _ lnwallet.FeeEstimator = (*mockFeeEstimator)(nil) +var _ chainfee.Estimator = (*mockFeeEstimator)(nil) type mockForwardingLog struct { sync.Mutex diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index b135e1fd..aa040f4a 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -18,6 +18,7 @@ import ( "github.com/lightningnetwork/lnd/htlcswitch/hop" "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/ticker" ) @@ -102,7 +103,7 @@ type ChanClose struct { // This value is only utilized if the closure type is CloseRegular. // This will be the starting offered fee when the fee negotiation // 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 // 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 // a starting point for close negotiation. func (s *Switch) CloseLink(chanPoint *wire.OutPoint, closeType ChannelCloseType, - targetFeePerKw lnwallet.SatPerKWeight) (chan interface{}, + targetFeePerKw chainfee.SatPerKWeight) (chan interface{}, chan error) { // TODO(roasbeef) abstract out the close updates. diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 3c11c3fd..3fb0d9b7 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -32,6 +32,7 @@ import ( "github.com/lightningnetwork/lnd/lntest/wait" "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/shachain" "github.com/lightningnetwork/lnd/ticker" @@ -286,7 +287,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte, return nil, nil, nil, err } - estimator := lnwallet.NewStaticFeeEstimator(6000, 0) + estimator := chainfee.NewStaticEstimator(6000, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { return nil, nil, nil, err @@ -1078,7 +1079,7 @@ func newHopNetwork() *hopNetwork { obfuscator := NewMockObfuscator() feeEstimator := &mockFeeEstimator{ - byteFeeIn: make(chan lnwallet.SatPerKWeight), + byteFeeIn: make(chan chainfee.SatPerKWeight), quit: make(chan struct{}), } diff --git a/lnrpc/walletrpc/config_active.go b/lnrpc/walletrpc/config_active.go index 9804bc11..aada57dc 100644 --- a/lnrpc/walletrpc/config_active.go +++ b/lnrpc/walletrpc/config_active.go @@ -5,6 +5,7 @@ package walletrpc import ( "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/sweep" ) @@ -30,7 +31,7 @@ type Config struct { // FeeEstimator is an instance of the primary fee estimator instance // 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 // any relevant requests to. diff --git a/lnrpc/walletrpc/walletkit_server.go b/lnrpc/walletrpc/walletkit_server.go index 023782f6..dbbd898d 100644 --- a/lnrpc/walletrpc/walletkit_server.go +++ b/lnrpc/walletrpc/walletkit_server.go @@ -19,6 +19,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/sweep" "google.golang.org/grpc" "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 // attempt to create this transaction. tx, err := w.cfg.Wallet.SendOutputs( - outputsToCreate, lnwallet.SatPerKWeight(req.SatPerKw), + outputsToCreate, chainfee.SatPerKWeight(req.SatPerKw), ) if err != nil { return nil, err @@ -468,7 +469,7 @@ func (w *WalletKit) BumpFee(ctx context.Context, } // Construct the request's fee preference. - satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() + satPerKw := chainfee.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() feePreference := sweep.FeePreference{ ConfTarget: uint32(in.TargetConf), FeeRate: satPerKw, diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index 84945c86..3e51605a 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -21,6 +21,7 @@ import ( "github.com/btcsuite/btcwallet/walletdb" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) const ( @@ -289,7 +290,7 @@ func (b *BtcWallet) IsOurAddress(a btcutil.Address) bool { // // This is a part of the WalletController interface. 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 // SendOutputs. @@ -314,7 +315,7 @@ func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut, // // This is a part of the WalletController interface. 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 // this to sat/KB as the CreateSimpleTx method requires this unit. diff --git a/lnwallet/fee_estimator.go b/lnwallet/chainfee/estimator.go similarity index 68% rename from lnwallet/fee_estimator.go rename to lnwallet/chainfee/estimator.go index bda93d63..5f89f122 100644 --- a/lnwallet/fee_estimator.go +++ b/lnwallet/chainfee/estimator.go @@ -1,4 +1,4 @@ -package lnwallet +package chainfee import ( "encoding/json" @@ -10,85 +10,41 @@ import ( "sync" "time" - "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/rpcclient" "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 - // 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 // estimate for. maxBlockTarget uint32 = 1009 // 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. minBlockTarget uint32 = 2 // 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 // 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 ) -// 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)) -} - -// FeeEstimator provides the ability to estimate on-chain transaction fees for +// Estimator provides the ability to estimate on-chain transaction fees for // various combinations of transaction sizes and desired confirmation time // (measured by number of blocks). -type FeeEstimator interface { +type Estimator interface { // EstimateFeePerKW takes in a target for the number of blocks until an // initial confirmation and returns the estimated fee expressed in // sat/kw. 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. Start() error @@ -102,11 +58,11 @@ type FeeEstimator interface { RelayFeePerKW() SatPerKWeight } -// StaticFeeEstimator will return a static value for all fee calculation -// requests. It is designed to be replaced by a proper fee calculation -// implementation. The fees are not accessible directly, because changing them -// would not be thread safe. -type StaticFeeEstimator struct { +// StaticEstimator will return a static value for all fee calculation requests. +// It is designed to be replaced by a proper fee calculation implementation. +// The fees are not accessible directly, because changing them would not be +// thread safe. +type StaticEstimator struct { // feePerKW is the static fee rate in satoshis-per-vbyte that will be // returned by this fee estimator. feePerKW SatPerKWeight @@ -116,11 +72,10 @@ type StaticFeeEstimator struct { relayFee SatPerKWeight } -// NewStaticFeeEstimator returns a new static fee estimator instance. -func NewStaticFeeEstimator(feePerKW, - relayFee SatPerKWeight) *StaticFeeEstimator { +// NewStaticEstimator returns a new static fee estimator instance. +func NewStaticEstimator(feePerKW, relayFee SatPerKWeight) *StaticEstimator { - return &StaticFeeEstimator{ + return &StaticEstimator{ feePerKW: feePerKW, relayFee: relayFee, } @@ -128,43 +83,43 @@ func NewStaticFeeEstimator(feePerKW, // EstimateFeePerKW will return a static value for fee calculations. // -// NOTE: This method is part of the FeeEstimator interface. -func (e StaticFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { +// NOTE: This method is part of the Estimator interface. +func (e StaticEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { return e.feePerKW, nil } // RelayFeePerKW returns the minimum fee rate required for transactions to be // relayed. // -// NOTE: This method is part of the FeeEstimator interface. -func (e StaticFeeEstimator) RelayFeePerKW() SatPerKWeight { +// NOTE: This method is part of the Estimator interface. +func (e StaticEstimator) RelayFeePerKW() SatPerKWeight { 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. // -// NOTE: This method is part of the FeeEstimator interface. -func (e StaticFeeEstimator) Start() error { +// NOTE: This method is part of the Estimator interface. +func (e StaticEstimator) Start() error { return nil } // Stop stops any spawned goroutines and cleans up the resources used // by the fee estimator. // -// NOTE: This method is part of the FeeEstimator interface. -func (e StaticFeeEstimator) Stop() error { +// NOTE: This method is part of the Estimator interface. +func (e StaticEstimator) Stop() error { return nil } // A compile-time assertion to ensure that StaticFeeEstimator implements the -// FeeEstimator interface. -var _ FeeEstimator = (*StaticFeeEstimator)(nil) +// Estimator interface. +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 // 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 // if the fee estimator does not yet have enough data to actually // produce fee estimates. @@ -179,13 +134,13 @@ type BtcdFeeEstimator struct { 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 // 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 // fee estimate. -func NewBtcdFeeEstimator(rpcConfig rpcclient.ConnConfig, - fallBackFeeRate SatPerKWeight) (*BtcdFeeEstimator, error) { +func NewBtcdEstimator(rpcConfig rpcclient.ConnConfig, + fallBackFeeRate SatPerKWeight) (*BtcdEstimator, error) { rpcConfig.DisableConnectOnNew = true rpcConfig.DisableAutoReconnect = false @@ -194,17 +149,17 @@ func NewBtcdFeeEstimator(rpcConfig rpcclient.ConnConfig, return nil, err } - return &BtcdFeeEstimator{ + return &BtcdEstimator{ fallbackFeePerKW: fallBackFeeRate, btcdConn: chainConn, }, 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. // -// NOTE: This method is part of the FeeEstimator interface. -func (b *BtcdFeeEstimator) Start() error { +// NOTE: This method is part of the Estimator interface. +func (b *BtcdEstimator) Start() error { if err := b.btcdConn.Connect(20); err != nil { return err } @@ -233,7 +188,7 @@ func (b *BtcdFeeEstimator) Start() error { 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)) return nil @@ -242,8 +197,8 @@ func (b *BtcdFeeEstimator) Start() error { // Stop stops any spawned goroutines and cleans up the resources used // by the fee estimator. // -// NOTE: This method is part of the FeeEstimator interface. -func (b *BtcdFeeEstimator) Stop() error { +// NOTE: This method is part of the Estimator interface. +func (b *BtcdEstimator) Stop() error { b.btcdConn.Shutdown() return nil @@ -252,15 +207,15 @@ func (b *BtcdFeeEstimator) Stop() error { // EstimateFeePerKW takes in a target for the number of blocks until an initial // confirmation and returns the estimated fee expressed in sat/kw. // -// NOTE: This method is part of the FeeEstimator interface. -func (b *BtcdFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { +// NOTE: This method is part of the Estimator interface. +func (b *BtcdEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { feeEstimate, err := b.fetchEstimate(numBlocks) switch { // 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 // fee rate. case err != nil: - walletLog.Errorf("unable to query estimator: %v", err) + log.Errorf("unable to query estimator: %v", err) fallthrough 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 // relayed. // -// NOTE: This method is part of the FeeEstimator interface. -func (b *BtcdFeeEstimator) RelayFeePerKW() SatPerKWeight { +// NOTE: This method is part of the Estimator interface. +func (b *BtcdEstimator) RelayFeePerKW() SatPerKWeight { return b.minFeePerKW } // fetchEstimate returns a fee estimate for a transaction to be confirmed in // 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. btcPerKB, err := b.btcdConn.EstimateFee(int64(confTarget)) if err != nil { @@ -300,26 +255,26 @@ func (b *BtcdFeeEstimator) fetchEstimate(confTarget uint32) (SatPerKWeight, erro // Finally, we'll enforce our fee floor. 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, 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) return satPerKw, nil } -// A compile-time assertion to ensure that BtcdFeeEstimator implements the -// FeeEstimator interface. -var _ FeeEstimator = (*BtcdFeeEstimator)(nil) +// A compile-time assertion to ensure that BtcdEstimator implements the +// Estimator interface. +var _ Estimator = (*BtcdEstimator)(nil) -// BitcoindFeeEstimator is an implementation of the FeeEstimator interface -// backed by the RPC interface of an active bitcoind node. This implementation -// will proxy any fee estimation requests to bitcoind's RPC interface. -type BitcoindFeeEstimator struct { +// BitcoindEstimator is an implementation of the Estimator interface backed by +// the RPC interface of an active bitcoind node. This implementation will proxy +// any fee estimation requests to bitcoind's RPC interface. +type BitcoindEstimator struct { // fallbackFeePerKW is the fallback fee rate in sat/kw that is returned // if the fee estimator does not yet have enough data to actually // produce fee estimates. @@ -334,13 +289,13 @@ type BitcoindFeeEstimator struct { bitcoindConn *rpcclient.Client } -// NewBitcoindFeeEstimator creates a new BitcoindFeeEstimator given a fully -// populated rpc config that is able to successfully connect and authenticate -// with the bitcoind 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 fee estimate. -func NewBitcoindFeeEstimator(rpcConfig rpcclient.ConnConfig, - fallBackFeeRate SatPerKWeight) (*BitcoindFeeEstimator, error) { +// NewBitcoindEstimator creates a new BitcoindEstimator given a fully populated +// rpc config that is able to successfully connect and authenticate with the +// bitcoind 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 fee estimate. +func NewBitcoindEstimator(rpcConfig rpcclient.ConnConfig, + fallBackFeeRate SatPerKWeight) (*BitcoindEstimator, error) { rpcConfig.DisableConnectOnNew = true rpcConfig.DisableAutoReconnect = false @@ -351,17 +306,17 @@ func NewBitcoindFeeEstimator(rpcConfig rpcclient.ConnConfig, return nil, err } - return &BitcoindFeeEstimator{ + return &BitcoindEstimator{ fallbackFeePerKW: fallBackFeeRate, bitcoindConn: chainConn, }, 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. // -// NOTE: This method is part of the FeeEstimator interface. -func (b *BitcoindFeeEstimator) Start() error { +// NOTE: This method is part of the Estimator interface. +func (b *BitcoindEstimator) Start() error { // 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 // deprecated for `bitcoind`, we'll need to send a `getnetworkinfo` @@ -396,7 +351,7 @@ func (b *BitcoindFeeEstimator) Start() error { 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)) return nil @@ -405,23 +360,23 @@ func (b *BitcoindFeeEstimator) Start() error { // Stop stops any spawned goroutines and cleans up the resources used // by the fee estimator. // -// NOTE: This method is part of the FeeEstimator interface. -func (b *BitcoindFeeEstimator) Stop() error { +// NOTE: This method is part of the Estimator interface. +func (b *BitcoindEstimator) Stop() error { return nil } // EstimateFeePerKW takes in a target for the number of blocks until an initial // confirmation and returns the estimated fee expressed in sat/kw. // -// NOTE: This method is part of the FeeEstimator interface. -func (b *BitcoindFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { +// NOTE: This method is part of the Estimator interface. +func (b *BitcoindEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { feeEstimate, err := b.fetchEstimate(numBlocks) switch { // 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 // fee rate. case err != nil: - walletLog.Errorf("unable to query estimator: %v", err) + log.Errorf("unable to query estimator: %v", err) fallthrough 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 // relayed. // -// NOTE: This method is part of the FeeEstimator interface. -func (b *BitcoindFeeEstimator) RelayFeePerKW() SatPerKWeight { +// NOTE: This method is part of the Estimator interface. +func (b *BitcoindEstimator) RelayFeePerKW() SatPerKWeight { return b.minFeePerKW } // fetchEstimate returns a fee estimate for a transaction to be confirmed in // 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, // since it isn't supported by btcd but is available in bitcoind. target, err := json.Marshal(uint64(confTarget)) @@ -478,26 +433,26 @@ func (b *BitcoindFeeEstimator) fetchEstimate(confTarget uint32) (SatPerKWeight, // Finally, we'll enforce our fee floor. 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, 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) return satPerKw, nil } -// A compile-time assertion to ensure that BitcoindFeeEstimator implements the -// FeeEstimator interface. -var _ FeeEstimator = (*BitcoindFeeEstimator)(nil) +// A compile-time assertion to ensure that BitcoindEstimator implements the +// Estimator interface. +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 -// 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. type WebAPIFeeSource interface { // 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. 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. -type WebAPIFeeEstimator struct { +type WebAPIEstimator struct { started sync.Once stopped sync.Once @@ -581,12 +536,12 @@ type WebAPIFeeEstimator struct { 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. -func NewWebAPIFeeEstimator( - api WebAPIFeeSource, defaultFee SatPerKWeight) *WebAPIFeeEstimator { +func NewWebAPIEstimator( + api WebAPIFeeSource, defaultFee SatPerKWeight) *WebAPIEstimator { - return &WebAPIFeeEstimator{ + return &WebAPIEstimator{ apiSource: api, feeByBlockTarget: make(map[uint32]uint32), defaultFeePerKw: defaultFee, @@ -597,8 +552,8 @@ func NewWebAPIFeeEstimator( // EstimateFeePerKW takes in a target for the number of blocks until an initial // confirmation and returns the estimated fee expressed in sat/kw. // -// NOTE: This method is part of the FeeEstimator interface. -func (w *WebAPIFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { +// NOTE: This method is part of the Estimator interface. +func (w *WebAPIEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) { if numBlocks > maxBlockTarget { numBlocks = maxBlockTarget } else if numBlocks < minBlockTarget { @@ -618,20 +573,20 @@ func (w *WebAPIFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, 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) 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. // -// NOTE: This method is part of the FeeEstimator interface. -func (w *WebAPIFeeEstimator) Start() error { +// NOTE: This method is part of the Estimator interface. +func (w *WebAPIEstimator) Start() error { var err error 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.updateFeeEstimates() @@ -646,10 +601,10 @@ func (w *WebAPIFeeEstimator) Start() error { // Stop stops any spawned goroutines and cleans up the resources used by the // fee estimator. // -// NOTE: This method is part of the FeeEstimator interface. -func (w *WebAPIFeeEstimator) Stop() error { +// NOTE: This method is part of the Estimator interface. +func (w *WebAPIEstimator) Stop() error { w.stopped.Do(func() { - walletLog.Infof("Stopping web API fee estimator") + log.Infof("Stopping web API fee estimator") w.updateFeeTicker.Stop() @@ -662,15 +617,15 @@ func (w *WebAPIFeeEstimator) Stop() error { // RelayFeePerKW returns the minimum fee rate required for transactions to be // relayed. // -// NOTE: This method is part of the FeeEstimator interface. -func (w *WebAPIFeeEstimator) RelayFeePerKW() SatPerKWeight { +// NOTE: This method is part of the Estimator interface. +func (w *WebAPIEstimator) RelayFeePerKW() SatPerKWeight { return FeePerKwFloor } // randomFeeUpdateTimeout returns a random timeout between minFeeUpdateTimeout // and maxFeeUpdateTimeout that will be used to determine how often the Estimator // should retrieve fresh fees from its API. -func (w *WebAPIFeeEstimator) randomFeeUpdateTimeout() time.Duration { +func (w *WebAPIEstimator) randomFeeUpdateTimeout() time.Duration { lower := int64(minFeeUpdateTimeout) upper := int64(maxFeeUpdateTimeout) 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 // confirmation and returns an estimated fee (if one was returned by the API). If // 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() 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. -func (w *WebAPIFeeEstimator) updateFeeEstimates() { +func (w *WebAPIEstimator) updateFeeEstimates() { // 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 // response from the service. This way, if the service is down or @@ -725,7 +680,7 @@ func (w *WebAPIFeeEstimator) updateFeeEstimates() { targetURL := w.apiSource.GenQueryURL() resp, err := netClient.Get(targetURL) 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) return } @@ -735,7 +690,7 @@ func (w *WebAPIFeeEstimator) updateFeeEstimates() { // to parse out the body to obtain our final result. feesByBlockTarget, err := w.apiSource.ParseResponse(resp.Body) 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) return } @@ -746,7 +701,7 @@ func (w *WebAPIFeeEstimator) updateFeeEstimates() { } // feeUpdateManager updates the fee estimates whenever a new block comes in. -func (w *WebAPIFeeEstimator) feeUpdateManager() { +func (w *WebAPIEstimator) feeUpdateManager() { defer w.wg.Done() for { @@ -759,6 +714,6 @@ func (w *WebAPIFeeEstimator) feeUpdateManager() { } } -// A compile-time assertion to ensure that WebAPIFeeEstimator implements the -// FeeEstimator interface. -var _ FeeEstimator = (*WebAPIFeeEstimator)(nil) +// A compile-time assertion to ensure that WebAPIEstimator implements the +// Estimator interface. +var _ Estimator = (*WebAPIEstimator)(nil) diff --git a/lnwallet/fee_estimator_test.go b/lnwallet/chainfee/estimator_test.go similarity index 89% rename from lnwallet/fee_estimator_test.go rename to lnwallet/chainfee/estimator_test.go index c2cfeb52..b8b8186f 100644 --- a/lnwallet/fee_estimator_test.go +++ b/lnwallet/chainfee/estimator_test.go @@ -1,4 +1,4 @@ -package lnwallet_test +package chainfee import ( "bytes" @@ -9,8 +9,6 @@ import ( "testing" "github.com/btcsuite/btcutil" - - "github.com/lightningnetwork/lnd/lnwallet" ) type mockSparseConfFeeSource struct { @@ -38,9 +36,9 @@ func TestFeeRateTypes(t *testing.T) { const weight = vsize * 4 // 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() - if feePerKB != lnwallet.SatPerKVByte(feePerKw*4) { + if feePerKB != SatPerKVByte(feePerKw*4) { t.Fatalf("expected %d sat/kb, got %d sat/kb when "+ "converting from %d sat/kw", feePerKw*4, feePerKB, feePerKw) @@ -62,9 +60,9 @@ func TestFeeRateTypes(t *testing.T) { } // 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() - if feePerKw != lnwallet.SatPerKWeight(feePerKB/4) { + if feePerKw != SatPerKWeight(feePerKB/4) { t.Fatalf("expected %d sat/kw, got %d sat/kw when "+ "converting from %d sat/kb", feePerKB/4, feePerKw, feePerKB) @@ -91,9 +89,9 @@ func TestFeeRateTypes(t *testing.T) { func TestStaticFeeEstimator(t *testing.T) { t.Parallel() - const feePerKw = lnwallet.FeePerKwFloor + const feePerKw = FeePerKwFloor - feeEstimator := lnwallet.NewStaticFeeEstimator(feePerKw, 0) + feeEstimator := NewStaticEstimator(feePerKw, 0) if err := feeEstimator.Start(); err != nil { 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. url := "test" - feeSource := lnwallet.SparseConfFeeSource{URL: url} + feeSource := SparseConfFeeSource{URL: url} queryURL := feeSource.GenQueryURL() if queryURL != url { 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) { t.Parallel() - feeFloor := uint32(lnwallet.FeePerKwFloor.FeePerKVByte()) + feeFloor := uint32(FeePerKwFloor.FeePerKVByte()) testCases := []struct { name string target uint32 @@ -194,7 +192,7 @@ func TestWebAPIFeeEstimator(t *testing.T) { fees: testFees, } - estimator := lnwallet.NewWebAPIFeeEstimator(feeSource, 10) + estimator := NewWebAPIEstimator(feeSource, 10) // Test that requesting a fee when no fees have been cached fails. _, err := estimator.EstimateFeePerKW(5) @@ -210,6 +208,7 @@ func TestWebAPIFeeEstimator(t *testing.T) { defer estimator.Stop() for _, tc := range testCases { + tc := tc t.Run(tc.name, func(t *testing.T) { est, err := estimator.EstimateFeePerKW(tc.target) if tc.err != "" { @@ -220,7 +219,7 @@ func TestWebAPIFeeEstimator(t *testing.T) { "fail, instead got: %v", err) } } else { - exp := lnwallet.SatPerKVByte(tc.est).FeePerKWeight() + exp := SatPerKVByte(tc.est).FeePerKWeight() if err != nil { t.Fatalf("unable to estimate fee for "+ "%v block target, got: %v", diff --git a/lnwallet/chainfee/log.go b/lnwallet/chainfee/log.go new file mode 100644 index 00000000..d5d04057 --- /dev/null +++ b/lnwallet/chainfee/log.go @@ -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 +} diff --git a/lnwallet/chainfee/rates.go b/lnwallet/chainfee/rates.go new file mode 100644 index 00000000..48c3d7df --- /dev/null +++ b/lnwallet/chainfee/rates.go @@ -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)) +} diff --git a/lnwallet/channel.go b/lnwallet/channel.go index f9d07787..3366f2f7 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -22,6 +22,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" ) @@ -512,7 +513,7 @@ type commitment struct { // feePerKw is the fee per kw used to calculate this commitment // transaction's fee. - feePerKw SatPerKWeight + feePerKw chainfee.SatPerKWeight // dustLimit is the limit on the commitment transaction such that no // 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 // restore commitment state written do disk back into memory once we need to // restart a channel session. -func (lc *LightningChannel) diskHtlcToPayDesc(feeRate SatPerKWeight, +func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, commitHeight uint64, htlc *channeldb.HTLC, localCommitKeys, 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 // for each side. func (lc *LightningChannel) extractPayDescs(commitHeight uint64, - feeRate SatPerKWeight, htlcs []channeldb.HTLC, localCommitKeys, + feeRate chainfee.SatPerKWeight, htlcs []channeldb.HTLC, localCommitKeys, remoteCommitKeys *CommitmentKeyRing) ([]PaymentDescriptor, []PaymentDescriptor, error) { var ( @@ -894,7 +895,8 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool, // HTLC"s into PaymentDescriptor's so we can re-insert them into our // update log. incomingHtlcs, outgoingHtlcs, err := lc.extractPayDescs( - diskCommit.CommitHeight, SatPerKWeight(diskCommit.FeePerKw), + diskCommit.CommitHeight, + chainfee.SatPerKWeight(diskCommit.FeePerKw), diskCommit.Htlcs, localCommitKeys, remoteCommitKeys, ) if err != nil { @@ -915,7 +917,7 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool, txn: diskCommit.CommitTx, sig: diskCommit.CommitSig, fee: diskCommit.CommitFee, - feePerKw: SatPerKWeight(diskCommit.FeePerKw), + feePerKw: chainfee.SatPerKWeight(diskCommit.FeePerKw), incomingHTLCs: incomingHtlcs, outgoingHTLCs: outgoingHtlcs, } @@ -1509,7 +1511,7 @@ func (lc *LightningChannel) ResetState() { // if nothing happened. func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, remoteUpdateLog *updateLog, commitHeight uint64, - feeRate SatPerKWeight, remoteCommitKeys *CommitmentKeyRing, + feeRate chainfee.SatPerKWeight, remoteCommitKeys *CommitmentKeyRing, remoteDustLimit btcutil.Amount) (*PaymentDescriptor, error) { // 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 { payDesc, err := lc.logUpdateToPayDesc( &logUpdate, lc.remoteUpdateLog, pendingHeight, - SatPerKWeight(pendingCommit.FeePerKw), pendingRemoteKeys, + chainfee.SatPerKWeight(pendingCommit.FeePerKw), + pendingRemoteKeys, lc.channelState.RemoteChanCfg.DustLimit, ) if err != nil { @@ -2109,7 +2112,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // an output on the commitment transaction. if htlcIsDust( htlc.Incoming, false, - SatPerKWeight(revokedSnapshot.FeePerKw), + chainfee.SatPerKWeight(revokedSnapshot.FeePerKw), htlc.Amt.ToSatoshis(), chanState.RemoteChanCfg.DustLimit, ) { continue @@ -2198,13 +2201,13 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // htlcTimeoutFee returns the fee in satoshis required for an HTLC timeout // 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) } // htlcSuccessFee returns the fee in satoshis required for an HTLC success // 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) } @@ -2214,7 +2217,7 @@ func htlcSuccessFee(feePerKw SatPerKWeight) btcutil.Amount { // 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 // 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 { // 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 { ourUpdates []*PaymentDescriptor theirUpdates []*PaymentDescriptor - feePerKw SatPerKWeight + feePerKw chainfee.SatPerKWeight } // 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 // to reflect this update. - view.feePerKw = SatPerKWeight(feeUpdate.Amount.ToSatoshis()) + view.feePerKw = chainfee.SatPerKWeight(feeUpdate.Amount.ToSatoshis()) if mutateState { *addHeight = nextHeight @@ -3131,9 +3134,9 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // Ensure that the fee being applied is enough to be relayed across the // 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", - feePerKw, FeePerKwFloor) + feePerKw, chainfee.FeePerKwFloor) } // 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 // had on their commitment transaction. htlcResolutions, err := extractHtlcResolutions( - SatPerKWeight(remoteCommit.FeePerKw), false, signer, + chainfee.SatPerKWeight(remoteCommit.FeePerKw), false, signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, *commitSpend.SpenderTxHash, ) @@ -5246,10 +5249,11 @@ type HtlcResolutions struct { // newOutgoingHtlcResolution generates a new HTLC resolution capable of // allowing the caller to sweep an outgoing HTLC present on either their, or // the remote party's commitment transaction. -func newOutgoingHtlcResolution(signer input.Signer, localChanCfg *channeldb.ChannelConfig, - commitHash chainhash.Hash, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, - feePerKw SatPerKWeight, dustLimit btcutil.Amount, csvDelay uint32, localCommit bool, -) (*OutgoingHtlcResolution, error) { +func newOutgoingHtlcResolution(signer input.Signer, + localChanCfg *channeldb.ChannelConfig, commitHash chainhash.Hash, + htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, + feePerKw chainfee.SatPerKWeight, csvDelay uint32, + localCommit bool) (*OutgoingHtlcResolution, error) { op := wire.OutPoint{ Hash: commitHash, @@ -5388,7 +5392,7 @@ func newOutgoingHtlcResolution(signer input.Signer, localChanCfg *channeldb.Chan // TODO(roasbeef) consolidate code with above func func newIncomingHtlcResolution(signer input.Signer, localChanCfg *channeldb.ChannelConfig, commitHash chainhash.Hash, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, - feePerKw SatPerKWeight, dustLimit btcutil.Amount, csvDelay uint32, + feePerKw chainfee.SatPerKWeight, csvDelay uint32, localCommit bool) (*IncomingHtlcResolution, error) { op := wire.OutPoint{ @@ -5542,7 +5546,7 @@ func (r *OutgoingHtlcResolution) HtlcPoint() wire.OutPoint { // extractHtlcResolutions creates a series of outgoing HTLC resolutions, and // the local key used when generating the HTLC scrips. This function is to be // 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, localChanCfg, remoteChanCfg *channeldb.ChannelConfig, commitHash chainhash.Hash) (*HtlcResolutions, error) { @@ -5573,7 +5577,7 @@ func extractHtlcResolutions(feePerKw SatPerKWeight, ourCommit bool, // as we can satisfy the contract. ihr, err := newIncomingHtlcResolution( signer, localChanCfg, commitHash, &htlc, keyRing, - feePerKw, dustLimit, uint32(csvDelay), ourCommit, + feePerKw, uint32(csvDelay), ourCommit, ) if err != nil { return nil, err @@ -5585,7 +5589,7 @@ func extractHtlcResolutions(feePerKw SatPerKWeight, ourCommit bool, ohr, err := newOutgoingHtlcResolution( signer, localChanCfg, commitHash, &htlc, keyRing, - feePerKw, dustLimit, uint32(csvDelay), ourCommit, + feePerKw, uint32(csvDelay), ourCommit, ) if err != nil { 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. txHash := commitTx.TxHash() htlcResolutions, err := extractHtlcResolutions( - SatPerKWeight(localCommit.FeePerKw), true, signer, + chainfee.SatPerKWeight(localCommit.FeePerKw), true, signer, localCommit.Htlcs, keyRing, &chanState.LocalChanCfg, &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, // and a new commitment is created (which evaluates this fee), then the // 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 // be above our reserve balance. Otherwise, we'll reject the fee // 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 // the channel initiator, and must be called before sending update_fee to // the remote. -func (lc *LightningChannel) UpdateFee(feePerKw SatPerKWeight) error { +func (lc *LightningChannel) UpdateFee(feePerKw chainfee.SatPerKWeight) error { lc.Lock() 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 // 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() defer lc.Unlock() @@ -6213,7 +6217,7 @@ func CreateCooperativeCloseTx(fundingTxIn wire.TxIn, // CalcFee returns the commitment fee to use for the given // 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) } @@ -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 // the initiator. -func (lc *LightningChannel) MaxFeeRate(maxAllocation float64) SatPerKWeight { +func (lc *LightningChannel) MaxFeeRate(maxAllocation float64) chainfee.SatPerKWeight { lc.RLock() 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. 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. @@ -6260,11 +6266,11 @@ func (lc *LightningChannel) IsInitiator() bool { // CommitFeeRate returns the current fee rate of the commitment transaction in // units of sat-per-kw. -func (lc *LightningChannel) CommitFeeRate() SatPerKWeight { +func (lc *LightningChannel) CommitFeeRate() chainfee.SatPerKWeight { lc.RLock() 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 diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index ef0b5234..665917cf 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -19,6 +19,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" ) @@ -456,8 +457,12 @@ func TestCooperativeChannelClosure(t *testing.T) { aliceDeliveryScript := bobsPrivKey[:] bobDeliveryScript := testHdSeed[:] - aliceFeeRate := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) - bobFeeRate := SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) + aliceFeeRate := chainfee.SatPerKWeight( + aliceChannel.channelState.LocalCommitment.FeePerKw, + ) + bobFeeRate := chainfee.SatPerKWeight( + bobChannel.channelState.LocalCommitment.FeePerKw, + ) // We'll store with both Alice and Bob creating a new close proposal // with the same fee. @@ -596,7 +601,9 @@ func TestForceClose(t *testing.T) { // Factoring in the fee rate, Alice's amount should properly reflect // that we've added two additional HTLC to the commitment transaction. totalCommitWeight := input.CommitWeight + (input.HtlcWeight * 2) - feePerKw := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) + feePerKw := chainfee.SatPerKWeight( + aliceChannel.channelState.LocalCommitment.FeePerKw, + ) commitFee := feePerKw.FeeForWeight(totalCommitWeight) expectedAmount := (aliceChannel.Capacity / 2) - htlcAmount.ToSatoshis() - commitFee 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 // Bob's dust limit. htlcSat := (btcutil.Amount(500) + htlcTimeoutFee( - SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw))) + chainfee.SatPerKWeight( + aliceChannel.channelState.LocalCommitment.FeePerKw, + ), + )) htlcAmount := lnwire.NewMSatFromSatoshis(htlcSat) 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. - estimator := NewStaticFeeEstimator(6000, 0) + estimator := chainfee.NewStaticEstimator(6000, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { t.Fatalf("unable to get fee: %v", err) @@ -1268,7 +1278,9 @@ func TestChannelBalanceDustLimit(t *testing.T) { aliceBalance := aliceChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis() htlcSat := aliceBalance - defaultFee htlcSat += htlcSuccessFee( - SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw), + chainfee.SatPerKWeight( + aliceChannel.channelState.LocalCommitment.FeePerKw, + ), ) htlcAmount := lnwire.NewMSatFromSatoshis(htlcSat) @@ -1366,7 +1378,7 @@ func TestStateUpdatePersistence(t *testing.T) { } // Also add a fee update to the update logs. - fee := SatPerKWeight(333) + fee := chainfee.SatPerKWeight(333) if err := aliceChannel.UpdateFee(fee); err != nil { t.Fatalf("unable to send fee update") } @@ -1779,8 +1791,12 @@ func TestCooperativeCloseDustAdherence(t *testing.T) { } defer cleanUp() - aliceFeeRate := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) - bobFeeRate := SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) + aliceFeeRate := chainfee.SatPerKWeight( + aliceChannel.channelState.LocalCommitment.FeePerKw, + ) + bobFeeRate := chainfee.SatPerKWeight( + bobChannel.channelState.LocalCommitment.FeePerKw, + ) setDustLimit := func(dustVal btcutil.Amount) { 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 // be committed without any issue. - newFee := SatPerKWeight(baseFeeRate * 5) + newFee := chainfee.SatPerKWeight(baseFeeRate * 5) if err := aliceChannel.UpdateFee(newFee); err != nil { 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 // fee. This should result in an error as Alice won't be able to pay // this new fee rate. - newFee = SatPerKWeight(baseFeeRate * 1000000) + newFee = chainfee.SatPerKWeight(baseFeeRate * 1000000) if err := aliceChannel.UpdateFee(newFee); err == nil { 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 // smaller than the initial base fee rate. The fee application and // state transition should proceed without issue. - newFee = SatPerKWeight(baseFeeRate / 10) + newFee = chainfee.SatPerKWeight(baseFeeRate / 10) if err := aliceChannel.UpdateFee(newFee); err != nil { 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. - fee := SatPerKWeight(333) + fee := chainfee.SatPerKWeight(333) if err := aliceChannel.UpdateFee(fee); err != nil { 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) } - if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee { + if chainfee.SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee { 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) } - if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee { + if chainfee.SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee { 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. - fee := SatPerKWeight(333) + fee := chainfee.SatPerKWeight(333) aliceChannel.UpdateFee(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) } - if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee { + if chainfee.SatPerKWeight( + bobChannel.channelState.LocalCommitment.FeePerKw, + ) == fee { 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) } - if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee { + if chainfee.SatPerKWeight( + bobChannel.channelState.LocalCommitment.FeePerKw, + ) != fee { 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) } - if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) == fee { + if chainfee.SatPerKWeight( + aliceChannel.channelState.LocalCommitment.FeePerKw, + ) == fee { 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) } - if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != fee { + if chainfee.SatPerKWeight( + aliceChannel.channelState.LocalCommitment.FeePerKw, + ) != fee { 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 - fee := SatPerKWeight(333) + fee := chainfee.SatPerKWeight(333) aliceChannel.UpdateFee(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) } - if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee { + if chainfee.SatPerKWeight( + bobChannel.channelState.LocalCommitment.FeePerKw, + ) == fee { 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) } - if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee { + if chainfee.SatPerKWeight( + bobChannel.channelState.LocalCommitment.FeePerKw, + ) != fee { 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) } - if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) == fee { + if chainfee.SatPerKWeight( + aliceChannel.channelState.LocalCommitment.FeePerKw, + ) == fee { 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) } - if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != fee { + if chainfee.SatPerKWeight( + aliceChannel.channelState.LocalCommitment.FeePerKw, + ) != fee { 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 // fee update - fee := SatPerKWeight(333) + fee := chainfee.SatPerKWeight(333) err = aliceChannel.ReceiveUpdateFee(fee) if err == nil { t.Fatalf("expected alice to fail receiving fee update") @@ -2400,9 +2432,9 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { defer cleanUp() // Simulate Alice sending update fee message to bob. - fee1 := SatPerKWeight(333) - fee2 := SatPerKWeight(333) - fee := SatPerKWeight(333) + fee1 := chainfee.SatPerKWeight(333) + fee2 := chainfee.SatPerKWeight(333) + fee := chainfee.SatPerKWeight(333) aliceChannel.UpdateFee(fee1) aliceChannel.UpdateFee(fee2) aliceChannel.UpdateFee(fee) @@ -2427,15 +2459,17 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { 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") } // Alice sending more fee updates now should not mess up the old fee // they both committed to. - fee3 := SatPerKWeight(444) - fee4 := SatPerKWeight(555) - fee5 := SatPerKWeight(666) + fee3 := chainfee.SatPerKWeight(444) + fee4 := chainfee.SatPerKWeight(555) + fee5 := chainfee.SatPerKWeight(666) aliceChannel.UpdateFee(fee3) aliceChannel.UpdateFee(fee4) aliceChannel.UpdateFee(fee5) @@ -2450,7 +2484,9 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { 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") } @@ -2475,7 +2511,9 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { 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") } @@ -2486,7 +2524,9 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { 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") } @@ -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 // starting fee rate. - startingFeeRate := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) + startingFeeRate := chainfee.SatPerKWeight( + aliceChannel.channelState.LocalCommitment.FeePerKw, + ) newFeeRate := startingFeeRate * 1000000 // 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 // 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 // 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. - if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != newFeeRate { + if chainfee.SatPerKWeight( + aliceChannel.channelState.LocalCommitment.FeePerKw, + ) != newFeeRate { 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") } @@ -4118,7 +4166,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { // First, we'll fetch the current fee rate present within the // commitment transactions. - startingFeeRate := SatPerKWeight( + startingFeeRate := chainfee.SatPerKWeight( aliceChannel.channelState.LocalCommitment.FeePerKw, ) newFeeRate := startingFeeRate @@ -4247,10 +4295,14 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { } // 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") } - if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != newFeeRate { + if chainfee.SatPerKWeight( + bobChannel.channelState.LocalCommitment.FeePerKw, + ) != newFeeRate { t.Fatalf("bob's feePerKw was not locked in") } @@ -4272,10 +4324,14 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { assertLogItems(0, numHTLCs+1) // ...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") } - if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != newFeeRate { + if chainfee.SatPerKWeight( + bobChannel.channelState.LocalCommitment.FeePerKw, + ) != newFeeRate { t.Fatalf("bob's feePerKw was not locked in") } } @@ -6624,7 +6680,9 @@ func TestChannelMaxFeeRate(t *testing.T) { } defer cleanUp() - assertMaxFeeRate := func(maxAlloc float64, expFeeRate SatPerKWeight) { + assertMaxFeeRate := func(maxAlloc float64, + expFeeRate chainfee.SatPerKWeight) { + maxFeeRate := aliceChannel.MaxFeeRate(maxAlloc) if maxFeeRate != expFeeRate { t.Fatalf("expected max fee rate of %v with max "+ @@ -6636,5 +6694,5 @@ func TestChannelMaxFeeRate(t *testing.T) { assertMaxFeeRate(1.0, 690607734) assertMaxFeeRate(0.001, 690607) assertMaxFeeRate(0.000001, 690) - assertMaxFeeRate(0.0000001, FeePerKwFloor) + assertMaxFeeRate(0.0000001, chainfee.FeePerKwFloor) } diff --git a/lnwallet/config.go b/lnwallet/config.go index e86a8cbe..a73120c0 100644 --- a/lnwallet/config.go +++ b/lnwallet/config.go @@ -6,6 +6,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) // 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 // calculation of on-chain transaction fees. - FeeEstimator FeeEstimator + FeeEstimator chainfee.Estimator // ChainIO is an instance of the BlockChainIO interface. ChainIO is // used to lookup the existence of outputs within the UTXO set. diff --git a/lnwallet/interface.go b/lnwallet/interface.go index bf6c255d..1c6c2bd7 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwallet/wallet/txauthor" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) // 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 // be used when crafting the transaction. SendOutputs(outputs []*wire.TxOut, - feeRate SatPerKWeight) (*wire.MsgTx, error) + feeRate chainfee.SatPerKWeight) (*wire.MsgTx, error) // CreateSimpleTx creates a Bitcoin transaction paying to the specified // 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 // doesn't alter the database. A tx created with this set to true // SHOULD NOT be broadcasted. - CreateSimpleTx(outputs []*wire.TxOut, feeRate SatPerKWeight, + CreateSimpleTx(outputs []*wire.TxOut, feeRate chainfee.SatPerKWeight, dryRun bool) (*txauthor.AuthoredTx, error) // ListUnspentWitness returns all unspent outputs which are version 0 diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index 04af443f..7ba6e6de 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -40,6 +40,7 @@ import ( "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "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. func sendCoins(t *testing.T, miner *rpctest.Harness, sender, receiver *lnwallet.LightningWallet, output *wire.TxOut, - feeRate lnwallet.SatPerKWeight) *wire.MsgTx { + feeRate chainfee.SatPerKWeight) *wire.MsgTx { //nolint:unparam t.Helper() @@ -330,7 +331,7 @@ func createTestWallet(tempTestDir string, miningNode *rpctest.Harness, WalletController: wc, Signer: signer, ChainIO: bio, - FeeEstimator: lnwallet.NewStaticFeeEstimator(2500, 0), + FeeEstimator: chainfee.NewStaticEstimator(2500, 0), DefaultConstraints: channeldb.ChannelConstraints{ DustLimit: 500, MaxPendingAmount: lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin) * 100, @@ -723,7 +724,7 @@ func testReservationInitiatorBalanceBelowDustCancel(miner *rpctest.Harness, t.Fatalf("unable to create amt: %v", err) } - feePerKw := lnwallet.SatPerKWeight( + feePerKw := chainfee.SatPerKWeight( numBTC * numBTC * btcutil.SatoshiPerBitcoin, ) req := &lnwallet.InitFundingReserveMsg{ @@ -2151,7 +2152,7 @@ func testChangeOutputSpendConfirmation(r *rpctest.Harness, // // TODO(wilmer): replace this once SendOutputs easily supports sending // all funds in one transaction. - txFeeRate := lnwallet.SatPerKWeight(2500) + txFeeRate := chainfee.SatPerKWeight(2500) txFee := btcutil.Amount(14380) output := &wire.TxOut{ Value: int64(aliceBalance - txFee), @@ -2247,7 +2248,7 @@ func testLastUnusedAddr(miner *rpctest.Harness, if err != nil { t.Fatalf("unable to convert addr to script: %v", err) } - feeRate := lnwallet.SatPerKWeight(2500) + feeRate := chainfee.SatPerKWeight(2500) output := &wire.TxOut{ Value: 1000000, PkScript: addrScript, @@ -2281,7 +2282,7 @@ func testCreateSimpleTx(r *rpctest.Harness, w *lnwallet.LightningWallet, // The test cases we will run through for all backends. testCases := []struct { outVals []int64 - feeRate lnwallet.SatPerKWeight + feeRate chainfee.SatPerKWeight valid bool }{ { diff --git a/lnwallet/log.go b/lnwallet/log.go index 97f1c21f..31881e2d 100644 --- a/lnwallet/log.go +++ b/lnwallet/log.go @@ -7,6 +7,7 @@ import ( "github.com/btcsuite/btcwallet/wtxmgr" "github.com/lightningnetwork/lnd/build" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) // walletLog is a logger that is initialized with no output filters. This @@ -34,6 +35,7 @@ func UseLogger(logger btclog.Logger) { btcwallet.UseLogger(logger) wtxmgr.UseLogger(logger) chain.UseLogger(logger) + chainfee.UseLogger(logger) } // logClosure is used to provide a closure over expensive logging operations diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index b96651ff..92023133 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" ) @@ -128,7 +129,7 @@ type ChannelReservation struct { // creation of all channel reservations should be carried out via the // lnwallet.InitChannelReservation interface. func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, - commitFeePerKw SatPerKWeight, wallet *LightningWallet, + commitFeePerKw chainfee.SatPerKWeight, wallet *LightningWallet, id uint64, pushMSat lnwire.MilliSatoshi, chainHash *chainhash.Hash, flags lnwire.FundingFlag, tweaklessCommit bool) (*ChannelReservation, error) { diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index efb30ac1..0aee8aa2 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -18,6 +18,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" ) @@ -233,7 +234,7 @@ func CreateTestChannels(tweaklessCommits bool) ( return nil, nil, nil, err } - estimator := NewStaticFeeEstimator(6000, 0) + estimator := chainfee.NewStaticEstimator(6000, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { return nil, nil, nil, err diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index ed4c533d..035a721f 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -17,6 +17,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" ) @@ -800,7 +801,7 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) { height: test.commitment.CommitHeight, ourBalance: test.commitment.LocalBalance, theirBalance: test.commitment.RemoteBalance, - feePerKw: SatPerKWeight(test.commitment.FeePerKw), + feePerKw: chainfee.SatPerKWeight(test.commitment.FeePerKw), dustLimit: tc.dustLimit, isOurs: true, } @@ -843,7 +844,7 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) { // Generate second-level HTLC transactions for HTLCs in // commitment tx. htlcResolutions, err := extractHtlcResolutions( - SatPerKWeight(test.commitment.FeePerKw), true, signer, + chainfee.SatPerKWeight(test.commitment.FeePerKw), true, signer, htlcs, keys, channel.localChanCfg, channel.remoteChanCfg, commitTx.TxHash(), ) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index ba65fe4b..c3c2020d 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -21,6 +21,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwallet/chanvalidate" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" @@ -89,11 +90,11 @@ type InitFundingReserveMsg struct { // of initial commitment transactions. In order to ensure timely // confirmation, it is recommended that this fee should be generous, // 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 // funding transaction. - FundingFeePerKw SatPerKWeight + FundingFeePerKw chainfee.SatPerKWeight // PushMSat is the number of milli-satoshis that should be pushed over // 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 // the selected outputs, and a function closure to unlock them in case of an // error is returned. -func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerKWeight, +func (l *LightningWallet) selectCoinsAndChange(feeRate chainfee.SatPerKWeight, amt btcutil.Amount, minConfs int32, subtractFees bool) ( *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 // specified fee rate should be expressed in sat/kw for coin selection to // function properly. -func coinSelect(feeRate SatPerKWeight, amt btcutil.Amount, +func coinSelect(feeRate chainfee.SatPerKWeight, amt btcutil.Amount, coins []*Utxo) ([]*Utxo, btcutil.Amount, error) { 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 // amt in total after fees, adhering to the specified fee rate. The selected // 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, btcutil.Amount, error) { diff --git a/lnwallet/wallet_test.go b/lnwallet/wallet_test.go index f9a6c0d3..903f5e4d 100644 --- a/lnwallet/wallet_test.go +++ b/lnwallet/wallet_test.go @@ -5,12 +5,15 @@ import ( "github.com/btcsuite/btcutil" "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 // with the given number of inputs and the optional change output. This matches // 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 // All inputs. @@ -39,7 +42,7 @@ func fundingFee(feeRate SatPerKWeight, numInput int, change bool) btcutil.Amount func TestCoinSelect(t *testing.T) { t.Parallel() - const feeRate = SatPerKWeight(100) + const feeRate = chainfee.SatPerKWeight(100) const dust = btcutil.Amount(100) type testCase struct { @@ -185,7 +188,7 @@ func TestCoinSelect(t *testing.T) { func TestCoinSelectSubtractFees(t *testing.T) { t.Parallel() - const feeRate = SatPerKWeight(100) + const feeRate = chainfee.SatPerKWeight(100) const dustLimit = btcutil.Amount(1000) const dust = btcutil.Amount(100) diff --git a/mock.go b/mock.go index cbcba646..609b7818 100644 --- a/mock.go +++ b/mock.go @@ -17,6 +17,7 @@ import ( "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) // 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, - _ lnwallet.SatPerKWeight) (*wire.MsgTx, error) { + _ chainfee.SatPerKWeight) (*wire.MsgTx, error) { return nil, nil } func (*mockWalletController) CreateSimpleTx(outputs []*wire.TxOut, - _ lnwallet.SatPerKWeight, _ bool) (*txauthor.AuthoredTx, error) { + _ chainfee.SatPerKWeight, _ bool) (*txauthor.AuthoredTx, error) { return nil, nil } diff --git a/peer_test.go b/peer_test.go index 9db7e7f3..424405fc 100644 --- a/peer_test.go +++ b/peer_test.go @@ -10,7 +10,7 @@ import ( "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/htlcswitch" - "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" ) @@ -156,7 +156,7 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) { dummyDeliveryScript), } - estimator := lnwallet.NewStaticFeeEstimator(12500, 0) + estimator := chainfee.NewStaticEstimator(12500, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { t.Fatalf("unable to query fee estimator: %v", err) @@ -446,7 +446,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) { msg: respShutdown, } - estimator := lnwallet.NewStaticFeeEstimator(12500, 0) + estimator := chainfee.NewStaticEstimator(12500, 0) initiatorIdealFeeRate, err := estimator.EstimateFeePerKW(1) if err != nil { t.Fatalf("unable to query fee estimator: %v", err) diff --git a/rpcserver.go b/rpcserver.go index 1502f384..4833eb71 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -44,6 +44,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "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/macaroons" "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 // address to a specified output value to be sent to that address. func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64, - feeRate lnwallet.SatPerKWeight) (*chainhash.Hash, error) { + feeRate chainfee.SatPerKWeight) (*chainhash.Hash, error) { outputs, err := addrPairsToOutputs(paymentMap) 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 // appropriate fee rate for this transaction. - satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() + satPerKw := chainfee.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() feePerKw, err := sweep.DetermineFeePerKw( r.server.cc.feeEstimator, sweep.FeePreference{ 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 // appropriate fee rate for this transaction. - satPerKw := lnwallet.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() + satPerKw := chainfee.SatPerKVByte(in.SatPerByte * 1000).FeePerKWeight() feePerKw, err := sweep.DetermineFeePerKw( r.server.cc.feeEstimator, sweep.FeePreference{ 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 // 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( r.server.cc.feeEstimator, sweep.FeePreference{ 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 // 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( r.server.cc.feeEstimator, sweep.FeePreference{ 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 // an appropriate fee rate for the cooperative closure // transaction. - satPerKw := lnwallet.SatPerKVByte( + satPerKw := chainfee.SatPerKVByte( in.SatPerByte * 1000, ).FeePerKWeight() feeRate, err := sweep.DetermineFeePerKw( diff --git a/server.go b/server.go index e43ad2db..b8872e9e 100644 --- a/server.go +++ b/server.go @@ -44,6 +44,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/nat" "github.com/lightningnetwork/lnd/netann" @@ -1136,7 +1137,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, if cfg.WtClient.SweepFeeRate != 0 { // We expose the sweep fee rate in sat/byte, but the // tower protocol operations on sat/kw. - sweepRateSatPerByte := lnwallet.SatPerKVByte( + sweepRateSatPerByte := chainfee.SatPerKVByte( 1000 * cfg.WtClient.SweepFeeRate, ) policy.SweepFeeRate = sweepRateSatPerByte.FeePerKWeight() @@ -3071,7 +3072,7 @@ type openChanReq struct { pushAmt lnwire.MilliSatoshi - fundingFeePerKw lnwallet.SatPerKWeight + fundingFeePerKw chainfee.SatPerKWeight private bool diff --git a/sweep/fee_estimator_mock_test.go b/sweep/fee_estimator_mock_test.go index b0510f36..4ca89f0c 100644 --- a/sweep/fee_estimator_mock_test.go +++ b/sweep/fee_estimator_mock_test.go @@ -3,38 +3,38 @@ package sweep import ( "sync" - "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) // mockFeeEstimator implements a mock fee estimator. It closely resembles // lnwallet.StaticFeeEstimator with the addition that fees can be changed for // testing purposes in a thread safe manner. 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 // mockFeeEstimator.EstimateFeePerKW method. - estimateFeePerKW func(numBlocks uint32) (lnwallet.SatPerKWeight, error) + estimateFeePerKW func(numBlocks uint32) (chainfee.SatPerKWeight, error) lock sync.Mutex } func newMockFeeEstimator(feePerKW, - relayFee lnwallet.SatPerKWeight) *mockFeeEstimator { + relayFee chainfee.SatPerKWeight) *mockFeeEstimator { return &mockFeeEstimator{ feePerKW: feePerKW, relayFee: relayFee, - blocksToFee: make(map[uint32]lnwallet.SatPerKWeight), + blocksToFee: make(map[uint32]chainfee.SatPerKWeight), } } func (e *mockFeeEstimator) updateFees(feePerKW, - relayFee lnwallet.SatPerKWeight) { + relayFee chainfee.SatPerKWeight) { e.lock.Lock() defer e.lock.Unlock() @@ -44,7 +44,7 @@ func (e *mockFeeEstimator) updateFees(feePerKW, } func (e *mockFeeEstimator) EstimateFeePerKW(numBlocks uint32) ( - lnwallet.SatPerKWeight, error) { + chainfee.SatPerKWeight, error) { e.lock.Lock() defer e.lock.Unlock() @@ -60,7 +60,7 @@ func (e *mockFeeEstimator) EstimateFeePerKW(numBlocks uint32) ( return e.feePerKW, nil } -func (e *mockFeeEstimator) RelayFeePerKW() lnwallet.SatPerKWeight { +func (e *mockFeeEstimator) RelayFeePerKW() chainfee.SatPerKWeight { e.lock.Lock() defer e.lock.Unlock() @@ -75,4 +75,4 @@ func (e *mockFeeEstimator) Stop() error { return nil } -var _ lnwallet.FeeEstimator = (*mockFeeEstimator)(nil) +var _ chainfee.Estimator = (*mockFeeEstimator)(nil) diff --git a/sweep/sweeper.go b/sweep/sweeper.go index 2e3a1e1c..51400f53 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -16,13 +16,14 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) const ( // DefaultMaxFeeRate is the default maximum fee rate allowed within the // UtxoSweeper. The current value is equivalent to a fee rate of 10,000 // sat/vbyte. - DefaultMaxFeeRate = lnwallet.FeePerKwFloor * 1e4 + DefaultMaxFeeRate = chainfee.FeePerKwFloor * 1e4 // DefaultFeeRateBucketSize is the default size of fee rate buckets // 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 // transaction broadcast to the network. - lastFeeRate lnwallet.SatPerKWeight + lastFeeRate chainfee.SatPerKWeight } // 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 // be swept with the specified fee rate. type inputCluster struct { - sweepFeeRate lnwallet.SatPerKWeight + sweepFeeRate chainfee.SatPerKWeight inputs pendingInputs } @@ -126,7 +127,7 @@ type PendingInput struct { // LastFeeRate is the most recent fee rate used for the input being // 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 // input. @@ -182,7 +183,7 @@ type UtxoSweeper struct { currentOutputScript []byte - relayFeeRate lnwallet.SatPerKWeight + relayFeeRate chainfee.SatPerKWeight quit chan struct{} wg sync.WaitGroup @@ -197,7 +198,7 @@ type UtxoSweeperConfig struct { // FeeEstimator is used when crafting sweep transactions to estimate // the necessary fee relative to the expected size of the sweep // transaction. - FeeEstimator lnwallet.FeeEstimator + FeeEstimator chainfee.Estimator // PublishTransaction facilitates the process of broadcasting a signed // transaction to the appropriate network. @@ -235,7 +236,7 @@ type UtxoSweeperConfig struct { // MaxFeeRate is the the maximum fee rate allowed within the // UtxoSweeper. - MaxFeeRate lnwallet.SatPerKWeight + MaxFeeRate chainfee.SatPerKWeight // FeeRateBucketSize is the default size of fee rate buckets we'll use // 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 // ensures that the fee rate respects the bounds of the UtxoSweeper. 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 // 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 // in order to batch inputs with similar fee rates together. func (s *UtxoSweeper) bucketForFeeRate( - feeRate lnwallet.SatPerKWeight) lnwallet.SatPerKWeight { + feeRate chainfee.SatPerKWeight) chainfee.SatPerKWeight { - minBucket := s.relayFeeRate + lnwallet.SatPerKWeight(s.cfg.FeeRateBucketSize) - return lnwallet.SatPerKWeight( + minBucket := s.relayFeeRate + chainfee.SatPerKWeight(s.cfg.FeeRateBucketSize) + return chainfee.SatPerKWeight( 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 // all inputs within that cluster. func (s *UtxoSweeper) clusterBySweepFeeRate() []inputCluster { - bucketInputs := make(map[lnwallet.SatPerKWeight]pendingInputs) - inputFeeRates := make(map[wire.OutPoint]lnwallet.SatPerKWeight) + bucketInputs := make(map[chainfee.SatPerKWeight]pendingInputs) + inputFeeRates := make(map[wire.OutPoint]chainfee.SatPerKWeight) // First, we'll group together all inputs with similar fee rates. This // 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. inputClusters := make([]inputCluster, 0, len(bucketInputs)) for _, inputs := range bucketInputs { - var sweepFeeRate lnwallet.SatPerKWeight + var sweepFeeRate chainfee.SatPerKWeight for op := range inputs { sweepFeeRate += inputFeeRates[op] } - sweepFeeRate /= lnwallet.SatPerKWeight(len(inputs)) + sweepFeeRate /= chainfee.SatPerKWeight(len(inputs)) inputClusters = append(inputClusters, inputCluster{ sweepFeeRate: sweepFeeRate, 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 // 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 { // Generate an output script if there isn't an unused script available. diff --git a/sweep/sweeper_test.go b/sweep/sweeper_test.go index 623b11f9..69c92daa 100644 --- a/sweep/sweeper_test.go +++ b/sweep/sweeper_test.go @@ -15,6 +15,7 @@ import ( "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) var ( @@ -99,7 +100,7 @@ func createSweeperTestContext(t *testing.T) *sweeperTestContext { backend := newMockBackend(notifier) - estimator := newMockFeeEstimator(10000, lnwallet.FeePerKwFloor) + estimator := newMockFeeEstimator(10000, chainfee.FeePerKwFloor) publishChan := make(chan wire.MsgTx, 2) 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 // only type of transaction the UtxoSweeper can create at the moment. func assertTxFeeRate(t *testing.T, tx *wire.MsgTx, - expectedFeeRate lnwallet.SatPerKWeight, inputs ...input.Input) { + expectedFeeRate chainfee.SatPerKWeight, inputs ...input.Input) { t.Helper() @@ -994,11 +995,11 @@ func TestDifferentFeePreferences(t *testing.T) { // this to ensure the sweeper can broadcast distinct transactions for // each sweep with a different fee preference. lowFeePref := FeePreference{ConfTarget: 12} - lowFeeRate := lnwallet.SatPerKWeight(5000) + lowFeeRate := chainfee.SatPerKWeight(5000) ctx.estimator.blocksToFee[lowFeePref.ConfTarget] = lowFeeRate highFeePref := FeePreference{ConfTarget: 6} - highFeeRate := lnwallet.SatPerKWeight(10000) + highFeeRate := chainfee.SatPerKWeight(10000) ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate input1 := spendableInputs[0] @@ -1116,7 +1117,7 @@ func TestBumpFeeRBF(t *testing.T) { ctx := createSweeperTestContext(t) lowFeePref := FeePreference{ConfTarget: 144} - lowFeeRate := lnwallet.FeePerKwFloor + lowFeeRate := chainfee.FeePerKwFloor ctx.estimator.blocksToFee[lowFeePref.ConfTarget] = lowFeeRate // We'll first try to bump the fee of an output currently unknown to the diff --git a/sweep/txgenerator.go b/sweep/txgenerator.go index 4c5c5bc7..f0ef575c 100644 --- a/sweep/txgenerator.go +++ b/sweep/txgenerator.go @@ -11,7 +11,7 @@ import ( "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwallet/wallet/txrules" "github.com/lightningnetwork/lnd/input" - "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) var ( @@ -31,7 +31,7 @@ type inputSet []input.Input // inputs are skipped. No input sets with a total value after fees below the // dust limit are returned. func generateInputPartitionings(sweepableInputs []input.Input, - relayFeePerKW, feePerKW lnwallet.SatPerKWeight, + relayFeePerKW, feePerKW chainfee.SatPerKWeight, maxInputsPerTx int) ([]inputSet, error) { // 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 // whole. func getPositiveYieldInputs(sweepableInputs []input.Input, maxInputs int, - feePerKW lnwallet.SatPerKWeight) (int, btcutil.Amount) { + feePerKW chainfee.SatPerKWeight) (int, btcutil.Amount) { 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. func createSweepTx(inputs []input.Input, outputPkScript []byte, - currentBlockHeight uint32, feePerKw lnwallet.SatPerKWeight, + currentBlockHeight uint32, feePerKw chainfee.SatPerKWeight, signer input.Signer) (*wire.MsgTx, error) { inputs, txWeight := getWeightEstimate(inputs) diff --git a/sweep/walletsweep.go b/sweep/walletsweep.go index e2145778..38d19488 100644 --- a/sweep/walletsweep.go +++ b/sweep/walletsweep.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) const ( @@ -27,7 +28,7 @@ type FeePreference struct { // FeeRate if non-zero, signals a fee pre fence expressed in the fee // rate expressed in sat/kw for a particular transaction. - FeeRate lnwallet.SatPerKWeight + FeeRate chainfee.SatPerKWeight } // 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 // value is chosen based on the two free parameters as one, or both of them can // be zero. -func DetermineFeePerKw(feeEstimator lnwallet.FeeEstimator, - feePref FeePreference) (lnwallet.SatPerKWeight, error) { +func DetermineFeePerKw(feeEstimator chainfee.Estimator, + feePref FeePreference) (chainfee.SatPerKWeight, error) { switch { // 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. case feePref.FeeRate != 0: feePerKW := feePref.FeeRate - if feePerKW < lnwallet.FeePerKwFloor { + if feePerKW < chainfee.FeePerKwFloor { log.Infof("Manual fee rate input of %d sat/kw is "+ "too low, using %d sat/kw instead", feePerKW, - lnwallet.FeePerKwFloor) + chainfee.FeePerKwFloor) - feePerKW = lnwallet.FeePerKwFloor + feePerKW = chainfee.FeePerKwFloor } return feePerKW, nil @@ -152,10 +153,10 @@ type WalletSweepPackage struct { // by the delivery address. The sweep transaction will be crafted with the // target fee rate, and will use the utxoSource and outpointLocker as sources // for wallet funds. -func CraftSweepAllTx(feeRate lnwallet.SatPerKWeight, blockHeight uint32, +func CraftSweepAllTx(feeRate chainfee.SatPerKWeight, blockHeight uint32, deliveryAddr btcutil.Address, coinSelectLocker CoinSelectionLocker, utxoSource UtxoSource, outpointLocker OutpointLocker, - feeEstimator lnwallet.FeeEstimator, + feeEstimator chainfee.Estimator, signer input.Signer) (*WalletSweepPackage, error) { // TODO(roasbeef): turn off ATPL as well when available? diff --git a/sweep/walletsweep_test.go b/sweep/walletsweep_test.go index 0b96e00b..acd99c6a 100644 --- a/sweep/walletsweep_test.go +++ b/sweep/walletsweep_test.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) // TestDetermineFeePerKw tests that given a fee preference, the @@ -17,8 +18,8 @@ import ( func TestDetermineFeePerKw(t *testing.T) { t.Parallel() - defaultFee := lnwallet.SatPerKWeight(999) - relayFee := lnwallet.SatPerKWeight(300) + defaultFee := chainfee.SatPerKWeight(999) + relayFee := chainfee.SatPerKWeight(300) feeEstimator := newMockFeeEstimator(defaultFee, relayFee) @@ -35,7 +36,7 @@ func TestDetermineFeePerKw(t *testing.T) { // fee is the value the DetermineFeePerKw should return given // the FeePreference above - fee lnwallet.SatPerKWeight + fee chainfee.SatPerKWeight // fail determines if this test case should fail or not. fail bool @@ -43,9 +44,9 @@ func TestDetermineFeePerKw(t *testing.T) { // A fee rate below the fee rate floor should output the floor. { 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 diff --git a/test_utils.go b/test_utils.go index 671bc0f4..610cfed6 100644 --- a/test_utils.go +++ b/test_utils.go @@ -22,6 +22,7 @@ import ( "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/shachain" @@ -214,7 +215,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier, return nil, nil, nil, nil, err } - estimator := lnwallet.NewStaticFeeEstimator(12500, 0) + estimator := chainfee.NewStaticEstimator(12500, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { return nil, nil, nil, nil, err diff --git a/tlv/bench_test.go b/tlv/bench_test.go index f71a7eb6..132ef784 100644 --- a/tlv/bench_test.go +++ b/tlv/bench_test.go @@ -6,7 +6,7 @@ import ( "io/ioutil" "testing" - "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/tlv" "github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/wtwire" @@ -19,7 +19,7 @@ type CreateSessionTLV struct { MaxUpdates uint16 RewardBase uint32 RewardRate uint32 - SweepFeeRate lnwallet.SatPerKWeight + SweepFeeRate chainfee.SatPerKWeight 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. 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.NewTypeForEncodingErr(val, "lnwallet.SatPerKWeight") + return tlv.NewTypeForEncodingErr(val, "chainfee.SatPerKWeight") } // DSatPerKW is an decoder for lnwallet.SatPerKWeight. 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 err := tlv.DUint64(r, &sat, buf, l) if err != nil { return err } - *v = lnwallet.SatPerKWeight(sat) + *v = chainfee.SatPerKWeight(sat) return nil } - return tlv.NewTypeForDecodingErr(val, "lnwallet.SatPerKWeight", l, 8) + return tlv.NewTypeForDecodingErr(val, "chainfee.SatPerKWeight", l, 8) } // NewCreateSessionTLV initializes a new CreateSessionTLV message. diff --git a/watchtower/wtclient/backup_task_internal_test.go b/watchtower/wtclient/backup_task_internal_test.go index cff6f2b8..d845631c 100644 --- a/watchtower/wtclient/backup_task_internal_test.go +++ b/watchtower/wtclient/backup_task_internal_test.go @@ -16,6 +16,7 @@ import ( "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/wtdb" @@ -86,7 +87,7 @@ func genTaskTest( toLocalAmt int64, toRemoteAmt int64, blobType blob.Type, - sweepFeeRate lnwallet.SatPerKWeight, + sweepFeeRate chainfee.SatPerKWeight, rewardScript []byte, expSweepAmt int64, expRewardAmt int64, diff --git a/watchtower/wtdb/codec.go b/watchtower/wtdb/codec.go index 4cfa6b6b..e6b99db6 100644 --- a/watchtower/wtdb/codec.go +++ b/watchtower/wtdb/codec.go @@ -4,7 +4,7 @@ import ( "io" "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/wtpolicy" ) @@ -58,7 +58,7 @@ func ReadElement(r io.Reader, element interface{}) error { } e.BlobType = blob.Type(blobType) - e.SweepFeeRate = lnwallet.SatPerKWeight(sweepFeeRate) + e.SweepFeeRate = chainfee.SatPerKWeight(sweepFeeRate) // Type is still unknown to wtdb extensions, fail. default: diff --git a/watchtower/wtpolicy/policy.go b/watchtower/wtpolicy/policy.go index db636b68..9c7f5e64 100644 --- a/watchtower/wtpolicy/policy.go +++ b/watchtower/wtpolicy/policy.go @@ -7,6 +7,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/watchtower/blob" ) @@ -27,11 +28,11 @@ const ( // DefaultSweepFeeRate specifies the fee rate used to construct justice // 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 // policy, the current value is 4 sat/vbyte. - MinSweepFeeRate = lnwallet.SatPerKWeight(1000) + MinSweepFeeRate = chainfee.SatPerKWeight(1000) ) var ( @@ -97,7 +98,7 @@ type TxPolicy struct { // constructing the justice transaction. All sweep transactions created // for this session must use this value during construction, and the // 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 diff --git a/watchtower/wtwire/create_session.go b/watchtower/wtwire/create_session.go index 338dda83..7047ce30 100644 --- a/watchtower/wtwire/create_session.go +++ b/watchtower/wtwire/create_session.go @@ -3,7 +3,7 @@ package wtwire import ( "io" - "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/watchtower/blob" ) @@ -34,7 +34,7 @@ type CreateSession struct { // constructing the justice transaction. All sweep transactions created // for this session must use this value during construction, and the // 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 diff --git a/watchtower/wtwire/wtwire.go b/watchtower/wtwire/wtwire.go index 1af9433d..44725560 100644 --- a/watchtower/wtwire/wtwire.go +++ b/watchtower/wtwire/wtwire.go @@ -8,7 +8,7 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" "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/watchtower/blob" ) @@ -74,7 +74,7 @@ func WriteElement(w io.Writer, element interface{}) error { return err } - case lnwallet.SatPerKWeight: + case chainfee.SatPerKWeight: var b [8]byte binary.BigEndian.PutUint64(b[:], uint64(e)) if _, err := w.Write(b[:]); err != nil { @@ -194,12 +194,12 @@ func ReadElement(r io.Reader, element interface{}) error { } *e = bytes - case *lnwallet.SatPerKWeight: + case *chainfee.SatPerKWeight: var b [8]byte if _, err := io.ReadFull(r, b[:]); err != nil { return err } - *e = lnwallet.SatPerKWeight(binary.BigEndian.Uint64(b[:])) + *e = chainfee.SatPerKWeight(binary.BigEndian.Uint64(b[:])) case *ErrorCode: var b [2]byte