@@ -15,28 +15,34 @@ import (
1515 "github.com/lightningnetwork/lnd/lnwallet/chainfee"
1616)
1717
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
2222// skips batches with at least MaxSweepsPerBatch swaps to keep tx standard. Then
2323// 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
2525// 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
2828// existing batch (may happen because of too distant timeouts), next batch is
2929// tried in the list returned by selectBatches(). If adding fails or new batch
3030// creation fails, this method returns an error. If this method fails for any
3131// 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+
3339 // Collect weight and fee rate info about the sweep and new batch.
3440 sweepFeeDetails , newBatchFeeDetails , err := estimateSweepFeeIncrement (
35- sweep ,
41+ sweeps ,
3642 )
3743 if err != nil {
3844 return fmt .Errorf ("failed to estimate tx weight for " +
39- "sweep %x: %w" , sweep . swapHash [:6 ], err )
45+ "sweep %x: %w" , swap [:6 ], err )
4046 }
4147
4248 // Collect weight and fee rate info about existing batches.
@@ -64,54 +70,65 @@ func (b *Batcher) greedyAddSweep(ctx context.Context, sweep *sweep) error {
6470 )
6571 if err != nil {
6672 return fmt .Errorf ("batch selection algorithm failed for sweep " +
67- "%x: %w" , sweep . swapHash [:6 ], err )
73+ "%x: %w" , swap [:6 ], err )
6874 }
6975
7076 // Try batches, starting with the best.
7177 for _ , batchId := range batchesIds {
7278 // If the best option is to start new batch, do it.
7379 if batchId == newBatchSignal {
74- return b .spinUpNewBatch (ctx , sweep )
80+ return b .spinUpNewBatch (ctx , sweeps )
7581 }
7682
77- // Locate the batch to add the sweep to.
83+ // Locate the batch to add the sweeps to.
7884 bestBatch , has := b .batches [batchId ]
7985 if ! has {
8086 return fmt .Errorf ("batch selection algorithm returned " +
8187 "batch id %d which doesn't exist, for sweep %x" ,
82- batchId , sweep . swapHash [:6 ])
88+ batchId , swap [:6 ])
8389 }
8490
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 )
8793 if err != nil {
8894 return fmt .Errorf ("batch selection algorithm returned " +
8995 "batch id %d for sweep %x, but adding failed: " +
90- "%w" , batchId , sweep . swapHash [:6 ], err )
96+ "%w" , batchId , swap [:6 ], err )
9197 }
9298 if accepted {
9399 return nil
94100 }
95101
96102 debugf ("Batch selection algorithm returned batch id %d " +
97103 "for sweep %x, but acceptance failed." , batchId ,
98- sweep . swapHash [:6 ])
104+ swap [:6 ])
99105 }
100106
101- return fmt .Errorf ("no batch accepted sweep %x" , sweep . swapHash [:6 ])
107+ return fmt .Errorf ("no batch accepted sweep group %x" , swap [:6 ])
102108}
103109
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.
108121 batch := & batch {
109122 rbfCache : rbfCache {
110- FeeRate : s .minFeeRate ,
111- },
112- sweeps : map [wire.OutPoint ]sweep {
113- s .outpoint : * s ,
123+ FeeRate : sweeps [0 ].minFeeRate ,
114124 },
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+ )
115132 }
116133
117134 // Estimate new batch.
@@ -120,14 +137,17 @@ func estimateSweepFeeIncrement(s *sweep) (feeDetails, feeDetails, error) {
120137 return feeDetails {}, feeDetails {}, err
121138 }
122139
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
129150 }
130- batch .sweeps [outpoint2 ] = * s
131151
132152 // Estimate weight of a batch with two sweeps.
133153 fd2 , err := estimateBatchWeight (batch )
@@ -137,8 +157,8 @@ func estimateSweepFeeIncrement(s *sweep) (feeDetails, feeDetails, error) {
137157
138158 // Create feeDetails for sweep.
139159 sweepFeeDetails := feeDetails {
140- FeeRate : s . minFeeRate ,
141- IsExternalAddr : s .isExternalAddr ,
160+ FeeRate : batch . rbfCache . FeeRate ,
161+ IsExternalAddr : sweeps [ 0 ] .isExternalAddr ,
142162
143163 // Calculate sweep weight as a difference.
144164 Weight : fd2 .Weight - fd1 .Weight ,
@@ -252,10 +272,10 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails {
252272// rate and a weight is provided. Also, a hint is provided to signal which
253273// spending path will be used by the batch.
254274//
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.
259279//
260280// The algorithm compares costs of adding the sweep to each existing batch, and
261281// 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 {
265285// having flag IsExternalAddr must go in individual batches. Cooperative
266286// spending may only be available for some sweeps supporting it, not for all.
267287func selectBatches (batches []feeDetails ,
268- sweep , oneSweepBatch feeDetails ) ([]int32 , error ) {
288+ added , newBatch feeDetails ) ([]int32 , error ) {
269289
270290 // If the sweep has IsExternalAddr flag, the sweep can't be added to
271291 // a batch, so create new batch for it.
272- if sweep .IsExternalAddr {
292+ if added .IsExternalAddr {
273293 return []int32 {newBatchSignal }, nil
274294 }
275295
@@ -286,7 +306,7 @@ func selectBatches(batches []feeDetails,
286306 // creation with this sweep only in it. The cost is its full fee.
287307 alternatives = append (alternatives , alternative {
288308 batchId : newBatchSignal ,
289- cost : oneSweepBatch .fee (),
309+ cost : newBatch .fee (),
290310 })
291311
292312 // Try to add the sweep to every batch, calculate the costs and
@@ -299,7 +319,7 @@ func selectBatches(batches []feeDetails,
299319 }
300320
301321 // Add the sweep to the batch virtually.
302- combinedBatch := batch .combine (sweep )
322+ combinedBatch := batch .combine (added )
303323
304324 // The cost is the fee increase.
305325 cost := combinedBatch .fee () - batch .fee ()
0 commit comments