From 3d26a70a05f2e95d693589fcea4f7024131e0518 Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Mon, 29 Jul 2024 23:36:50 -0300 Subject: [PATCH] sweepbatcher: make greedy algo mixed batch aware --- sweepbatcher/greedy_batch_selection.go | 78 +++++++---- sweepbatcher/greedy_batch_selection_test.go | 148 +++++++++++++++++++- 2 files changed, 199 insertions(+), 27 deletions(-) diff --git a/sweepbatcher/greedy_batch_selection.go b/sweepbatcher/greedy_batch_selection.go index b2f0e88bb..78a2a5dc6 100644 --- a/sweepbatcher/greedy_batch_selection.go +++ b/sweepbatcher/greedy_batch_selection.go @@ -51,7 +51,7 @@ func (b *Batcher) greedyAddSweep(ctx context.Context, sweep *sweep) error { // Run the algorithm. Get batchId of possible batches, sorted from best // to worst. batchesIds, err := selectBatches( - batches, sweepFeeDetails, newBatchFeeDetails, + batches, sweepFeeDetails, newBatchFeeDetails, b.mixedBatch, ) if err != nil { return fmt.Errorf("batch selection algorithm failed for sweep "+ @@ -129,6 +129,7 @@ func estimateSweepFeeIncrement(s *sweep) (feeDetails, feeDetails, error) { IsExternalAddr: s.isExternalAddr, // Calculate sweep weight as a difference. + MixedWeight: fd2.MixedWeight - fd1.MixedWeight, CoopWeight: fd2.CoopWeight - fd1.CoopWeight, NonCoopWeight: fd2.NonCoopWeight - fd1.NonCoopWeight, } @@ -177,11 +178,16 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) { destAddr = (*btcutil.AddressTaproot)(nil) } - // Make two estimators: for coop and non-coop cases. - var coopWeight, nonCoopWeight input.TxWeightEstimator + // Make three estimators: for mixed, coop and non-coop cases. + var mixedWeight, coopWeight, nonCoopWeight input.TxWeightEstimator // Add output weight to the estimator. - err := sweeppkg.AddOutputEstimate(&coopWeight, destAddr) + err := sweeppkg.AddOutputEstimate(&mixedWeight, destAddr) + if err != nil { + return feeDetails{}, fmt.Errorf("sweep.AddOutputEstimate: %w", + err) + } + err = sweeppkg.AddOutputEstimate(&coopWeight, destAddr) if err != nil { return feeDetails{}, fmt.Errorf("sweep.AddOutputEstimate: %w", err) @@ -194,6 +200,19 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) { // Add inputs. for _, sweep := range batch.sweeps { + if sweep.nonCoopHint || sweep.coopFailed { + err = sweep.htlcSuccessEstimator(&mixedWeight) + if err != nil { + return feeDetails{}, fmt.Errorf( + "htlcSuccessEstimator failed: %w", err, + ) + } + } else { + mixedWeight.AddTaprootKeySpendInput( + txscript.SigHashDefault, + ) + } + coopWeight.AddTaprootKeySpendInput(txscript.SigHashDefault) err = sweep.htlcSuccessEstimator(&nonCoopWeight) @@ -206,6 +225,7 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) { return feeDetails{ BatchId: batch.id, FeeRate: batch.rbfCache.FeeRate, + MixedWeight: mixedWeight.Weight(), CoopWeight: coopWeight.Weight(), NonCoopWeight: nonCoopWeight.Weight(), NonCoopHint: hasNonCoop, @@ -222,6 +242,7 @@ const newBatchSignal = -1 type feeDetails struct { BatchId int32 FeeRate chainfee.SatPerKWeight + MixedWeight lntypes.WeightUnit CoopWeight lntypes.WeightUnit NonCoopWeight lntypes.WeightUnit NonCoopHint bool @@ -229,11 +250,14 @@ type feeDetails struct { } // fee returns fee of onchain transaction representing this instance. -func (e feeDetails) fee() btcutil.Amount { +func (e feeDetails) fee(mixedBatch bool) btcutil.Amount { var weight lntypes.WeightUnit - if e.NonCoopHint { + switch { + case mixedBatch: + weight = e.MixedWeight + case e.NonCoopHint: weight = e.NonCoopWeight - } else { + default: weight = e.CoopWeight } @@ -250,6 +274,7 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails { return feeDetails{ FeeRate: feeRate, + MixedWeight: e1.MixedWeight + e2.MixedWeight, CoopWeight: e1.CoopWeight + e2.CoopWeight, NonCoopWeight: e1.NonCoopWeight + e2.NonCoopWeight, NonCoopHint: e1.NonCoopHint || e2.NonCoopHint, @@ -259,21 +284,26 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails { // selectBatches returns the list of id of batches sorted from best to worst. // Creation a new batch is encoded as newBatchSignal. For each batch its fee -// rate and two weights are provided: weight in case of cooperative spending and -// weight in case non-cooperative spending (using preimages instead of taproot -// key spend). Also, a hint is provided to signal if the batch has to use -// non-cooperative spending path. The same data is also provided to the sweep -// for which we are selecting a batch to add. In case of the sweep weights are -// weight deltas resulted from adding the sweep. Finally, the same data is -// provided for new batch having this sweep only. The algorithm compares costs -// of adding the sweep to each existing batch, and costs of new batch creation -// for this sweep and returns BatchId of the winning batch. If the best option -// is to create a new batch, return newBatchSignal. Each fee details has also -// IsExternalAddr flag. There is a rule that sweeps having flag IsExternalAddr -// must go in individual batches. Cooperative spending is only available if all -// the sweeps support cooperative spending path. -func selectBatches(batches []feeDetails, sweep, oneSweepBatch feeDetails) ( - []int32, error) { +// rate and a set of weights are provided: weight in case of a mixed batch, +// weight in case of cooperative spending and weight in case non-cooperative +// spending. Also, a hint is provided to signal what spending path will be used +// by the batch. +// +// The same data is also provided for the sweep for which we are selecting a +// batch to add. In case of the sweep weights are weight deltas resulted from +// adding the sweep. Finally, the same data is provided for new batch having +// this sweep only. +// +// The algorithm compares costs of adding the sweep to each existing batch, and +// costs of new batch creation for this sweep and returns BatchId of the winning +// batch. If the best option is to create a new batch, return newBatchSignal. +// +// Each fee details has also IsExternalAddr flag. There is a rule that sweeps +// having flag IsExternalAddr must go in individual batches. Cooperative +// spending is only available if all the sweeps support cooperative spending +// path of in a mixed batch. +func selectBatches(batches []feeDetails, sweep, oneSweepBatch feeDetails, + mixedBatch bool) ([]int32, error) { // If the sweep has IsExternalAddr flag, the sweep can't be added to // a batch, so create new batch for it. @@ -294,7 +324,7 @@ func selectBatches(batches []feeDetails, sweep, oneSweepBatch feeDetails) ( // creation with this sweep only in it. The cost is its full fee. alternatives = append(alternatives, alternative{ batchId: newBatchSignal, - cost: oneSweepBatch.fee(), + cost: oneSweepBatch.fee(mixedBatch), }) // Try to add the sweep to every batch, calculate the costs and @@ -310,7 +340,7 @@ func selectBatches(batches []feeDetails, sweep, oneSweepBatch feeDetails) ( combinedBatch := batch.combine(sweep) // The cost is the fee increase. - cost := combinedBatch.fee() - batch.fee() + cost := combinedBatch.fee(mixedBatch) - batch.fee(mixedBatch) // The cost must be positive, because we added a sweep. if cost <= 0 { diff --git a/sweepbatcher/greedy_batch_selection_test.go b/sweepbatcher/greedy_batch_selection_test.go index 6be9bd9af..44a0ec40a 100644 --- a/sweepbatcher/greedy_batch_selection_test.go +++ b/sweepbatcher/greedy_batch_selection_test.go @@ -29,9 +29,9 @@ const ( ) * 4 coopTwoSweepBatchWeight = coopNewBatchWeight + coopInputWeight - nonCoopTwoSweepBatchWeight = coopTwoSweepBatchWeight + - 2*nonCoopPenalty - v2v3BatchWeight = nonCoopTwoSweepBatchWeight - 25 + nonCoopTwoSweepBatchWeight = coopTwoSweepBatchWeight + 2*nonCoopPenalty + v2v3BatchWeight = nonCoopTwoSweepBatchWeight - 25 + mixedTwoSweepBatchWeight = coopTwoSweepBatchWeight + nonCoopPenalty ) // testHtlcV2SuccessEstimator adds weight of non-cooperative input to estimator @@ -86,11 +86,13 @@ func TestEstimateSweepFeeIncrement(t *testing.T) { }, wantSweepFeeDetails: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, }, wantNewBatchFeeDetails: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, @@ -104,11 +106,13 @@ func TestEstimateSweepFeeIncrement(t *testing.T) { }, wantSweepFeeDetails: feeDetails{ FeeRate: highFeeRate, + MixedWeight: coopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, }, wantNewBatchFeeDetails: feeDetails{ FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, @@ -124,12 +128,14 @@ func TestEstimateSweepFeeIncrement(t *testing.T) { }, wantSweepFeeDetails: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, IsExternalAddr: true, }, wantNewBatchFeeDetails: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, IsExternalAddr: true, @@ -146,12 +152,15 @@ func TestEstimateSweepFeeIncrement(t *testing.T) { }, wantSweepFeeDetails: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, IsExternalAddr: true, }, wantNewBatchFeeDetails: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight - + p2pkhDiscount, CoopWeight: coopNewBatchWeight - p2pkhDiscount, NonCoopWeight: nonCoopNewBatchWeight - @@ -169,12 +178,14 @@ func TestEstimateSweepFeeIncrement(t *testing.T) { }, wantSweepFeeDetails: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: nonCoopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, NonCoopHint: true, }, wantNewBatchFeeDetails: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: nonCoopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, NonCoopHint: true, @@ -190,12 +201,14 @@ func TestEstimateSweepFeeIncrement(t *testing.T) { }, wantSweepFeeDetails: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: nonCoopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, NonCoopHint: true, }, wantNewBatchFeeDetails: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: nonCoopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, NonCoopHint: true, @@ -251,6 +264,7 @@ func TestEstimateBatchWeight(t *testing.T) { wantBatchFeeDetails: feeDetails{ BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, @@ -275,6 +289,7 @@ func TestEstimateBatchWeight(t *testing.T) { wantBatchFeeDetails: feeDetails{ BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: coopTwoSweepBatchWeight, CoopWeight: coopTwoSweepBatchWeight, NonCoopWeight: nonCoopTwoSweepBatchWeight, }, @@ -299,6 +314,7 @@ func TestEstimateBatchWeight(t *testing.T) { wantBatchFeeDetails: feeDetails{ BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: coopTwoSweepBatchWeight, CoopWeight: coopTwoSweepBatchWeight, NonCoopWeight: v2v3BatchWeight, }, @@ -320,6 +336,7 @@ func TestEstimateBatchWeight(t *testing.T) { wantBatchFeeDetails: feeDetails{ BatchId: 1, FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, @@ -345,6 +362,7 @@ func TestEstimateBatchWeight(t *testing.T) { wantBatchFeeDetails: feeDetails{ BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: mixedTwoSweepBatchWeight, CoopWeight: coopTwoSweepBatchWeight, NonCoopWeight: nonCoopTwoSweepBatchWeight, NonCoopHint: true, @@ -371,6 +389,7 @@ func TestEstimateBatchWeight(t *testing.T) { wantBatchFeeDetails: feeDetails{ BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: mixedTwoSweepBatchWeight, CoopWeight: coopTwoSweepBatchWeight, NonCoopWeight: nonCoopTwoSweepBatchWeight, NonCoopHint: true, @@ -395,6 +414,7 @@ func TestEstimateBatchWeight(t *testing.T) { wantBatchFeeDetails: feeDetails{ BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, IsExternalAddr: true, @@ -420,6 +440,7 @@ func TestSelectBatches(t *testing.T) { name string batches []feeDetails sweep, oneSweepBatch feeDetails + mixedBatch bool wantBestBatchesIds []int32 }{ { @@ -427,11 +448,13 @@ func TestSelectBatches(t *testing.T) { batches: []feeDetails{}, sweep: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, }, oneSweepBatch: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, @@ -444,17 +467,20 @@ func TestSelectBatches(t *testing.T) { { BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, }, sweep: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, }, oneSweepBatch: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, @@ -467,17 +493,20 @@ func TestSelectBatches(t *testing.T) { { BatchId: 1, FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, }, sweep: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, }, oneSweepBatch: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, @@ -490,23 +519,27 @@ func TestSelectBatches(t *testing.T) { { BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, { BatchId: 2, FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, }, sweep: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, }, oneSweepBatch: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, @@ -519,23 +552,27 @@ func TestSelectBatches(t *testing.T) { { BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, { BatchId: 2, FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, }, sweep: feeDetails{ FeeRate: highFeeRate, + MixedWeight: coopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, }, oneSweepBatch: feeDetails{ FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, @@ -548,24 +585,28 @@ func TestSelectBatches(t *testing.T) { { BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, { BatchId: 2, FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, }, sweep: feeDetails{ FeeRate: highFeeRate, + MixedWeight: nonCoopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, NonCoopHint: true, }, oneSweepBatch: feeDetails{ FeeRate: highFeeRate, + MixedWeight: nonCoopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, NonCoopHint: true, @@ -579,24 +620,28 @@ func TestSelectBatches(t *testing.T) { { BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: 10000, CoopWeight: 10000, NonCoopWeight: 15000, }, { BatchId: 2, FeeRate: highFeeRate, + MixedWeight: 10000, CoopWeight: 10000, NonCoopWeight: 15000, }, }, sweep: feeDetails{ FeeRate: highFeeRate, + MixedWeight: nonCoopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, NonCoopHint: true, }, oneSweepBatch: feeDetails{ FeeRate: highFeeRate, + MixedWeight: nonCoopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, NonCoopHint: true, @@ -604,18 +649,56 @@ func TestSelectBatches(t *testing.T) { wantBestBatchesIds: []int32{newBatchSignal, 2, 1}, }, + { + name: "high fee noncoop sweep, large batches, mixed", + batches: []feeDetails{ + { + BatchId: 1, + FeeRate: lowFeeRate, + MixedWeight: 10000, + CoopWeight: 10000, + NonCoopWeight: 15000, + }, + { + BatchId: 2, + FeeRate: highFeeRate, + MixedWeight: 10000, + CoopWeight: 10000, + NonCoopWeight: 15000, + }, + }, + sweep: feeDetails{ + FeeRate: highFeeRate, + MixedWeight: nonCoopInputWeight, + CoopWeight: coopInputWeight, + NonCoopWeight: nonCoopInputWeight, + NonCoopHint: true, + }, + oneSweepBatch: feeDetails{ + FeeRate: highFeeRate, + MixedWeight: nonCoopNewBatchWeight, + CoopWeight: coopNewBatchWeight, + NonCoopWeight: nonCoopNewBatchWeight, + NonCoopHint: true, + }, + mixedBatch: true, + wantBestBatchesIds: []int32{2, newBatchSignal, 1}, + }, + { name: "high fee noncoop sweep, high batch noncoop", batches: []feeDetails{ { BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, { BatchId: 2, FeeRate: highFeeRate, + MixedWeight: nonCoopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, NonCoopHint: true, @@ -623,12 +706,14 @@ func TestSelectBatches(t *testing.T) { }, sweep: feeDetails{ FeeRate: highFeeRate, + MixedWeight: nonCoopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, NonCoopHint: true, }, oneSweepBatch: feeDetails{ FeeRate: highFeeRate, + MixedWeight: nonCoopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, NonCoopHint: true, @@ -642,24 +727,28 @@ func TestSelectBatches(t *testing.T) { { BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, { BatchId: 2, FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, }, sweep: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: nonCoopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, NonCoopHint: true, }, oneSweepBatch: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: nonCoopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, NonCoopHint: true, @@ -673,24 +762,28 @@ func TestSelectBatches(t *testing.T) { { BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: 10000, CoopWeight: 10000, NonCoopWeight: 15000, }, { BatchId: 2, FeeRate: highFeeRate, + MixedWeight: 10000, CoopWeight: 10000, NonCoopWeight: 15000, }, }, sweep: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: nonCoopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, NonCoopHint: true, }, oneSweepBatch: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: nonCoopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, NonCoopHint: true, @@ -698,12 +791,49 @@ func TestSelectBatches(t *testing.T) { wantBestBatchesIds: []int32{newBatchSignal, 1, 2}, }, + { + name: "low fee noncoop sweep, large batches, mixed", + batches: []feeDetails{ + { + BatchId: 1, + FeeRate: lowFeeRate, + MixedWeight: 10000, + CoopWeight: 10000, + NonCoopWeight: 15000, + }, + { + BatchId: 2, + FeeRate: highFeeRate, + MixedWeight: 10000, + CoopWeight: 10000, + NonCoopWeight: 15000, + }, + }, + sweep: feeDetails{ + FeeRate: lowFeeRate, + MixedWeight: nonCoopInputWeight, + CoopWeight: coopInputWeight, + NonCoopWeight: nonCoopInputWeight, + NonCoopHint: true, + }, + oneSweepBatch: feeDetails{ + FeeRate: lowFeeRate, + MixedWeight: nonCoopNewBatchWeight, + CoopWeight: coopNewBatchWeight, + NonCoopWeight: nonCoopNewBatchWeight, + NonCoopHint: true, + }, + mixedBatch: true, + wantBestBatchesIds: []int32{1, newBatchSignal, 2}, + }, + { name: "low fee noncoop sweep, low batch noncoop", batches: []feeDetails{ { BatchId: 1, FeeRate: lowFeeRate, + MixedWeight: nonCoopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, NonCoopHint: true, @@ -711,18 +841,21 @@ func TestSelectBatches(t *testing.T) { { BatchId: 2, FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, }, sweep: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: nonCoopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, NonCoopHint: true, }, oneSweepBatch: feeDetails{ FeeRate: lowFeeRate, + MixedWeight: nonCoopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, NonCoopHint: true, @@ -736,24 +869,28 @@ func TestSelectBatches(t *testing.T) { { BatchId: 1, FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, { BatchId: 2, FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, }, sweep: feeDetails{ FeeRate: highFeeRate, + MixedWeight: coopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, IsExternalAddr: true, }, oneSweepBatch: feeDetails{ FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, IsExternalAddr: true, @@ -767,12 +904,14 @@ func TestSelectBatches(t *testing.T) { { BatchId: 1, FeeRate: highFeeRate - 1, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, { BatchId: 2, FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, IsExternalAddr: true, @@ -780,11 +919,13 @@ func TestSelectBatches(t *testing.T) { }, sweep: feeDetails{ FeeRate: highFeeRate, + MixedWeight: coopInputWeight, CoopWeight: coopInputWeight, NonCoopWeight: nonCoopInputWeight, }, oneSweepBatch: feeDetails{ FeeRate: highFeeRate, + MixedWeight: coopNewBatchWeight, CoopWeight: coopNewBatchWeight, NonCoopWeight: nonCoopNewBatchWeight, }, @@ -797,6 +938,7 @@ func TestSelectBatches(t *testing.T) { t.Run(tc.name, func(t *testing.T) { gotBestBatchesIds, err := selectBatches( tc.batches, tc.sweep, tc.oneSweepBatch, + tc.mixedBatch, ) require.NoError(t, err) require.Equal(