@@ -15,28 +15,34 @@ import (
15
15
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
16
16
)
17
17
18
- // greedyAddSweep selects a batch for the sweep using the greedy algorithm,
19
- // which minimizes costs, and adds the sweep to the batch. To accomplish this,
20
- // it first collects fee details about the sweep being added, about a potential
21
- // new batch composed of this sweep only, and about all existing batches. It
18
+ // greedyAddSweeps selects a batch for the sweeps using the greedy algorithm,
19
+ // which minimizes costs, and adds the sweeps to the batch. To accomplish this,
20
+ // it first collects fee details about the sweeps being added, about a potential
21
+ // new batch composed of these sweeps only, and about all existing batches. It
22
22
// skips batches with at least MaxSweepsPerBatch swaps to keep tx standard. Then
23
23
// it passes the data to selectBatches() function, which emulates adding the
24
- // sweep to each batch and creating new batch for the sweep , and calculates the
24
+ // sweep to each batch and creating new batch for the sweeps , and calculates the
25
25
// costs of each alternative. Based on the estimates of selectBatches(), this
26
- // method adds the sweep to the batch that results in the least overall fee
27
- // increase, or creates new batch for it. If the sweep is not accepted by an
26
+ // method adds the sweeps to the batch that results in the least overall fee
27
+ // increase, or creates new batch for it. If the sweeps are not accepted by an
28
28
// existing batch (may happen because of too distant timeouts), next batch is
29
29
// tried in the list returned by selectBatches(). If adding fails or new batch
30
30
// creation fails, this method returns an error. If this method fails for any
31
31
// reason, the caller falls back to the simple algorithm (method handleSweep).
32
- func (b * Batcher ) greedyAddSweep (ctx context.Context , sweep * sweep ) error {
32
+ func (b * Batcher ) greedyAddSweeps (ctx context.Context , sweeps []* sweep ) error {
33
+ if len (sweeps ) == 0 {
34
+ return fmt .Errorf ("trying to greedy add an empty sweeps group" )
35
+ }
36
+
37
+ swap := sweeps [0 ].swapHash
38
+
33
39
// Collect weight and fee rate info about the sweep and new batch.
34
40
sweepFeeDetails , newBatchFeeDetails , err := estimateSweepFeeIncrement (
35
- sweep ,
41
+ sweeps ,
36
42
)
37
43
if err != nil {
38
44
return fmt .Errorf ("failed to estimate tx weight for " +
39
- "sweep %x: %w" , sweep . swapHash [:6 ], err )
45
+ "sweep %x: %w" , swap [:6 ], err )
40
46
}
41
47
42
48
// Collect weight and fee rate info about existing batches.
@@ -64,54 +70,65 @@ func (b *Batcher) greedyAddSweep(ctx context.Context, sweep *sweep) error {
64
70
)
65
71
if err != nil {
66
72
return fmt .Errorf ("batch selection algorithm failed for sweep " +
67
- "%x: %w" , sweep . swapHash [:6 ], err )
73
+ "%x: %w" , swap [:6 ], err )
68
74
}
69
75
70
76
// Try batches, starting with the best.
71
77
for _ , batchId := range batchesIds {
72
78
// If the best option is to start new batch, do it.
73
79
if batchId == newBatchSignal {
74
- return b .spinUpNewBatch (ctx , sweep )
80
+ return b .spinUpNewBatch (ctx , sweeps )
75
81
}
76
82
77
- // Locate the batch to add the sweep to.
83
+ // Locate the batch to add the sweeps to.
78
84
bestBatch , has := b .batches [batchId ]
79
85
if ! has {
80
86
return fmt .Errorf ("batch selection algorithm returned " +
81
87
"batch id %d which doesn't exist, for sweep %x" ,
82
- batchId , sweep . swapHash [:6 ])
88
+ batchId , swap [:6 ])
83
89
}
84
90
85
- // Add the sweep to the batch.
86
- accepted , err := bestBatch .addSweep (ctx , sweep )
91
+ // Add the sweeps to the batch.
92
+ accepted , err := bestBatch .addSweeps (ctx , sweeps )
87
93
if err != nil {
88
94
return fmt .Errorf ("batch selection algorithm returned " +
89
95
"batch id %d for sweep %x, but adding failed: " +
90
- "%w" , batchId , sweep . swapHash [:6 ], err )
96
+ "%w" , batchId , swap [:6 ], err )
91
97
}
92
98
if accepted {
93
99
return nil
94
100
}
95
101
96
102
debugf ("Batch selection algorithm returned batch id %d " +
97
103
"for sweep %x, but acceptance failed." , batchId ,
98
- sweep . swapHash [:6 ])
104
+ swap [:6 ])
99
105
}
100
106
101
- return fmt .Errorf ("no batch accepted sweep %x" , sweep . swapHash [:6 ])
107
+ return fmt .Errorf ("no batch accepted sweep group %x" , swap [:6 ])
102
108
}
103
109
104
- // estimateSweepFeeIncrement returns fee details for adding the sweep to a batch
105
- // and for creating new batch with this sweep only.
106
- func estimateSweepFeeIncrement (s * sweep ) (feeDetails , feeDetails , error ) {
107
- // Create a fake batch with this sweep.
110
+ // estimateSweepFeeIncrement returns fee details for adding the sweeps to
111
+ // a batch and for creating new batch with these sweeps only.
112
+ func estimateSweepFeeIncrement (
113
+ sweeps []* sweep ) (feeDetails , feeDetails , error ) {
114
+
115
+ if len (sweeps ) == 0 {
116
+ return feeDetails {}, feeDetails {}, fmt .Errorf ("estimating an " +
117
+ "empty group of sweeps" )
118
+ }
119
+
120
+ // Create a fake batch with the sweeps.
108
121
batch := & batch {
109
122
rbfCache : rbfCache {
110
- FeeRate : s .minFeeRate ,
111
- },
112
- sweeps : map [wire.OutPoint ]sweep {
113
- s .outpoint : * s ,
123
+ FeeRate : sweeps [0 ].minFeeRate ,
114
124
},
125
+ sweeps : make (map [wire.OutPoint ]sweep , len (sweeps )),
126
+ }
127
+ for _ , s := range sweeps {
128
+ batch .sweeps [s .outpoint ] = * s
129
+ batch .rbfCache .FeeRate = max (
130
+ batch .rbfCache .FeeRate , s .minFeeRate ,
131
+ )
115
132
}
116
133
117
134
// Estimate new batch.
@@ -120,14 +137,17 @@ func estimateSweepFeeIncrement(s *sweep) (feeDetails, feeDetails, error) {
120
137
return feeDetails {}, feeDetails {}, err
121
138
}
122
139
123
- // Add the same sweep again to measure weight increments.
124
- outpoint2 := s .outpoint
125
- outpoint2 .Hash [0 ]++
126
- if _ , has := batch .sweeps [outpoint2 ]; has {
127
- return feeDetails {}, feeDetails {}, fmt .Errorf ("dummy outpoint " +
128
- "%s is present in the batch" , outpoint2 )
140
+ // Add the same sweeps again with different outpoints to measure weight
141
+ // increments.
142
+ for _ , s := range sweeps {
143
+ dummy := s .outpoint
144
+ dummy .Hash [0 ]++
145
+ if _ , has := batch .sweeps [dummy ]; has {
146
+ return feeDetails {}, feeDetails {}, fmt .Errorf ("dummy " +
147
+ "outpoint %s is present in the batch" , dummy )
148
+ }
149
+ batch .sweeps [dummy ] = * s
129
150
}
130
- batch .sweeps [outpoint2 ] = * s
131
151
132
152
// Estimate weight of a batch with two sweeps.
133
153
fd2 , err := estimateBatchWeight (batch )
@@ -137,8 +157,8 @@ func estimateSweepFeeIncrement(s *sweep) (feeDetails, feeDetails, error) {
137
157
138
158
// Create feeDetails for sweep.
139
159
sweepFeeDetails := feeDetails {
140
- FeeRate : s . minFeeRate ,
141
- IsExternalAddr : s .isExternalAddr ,
160
+ FeeRate : batch . rbfCache . FeeRate ,
161
+ IsExternalAddr : sweeps [ 0 ] .isExternalAddr ,
142
162
143
163
// Calculate sweep weight as a difference.
144
164
Weight : fd2 .Weight - fd1 .Weight ,
@@ -252,10 +272,10 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails {
252
272
// rate and a weight is provided. Also, a hint is provided to signal which
253
273
// spending path will be used by the batch.
254
274
//
255
- // The same data is also provided for the sweep for which we are selecting a
256
- // batch to add. In case of the sweep weights are weight deltas resulted from
257
- // adding the sweep. Finally, the same data is provided for new batch having
258
- // this sweep only.
275
+ // The same data is also provided for the sweep (or sweeps) for which we are
276
+ // selecting a batch to add. In case of the sweep weights are weight deltas
277
+ // resulted from adding the sweep. Finally, the same data is provided for new
278
+ // batch having this sweep(s) only.
259
279
//
260
280
// The algorithm compares costs of adding the sweep to each existing batch, and
261
281
// costs of new batch creation for this sweep and returns BatchId of the winning
@@ -265,11 +285,11 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails {
265
285
// having flag IsExternalAddr must go in individual batches. Cooperative
266
286
// spending may only be available for some sweeps supporting it, not for all.
267
287
func selectBatches (batches []feeDetails ,
268
- sweep , oneSweepBatch feeDetails ) ([]int32 , error ) {
288
+ added , newBatch feeDetails ) ([]int32 , error ) {
269
289
270
290
// If the sweep has IsExternalAddr flag, the sweep can't be added to
271
291
// a batch, so create new batch for it.
272
- if sweep .IsExternalAddr {
292
+ if added .IsExternalAddr {
273
293
return []int32 {newBatchSignal }, nil
274
294
}
275
295
@@ -286,7 +306,7 @@ func selectBatches(batches []feeDetails,
286
306
// creation with this sweep only in it. The cost is its full fee.
287
307
alternatives = append (alternatives , alternative {
288
308
batchId : newBatchSignal ,
289
- cost : oneSweepBatch .fee (),
309
+ cost : newBatch .fee (),
290
310
})
291
311
292
312
// Try to add the sweep to every batch, calculate the costs and
@@ -299,7 +319,7 @@ func selectBatches(batches []feeDetails,
299
319
}
300
320
301
321
// Add the sweep to the batch virtually.
302
- combinedBatch := batch .combine (sweep )
322
+ combinedBatch := batch .combine (added )
303
323
304
324
// The cost is the fee increase.
305
325
cost := combinedBatch .fee () - batch .fee ()
0 commit comments