2024-01-24 19:04:43 +01:00
|
|
|
package sweep
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
2024-04-11 11:08:36 +02:00
|
|
|
"github.com/lightningnetwork/lnd/fn"
|
2024-01-24 19:04:43 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2024-05-16 23:07:29 +02:00
|
|
|
// TestLinearFeeFunctionNewMaxFeeRateUsed tests when the conf target is <= 1,
|
|
|
|
// the max fee rate is used.
|
|
|
|
func TestLinearFeeFunctionNewMaxFeeRateUsed(t *testing.T) {
|
2024-01-24 19:04:43 +01:00
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
rt := require.New(t)
|
|
|
|
|
|
|
|
// Create a mock fee estimator.
|
|
|
|
estimator := &chainfee.MockEstimator{}
|
2024-05-16 23:07:29 +02:00
|
|
|
defer estimator.AssertExpectations(t)
|
2024-01-24 19:04:43 +01:00
|
|
|
|
|
|
|
// Create testing params.
|
|
|
|
maxFeeRate := chainfee.SatPerKWeight(10000)
|
2024-04-11 11:08:36 +02:00
|
|
|
noStartFeeRate := fn.None[chainfee.SatPerKWeight]()
|
2024-01-24 19:04:43 +01:00
|
|
|
|
2024-04-03 10:13:49 +02:00
|
|
|
// Assert init fee function with zero conf value will end up using the
|
|
|
|
// max fee rate.
|
2024-04-11 11:08:36 +02:00
|
|
|
f, err := NewLinearFeeFunction(maxFeeRate, 0, estimator, noStartFeeRate)
|
2024-04-03 10:13:49 +02:00
|
|
|
rt.NoError(err)
|
|
|
|
rt.NotNil(f)
|
|
|
|
|
|
|
|
// Assert the internal state.
|
|
|
|
rt.Equal(maxFeeRate, f.startingFeeRate)
|
|
|
|
rt.Equal(maxFeeRate, f.endingFeeRate)
|
|
|
|
rt.Equal(maxFeeRate, f.currentFeeRate)
|
2024-01-24 19:04:43 +01:00
|
|
|
|
2024-05-16 23:07:29 +02:00
|
|
|
// Assert init fee function with conf of one will end up using the max
|
|
|
|
// fee rate.
|
|
|
|
f, err = NewLinearFeeFunction(maxFeeRate, 1, estimator, noStartFeeRate)
|
|
|
|
rt.NoError(err)
|
|
|
|
rt.NotNil(f)
|
|
|
|
|
|
|
|
// Assert the internal state.
|
|
|
|
rt.Equal(maxFeeRate, f.startingFeeRate)
|
|
|
|
rt.Equal(maxFeeRate, f.endingFeeRate)
|
|
|
|
rt.Equal(maxFeeRate, f.currentFeeRate)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestLinearFeeFunctionNewZeroFeeRateDelta tests when the fee rate delta is
|
|
|
|
// zero, it will return an error except when the width is one.
|
|
|
|
func TestLinearFeeFunctionNewZeroFeeRateDelta(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
rt := require.New(t)
|
|
|
|
|
|
|
|
// Create a mock fee estimator.
|
|
|
|
estimator := &chainfee.MockEstimator{}
|
|
|
|
defer estimator.AssertExpectations(t)
|
|
|
|
|
|
|
|
// Create testing params.
|
|
|
|
maxFeeRate := chainfee.SatPerKWeight(10000)
|
|
|
|
estimatedFeeRate := chainfee.SatPerKWeight(500)
|
|
|
|
confTarget := uint32(6)
|
|
|
|
noStartFeeRate := fn.None[chainfee.SatPerKWeight]()
|
|
|
|
|
|
|
|
// When the calculated fee rate delta is 0, an error should be returned
|
|
|
|
// when the width is not one.
|
2024-01-24 19:04:43 +01:00
|
|
|
//
|
2024-05-16 23:07:29 +02:00
|
|
|
// Mock the fee estimator to return the fee rate.
|
2024-01-24 19:04:43 +01:00
|
|
|
estimator.On("EstimateFeePerKW", confTarget).Return(
|
2024-05-16 23:07:29 +02:00
|
|
|
// The starting fee rate is the max fee rate.
|
|
|
|
maxFeeRate, nil).Once()
|
|
|
|
estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
|
2024-01-24 19:04:43 +01:00
|
|
|
|
2024-05-16 23:07:29 +02:00
|
|
|
f, err := NewLinearFeeFunction(
|
2024-04-11 11:08:36 +02:00
|
|
|
maxFeeRate, confTarget, estimator, noStartFeeRate,
|
|
|
|
)
|
2024-05-16 23:07:29 +02:00
|
|
|
rt.ErrorContains(err, "fee rate delta is zero")
|
2024-01-24 19:04:43 +01:00
|
|
|
rt.Nil(f)
|
|
|
|
|
2024-05-16 23:07:29 +02:00
|
|
|
// When the calculated fee rate delta is 0, an error should NOT be
|
|
|
|
// returned when the width is one, and the starting feerate is capped
|
|
|
|
// at the max fee rate.
|
2024-01-24 19:04:43 +01:00
|
|
|
//
|
|
|
|
// Mock the fee estimator to return the fee rate.
|
2024-05-16 23:07:29 +02:00
|
|
|
smallConf := uint32(2)
|
2024-01-24 19:04:43 +01:00
|
|
|
estimator.On("EstimateFeePerKW", smallConf).Return(
|
|
|
|
// The fee rate is greater than the max fee rate.
|
|
|
|
maxFeeRate+1, nil).Once()
|
|
|
|
estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
|
|
|
|
|
2024-04-11 11:08:36 +02:00
|
|
|
f, err = NewLinearFeeFunction(
|
|
|
|
maxFeeRate, smallConf, estimator, noStartFeeRate,
|
|
|
|
)
|
2024-01-24 19:04:43 +01:00
|
|
|
rt.NoError(err)
|
|
|
|
rt.NotNil(f)
|
|
|
|
|
2024-05-16 23:07:29 +02:00
|
|
|
// Assert the internal state.
|
|
|
|
rt.Equal(maxFeeRate, f.startingFeeRate)
|
|
|
|
rt.Equal(maxFeeRate, f.endingFeeRate)
|
|
|
|
rt.Equal(maxFeeRate, f.currentFeeRate)
|
|
|
|
rt.Zero(f.deltaFeeRate)
|
|
|
|
rt.Equal(smallConf-1, f.width)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestLinearFeeFunctionNewEsimator tests the NewLinearFeeFunction function
|
|
|
|
// properly reacts to the response or error returned from the fee estimator.
|
|
|
|
func TestLinearFeeFunctionNewEsimator(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
rt := require.New(t)
|
|
|
|
|
|
|
|
// Create a mock fee estimator.
|
|
|
|
estimator := &chainfee.MockEstimator{}
|
|
|
|
defer estimator.AssertExpectations(t)
|
|
|
|
|
|
|
|
// Create testing params.
|
|
|
|
maxFeeRate := chainfee.SatPerKWeight(10000)
|
|
|
|
minRelayFeeRate := chainfee.SatPerKWeight(100)
|
|
|
|
confTarget := uint32(6)
|
|
|
|
noStartFeeRate := fn.None[chainfee.SatPerKWeight]()
|
|
|
|
|
|
|
|
// When the fee estimator returns an error, it's returned.
|
2024-01-24 19:04:43 +01:00
|
|
|
//
|
2024-05-16 23:07:29 +02:00
|
|
|
// Mock the fee estimator to return an error.
|
2024-01-24 19:04:43 +01:00
|
|
|
estimator.On("EstimateFeePerKW", confTarget).Return(
|
2024-05-16 23:07:29 +02:00
|
|
|
chainfee.SatPerKWeight(0), errDummy).Once()
|
2024-01-24 19:04:43 +01:00
|
|
|
|
2024-05-16 23:07:29 +02:00
|
|
|
f, err := NewLinearFeeFunction(
|
2024-04-11 11:08:36 +02:00
|
|
|
maxFeeRate, confTarget, estimator, noStartFeeRate,
|
|
|
|
)
|
2024-05-16 23:07:29 +02:00
|
|
|
rt.ErrorIs(err, errDummy)
|
2024-01-24 19:04:43 +01:00
|
|
|
rt.Nil(f)
|
|
|
|
|
2024-03-18 03:56:41 +01:00
|
|
|
// When the conf target is >= 1008, the min relay fee should be used.
|
|
|
|
//
|
|
|
|
// Mock the fee estimator to reutrn the fee rate.
|
|
|
|
estimator.On("RelayFeePerKW").Return(minRelayFeeRate).Once()
|
|
|
|
|
|
|
|
largeConf := uint32(1008)
|
2024-04-11 11:08:36 +02:00
|
|
|
f, err = NewLinearFeeFunction(
|
|
|
|
maxFeeRate, largeConf, estimator, noStartFeeRate,
|
|
|
|
)
|
2024-03-18 03:56:41 +01:00
|
|
|
rt.NoError(err)
|
|
|
|
rt.NotNil(f)
|
|
|
|
|
|
|
|
// Assert the internal state.
|
|
|
|
rt.Equal(minRelayFeeRate, f.startingFeeRate)
|
|
|
|
rt.Equal(maxFeeRate, f.endingFeeRate)
|
|
|
|
rt.Equal(minRelayFeeRate, f.currentFeeRate)
|
|
|
|
rt.NotZero(f.deltaFeeRate)
|
2024-05-16 23:07:29 +02:00
|
|
|
rt.Equal(largeConf-1, f.width)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestLinearFeeFunctionNewSuccess tests we can create the fee function
|
|
|
|
// successfully.
|
|
|
|
func TestLinearFeeFunctionNewSuccess(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
rt := require.New(t)
|
|
|
|
|
|
|
|
// Create a mock fee estimator.
|
|
|
|
estimator := &chainfee.MockEstimator{}
|
|
|
|
defer estimator.AssertExpectations(t)
|
|
|
|
|
|
|
|
// Create testing params.
|
|
|
|
maxFeeRate := chainfee.SatPerKWeight(10000)
|
|
|
|
estimatedFeeRate := chainfee.SatPerKWeight(500)
|
|
|
|
minRelayFeeRate := chainfee.SatPerKWeight(100)
|
|
|
|
confTarget := uint32(6)
|
|
|
|
noStartFeeRate := fn.None[chainfee.SatPerKWeight]()
|
|
|
|
startFeeRate := chainfee.SatPerKWeight(1000)
|
2024-03-18 03:56:41 +01:00
|
|
|
|
2024-01-24 19:04:43 +01:00
|
|
|
// Check a successfully created fee function.
|
|
|
|
//
|
|
|
|
// Mock the fee estimator to return the fee rate.
|
|
|
|
estimator.On("EstimateFeePerKW", confTarget).Return(
|
|
|
|
estimatedFeeRate, nil).Once()
|
2024-05-16 23:07:29 +02:00
|
|
|
estimator.On("RelayFeePerKW").Return(minRelayFeeRate).Once()
|
2024-01-24 19:04:43 +01:00
|
|
|
|
2024-05-16 23:07:29 +02:00
|
|
|
f, err := NewLinearFeeFunction(
|
2024-04-11 11:08:36 +02:00
|
|
|
maxFeeRate, confTarget, estimator, noStartFeeRate,
|
|
|
|
)
|
2024-01-24 19:04:43 +01:00
|
|
|
rt.NoError(err)
|
|
|
|
rt.NotNil(f)
|
|
|
|
|
|
|
|
// Assert the internal state.
|
|
|
|
rt.Equal(estimatedFeeRate, f.startingFeeRate)
|
|
|
|
rt.Equal(maxFeeRate, f.endingFeeRate)
|
|
|
|
rt.Equal(estimatedFeeRate, f.currentFeeRate)
|
|
|
|
rt.NotZero(f.deltaFeeRate)
|
2024-05-16 23:07:29 +02:00
|
|
|
rt.Equal(confTarget-1, f.width)
|
2024-04-11 11:08:36 +02:00
|
|
|
|
|
|
|
// Check a successfully created fee function using the specified
|
|
|
|
// starting fee rate.
|
|
|
|
//
|
|
|
|
// NOTE: by NOT mocking the fee estimator, we assert the
|
|
|
|
// estimateFeeRate is NOT called.
|
|
|
|
f, err = NewLinearFeeFunction(
|
|
|
|
maxFeeRate, confTarget, estimator, fn.Some(startFeeRate),
|
|
|
|
)
|
|
|
|
|
|
|
|
rt.NoError(err)
|
|
|
|
rt.NotNil(f)
|
|
|
|
|
|
|
|
// Assert the customized starting fee rate is used.
|
|
|
|
rt.Equal(startFeeRate, f.startingFeeRate)
|
2024-05-16 23:07:29 +02:00
|
|
|
rt.Equal(maxFeeRate, f.endingFeeRate)
|
2024-04-11 11:08:36 +02:00
|
|
|
rt.Equal(startFeeRate, f.currentFeeRate)
|
2024-05-16 23:07:29 +02:00
|
|
|
rt.NotZero(f.deltaFeeRate)
|
|
|
|
rt.Equal(confTarget-1, f.width)
|
2024-01-24 19:04:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// TestLinearFeeFunctionFeeRateAtPosition checks the expected feerate is
|
|
|
|
// calculated and returned.
|
|
|
|
func TestLinearFeeFunctionFeeRateAtPosition(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
rt := require.New(t)
|
|
|
|
|
|
|
|
// Create a fee func which has three positions:
|
|
|
|
// - position 0: 1000
|
|
|
|
// - position 1: 2000
|
|
|
|
// - position 2: 3000
|
|
|
|
f := &LinearFeeFunction{
|
|
|
|
startingFeeRate: 1000,
|
|
|
|
endingFeeRate: 3000,
|
|
|
|
position: 0,
|
2024-03-18 04:35:55 +01:00
|
|
|
deltaFeeRate: 1_000_000,
|
2024-01-24 19:04:43 +01:00
|
|
|
width: 3,
|
|
|
|
}
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
name string
|
|
|
|
pos uint32
|
|
|
|
expectedFeerate chainfee.SatPerKWeight
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "position 0",
|
|
|
|
pos: 0,
|
|
|
|
expectedFeerate: 1000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "position 1",
|
|
|
|
pos: 1,
|
|
|
|
expectedFeerate: 2000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "position 2",
|
|
|
|
pos: 2,
|
|
|
|
expectedFeerate: 3000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "position 3",
|
|
|
|
pos: 3,
|
|
|
|
expectedFeerate: 3000,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
tc := tc
|
|
|
|
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
result := f.feeRateAtPosition(tc.pos)
|
|
|
|
rt.Equal(tc.expectedFeerate, result)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestLinearFeeFunctionIncrement checks the internal state is updated
|
|
|
|
// correctly when the fee rate is incremented.
|
|
|
|
func TestLinearFeeFunctionIncrement(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
rt := require.New(t)
|
|
|
|
|
|
|
|
// Create a mock fee estimator.
|
|
|
|
estimator := &chainfee.MockEstimator{}
|
2024-05-16 23:07:29 +02:00
|
|
|
defer estimator.AssertExpectations(t)
|
2024-01-24 19:04:43 +01:00
|
|
|
|
|
|
|
// Create testing params. These params are chosen so the delta value is
|
|
|
|
// 100.
|
2024-05-16 23:07:29 +02:00
|
|
|
maxFeeRate := chainfee.SatPerKWeight(900)
|
2024-01-24 19:04:43 +01:00
|
|
|
estimatedFeeRate := chainfee.SatPerKWeight(100)
|
2024-05-16 23:07:29 +02:00
|
|
|
confTarget := uint32(9) // This means the width is 8.
|
2024-01-24 19:04:43 +01:00
|
|
|
|
|
|
|
// Mock the fee estimator to return the fee rate.
|
|
|
|
estimator.On("EstimateFeePerKW", confTarget).Return(
|
|
|
|
estimatedFeeRate, nil).Once()
|
|
|
|
estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
|
|
|
|
|
2024-04-11 11:08:36 +02:00
|
|
|
f, err := NewLinearFeeFunction(
|
|
|
|
maxFeeRate, confTarget, estimator,
|
|
|
|
fn.None[chainfee.SatPerKWeight](),
|
|
|
|
)
|
2024-01-24 19:04:43 +01:00
|
|
|
rt.NoError(err)
|
|
|
|
|
2024-05-16 23:07:29 +02:00
|
|
|
// We now increase the position from 1 to 8.
|
|
|
|
for i := uint32(1); i <= confTarget-1; i++ {
|
2024-01-24 19:04:43 +01:00
|
|
|
// Increase the fee rate.
|
|
|
|
increased, err := f.Increment()
|
|
|
|
rt.NoError(err)
|
|
|
|
rt.True(increased)
|
|
|
|
|
|
|
|
// Assert the internal state.
|
|
|
|
rt.Equal(i, f.position)
|
|
|
|
|
|
|
|
delta := chainfee.SatPerKWeight(i * 100)
|
|
|
|
rt.Equal(estimatedFeeRate+delta, f.currentFeeRate)
|
|
|
|
|
|
|
|
// Check public method returns the expected fee rate.
|
|
|
|
rt.Equal(estimatedFeeRate+delta, f.FeeRate())
|
|
|
|
}
|
|
|
|
|
2024-05-16 23:07:29 +02:00
|
|
|
// Now the position is at 8th, increase it again should give us an
|
2024-01-24 19:04:43 +01:00
|
|
|
// error.
|
|
|
|
increased, err := f.Increment()
|
|
|
|
rt.ErrorIs(err, ErrMaxPosition)
|
|
|
|
rt.False(increased)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestLinearFeeFunctionIncreaseFeeRate checks the internal state is updated
|
|
|
|
// correctly when the fee rate is increased using conf targets.
|
|
|
|
func TestLinearFeeFunctionIncreaseFeeRate(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
rt := require.New(t)
|
|
|
|
|
|
|
|
// Create a mock fee estimator.
|
|
|
|
estimator := &chainfee.MockEstimator{}
|
2024-05-16 23:07:29 +02:00
|
|
|
defer estimator.AssertExpectations(t)
|
2024-01-24 19:04:43 +01:00
|
|
|
|
|
|
|
// Create testing params. These params are chosen so the delta value is
|
|
|
|
// 100.
|
2024-05-16 23:07:29 +02:00
|
|
|
maxFeeRate := chainfee.SatPerKWeight(900)
|
2024-01-24 19:04:43 +01:00
|
|
|
estimatedFeeRate := chainfee.SatPerKWeight(100)
|
2024-05-16 23:07:29 +02:00
|
|
|
confTarget := uint32(9) // This means the width is 8.
|
2024-01-24 19:04:43 +01:00
|
|
|
|
|
|
|
// Mock the fee estimator to return the fee rate.
|
|
|
|
estimator.On("EstimateFeePerKW", confTarget).Return(
|
|
|
|
estimatedFeeRate, nil).Once()
|
|
|
|
estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
|
|
|
|
|
2024-04-11 11:08:36 +02:00
|
|
|
f, err := NewLinearFeeFunction(
|
|
|
|
maxFeeRate, confTarget, estimator,
|
|
|
|
fn.None[chainfee.SatPerKWeight](),
|
|
|
|
)
|
2024-01-24 19:04:43 +01:00
|
|
|
rt.NoError(err)
|
|
|
|
|
|
|
|
// If we are increasing the fee rate using the initial conf target, we
|
|
|
|
// should get a nil error and false.
|
|
|
|
increased, err := f.IncreaseFeeRate(confTarget)
|
|
|
|
rt.NoError(err)
|
|
|
|
rt.False(increased)
|
|
|
|
|
|
|
|
// Test that we are allowed to use a larger conf target.
|
|
|
|
increased, err = f.IncreaseFeeRate(confTarget + 1)
|
|
|
|
rt.NoError(err)
|
|
|
|
rt.False(increased)
|
|
|
|
|
2024-05-16 23:07:29 +02:00
|
|
|
// We now increase the fee rate from conf target 8 to 2 and assert we
|
2024-01-24 19:04:43 +01:00
|
|
|
// get no error and true.
|
2024-05-16 23:07:29 +02:00
|
|
|
for i := uint32(1); i < confTarget-1; i++ {
|
2024-01-24 19:04:43 +01:00
|
|
|
// Increase the fee rate.
|
|
|
|
increased, err := f.IncreaseFeeRate(confTarget - i)
|
|
|
|
rt.NoError(err)
|
|
|
|
rt.True(increased)
|
|
|
|
|
|
|
|
// Assert the internal state.
|
|
|
|
rt.Equal(i, f.position)
|
|
|
|
|
|
|
|
delta := chainfee.SatPerKWeight(i * 100)
|
|
|
|
rt.Equal(estimatedFeeRate+delta, f.currentFeeRate)
|
|
|
|
|
|
|
|
// Check public method returns the expected fee rate.
|
|
|
|
rt.Equal(estimatedFeeRate+delta, f.FeeRate())
|
|
|
|
}
|
2024-04-03 10:13:49 +02:00
|
|
|
|
2024-05-16 23:07:29 +02:00
|
|
|
// Test that when we use a conf target of 1, we get the ending fee
|
2024-04-03 10:13:49 +02:00
|
|
|
// rate.
|
2024-05-16 23:07:29 +02:00
|
|
|
increased, err = f.IncreaseFeeRate(1)
|
2024-04-03 10:13:49 +02:00
|
|
|
rt.NoError(err)
|
|
|
|
rt.True(increased)
|
2024-05-16 23:07:29 +02:00
|
|
|
rt.Equal(confTarget-1, f.position)
|
2024-04-03 10:13:49 +02:00
|
|
|
rt.Equal(maxFeeRate, f.currentFeeRate)
|
2024-05-16 23:07:29 +02:00
|
|
|
|
|
|
|
// Test that when we use a conf target of 0, ErrMaxPosition is
|
|
|
|
// returned.
|
|
|
|
increased, err = f.IncreaseFeeRate(0)
|
|
|
|
rt.ErrorIs(err, ErrMaxPosition)
|
|
|
|
rt.False(increased)
|
2024-01-24 19:04:43 +01:00
|
|
|
}
|