• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 13536249039

26 Feb 2025 03:42AM UTC coverage: 57.462% (-1.4%) from 58.835%
13536249039

Pull #8453

github

Roasbeef
peer: update chooseDeliveryScript to gen script if needed

In this commit, we update `chooseDeliveryScript` to generate a new
script if needed. This allows us to fold in a few other lines that
always followed this function into this expanded function.

The tests have been updated accordingly.
Pull Request #8453: [4/4] - multi: integrate new rbf coop close FSM into the existing peer flow

275 of 1318 new or added lines in 22 files covered. (20.86%)

19521 existing lines in 257 files now uncovered.

103858 of 180741 relevant lines covered (57.46%)

24750.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.56
/sweep/fee_function.go
1
package sweep
2

3
import (
4
        "errors"
5
        "fmt"
6

7
        "github.com/btcsuite/btcd/btcutil"
8
        "github.com/lightningnetwork/lnd/fn/v2"
9
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
10
        "github.com/lightningnetwork/lnd/lnwire"
11
)
12

13
var (
14
        // ErrMaxPosition is returned when trying to increase the position of
15
        // the fee function while it's already at its max.
16
        ErrMaxPosition = errors.New("position already at max")
17

18
        // ErrZeroFeeRateDelta is returned when the fee rate delta is zero.
19
        ErrZeroFeeRateDelta = errors.New("fee rate delta is zero")
20
)
21

22
// mSatPerKWeight represents a fee rate in msat/kw.
23
//
24
// TODO(yy): unify all the units to be virtual bytes.
25
type mSatPerKWeight lnwire.MilliSatoshi
26

27
// String returns a human-readable string of the fee rate.
UNCOV
28
func (m mSatPerKWeight) String() string {
×
UNCOV
29
        s := lnwire.MilliSatoshi(m)
×
UNCOV
30
        return fmt.Sprintf("%v/kw", s)
×
UNCOV
31
}
×
32

33
// FeeFunction defines an interface that is used to calculate fee rates for
34
// transactions. It's expected the implementations use three params, the
35
// starting fee rate, the ending fee rate, and number of blocks till deadline
36
// block height, to build an algorithm to calculate the fee rate based on the
37
// current block height.
38
type FeeFunction interface {
39
        // FeeRate returns the current fee rate calculated by the fee function.
40
        FeeRate() chainfee.SatPerKWeight
41

42
        // Increment increases the fee rate by one step. The definition of one
43
        // step is up to the implementation. After calling this method, it's
44
        // expected to change the state of the fee function such that calling
45
        // `FeeRate` again will return the increased value.
46
        //
47
        // It returns a boolean to indicate whether the fee rate is increased,
48
        // as fee bump should not be attempted if the increased fee rate is not
49
        // greater than the current fee rate, which may happen if the algorithm
50
        // gives the same fee rates at two positions.
51
        //
52
        // An error is returned when the max fee rate is reached.
53
        //
54
        // NOTE: we intentionally don't return the new fee rate here, so both
55
        // the implementation and the caller are aware of the state change.
56
        Increment() (bool, error)
57

58
        // IncreaseFeeRate increases the fee rate to the new position
59
        // calculated using (width - confTarget). It returns a boolean to
60
        // indicate whether the fee rate is increased, and an error if the
61
        // position is greater than the width.
62
        //
63
        // NOTE: this method is provided to allow the caller to increase the
64
        // fee rate based on a conf target without taking care of the fee
65
        // function's current state (position).
66
        IncreaseFeeRate(confTarget uint32) (bool, error)
67
}
68

69
// LinearFeeFunction implements the FeeFunction interface with a linear
70
// function:
71
//
72
//        feeRate = startingFeeRate + position * delta.
73
//             - width: deadlineBlockHeight - startingBlockHeight
74
//             - delta: (endingFeeRate - startingFeeRate) / width
75
//             - position: currentBlockHeight - startingBlockHeight
76
//
77
// The fee rate will be capped at endingFeeRate.
78
//
79
// TODO(yy): implement more functions specified here:
80
// - https://github.com/lightningnetwork/lnd/issues/4215
81
type LinearFeeFunction struct {
82
        // startingFeeRate specifies the initial fee rate to begin with.
83
        startingFeeRate chainfee.SatPerKWeight
84

85
        // endingFeeRate specifies the max allowed fee rate.
86
        endingFeeRate chainfee.SatPerKWeight
87

88
        // currentFeeRate specifies the current calculated fee rate.
89
        currentFeeRate chainfee.SatPerKWeight
90

91
        // width is the number of blocks between the starting block height
92
        // and the deadline block height minus one.
93
        //
94
        // NOTE: We do minus one from the conf target here because we want to
95
        // max out the budget before the deadline height is reached.
96
        width uint32
97

98
        // position is the fee function's current position, given a width of w,
99
        // a valid position should lie in range [0, w].
100
        position uint32
101

102
        // deltaFeeRate is the fee rate (msat/kw) increase per block.
103
        //
104
        // NOTE: this is used to increase precision.
105
        deltaFeeRate mSatPerKWeight
106

107
        // estimator is the fee estimator used to estimate the fee rate. We use
108
        // it to get the initial fee rate and, use it as a benchmark to decide
109
        // whether we want to used the estimated fee rate or the calculated fee
110
        // rate based on different strategies.
111
        estimator chainfee.Estimator
112
}
113

114
// Compile-time check to ensure LinearFeeFunction satisfies the FeeFunction.
115
var _ FeeFunction = (*LinearFeeFunction)(nil)
116

117
// NewLinearFeeFunction creates a new linear fee function and initializes it
118
// with a starting fee rate which is an estimated value returned from the fee
119
// estimator using the initial conf target.
120
func NewLinearFeeFunction(maxFeeRate chainfee.SatPerKWeight,
121
        confTarget uint32, estimator chainfee.Estimator,
122
        startingFeeRate fn.Option[chainfee.SatPerKWeight]) (
123
        *LinearFeeFunction, error) {
18✔
124

18✔
125
        // If the deadline is one block away or has already been reached,
18✔
126
        // there's nothing the fee function can do. In this case, we'll use the
18✔
127
        // max fee rate immediately.
18✔
128
        if confTarget <= 1 {
22✔
129
                return &LinearFeeFunction{
4✔
130
                        startingFeeRate: maxFeeRate,
4✔
131
                        endingFeeRate:   maxFeeRate,
4✔
132
                        currentFeeRate:  maxFeeRate,
4✔
133
                }, nil
4✔
134
        }
4✔
135

136
        l := &LinearFeeFunction{
14✔
137
                endingFeeRate: maxFeeRate,
14✔
138
                width:         confTarget - 1,
14✔
139
                estimator:     estimator,
14✔
140
        }
14✔
141

14✔
142
        // If the caller specifies the starting fee rate, we'll use it instead
14✔
143
        // of estimating it based on the deadline.
14✔
144
        start, err := startingFeeRate.UnwrapOrFuncErr(
14✔
145
                func() (chainfee.SatPerKWeight, error) {
27✔
146
                        // Estimate the initial fee rate.
13✔
147
                        //
13✔
148
                        // NOTE: estimateFeeRate guarantees the returned fee
13✔
149
                        // rate is capped by the ending fee rate, so we don't
13✔
150
                        // need to worry about overpay.
13✔
151
                        return l.estimateFeeRate(confTarget)
13✔
152
                })
13✔
153
        if err != nil {
17✔
154
                return nil, fmt.Errorf("estimate initial fee rate: %w", err)
3✔
155
        }
3✔
156

157
        // Calculate how much fee rate should be increased per block.
158
        end := l.endingFeeRate
11✔
159

11✔
160
        // The starting and ending fee rates are in sat/kw, so we need to
11✔
161
        // convert them to msat/kw by multiplying by 1000.
11✔
162
        delta := btcutil.Amount(end - start).MulF64(1000 / float64(l.width))
11✔
163
        l.deltaFeeRate = mSatPerKWeight(delta)
11✔
164

11✔
165
        // We only allow the delta to be zero if the width is one - when the
11✔
166
        // delta is zero, it means the starting and ending fee rates are the
11✔
167
        // same, which means there's nothing to increase, so any width greater
11✔
168
        // than 1 doesn't provide any utility. This could happen when the
11✔
169
        // budget is too small.
11✔
170
        if l.deltaFeeRate == 0 && l.width != 1 {
12✔
171
                log.Errorf("Failed to init fee function: startingFeeRate=%v, "+
1✔
172
                        "endingFeeRate=%v, width=%v, delta=%v", start, end,
1✔
173
                        l.width, l.deltaFeeRate)
1✔
174

1✔
175
                return nil, ErrZeroFeeRateDelta
1✔
176
        }
1✔
177

178
        // Attach the calculated values to the fee function.
179
        l.startingFeeRate = start
10✔
180
        l.currentFeeRate = start
10✔
181

10✔
182
        log.Debugf("Linear fee function initialized with startingFeeRate=%v, "+
10✔
183
                "endingFeeRate=%v, width=%v, delta=%v", start, end,
10✔
184
                l.width, l.deltaFeeRate)
10✔
185

10✔
186
        return l, nil
10✔
187
}
188

189
// FeeRate returns the current fee rate.
190
//
191
// NOTE: part of the FeeFunction interface.
192
func (l *LinearFeeFunction) FeeRate() chainfee.SatPerKWeight {
27✔
193
        return l.currentFeeRate
27✔
194
}
27✔
195

196
// Increment increases the fee rate by one position, returns a boolean to
197
// indicate whether the fee rate was increased, and an error if the position is
198
// greater than the width. The increased fee rate will be set as the current
199
// fee rate, and the internal position will be incremented.
200
//
201
// NOTE: this method will change the state of the fee function as it increases
202
// its current fee rate.
203
//
204
// NOTE: part of the FeeFunction interface.
205
func (l *LinearFeeFunction) Increment() (bool, error) {
10✔
206
        return l.increaseFeeRate(l.position + 1)
10✔
207
}
10✔
208

209
// IncreaseFeeRate calculate a new position using the given conf target, and
210
// increases the fee rate to the new position by calling the Increment method.
211
//
212
// NOTE: this method will change the state of the fee function as it increases
213
// its current fee rate.
214
//
215
// NOTE: part of the FeeFunction interface.
216
func (l *LinearFeeFunction) IncreaseFeeRate(confTarget uint32) (bool, error) {
11✔
217
        newPosition := uint32(0)
11✔
218

11✔
219
        // Only calculate the new position when the conf target is less than
11✔
220
        // the function's width - the width is the initial conf target-1, and
11✔
221
        // we expect the current conf target to decrease over time. However, we
11✔
222
        // still allow the supplied conf target to be greater than the width,
11✔
223
        // and we won't increase the fee rate in that case.
11✔
224
        if confTarget < l.width+1 {
20✔
225
                newPosition = l.width + 1 - confTarget
9✔
226
                log.Tracef("Increasing position from %v to %v", l.position,
9✔
227
                        newPosition)
9✔
228
        }
9✔
229

230
        if newPosition <= l.position {
13✔
231
                log.Tracef("Skipped increase feerate: position=%v, "+
2✔
232
                        "newPosition=%v ", l.position, newPosition)
2✔
233

2✔
234
                return false, nil
2✔
235
        }
2✔
236

237
        return l.increaseFeeRate(newPosition)
9✔
238
}
239

240
// increaseFeeRate increases the fee rate by the specified position, returns a
241
// boolean to indicate whether the fee rate was increased, and an error if the
242
// position is greater than the width. The increased fee rate will be set as
243
// the current fee rate, and the internal position will be set to the specified
244
// position.
245
//
246
// NOTE: this method will change the state of the fee function as it increases
247
// its current fee rate.
248
func (l *LinearFeeFunction) increaseFeeRate(position uint32) (bool, error) {
19✔
249
        // If the new position is already at the end, we return an error.
19✔
250
        if l.position >= l.width {
22✔
251
                return false, ErrMaxPosition
3✔
252
        }
3✔
253

254
        // Get the old fee rate.
255
        oldFeeRate := l.currentFeeRate
16✔
256

16✔
257
        // Update its internal state.
16✔
258
        l.position = position
16✔
259
        l.currentFeeRate = l.feeRateAtPosition(position)
16✔
260

16✔
261
        log.Tracef("Fee rate increased from %v to %v at position %v",
16✔
262
                oldFeeRate, l.currentFeeRate, l.position)
16✔
263

16✔
264
        return l.currentFeeRate > oldFeeRate, nil
16✔
265
}
266

267
// feeRateAtPosition calculates the fee rate at a given position and caps it at
268
// the ending fee rate.
269
func (l *LinearFeeFunction) feeRateAtPosition(p uint32) chainfee.SatPerKWeight {
20✔
270
        if p >= l.width {
23✔
271
                return l.endingFeeRate
3✔
272
        }
3✔
273

274
        // deltaFeeRate is in msat/kw, so we need to divide by 1000 to get the
275
        // fee rate in sat/kw.
276
        feeRateDelta := btcutil.Amount(l.deltaFeeRate).MulF64(float64(p) / 1000)
17✔
277

17✔
278
        feeRate := l.startingFeeRate + chainfee.SatPerKWeight(feeRateDelta)
17✔
279
        if feeRate > l.endingFeeRate {
17✔
280
                return l.endingFeeRate
×
281
        }
×
282

283
        return feeRate
17✔
284
}
285

286
// estimateFeeRate asks the fee estimator to estimate the fee rate based on its
287
// conf target.
288
func (l *LinearFeeFunction) estimateFeeRate(
289
        confTarget uint32) (chainfee.SatPerKWeight, error) {
13✔
290

13✔
291
        fee := FeeEstimateInfo{
13✔
292
                ConfTarget: confTarget,
13✔
293
        }
13✔
294

13✔
295
        // If the conf target is greater or equal to the max allowed value
13✔
296
        // (1008), we will use the min relay fee instead.
13✔
297
        if confTarget >= chainfee.MaxBlockTarget {
14✔
298
                minFeeRate := l.estimator.RelayFeePerKW()
1✔
299
                log.Infof("Conf target %v is greater than max block target, "+
1✔
300
                        "using min relay fee rate %v", confTarget, minFeeRate)
1✔
301

1✔
302
                return minFeeRate, nil
1✔
303
        }
1✔
304

305
        // endingFeeRate comes from budget/txWeight, which means the returned
306
        // fee rate will always be capped by this value, hence we don't need to
307
        // worry about overpay.
308
        estimatedFeeRate, err := fee.Estimate(l.estimator, l.endingFeeRate)
12✔
309
        if err != nil {
15✔
310
                return 0, err
3✔
311
        }
3✔
312

313
        return estimatedFeeRate, nil
9✔
314
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc