fee_function_test.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. package sweep
  2. import (
  3. "testing"
  4. "github.com/lightningnetwork/lnd/fn"
  5. "github.com/lightningnetwork/lnd/lnwallet/chainfee"
  6. "github.com/stretchr/testify/require"
  7. )
  8. // TestLinearFeeFunctionNew tests the NewLinearFeeFunction function.
  9. func TestLinearFeeFunctionNew(t *testing.T) {
  10. t.Parallel()
  11. rt := require.New(t)
  12. // Create a mock fee estimator.
  13. estimator := &chainfee.MockEstimator{}
  14. // Create testing params.
  15. maxFeeRate := chainfee.SatPerKWeight(10000)
  16. estimatedFeeRate := chainfee.SatPerKWeight(500)
  17. minRelayFeeRate := chainfee.SatPerKWeight(100)
  18. confTarget := uint32(6)
  19. noStartFeeRate := fn.None[chainfee.SatPerKWeight]()
  20. startFeeRate := chainfee.SatPerKWeight(1000)
  21. // Assert init fee function with zero conf value will end up using the
  22. // max fee rate.
  23. f, err := NewLinearFeeFunction(maxFeeRate, 0, estimator, noStartFeeRate)
  24. rt.NoError(err)
  25. rt.NotNil(f)
  26. // Assert the internal state.
  27. rt.Equal(maxFeeRate, f.startingFeeRate)
  28. rt.Equal(maxFeeRate, f.endingFeeRate)
  29. rt.Equal(maxFeeRate, f.currentFeeRate)
  30. // When the fee estimator returns an error, it's returned.
  31. //
  32. // Mock the fee estimator to return an error.
  33. estimator.On("EstimateFeePerKW", confTarget).Return(
  34. chainfee.SatPerKWeight(0), errDummy).Once()
  35. f, err = NewLinearFeeFunction(
  36. maxFeeRate, confTarget, estimator, noStartFeeRate,
  37. )
  38. rt.ErrorIs(err, errDummy)
  39. rt.Nil(f)
  40. // When the starting feerate is greater than the ending feerate, the
  41. // starting feerate is capped.
  42. //
  43. // Mock the fee estimator to return the fee rate.
  44. smallConf := uint32(1)
  45. estimator.On("EstimateFeePerKW", smallConf).Return(
  46. // The fee rate is greater than the max fee rate.
  47. maxFeeRate+1, nil).Once()
  48. estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
  49. f, err = NewLinearFeeFunction(
  50. maxFeeRate, smallConf, estimator, noStartFeeRate,
  51. )
  52. rt.NoError(err)
  53. rt.NotNil(f)
  54. // When the calculated fee rate delta is 0, an error should be returned.
  55. //
  56. // Mock the fee estimator to return the fee rate.
  57. estimator.On("EstimateFeePerKW", confTarget).Return(
  58. // The starting fee rate is the max fee rate.
  59. maxFeeRate, nil).Once()
  60. estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
  61. f, err = NewLinearFeeFunction(
  62. maxFeeRate, confTarget, estimator, noStartFeeRate,
  63. )
  64. rt.ErrorContains(err, "fee rate delta is zero")
  65. rt.Nil(f)
  66. // When the conf target is >= 1008, the min relay fee should be used.
  67. //
  68. // Mock the fee estimator to reutrn the fee rate.
  69. estimator.On("RelayFeePerKW").Return(minRelayFeeRate).Once()
  70. largeConf := uint32(1008)
  71. f, err = NewLinearFeeFunction(
  72. maxFeeRate, largeConf, estimator, noStartFeeRate,
  73. )
  74. rt.NoError(err)
  75. rt.NotNil(f)
  76. // Assert the internal state.
  77. rt.Equal(minRelayFeeRate, f.startingFeeRate)
  78. rt.Equal(maxFeeRate, f.endingFeeRate)
  79. rt.Equal(minRelayFeeRate, f.currentFeeRate)
  80. rt.NotZero(f.deltaFeeRate)
  81. rt.Equal(largeConf, f.width)
  82. // Check a successfully created fee function.
  83. //
  84. // Mock the fee estimator to return the fee rate.
  85. estimator.On("EstimateFeePerKW", confTarget).Return(
  86. estimatedFeeRate, nil).Once()
  87. estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
  88. f, err = NewLinearFeeFunction(
  89. maxFeeRate, confTarget, estimator, noStartFeeRate,
  90. )
  91. rt.NoError(err)
  92. rt.NotNil(f)
  93. // Assert the internal state.
  94. rt.Equal(estimatedFeeRate, f.startingFeeRate)
  95. rt.Equal(maxFeeRate, f.endingFeeRate)
  96. rt.Equal(estimatedFeeRate, f.currentFeeRate)
  97. rt.NotZero(f.deltaFeeRate)
  98. rt.Equal(confTarget, f.width)
  99. // Check a successfully created fee function using the specified
  100. // starting fee rate.
  101. //
  102. // NOTE: by NOT mocking the fee estimator, we assert the
  103. // estimateFeeRate is NOT called.
  104. f, err = NewLinearFeeFunction(
  105. maxFeeRate, confTarget, estimator, fn.Some(startFeeRate),
  106. )
  107. rt.NoError(err)
  108. rt.NotNil(f)
  109. // Assert the customized starting fee rate is used.
  110. rt.Equal(startFeeRate, f.startingFeeRate)
  111. rt.Equal(startFeeRate, f.currentFeeRate)
  112. }
  113. // TestLinearFeeFunctionFeeRateAtPosition checks the expected feerate is
  114. // calculated and returned.
  115. func TestLinearFeeFunctionFeeRateAtPosition(t *testing.T) {
  116. t.Parallel()
  117. rt := require.New(t)
  118. // Create a fee func which has three positions:
  119. // - position 0: 1000
  120. // - position 1: 2000
  121. // - position 2: 3000
  122. f := &LinearFeeFunction{
  123. startingFeeRate: 1000,
  124. endingFeeRate: 3000,
  125. position: 0,
  126. deltaFeeRate: 1_000_000,
  127. width: 3,
  128. }
  129. testCases := []struct {
  130. name string
  131. pos uint32
  132. expectedFeerate chainfee.SatPerKWeight
  133. }{
  134. {
  135. name: "position 0",
  136. pos: 0,
  137. expectedFeerate: 1000,
  138. },
  139. {
  140. name: "position 1",
  141. pos: 1,
  142. expectedFeerate: 2000,
  143. },
  144. {
  145. name: "position 2",
  146. pos: 2,
  147. expectedFeerate: 3000,
  148. },
  149. {
  150. name: "position 3",
  151. pos: 3,
  152. expectedFeerate: 3000,
  153. },
  154. }
  155. for _, tc := range testCases {
  156. tc := tc
  157. t.Run(tc.name, func(t *testing.T) {
  158. t.Parallel()
  159. result := f.feeRateAtPosition(tc.pos)
  160. rt.Equal(tc.expectedFeerate, result)
  161. })
  162. }
  163. }
  164. // TestLinearFeeFunctionIncrement checks the internal state is updated
  165. // correctly when the fee rate is incremented.
  166. func TestLinearFeeFunctionIncrement(t *testing.T) {
  167. t.Parallel()
  168. rt := require.New(t)
  169. // Create a mock fee estimator.
  170. estimator := &chainfee.MockEstimator{}
  171. // Create testing params. These params are chosen so the delta value is
  172. // 100.
  173. maxFeeRate := chainfee.SatPerKWeight(1000)
  174. estimatedFeeRate := chainfee.SatPerKWeight(100)
  175. confTarget := uint32(9)
  176. // Mock the fee estimator to return the fee rate.
  177. estimator.On("EstimateFeePerKW", confTarget).Return(
  178. estimatedFeeRate, nil).Once()
  179. estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
  180. f, err := NewLinearFeeFunction(
  181. maxFeeRate, confTarget, estimator,
  182. fn.None[chainfee.SatPerKWeight](),
  183. )
  184. rt.NoError(err)
  185. // We now increase the position from 1 to 9.
  186. for i := uint32(1); i <= confTarget; i++ {
  187. // Increase the fee rate.
  188. increased, err := f.Increment()
  189. rt.NoError(err)
  190. rt.True(increased)
  191. // Assert the internal state.
  192. rt.Equal(i, f.position)
  193. delta := chainfee.SatPerKWeight(i * 100)
  194. rt.Equal(estimatedFeeRate+delta, f.currentFeeRate)
  195. // Check public method returns the expected fee rate.
  196. rt.Equal(estimatedFeeRate+delta, f.FeeRate())
  197. }
  198. // Now the position is at 9th, increase it again should give us an
  199. // error.
  200. increased, err := f.Increment()
  201. rt.ErrorIs(err, ErrMaxPosition)
  202. rt.False(increased)
  203. }
  204. // TestLinearFeeFunctionIncreaseFeeRate checks the internal state is updated
  205. // correctly when the fee rate is increased using conf targets.
  206. func TestLinearFeeFunctionIncreaseFeeRate(t *testing.T) {
  207. t.Parallel()
  208. rt := require.New(t)
  209. // Create a mock fee estimator.
  210. estimator := &chainfee.MockEstimator{}
  211. // Create testing params. These params are chosen so the delta value is
  212. // 100.
  213. maxFeeRate := chainfee.SatPerKWeight(1000)
  214. estimatedFeeRate := chainfee.SatPerKWeight(100)
  215. confTarget := uint32(9)
  216. // Mock the fee estimator to return the fee rate.
  217. estimator.On("EstimateFeePerKW", confTarget).Return(
  218. estimatedFeeRate, nil).Once()
  219. estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once()
  220. f, err := NewLinearFeeFunction(
  221. maxFeeRate, confTarget, estimator,
  222. fn.None[chainfee.SatPerKWeight](),
  223. )
  224. rt.NoError(err)
  225. // If we are increasing the fee rate using the initial conf target, we
  226. // should get a nil error and false.
  227. increased, err := f.IncreaseFeeRate(confTarget)
  228. rt.NoError(err)
  229. rt.False(increased)
  230. // Test that we are allowed to use a larger conf target.
  231. increased, err = f.IncreaseFeeRate(confTarget + 1)
  232. rt.NoError(err)
  233. rt.False(increased)
  234. // We now increase the fee rate from conf target 8 to 1 and assert we
  235. // get no error and true.
  236. for i := uint32(1); i < confTarget; i++ {
  237. // Increase the fee rate.
  238. increased, err := f.IncreaseFeeRate(confTarget - i)
  239. rt.NoError(err)
  240. rt.True(increased)
  241. // Assert the internal state.
  242. rt.Equal(i, f.position)
  243. delta := chainfee.SatPerKWeight(i * 100)
  244. rt.Equal(estimatedFeeRate+delta, f.currentFeeRate)
  245. // Check public method returns the expected fee rate.
  246. rt.Equal(estimatedFeeRate+delta, f.FeeRate())
  247. }
  248. // Test that when we use a conf target of 0, we get the ending fee
  249. // rate.
  250. increased, err = f.IncreaseFeeRate(0)
  251. rt.NoError(err)
  252. rt.True(increased)
  253. rt.Equal(confTarget, f.position)
  254. rt.Equal(maxFeeRate, f.currentFeeRate)
  255. }