123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- package sweep
- import (
- "testing"
- "github.com/lightningnetwork/lnd/fn"
- "github.com/lightningnetwork/lnd/lnwallet/chainfee"
- "github.com/stretchr/testify/require"
- )
- // TestLinearFeeFunctionNew tests the NewLinearFeeFunction function.
- func TestLinearFeeFunctionNew(t *testing.T) {
- t.Parallel()
- rt := require.New(t)
- // Create a mock fee estimator.
- estimator := &chainfee.MockEstimator{}
- // 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)
- // Assert init fee function with zero conf value will end up using the
- // max fee rate.
- f, err := NewLinearFeeFunction(maxFeeRate, 0, 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)
- // When the fee estimator returns an error, it's returned.
- //
- // Mock the fee estimator to return an error.
- estimator.On("EstimateFeePerKW", confTarget).Return(
- chainfee.SatPerKWeight(0), errDummy).Once()
- f, err = NewLinearFeeFunction(
- maxFeeRate, confTarget, estimator, noStartFeeRate,
- )
- rt.ErrorIs(err, errDummy)
- rt.Nil(f)
- // When the starting feerate is greater than the ending feerate, the
- // starting feerate is capped.
- //
- // Mock the fee estimator to return the fee rate.
- smallConf := uint32(1)
- 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()
- f, err = NewLinearFeeFunction(
- maxFeeRate, smallConf, estimator, noStartFeeRate,
- )
- rt.NoError(err)
- rt.NotNil(f)
- // When the calculated fee rate delta is 0, an error should be returned.
- //
- // Mock the fee estimator to return the fee rate.
- estimator.On("EstimateFeePerKW", confTarget).Return(
- // The starting fee rate is the max fee rate.
- maxFeeRate, nil).Once()
- estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
- f, err = NewLinearFeeFunction(
- maxFeeRate, confTarget, estimator, noStartFeeRate,
- )
- rt.ErrorContains(err, "fee rate delta is zero")
- rt.Nil(f)
- // 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)
- f, err = NewLinearFeeFunction(
- maxFeeRate, largeConf, estimator, noStartFeeRate,
- )
- 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)
- rt.Equal(largeConf, f.width)
- // Check a successfully created fee function.
- //
- // Mock the fee estimator to return the fee rate.
- estimator.On("EstimateFeePerKW", confTarget).Return(
- estimatedFeeRate, nil).Once()
- estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
- f, err = NewLinearFeeFunction(
- maxFeeRate, confTarget, estimator, noStartFeeRate,
- )
- 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)
- rt.Equal(confTarget, f.width)
- // 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)
- rt.Equal(startFeeRate, f.currentFeeRate)
- }
- // 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,
- deltaFeeRate: 1_000_000,
- 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{}
- // Create testing params. These params are chosen so the delta value is
- // 100.
- maxFeeRate := chainfee.SatPerKWeight(1000)
- estimatedFeeRate := chainfee.SatPerKWeight(100)
- confTarget := uint32(9)
- // Mock the fee estimator to return the fee rate.
- estimator.On("EstimateFeePerKW", confTarget).Return(
- estimatedFeeRate, nil).Once()
- estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
- f, err := NewLinearFeeFunction(
- maxFeeRate, confTarget, estimator,
- fn.None[chainfee.SatPerKWeight](),
- )
- rt.NoError(err)
- // We now increase the position from 1 to 9.
- for i := uint32(1); i <= confTarget; i++ {
- // 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())
- }
- // Now the position is at 9th, increase it again should give us an
- // 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{}
- // Create testing params. These params are chosen so the delta value is
- // 100.
- maxFeeRate := chainfee.SatPerKWeight(1000)
- estimatedFeeRate := chainfee.SatPerKWeight(100)
- confTarget := uint32(9)
- // Mock the fee estimator to return the fee rate.
- estimator.On("EstimateFeePerKW", confTarget).Return(
- estimatedFeeRate, nil).Once()
- estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
- f, err := NewLinearFeeFunction(
- maxFeeRate, confTarget, estimator,
- fn.None[chainfee.SatPerKWeight](),
- )
- 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)
- // We now increase the fee rate from conf target 8 to 1 and assert we
- // get no error and true.
- for i := uint32(1); i < confTarget; i++ {
- // 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())
- }
- // Test that when we use a conf target of 0, we get the ending fee
- // rate.
- increased, err = f.IncreaseFeeRate(0)
- rt.NoError(err)
- rt.True(increased)
- rt.Equal(confTarget, f.position)
- rt.Equal(maxFeeRate, f.currentFeeRate)
- }
|