@@ -56,17 +56,14 @@ struct {
56
56
* bound of the range.
57
57
* @param const CAmount& cost_of_change This is the cost of creating and spending a change output.
58
58
* This plus selection_target is the upper bound of the range.
59
- * @param std::set<CInputCoin>& out_set -> This is an output parameter for the set of CInputCoins
60
- * that have been selected.
61
- * @param CAmount& value_ret -> This is an output parameter for the total value of the CInputCoins
62
- * that were selected.
59
+ * @returns The result of this coin selection algorithm, or std::nullopt
63
60
*/
64
61
65
62
static const size_t TOTAL_TRIES = 100000 ;
66
63
67
- bool SelectCoinsBnB (std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret )
64
+ std::optional<SelectionResult> SelectCoinsBnB (std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change)
68
65
{
69
- out_set. clear ( );
66
+ SelectionResult result (selection_target );
70
67
CAmount curr_value = 0 ;
71
68
72
69
std::vector<bool > curr_selection; // select the utxo at this index
@@ -80,7 +77,7 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selectio
80
77
curr_available_value += utxo.GetSelectionAmount ();
81
78
}
82
79
if (curr_available_value < selection_target) {
83
- return false ;
80
+ return std::nullopt ;
84
81
}
85
82
86
83
// Sort the utxo_pool
@@ -156,25 +153,22 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selectio
156
153
157
154
// Check for solution
158
155
if (best_selection.empty ()) {
159
- return false ;
156
+ return std::nullopt ;
160
157
}
161
158
162
159
// Set output set
163
- value_ret = 0 ;
164
160
for (size_t i = 0 ; i < best_selection.size (); ++i) {
165
161
if (best_selection.at (i)) {
166
- util::insert (out_set, utxo_pool.at (i).m_outputs );
167
- value_ret += utxo_pool.at (i).m_value ;
162
+ result.AddInput (utxo_pool.at (i));
168
163
}
169
164
}
170
165
171
- return true ;
166
+ return result ;
172
167
}
173
168
174
- std::optional<std::pair<std::set<CInputCoin>, CAmount> > SelectCoinsSRD (const std::vector<OutputGroup>& utxo_pool, CAmount target_value)
169
+ std::optional<SelectionResult > SelectCoinsSRD (const std::vector<OutputGroup>& utxo_pool, CAmount target_value)
175
170
{
176
- std::set<CInputCoin> out_set;
177
- CAmount value_ret = 0 ;
171
+ SelectionResult result (target_value);
178
172
179
173
std::vector<size_t > indexes;
180
174
indexes.resize (utxo_pool.size ());
@@ -186,10 +180,9 @@ std::optional<std::pair<std::set<CInputCoin>, CAmount>> SelectCoinsSRD(const std
186
180
const OutputGroup& group = utxo_pool.at (i);
187
181
Assume (group.GetSelectionAmount () > 0 );
188
182
selected_eff_value += group.GetSelectionAmount ();
189
- value_ret += group.m_value ;
190
- util::insert (out_set, group.m_outputs );
183
+ result.AddInput (group);
191
184
if (selected_eff_value >= target_value) {
192
- return std::make_pair (out_set, value_ret) ;
185
+ return result ;
193
186
}
194
187
}
195
188
return std::nullopt;
@@ -241,10 +234,9 @@ static void ApproximateBestSubset(const std::vector<OutputGroup>& groups, const
241
234
}
242
235
}
243
236
244
- bool KnapsackSolver (const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet )
237
+ std::optional<SelectionResult> KnapsackSolver (std::vector<OutputGroup>& groups, const CAmount& nTargetValue )
245
238
{
246
- setCoinsRet.clear ();
247
- nValueRet = 0 ;
239
+ SelectionResult result (nTargetValue);
248
240
249
241
// List of values less than target
250
242
std::optional<OutputGroup> lowest_larger;
@@ -255,9 +247,8 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
255
247
256
248
for (const OutputGroup& group : groups) {
257
249
if (group.GetSelectionAmount () == nTargetValue) {
258
- util::insert (setCoinsRet, group.m_outputs );
259
- nValueRet += group.m_value ;
260
- return true ;
250
+ result.AddInput (group);
251
+ return result;
261
252
} else if (group.GetSelectionAmount () < nTargetValue + MIN_CHANGE) {
262
253
applicable_groups.push_back (group);
263
254
nTotalLower += group.GetSelectionAmount ();
@@ -268,17 +259,15 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
268
259
269
260
if (nTotalLower == nTargetValue) {
270
261
for (const auto & group : applicable_groups) {
271
- util::insert (setCoinsRet, group.m_outputs );
272
- nValueRet += group.m_value ;
262
+ result.AddInput (group);
273
263
}
274
- return true ;
264
+ return result ;
275
265
}
276
266
277
267
if (nTotalLower < nTargetValue) {
278
- if (!lowest_larger) return false ;
279
- util::insert (setCoinsRet, lowest_larger->m_outputs );
280
- nValueRet += lowest_larger->m_value ;
281
- return true ;
268
+ if (!lowest_larger) return std::nullopt;
269
+ result.AddInput (*lowest_larger);
270
+ return result;
282
271
}
283
272
284
273
// Solve subset sum by stochastic approximation
@@ -295,13 +284,11 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
295
284
// or the next bigger coin is closer), return the bigger coin
296
285
if (lowest_larger &&
297
286
((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || lowest_larger->GetSelectionAmount () <= nBest)) {
298
- util::insert (setCoinsRet, lowest_larger->m_outputs );
299
- nValueRet += lowest_larger->m_value ;
287
+ result.AddInput (*lowest_larger);
300
288
} else {
301
289
for (unsigned int i = 0 ; i < applicable_groups.size (); i++) {
302
290
if (vfBest[i]) {
303
- util::insert (setCoinsRet, applicable_groups[i].m_outputs );
304
- nValueRet += applicable_groups[i].m_value ;
291
+ result.AddInput (applicable_groups[i]);
305
292
}
306
293
}
307
294
@@ -316,7 +303,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
316
303
}
317
304
}
318
305
319
- return true ;
306
+ return result ;
320
307
}
321
308
322
309
/* *****************************************************************************
@@ -395,3 +382,51 @@ CAmount GetSelectionWaste(const std::set<CInputCoin>& inputs, CAmount change_cos
395
382
396
383
return waste;
397
384
}
385
+
386
+ void SelectionResult::ComputeAndSetWaste (CAmount change_cost)
387
+ {
388
+ m_waste = GetSelectionWaste (m_selected_inputs, change_cost, m_target, m_use_effective);
389
+ }
390
+
391
+ CAmount SelectionResult::GetWaste () const
392
+ {
393
+ Assume (m_waste != std::nullopt);
394
+ return *m_waste;
395
+ }
396
+
397
+ CAmount SelectionResult::GetSelectedValue () const
398
+ {
399
+ return std::accumulate (m_selected_inputs.cbegin (), m_selected_inputs.cend (), CAmount{0 }, [](CAmount sum, const auto & coin) { return sum + coin.txout .nValue ; });
400
+ }
401
+
402
+ void SelectionResult::Clear ()
403
+ {
404
+ m_selected_inputs.clear ();
405
+ m_waste.reset ();
406
+ }
407
+
408
+ void SelectionResult::AddInput (const OutputGroup& group)
409
+ {
410
+ util::insert (m_selected_inputs, group.m_outputs );
411
+ m_use_effective = !group.m_subtract_fee_outputs ;
412
+ }
413
+
414
+ const std::set<CInputCoin>& SelectionResult::GetInputSet () const
415
+ {
416
+ return m_selected_inputs;
417
+ }
418
+
419
+ std::vector<CInputCoin> SelectionResult::GetShuffledInputVector () const
420
+ {
421
+ std::vector<CInputCoin> coins (m_selected_inputs.begin (), m_selected_inputs.end ());
422
+ Shuffle (coins.begin (), coins.end (), FastRandomContext ());
423
+ return coins;
424
+ }
425
+
426
+ bool SelectionResult::operator <(SelectionResult other) const
427
+ {
428
+ Assume (m_waste != std::nullopt);
429
+ Assume (other.m_waste != std::nullopt);
430
+ // As this operator is only used in std::min_element, we want the result that has more inputs when waste are equal.
431
+ return *m_waste < *other.m_waste || (*m_waste == *other.m_waste && m_selected_inputs.size () > other.m_selected_inputs .size ());
432
+ }
0 commit comments