@@ -84,14 +84,14 @@ struct {
84
84
* bound of the range.
85
85
* @param const CAmount& cost_of_change This is the cost of creating and spending a change output.
86
86
* This plus selection_target is the upper bound of the range.
87
- * @param int max_weight The maximum weight available for the input set .
87
+ * @param int max_selection_weight The maximum allowed weight for a selection result to be valid .
88
88
* @returns The result of this coin selection algorithm, or std::nullopt
89
89
*/
90
90
91
91
static const size_t TOTAL_TRIES = 100000 ;
92
92
93
93
util::Result<SelectionResult> SelectCoinsBnB (std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change,
94
- int max_weight )
94
+ int max_selection_weight )
95
95
{
96
96
SelectionResult result (selection_target, SelectionAlgorithm::BNB);
97
97
CAmount curr_value = 0 ;
@@ -128,7 +128,7 @@ util::Result<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool
128
128
curr_value > selection_target + cost_of_change || // Selected value is out of range, go back and try other branch
129
129
(curr_waste > best_waste && is_feerate_high)) { // Don't select things which we know will be more wasteful if the waste is increasing
130
130
backtrack = true ;
131
- } else if (curr_selection_weight > max_weight ) { // Exceeding weight for standard tx , cannot find more solutions by adding more inputs
131
+ } else if (curr_selection_weight > max_selection_weight ) { // Selected UTXOs weight exceeds the maximum weight allowed , cannot find more solutions by adding more inputs
132
132
max_tx_weight_exceeded = true ; // at least one selection attempt exceeded the max weight
133
133
backtrack = true ;
134
134
} else if (curr_value >= selection_target) { // Selected value is within range
@@ -319,10 +319,10 @@ util::Result<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool
319
319
* group with multiple as a heavier UTXO with the combined amount here.)
320
320
* @param const CAmount& selection_target This is the minimum amount that we need for the transaction without considering change.
321
321
* @param const CAmount& change_target The minimum budget for creating a change output, by which we increase the selection_target.
322
- * @param int max_weight The maximum permitted weight for the input set .
322
+ * @param int max_selection_weight The maximum allowed weight for a selection result to be valid .
323
323
* @returns The result of this coin selection algorithm, or std::nullopt
324
324
*/
325
- util::Result<SelectionResult> CoinGrinder (std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, CAmount change_target, int max_weight )
325
+ util::Result<SelectionResult> CoinGrinder (std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, CAmount change_target, int max_selection_weight )
326
326
{
327
327
std::sort (utxo_pool.begin (), utxo_pool.end (), descending_effval_weight);
328
328
// The sum of UTXO amounts after this UTXO index, e.g. lookahead[5] = Σ(UTXO[6+].amount)
@@ -359,7 +359,7 @@ util::Result<SelectionResult> CoinGrinder(std::vector<OutputGroup>& utxo_pool, c
359
359
360
360
// The weight of the currently selected input set, and the weight of the best selection
361
361
int curr_weight = 0 ;
362
- int best_selection_weight = max_weight ; // Tie is fine, because we prefer lower selection amount
362
+ int best_selection_weight = max_selection_weight ; // Tie is fine, because we prefer lower selection amount
363
363
364
364
// Whether the input sets generated during this search have exceeded the maximum transaction weight at any point
365
365
bool max_tx_weight_exceeded = false ;
@@ -436,8 +436,8 @@ util::Result<SelectionResult> CoinGrinder(std::vector<OutputGroup>& utxo_pool, c
436
436
// Insufficient funds with lookahead: CUT
437
437
should_cut = true ;
438
438
} else if (curr_weight > best_selection_weight) {
439
- // best_selection_weight is initialized to max_weight
440
- if (curr_weight > max_weight ) max_tx_weight_exceeded = true ;
439
+ // best_selection_weight is initialized to max_selection_weight
440
+ if (curr_weight > max_selection_weight ) max_tx_weight_exceeded = true ;
441
441
// Worse weight than best solution. More UTXOs only increase weight:
442
442
// CUT if last selected group had minimal weight, else SHIFT
443
443
if (utxo_pool[curr_tail].m_weight <= min_tail_weight[curr_tail]) {
@@ -535,7 +535,7 @@ class MinOutputGroupComparator
535
535
};
536
536
537
537
util::Result<SelectionResult> SelectCoinsSRD (const std::vector<OutputGroup>& utxo_pool, CAmount target_value, CAmount change_fee, FastRandomContext& rng,
538
- int max_weight )
538
+ int max_selection_weight )
539
539
{
540
540
SelectionResult result (target_value, SelectionAlgorithm::SRD);
541
541
std::priority_queue<OutputGroup, std::vector<OutputGroup>, MinOutputGroupComparator> heap;
@@ -565,14 +565,14 @@ util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utx
565
565
566
566
// If the selection weight exceeds the maximum allowed size, remove the least valuable inputs until we
567
567
// are below max weight.
568
- if (weight > max_weight ) {
568
+ if (weight > max_selection_weight ) {
569
569
max_tx_weight_exceeded = true ; // mark it in case we don't find any useful result.
570
570
do {
571
571
const OutputGroup& to_remove_group = heap.top ();
572
572
selected_eff_value -= to_remove_group.GetSelectionAmount ();
573
573
weight -= to_remove_group.m_weight ;
574
574
heap.pop ();
575
- } while (!heap.empty () && weight > max_weight );
575
+ } while (!heap.empty () && weight > max_selection_weight );
576
576
}
577
577
578
578
// Now check if we are above the target
@@ -597,11 +597,12 @@ util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utx
597
597
* nTargetValue, with indices corresponding to groups. If the ith
598
598
* entry is true, that means the ith group in groups was selected.
599
599
* param@[out] nBest Total amount of subset chosen that is closest to nTargetValue.
600
+ * paramp[in] max_selection_weight The maximum allowed weight for a selection result to be valid.
600
601
* param@[in] iterations Maximum number of tries.
601
602
*/
602
603
static void ApproximateBestSubset (FastRandomContext& insecure_rand, const std::vector<OutputGroup>& groups,
603
604
const CAmount& nTotalLower, const CAmount& nTargetValue,
604
- std::vector<char >& vfBest, CAmount& nBest, int iterations = 1000 )
605
+ std::vector<char >& vfBest, CAmount& nBest, int max_selection_weight, int iterations = 1000 )
605
606
{
606
607
std::vector<char > vfIncluded;
607
608
@@ -613,6 +614,7 @@ static void ApproximateBestSubset(FastRandomContext& insecure_rand, const std::v
613
614
{
614
615
vfIncluded.assign (groups.size (), false );
615
616
CAmount nTotal = 0 ;
617
+ int selected_coins_weight{0 };
616
618
bool fReachedTarget = false ;
617
619
for (int nPass = 0 ; nPass < 2 && !fReachedTarget ; nPass++)
618
620
{
@@ -627,9 +629,9 @@ static void ApproximateBestSubset(FastRandomContext& insecure_rand, const std::v
627
629
if (nPass == 0 ? insecure_rand.randbool () : !vfIncluded[i])
628
630
{
629
631
nTotal += groups[i].GetSelectionAmount ();
632
+ selected_coins_weight += groups[i].m_weight ;
630
633
vfIncluded[i] = true ;
631
- if (nTotal >= nTargetValue)
632
- {
634
+ if (nTotal >= nTargetValue && selected_coins_weight <= max_selection_weight) {
633
635
fReachedTarget = true ;
634
636
// If the total is between nTargetValue and nBest, it's our new best
635
637
// approximation.
@@ -639,6 +641,7 @@ static void ApproximateBestSubset(FastRandomContext& insecure_rand, const std::v
639
641
vfBest = vfIncluded;
640
642
}
641
643
nTotal -= groups[i].GetSelectionAmount ();
644
+ selected_coins_weight -= groups[i].m_weight ;
642
645
vfIncluded[i] = false ;
643
646
}
644
647
}
@@ -648,10 +651,11 @@ static void ApproximateBestSubset(FastRandomContext& insecure_rand, const std::v
648
651
}
649
652
650
653
util::Result<SelectionResult> KnapsackSolver (std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
651
- CAmount change_target, FastRandomContext& rng, int max_weight )
654
+ CAmount change_target, FastRandomContext& rng, int max_selection_weight )
652
655
{
653
656
SelectionResult result (nTargetValue, SelectionAlgorithm::KNAPSACK);
654
657
658
+ bool max_weight_exceeded{false };
655
659
// List of values less than target
656
660
std::optional<OutputGroup> lowest_larger;
657
661
// Groups with selection amount smaller than the target and any change we might produce.
@@ -662,6 +666,10 @@ util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, c
662
666
std::shuffle (groups.begin (), groups.end (), rng);
663
667
664
668
for (const OutputGroup& group : groups) {
669
+ if (group.m_weight > max_selection_weight) {
670
+ max_weight_exceeded = true ;
671
+ continue ;
672
+ }
665
673
if (group.GetSelectionAmount () == nTargetValue) {
666
674
result.AddInput (group);
667
675
return result;
@@ -677,11 +685,18 @@ util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, c
677
685
for (const auto & group : applicable_groups) {
678
686
result.AddInput (group);
679
687
}
680
- return result;
688
+ if (result.GetWeight () <= max_selection_weight) return result;
689
+ else max_weight_exceeded = true ;
690
+
691
+ // Try something else
692
+ result.Clear ();
681
693
}
682
694
683
695
if (nTotalLower < nTargetValue) {
684
- if (!lowest_larger) return util::Error ();
696
+ if (!lowest_larger) {
697
+ if (max_weight_exceeded) return ErrorMaxWeightExceeded ();
698
+ return util::Error ();
699
+ }
685
700
result.AddInput (*lowest_larger);
686
701
return result;
687
702
}
@@ -691,9 +706,9 @@ util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, c
691
706
std::vector<char > vfBest;
692
707
CAmount nBest;
693
708
694
- ApproximateBestSubset (rng, applicable_groups, nTotalLower, nTargetValue, vfBest, nBest);
709
+ ApproximateBestSubset (rng, applicable_groups, nTotalLower, nTargetValue, vfBest, nBest, max_selection_weight );
695
710
if (nBest != nTargetValue && nTotalLower >= nTargetValue + change_target) {
696
- ApproximateBestSubset (rng, applicable_groups, nTotalLower, nTargetValue + change_target, vfBest, nBest);
711
+ ApproximateBestSubset (rng, applicable_groups, nTotalLower, nTargetValue + change_target, vfBest, nBest, max_selection_weight );
697
712
}
698
713
699
714
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
@@ -709,7 +724,7 @@ util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, c
709
724
}
710
725
711
726
// If the result exceeds the maximum allowed size, return closest UTXO above the target
712
- if (result.GetWeight () > max_weight ) {
727
+ if (result.GetWeight () > max_selection_weight ) {
713
728
// No coin above target, nothing to do.
714
729
if (!lowest_larger) return ErrorMaxWeightExceeded ();
715
730
@@ -728,7 +743,7 @@ util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, c
728
743
LogPrint (BCLog::SELECTCOINS, " %stotal %s\n " , log_message, FormatMoney (nBest));
729
744
}
730
745
}
731
-
746
+ Assume (result. GetWeight () <= max_selection_weight);
732
747
return result;
733
748
}
734
749
0 commit comments