Browse Source

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.
master
Olaoluwa Osuntokun 5 years ago
parent
commit
777ed104a3
No known key found for this signature in database
GPG Key ID: BC13F65E2DC84465
  1. 3
      breacharbiter.go
  2. 5
      breacharbiter_test.go
  3. 27
      chainregistry.go
  4. 3
      chancloser.go
  5. 3
      contractcourt/chain_arbitrator.go
  6. 5
      fundingmanager.go
  7. 5
      fundingmanager_test.go
  8. 13
      htlcswitch/link.go
  9. 19
      htlcswitch/link_test.go
  10. 10
      htlcswitch/mock.go
  11. 5
      htlcswitch/switch.go
  12. 5
      htlcswitch/test_utils.go
  13. 3
      lnrpc/walletrpc/config_active.go
  14. 5
      lnrpc/walletrpc/walletkit_server.go
  15. 5
      lnwallet/btcwallet/btcwallet.go
  16. 261
      lnwallet/chainfee/estimator.go
  17. 25
      lnwallet/chainfee/estimator_test.go
  18. 29
      lnwallet/chainfee/log.go
  19. 53
      lnwallet/chainfee/rates.go
  20. 72
      lnwallet/channel.go
  21. 152
      lnwallet/channel_test.go
  22. 3
      lnwallet/config.go
  23. 5
      lnwallet/interface.go
  24. 13
      lnwallet/interface_test.go
  25. 2
      lnwallet/log.go
  26. 3
      lnwallet/reservation.go
  27. 3
      lnwallet/test_utils.go
  28. 5
      lnwallet/transactions_test.go
  29. 11
      lnwallet/wallet.go
  30. 9
      lnwallet/wallet_test.go
  31. 5
      mock.go
  32. 6
      peer_test.go
  33. 13
      rpcserver.go
  34. 5
      server.go
  35. 22
      sweep/fee_estimator_mock_test.go
  36. 33
      sweep/sweeper.go
  37. 11
      sweep/sweeper_test.go
  38. 8
      sweep/txgenerator.go
  39. 17
      sweep/walletsweep.go
  40. 11
      sweep/walletsweep_test.go
  41. 3
      test_utils.go
  42. 14
      tlv/bench_test.go
  43. 3
      watchtower/wtclient/backup_task_internal_test.go
  44. 4
      watchtower/wtdb/codec.go
  45. 7
      watchtower/wtpolicy/policy.go
  46. 4
      watchtower/wtwire/create_session.go
  47. 8
      watchtower/wtwire/wtwire.go

3
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)

5
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

27
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 {

3
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_

3
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

5
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,

5
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),

13
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)

19
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)
}

10
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

5
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.

5
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{}),
}

3
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.

5
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,

5
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.

261
lnwallet/fee_estimator.go → 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)

25
lnwallet/fee_estimator_test.go → 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",

29
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
}

53
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))
}

72
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

152
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)
}

3
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.

5
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

13
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
}{
{

2
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

3
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) {

3
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

5
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(),
)

11
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) {

9
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)

5
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
}

6
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)

13
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(

5
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

22
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)

33
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.

11
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

8
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)

17
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?

11
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

3
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

14
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.

3
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,

4
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:

7
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

4
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

8
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

Loading…
Cancel
Save