@@ -51,7 +51,7 @@ func (b *Batcher) greedyAddSweep(ctx context.Context, sweep *sweep) error {
5151 // Run the algorithm. Get batchId of possible batches, sorted from best
5252 // to worst.
5353 batchesIds , err := selectBatches (
54- batches , sweepFeeDetails , newBatchFeeDetails ,
54+ batches , sweepFeeDetails , newBatchFeeDetails , b . mixedBatch ,
5555 )
5656 if err != nil {
5757 return fmt .Errorf ("batch selection algorithm failed for sweep " +
@@ -125,10 +125,11 @@ func estimateSweepFeeIncrement(s *sweep) (feeDetails, feeDetails, error) {
125125 // Create feeDetails for sweep.
126126 sweepFeeDetails := feeDetails {
127127 FeeRate : s .minFeeRate ,
128- NonCoopHint : s .nonCoopHint ,
128+ NonCoopHint : s .nonCoopHint || s . coopFailed ,
129129 IsExternalAddr : s .isExternalAddr ,
130130
131131 // Calculate sweep weight as a difference.
132+ MixedWeight : fd2 .MixedWeight - fd1 .MixedWeight ,
132133 CoopWeight : fd2 .CoopWeight - fd1 .CoopWeight ,
133134 NonCoopWeight : fd2 .NonCoopWeight - fd1 .NonCoopWeight ,
134135 }
@@ -152,7 +153,7 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
152153 // Find if the batch has at least one non-cooperative sweep.
153154 hasNonCoop := false
154155 for _ , sweep := range batch .sweeps {
155- if sweep .nonCoopHint {
156+ if sweep .nonCoopHint || sweep . coopFailed {
156157 hasNonCoop = true
157158 }
158159 }
@@ -177,11 +178,16 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
177178 destAddr = (* btcutil .AddressTaproot )(nil )
178179 }
179180
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
182183
183184 // 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 )
185191 if err != nil {
186192 return feeDetails {}, fmt .Errorf ("sweep.AddOutputEstimate: %w" ,
187193 err )
@@ -194,6 +200,19 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
194200
195201 // Add inputs.
196202 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+
197216 coopWeight .AddTaprootKeySpendInput (txscript .SigHashDefault )
198217
199218 err = sweep .htlcSuccessEstimator (& nonCoopWeight )
@@ -206,6 +225,7 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
206225 return feeDetails {
207226 BatchId : batch .id ,
208227 FeeRate : batch .rbfCache .FeeRate ,
228+ MixedWeight : mixedWeight .Weight (),
209229 CoopWeight : coopWeight .Weight (),
210230 NonCoopWeight : nonCoopWeight .Weight (),
211231 NonCoopHint : hasNonCoop ,
@@ -222,18 +242,22 @@ const newBatchSignal = -1
222242type feeDetails struct {
223243 BatchId int32
224244 FeeRate chainfee.SatPerKWeight
245+ MixedWeight lntypes.WeightUnit
225246 CoopWeight lntypes.WeightUnit
226247 NonCoopWeight lntypes.WeightUnit
227248 NonCoopHint bool
228249 IsExternalAddr bool
229250}
230251
231252// 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 {
233254 var weight lntypes.WeightUnit
234- if e .NonCoopHint {
255+ switch {
256+ case mixedBatch :
257+ weight = e .MixedWeight
258+ case e .NonCoopHint :
235259 weight = e .NonCoopWeight
236- } else {
260+ default :
237261 weight = e .CoopWeight
238262 }
239263
@@ -250,6 +274,7 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails {
250274
251275 return feeDetails {
252276 FeeRate : feeRate ,
277+ MixedWeight : e1 .MixedWeight + e2 .MixedWeight ,
253278 CoopWeight : e1 .CoopWeight + e2 .CoopWeight ,
254279 NonCoopWeight : e1 .NonCoopWeight + e2 .NonCoopWeight ,
255280 NonCoopHint : e1 .NonCoopHint || e2 .NonCoopHint ,
@@ -259,21 +284,26 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails {
259284
260285// selectBatches returns the list of id of batches sorted from best to worst.
261286// 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 ) {
277307
278308 // If the sweep has IsExternalAddr flag, the sweep can't be added to
279309 // a batch, so create new batch for it.
@@ -294,7 +324,7 @@ func selectBatches(batches []feeDetails, sweep, oneSweepBatch feeDetails) (
294324 // creation with this sweep only in it. The cost is its full fee.
295325 alternatives = append (alternatives , alternative {
296326 batchId : newBatchSignal ,
297- cost : oneSweepBatch .fee (),
327+ cost : oneSweepBatch .fee (mixedBatch ),
298328 })
299329
300330 // Try to add the sweep to every batch, calculate the costs and
@@ -310,7 +340,7 @@ func selectBatches(batches []feeDetails, sweep, oneSweepBatch feeDetails) (
310340 combinedBatch := batch .combine (sweep )
311341
312342 // The cost is the fee increase.
313- cost := combinedBatch .fee () - batch .fee ()
343+ cost := combinedBatch .fee (mixedBatch ) - batch .fee (mixedBatch )
314344
315345 // The cost must be positive, because we added a sweep.
316346 if cost <= 0 {
0 commit comments