@@ -51,7 +51,7 @@ func (b *Batcher) greedyAddSweep(ctx context.Context, sweep *sweep) error {
51
51
// Run the algorithm. Get batchId of possible batches, sorted from best
52
52
// to worst.
53
53
batchesIds , err := selectBatches (
54
- batches , sweepFeeDetails , newBatchFeeDetails ,
54
+ batches , sweepFeeDetails , newBatchFeeDetails , b . mixedBatch ,
55
55
)
56
56
if err != nil {
57
57
return fmt .Errorf ("batch selection algorithm failed for sweep " +
@@ -125,10 +125,11 @@ func estimateSweepFeeIncrement(s *sweep) (feeDetails, feeDetails, error) {
125
125
// Create feeDetails for sweep.
126
126
sweepFeeDetails := feeDetails {
127
127
FeeRate : s .minFeeRate ,
128
- NonCoopHint : s .nonCoopHint ,
128
+ NonCoopHint : s .nonCoopHint || s . coopFailed ,
129
129
IsExternalAddr : s .isExternalAddr ,
130
130
131
131
// Calculate sweep weight as a difference.
132
+ MixedWeight : fd2 .MixedWeight - fd1 .MixedWeight ,
132
133
CoopWeight : fd2 .CoopWeight - fd1 .CoopWeight ,
133
134
NonCoopWeight : fd2 .NonCoopWeight - fd1 .NonCoopWeight ,
134
135
}
@@ -152,7 +153,7 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
152
153
// Find if the batch has at least one non-cooperative sweep.
153
154
hasNonCoop := false
154
155
for _ , sweep := range batch .sweeps {
155
- if sweep .nonCoopHint {
156
+ if sweep .nonCoopHint || sweep . coopFailed {
156
157
hasNonCoop = true
157
158
}
158
159
}
@@ -177,11 +178,16 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
177
178
destAddr = (* btcutil .AddressTaproot )(nil )
178
179
}
179
180
180
- // Make two estimators: for coop and non-coop cases.
181
- var coopWeight , nonCoopWeight input.TxWeightEstimator
181
+ // Make three estimators: for mixed, coop and non-coop cases.
182
+ var mixedWeight , coopWeight , nonCoopWeight input.TxWeightEstimator
182
183
183
184
// Add output weight to the estimator.
184
- err := sweeppkg .AddOutputEstimate (& coopWeight , destAddr )
185
+ err := sweeppkg .AddOutputEstimate (& mixedWeight , destAddr )
186
+ if err != nil {
187
+ return feeDetails {}, fmt .Errorf ("sweep.AddOutputEstimate: %w" ,
188
+ err )
189
+ }
190
+ err = sweeppkg .AddOutputEstimate (& coopWeight , destAddr )
185
191
if err != nil {
186
192
return feeDetails {}, fmt .Errorf ("sweep.AddOutputEstimate: %w" ,
187
193
err )
@@ -194,6 +200,19 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
194
200
195
201
// Add inputs.
196
202
for _ , sweep := range batch .sweeps {
203
+ if sweep .nonCoopHint || sweep .coopFailed {
204
+ err = sweep .htlcSuccessEstimator (& mixedWeight )
205
+ if err != nil {
206
+ return feeDetails {}, fmt .Errorf (
207
+ "htlcSuccessEstimator failed: %w" , err ,
208
+ )
209
+ }
210
+ } else {
211
+ mixedWeight .AddTaprootKeySpendInput (
212
+ txscript .SigHashDefault ,
213
+ )
214
+ }
215
+
197
216
coopWeight .AddTaprootKeySpendInput (txscript .SigHashDefault )
198
217
199
218
err = sweep .htlcSuccessEstimator (& nonCoopWeight )
@@ -206,6 +225,7 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
206
225
return feeDetails {
207
226
BatchId : batch .id ,
208
227
FeeRate : batch .rbfCache .FeeRate ,
228
+ MixedWeight : mixedWeight .Weight (),
209
229
CoopWeight : coopWeight .Weight (),
210
230
NonCoopWeight : nonCoopWeight .Weight (),
211
231
NonCoopHint : hasNonCoop ,
@@ -222,18 +242,22 @@ const newBatchSignal = -1
222
242
type feeDetails struct {
223
243
BatchId int32
224
244
FeeRate chainfee.SatPerKWeight
245
+ MixedWeight lntypes.WeightUnit
225
246
CoopWeight lntypes.WeightUnit
226
247
NonCoopWeight lntypes.WeightUnit
227
248
NonCoopHint bool
228
249
IsExternalAddr bool
229
250
}
230
251
231
252
// fee returns fee of onchain transaction representing this instance.
232
- func (e feeDetails ) fee () btcutil.Amount {
253
+ func (e feeDetails ) fee (mixedBatch bool ) btcutil.Amount {
233
254
var weight lntypes.WeightUnit
234
- if e .NonCoopHint {
255
+ switch {
256
+ case mixedBatch :
257
+ weight = e .MixedWeight
258
+ case e .NonCoopHint :
235
259
weight = e .NonCoopWeight
236
- } else {
260
+ default :
237
261
weight = e .CoopWeight
238
262
}
239
263
@@ -250,6 +274,7 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails {
250
274
251
275
return feeDetails {
252
276
FeeRate : feeRate ,
277
+ MixedWeight : e1 .MixedWeight + e2 .MixedWeight ,
253
278
CoopWeight : e1 .CoopWeight + e2 .CoopWeight ,
254
279
NonCoopWeight : e1 .NonCoopWeight + e2 .NonCoopWeight ,
255
280
NonCoopHint : e1 .NonCoopHint || e2 .NonCoopHint ,
@@ -259,21 +284,26 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails {
259
284
260
285
// selectBatches returns the list of id of batches sorted from best to worst.
261
286
// Creation a new batch is encoded as newBatchSignal. For each batch its fee
262
- // rate and two weights are provided: weight in case of cooperative spending and
263
- // weight in case non-cooperative spending (using preimages instead of taproot
264
- // key spend). Also, a hint is provided to signal if the batch has to use
265
- // non-cooperative spending path. The same data is also provided to the sweep
266
- // for which we are selecting a batch to add. In case of the sweep weights are
267
- // weight deltas resulted from adding the sweep. Finally, the same data is
268
- // provided for new batch having this sweep only. The algorithm compares costs
269
- // of adding the sweep to each existing batch, and costs of new batch creation
270
- // for this sweep and returns BatchId of the winning batch. If the best option
271
- // is to create a new batch, return newBatchSignal. Each fee details has also
272
- // IsExternalAddr flag. There is a rule that sweeps having flag IsExternalAddr
273
- // must go in individual batches. Cooperative spending is only available if all
274
- // the sweeps support cooperative spending path.
275
- func selectBatches (batches []feeDetails , sweep , oneSweepBatch feeDetails ) (
276
- []int32 , error ) {
287
+ // rate and a set of weights are provided: weight in case of a mixed batch,
288
+ // weight in case of cooperative spending and weight in case non-cooperative
289
+ // spending. Also, a hint is provided to signal what spending path will be used
290
+ // by the batch.
291
+ //
292
+ // The same data is also provided for the sweep for which we are selecting a
293
+ // batch to add. In case of the sweep weights are weight deltas resulted from
294
+ // adding the sweep. Finally, the same data is provided for new batch having
295
+ // this sweep only.
296
+ //
297
+ // The algorithm compares costs of adding the sweep to each existing batch, and
298
+ // costs of new batch creation for this sweep and returns BatchId of the winning
299
+ // batch. If the best option is to create a new batch, return newBatchSignal.
300
+ //
301
+ // Each fee details has also IsExternalAddr flag. There is a rule that sweeps
302
+ // having flag IsExternalAddr must go in individual batches. Cooperative
303
+ // spending is only available if all the sweeps support cooperative spending
304
+ // path of in a mixed batch.
305
+ func selectBatches (batches []feeDetails , sweep , oneSweepBatch feeDetails ,
306
+ mixedBatch bool ) ([]int32 , error ) {
277
307
278
308
// If the sweep has IsExternalAddr flag, the sweep can't be added to
279
309
// a batch, so create new batch for it.
@@ -294,7 +324,7 @@ func selectBatches(batches []feeDetails, sweep, oneSweepBatch feeDetails) (
294
324
// creation with this sweep only in it. The cost is its full fee.
295
325
alternatives = append (alternatives , alternative {
296
326
batchId : newBatchSignal ,
297
- cost : oneSweepBatch .fee (),
327
+ cost : oneSweepBatch .fee (mixedBatch ),
298
328
})
299
329
300
330
// Try to add the sweep to every batch, calculate the costs and
@@ -310,7 +340,7 @@ func selectBatches(batches []feeDetails, sweep, oneSweepBatch feeDetails) (
310
340
combinedBatch := batch .combine (sweep )
311
341
312
342
// The cost is the fee increase.
313
- cost := combinedBatch .fee () - batch .fee ()
343
+ cost := combinedBatch .fee (mixedBatch ) - batch .fee (mixedBatch )
314
344
315
345
// The cost must be positive, because we added a sweep.
316
346
if cost <= 0 {
0 commit comments