@@ -66,9 +66,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
66
66
{
67
67
SelectionResult result (selection_target);
68
68
CAmount curr_value = 0 ;
69
-
70
- std::vector<bool > curr_selection; // select the utxo at this index
71
- curr_selection.reserve (utxo_pool.size ());
69
+ std::vector<size_t > curr_selection; // selected utxo indexes
72
70
73
71
// Calculate curr_available_value
74
72
CAmount curr_available_value = 0 ;
@@ -85,11 +83,11 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
85
83
std::sort (utxo_pool.begin (), utxo_pool.end (), descending);
86
84
87
85
CAmount curr_waste = 0 ;
88
- std::vector<bool > best_selection;
86
+ std::vector<size_t > best_selection;
89
87
CAmount best_waste = MAX_MONEY;
90
88
91
89
// Depth First search loop for choosing the UTXOs
92
- for (size_t i = 0 ; i < TOTAL_TRIES; ++i) {
90
+ for (size_t i = 0 , utxo_pool_index = 0 ; i < TOTAL_TRIES; ++i, ++utxo_pool_index ) {
93
91
// Conditions for starting a backtrack
94
92
bool backtrack = false ;
95
93
if (curr_value + curr_available_value < selection_target || // Cannot possibly reach target with the amount remaining in the curr_available_value.
@@ -104,7 +102,6 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
104
102
// explore any more UTXOs to avoid burning money like that.
105
103
if (curr_waste <= best_waste) {
106
104
best_selection = curr_selection;
107
- best_selection.resize (utxo_pool.size ());
108
105
best_waste = curr_waste;
109
106
if (best_waste == 0 ) {
110
107
break ;
@@ -116,36 +113,37 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
116
113
117
114
// Backtracking, moving backwards
118
115
if (backtrack) {
119
- // Walk backwards to find the last included UTXO that still needs to have its omission branch traversed.
120
- while (!curr_selection.empty () && !curr_selection.back ()) {
121
- curr_selection.pop_back ();
122
- curr_available_value += utxo_pool.at (curr_selection.size ()).GetSelectionAmount ();
123
- }
124
-
125
116
if (curr_selection.empty ()) { // We have walked back to the first utxo and no branch is untraversed. All solutions searched
126
117
break ;
127
118
}
128
119
120
+ // Walk backwards to find the last included UTXO that still needs to have its omission branch traversed.
121
+ for (--utxo_pool_index; utxo_pool_index > curr_selection.back (); --utxo_pool_index) {
122
+ curr_available_value += utxo_pool.at (utxo_pool_index).GetSelectionAmount ();
123
+ }
124
+
129
125
// Output was included on previous iterations, try excluding now.
130
- curr_selection.back () = false ;
131
- OutputGroup& utxo = utxo_pool.at (curr_selection. size () - 1 );
126
+ assert (utxo_pool_index == curr_selection.back ()) ;
127
+ OutputGroup& utxo = utxo_pool.at (utxo_pool_index );
132
128
curr_value -= utxo.GetSelectionAmount ();
133
129
curr_waste -= utxo.fee - utxo.long_term_fee ;
130
+ curr_selection.pop_back ();
134
131
} else { // Moving forwards, continuing down this branch
135
- OutputGroup& utxo = utxo_pool.at (curr_selection. size () );
132
+ OutputGroup& utxo = utxo_pool.at (utxo_pool_index );
136
133
137
134
// Remove this utxo from the curr_available_value utxo amount
138
135
curr_available_value -= utxo.GetSelectionAmount ();
139
136
140
137
// Avoid searching a branch if the previous UTXO has the same value and same waste and was excluded. Since the ratio of fee to
141
138
// long term fee is the same, we only need to check if one of those values match in order to know that the waste is the same.
142
- if (!curr_selection.empty () && !curr_selection.back () &&
143
- utxo.GetSelectionAmount () == utxo_pool.at (curr_selection.size () - 1 ).GetSelectionAmount () &&
144
- utxo.fee == utxo_pool.at (curr_selection.size () - 1 ).fee ) {
145
- curr_selection.push_back (false );
146
- } else {
139
+ if (curr_selection.empty () ||
140
+ // The previous index is included and therefore not relevant for exclusion shortcut
141
+ (utxo_pool_index - 1 ) == curr_selection.back () ||
142
+ utxo.GetSelectionAmount () != utxo_pool.at (utxo_pool_index - 1 ).GetSelectionAmount () ||
143
+ utxo.fee != utxo_pool.at (utxo_pool_index - 1 ).fee )
144
+ {
147
145
// Inclusion branch first (Largest First Exploration)
148
- curr_selection.push_back (true );
146
+ curr_selection.push_back (utxo_pool_index );
149
147
curr_value += utxo.GetSelectionAmount ();
150
148
curr_waste += utxo.fee - utxo.long_term_fee ;
151
149
}
@@ -158,10 +156,8 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
158
156
}
159
157
160
158
// Set output set
161
- for (size_t i = 0 ; i < best_selection.size (); ++i) {
162
- if (best_selection.at (i)) {
163
- result.AddInput (utxo_pool.at (i));
164
- }
159
+ for (const size_t & i : best_selection) {
160
+ result.AddInput (utxo_pool.at (i));
165
161
}
166
162
167
163
return result;
0 commit comments