chainfee: create new chainfee package extracting fees from lnwallet
In this commit, we create a new chainfee package, that houses all fee related functionality used within the codebase. The creation of this new package furthers our long-term goal of extracting functionality from the bloated `lnwallet` package into new distinct packages. Additionally, this new packages resolves a class of import cycle that could arise if a new package that was imported by something in `lnwallet` wanted to use the existing fee related functions in the prior `lnwallet` package.
This commit is contained in:
47 files changed
+536
-400
No files matched your search
@@ -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)
|
||||
+17
-16
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user