@@ -246,6 +246,73 @@ util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utx
246
246
return max_tx_weight_exceeded ? ErrorMaxWeightExceeded () : util::Error ();
247
247
}
248
248
249
+ util::Result<SelectionResult> SelectCoinsGG (std::vector<OutputGroup>& utxo_pool, CAmount target_value, CAmount change_fee, FastRandomContext& rng,
250
+ int max_weight)
251
+ {
252
+ SelectionResult result (target_value, SelectionAlgorithm::GG);
253
+ std::priority_queue<OutputGroup, std::vector<OutputGroup>, MinOutputGroupComparator> heap;
254
+
255
+ // Include change for Gutter Guard Selector as we want to avoid making really small change if the selection just
256
+ // barely meets the target.
257
+ target_value += CHANGE_LOWER + change_fee;
258
+
259
+ // Use largest-first selection to determine minimum count of necessary output groups
260
+ std::sort (utxo_pool.begin (), utxo_pool.end (), descending);
261
+ CAmount selected_lf_amount = 0 ;
262
+ size_t lf_group_count = 0 ;
263
+
264
+ for (const OutputGroup& group : utxo_pool) {
265
+ selected_lf_amount += group.GetSelectionAmount ();
266
+ lf_group_count++;
267
+ if (selected_lf_amount >= target_value) {
268
+ break ;
269
+ }
270
+ }
271
+
272
+ // Gutter Guard selection
273
+ size_t gg_group_limit = lf_group_count + 3 ;
274
+
275
+ std::vector<size_t > indexes;
276
+ indexes.resize (utxo_pool.size ());
277
+ std::iota (indexes.begin (), indexes.end (), 0 );
278
+ Shuffle (indexes.begin (), indexes.end (), rng);
279
+
280
+ CAmount selected_eff_value = 0 ;
281
+ int weight = 0 ;
282
+ bool max_tx_weight_exceeded = false ;
283
+ for (const size_t i : indexes) {
284
+ // Select random additional group
285
+ const OutputGroup& group = utxo_pool.at (i);
286
+ Assume (group.GetSelectionAmount () > 0 );
287
+ heap.push (group);
288
+ selected_eff_value += group.GetSelectionAmount ();
289
+ weight += group.m_weight ;
290
+
291
+ if (weight > max_weight) {
292
+ // Store error in case no useful result is found
293
+ max_tx_weight_exceeded = true ;
294
+ }
295
+
296
+ while (!heap.empty () && (weight > max_weight || heap.size () > gg_group_limit)) {
297
+ // Drop output group with lowest effective value
298
+ const OutputGroup& to_remove_group = heap.top ();
299
+ selected_eff_value -= to_remove_group.GetSelectionAmount ();
300
+ weight -= to_remove_group.m_weight ;
301
+ heap.pop ();
302
+ }
303
+
304
+ if (selected_eff_value >= target_value) {
305
+ // Success, return heap content
306
+ while (!heap.empty ()) {
307
+ result.AddInput (heap.top ());
308
+ heap.pop ();
309
+ }
310
+ return result;
311
+ }
312
+ }
313
+ return max_tx_weight_exceeded ? ErrorMaxWeightExceeded () : util::Error ();
314
+ }
315
+
249
316
/* * Find a subset of the OutputGroups that is at least as large as, but as close as possible to, the
250
317
* target amount; solve subset sum.
251
318
* param@[in] groups OutputGroups to choose from, sorted by value in descending order.
@@ -602,6 +669,7 @@ std::string GetAlgorithmName(const SelectionAlgorithm algo)
602
669
case SelectionAlgorithm::BNB: return " bnb" ;
603
670
case SelectionAlgorithm::KNAPSACK: return " knapsack" ;
604
671
case SelectionAlgorithm::SRD: return " srd" ;
672
+ case SelectionAlgorithm::GG: return " gg" ;
605
673
case SelectionAlgorithm::MANUAL: return " manual" ;
606
674
// No default case to allow for compiler to warn
607
675
}
0 commit comments