@@ -63,9 +63,9 @@ static void AddCoins(std::vector<OutputGroup>& utxo_pool, std::vector<CAmount> c
63
63
}
64
64
65
65
/* * Make multiple coins that share the same effective value */
66
- static void AddDuplicateCoins (std::vector<OutputGroup>& utxo_pool, int count, int amount) {
66
+ static void AddDuplicateCoins (std::vector<OutputGroup>& utxo_pool, int count, int amount, CoinSelectionParams cs_params = default_cs_params ) {
67
67
for (int i = 0 ; i < count; ++i) {
68
- utxo_pool.push_back (MakeCoin (amount));
68
+ utxo_pool.push_back (MakeCoin (amount, true , cs_params ));
69
69
}
70
70
}
71
71
@@ -116,72 +116,80 @@ static void TestBnBFail(std::string test_title, std::vector<OutputGroup>& utxo_p
116
116
117
117
BOOST_AUTO_TEST_CASE (bnb_test)
118
118
{
119
- std::vector<OutputGroup> utxo_pool;
120
-
121
- // Fail for empty UTXO pool
122
- TestBnBFail (" Empty UTXO pool" , utxo_pool, /* selection_target=*/ 1 * CENT);
123
-
124
- AddCoins (utxo_pool, {1 * CENT, 3 * CENT, 5 * CENT});
125
-
126
- // Simple success cases
127
- TestBnBSuccess (" Select smallest UTXO" , utxo_pool, /* selection_target=*/ 1 * CENT, /* expected_input_amounts=*/ {1 * CENT});
128
- TestBnBSuccess (" Select middle UTXO" , utxo_pool, /* selection_target=*/ 3 * CENT, /* expected_input_amounts=*/ {3 * CENT});
129
- TestBnBSuccess (" Select biggest UTXO" , utxo_pool, /* selection_target=*/ 5 * CENT, /* expected_input_amounts=*/ {5 * CENT});
130
- TestBnBSuccess (" Select two UTXOs" , utxo_pool, /* selection_target=*/ 4 * CENT, /* expected_input_amounts=*/ {1 * CENT, 3 * CENT});
131
- TestBnBSuccess (" Select all UTXOs" , utxo_pool, /* selection_target=*/ 9 * CENT, /* expected_input_amounts=*/ {1 * CENT, 3 * CENT, 5 * CENT});
132
-
133
- // BnB finds changeless solution while overshooting by up to cost_of_change
134
- TestBnBSuccess (" Select upper bound" , utxo_pool, /* selection_target=*/ 4 * CENT - default_cs_params.m_cost_of_change , /* expected_input_amounts=*/ {1 * CENT, 3 * CENT});
135
-
136
- // BnB fails to find changeless solution when overshooting by cost_of_change + 1 sat
137
- TestBnBFail (" Overshoot upper bound" , utxo_pool, /* selection_target=*/ 4 * CENT - default_cs_params.m_cost_of_change - 1 );
138
-
139
- // Simple cases without BnB solution
140
- TestBnBFail (" Smallest combination too big" , utxo_pool, /* selection_target=*/ 0.5 * CENT);
141
- TestBnBFail (" No UTXO combination in target window" , utxo_pool, /* selection_target=*/ 7 * CENT);
142
- TestBnBFail (" Select more than available" , utxo_pool, /* selection_target=*/ 10 * CENT);
143
-
144
- // Test skipping of equivalent input sets
145
- std::vector<OutputGroup> clone_pool;
146
- AddCoins (clone_pool, {2 * CENT, 7 * CENT, 7 * CENT});
147
- AddDuplicateCoins (clone_pool, 50'000 , 5 * CENT);
148
- TestBnBSuccess (" Skip equivalent input sets" , clone_pool, /* selection_target=*/ 16 * CENT, /* expected_input_amounts=*/ {2 * CENT, 7 * CENT, 7 * CENT});
149
-
150
- /* Test BnB attempt limit (`TOTAL_TRIES`)
151
- *
152
- * Generally, on a diverse UTXO pool BnB will quickly pass over UTXOs bigger than the target and then start
153
- * combining small counts of UTXOs that in sum remain under the selection_target+cost_of_change. When there are
154
- * multiple UTXOs that have matching amount and cost, combinations with equivalent input sets are skipped. The UTXO
155
- * pool for this test is specifically crafted to create as much branching as possible. The selection target is
156
- * 8 CENT while all UTXOs are slightly bigger than 1 CENT. The smallest eight are 100,000…100,007 sats, while the larger
157
- * nine are 100,368…100,375 (i.e., 100,008…100,016 sats plus cost_of_change (359 sats)).
158
- *
159
- * Because BnB will only select input sets that fall between selection_target and selection_target + cost_of_change,
160
- * and the search traverses the UTXO pool from large to small amounts, the search will visit every single
161
- * combination of eight inputs. All except the last combination will overshoot by more than cost_of_change on the
162
- * eighth input, because the larger nine inputs each exceed 1 CENT by more than cost_of_change. Only the last
163
- * combination consisting of the eight smallest UTXOs falls into the target window.
164
- */
165
- std::vector<OutputGroup> doppelganger_pool;
166
- std::vector<CAmount> doppelgangers;
167
- std::vector<CAmount> expected_inputs;
168
- for (int i = 0 ; i < 17 ; ++i) {
169
- if (i < 8 ) {
170
- // The eight smallest UTXOs can be combined to create expected_result
171
- doppelgangers.push_back (1 * CENT + i);
172
- expected_inputs.push_back (doppelgangers[i]);
173
- } else {
174
- // Any eight UTXOs including at least one UTXO with the added cost_of_change will exceed target window
175
- doppelgangers.push_back (1 * CENT + default_cs_params.m_cost_of_change + i);
119
+ std::vector<int > feerates = {0 , 1 , 5'000 , 10'000 , 25'000 , 59'764 , 500'000 , 999'000 , 1'500'000 };
120
+
121
+ for (int feerate : feerates) {
122
+ std::vector<OutputGroup> utxo_pool;
123
+
124
+ CoinSelectionParams cs_params = init_default_params ();
125
+ cs_params.m_effective_feerate = CFeeRate{feerate};
126
+
127
+ // Fail for empty UTXO pool
128
+ TestBnBFail (" Empty UTXO pool" , utxo_pool, /* selection_target=*/ 1 * CENT);
129
+
130
+ AddCoins (utxo_pool, {1 * CENT, 3 * CENT, 5 * CENT}, cs_params);
131
+
132
+ // Simple success cases
133
+ TestBnBSuccess (" Select smallest UTXO" , utxo_pool, /* selection_target=*/ 1 * CENT, /* expected_input_amounts=*/ {1 * CENT}, cs_params);
134
+ TestBnBSuccess (" Select middle UTXO" , utxo_pool, /* selection_target=*/ 3 * CENT, /* expected_input_amounts=*/ {3 * CENT}, cs_params);
135
+ TestBnBSuccess (" Select biggest UTXO" , utxo_pool, /* selection_target=*/ 5 * CENT, /* expected_input_amounts=*/ {5 * CENT}, cs_params);
136
+ TestBnBSuccess (" Select two UTXOs" , utxo_pool, /* selection_target=*/ 4 * CENT, /* expected_input_amounts=*/ {1 * CENT, 3 * CENT}, cs_params);
137
+ TestBnBSuccess (" Select all UTXOs" , utxo_pool, /* selection_target=*/ 9 * CENT, /* expected_input_amounts=*/ {1 * CENT, 3 * CENT, 5 * CENT}, cs_params);
138
+
139
+ // BnB finds changeless solution while overshooting by up to cost_of_change
140
+ TestBnBSuccess (" Select upper bound" , utxo_pool, /* selection_target=*/ 4 * CENT - default_cs_params.m_cost_of_change , /* expected_input_amounts=*/ {1 * CENT, 3 * CENT}, cs_params);
141
+
142
+ // BnB fails to find changeless solution when overshooting by cost_of_change + 1 sat
143
+ TestBnBFail (" Overshoot upper bound" , utxo_pool, /* selection_target=*/ 4 * CENT - default_cs_params.m_cost_of_change - 1 );
144
+
145
+ // Simple cases without BnB solution
146
+ TestBnBFail (" Smallest combination too big" , utxo_pool, /* selection_target=*/ 0.5 * CENT);
147
+ TestBnBFail (" No UTXO combination in target window" , utxo_pool, /* selection_target=*/ 7 * CENT);
148
+ TestBnBFail (" Select more than available" , utxo_pool, /* selection_target=*/ 10 * CENT);
149
+
150
+ // Test skipping of equivalent input sets
151
+ std::vector<OutputGroup> clone_pool;
152
+ AddCoins (clone_pool, {2 * CENT, 7 * CENT, 7 * CENT}, cs_params);
153
+ AddDuplicateCoins (clone_pool, 50'000 , 5 * CENT, cs_params);
154
+ TestBnBSuccess (" Skip equivalent input sets" , clone_pool, /* selection_target=*/ 16 * CENT, /* expected_input_amounts=*/ {2 * CENT, 7 * CENT, 7 * CENT}, cs_params);
155
+
156
+ /* Test BnB attempt limit (`TOTAL_TRIES`)
157
+ *
158
+ * Generally, on a diverse UTXO pool BnB will quickly pass over UTXOs bigger than the target and then start
159
+ * combining small counts of UTXOs that in sum remain under the selection_target+cost_of_change. When there are
160
+ * multiple UTXOs that have matching amount and cost, combinations with equivalent input sets are skipped. The
161
+ * UTXO pool for this test is specifically crafted to create as much branching as possible. The selection target
162
+ * is 8 CENT while all UTXOs are slightly bigger than 1 CENT. The smallest eight are 100,000…100,007 sats, while
163
+ * the larger nine are 100,368…100,375 (i.e., 100,008…100,016 sats plus cost_of_change (359 sats)).
164
+ *
165
+ * Because BnB will only select input sets that fall between selection_target and selection_target +
166
+ * cost_of_change, and the search traverses the UTXO pool from large to small amounts, the search will visit
167
+ * every single combination of eight inputs. All except the last combination will overshoot by more than
168
+ * cost_of_change on the eighth input, because the larger nine inputs each exceed 1 CENT by more than
169
+ * cost_of_change. Only the last combination consisting of the eight smallest UTXOs falls into the target
170
+ * window.
171
+ */
172
+ std::vector<OutputGroup> doppelganger_pool;
173
+ std::vector<CAmount> doppelgangers;
174
+ std::vector<CAmount> expected_inputs;
175
+ for (int i = 0 ; i < 17 ; ++i) {
176
+ if (i < 8 ) {
177
+ // The eight smallest UTXOs can be combined to create expected_result
178
+ doppelgangers.push_back (1 * CENT + i);
179
+ expected_inputs.push_back (doppelgangers[i]);
180
+ } else {
181
+ // Any eight UTXOs including at least one UTXO with the added cost_of_change will exceed target window
182
+ doppelgangers.push_back (1 * CENT + default_cs_params.m_cost_of_change + i);
183
+ }
176
184
}
177
- }
178
- AddCoins (doppelganger_pool, doppelgangers);
179
- // Among up to 17 unique UTXOs of similar effective value we will find a solution composed of the eight smallest UTXOs
180
- TestBnBSuccess (" Combine smallest 8 of 17 unique UTXOs" , doppelganger_pool, /* selection_target=*/ 8 * CENT, /* expected_input_amounts=*/ expected_inputs);
185
+ AddCoins (doppelganger_pool, doppelgangers, cs_params);
186
+ // Among up to 17 unique UTXOs of similar effective value we will find a solution composed of the eight smallest UTXOs
187
+ TestBnBSuccess (" Combine smallest 8 of 17 unique UTXOs" , doppelganger_pool, /* selection_target=*/ 8 * CENT, /* expected_input_amounts=*/ expected_inputs, cs_params);
181
188
182
- // Starting with 18 unique UTXOs of similar effective value we will not find the solution due to exceeding the attempt limit
183
- AddCoins (doppelganger_pool, {1 * CENT + default_cs_params.m_cost_of_change + 17 });
184
- TestBnBFail (" Exhaust looking for smallest 8 of 18 unique UTXOs" , doppelganger_pool, /* selection_target=*/ 8 * CENT);
189
+ // Starting with 18 unique UTXOs of similar effective value we will not find the solution due to exceeding the attempt limit
190
+ AddCoins (doppelganger_pool, {1 * CENT + default_cs_params.m_cost_of_change + 17 }, cs_params);
191
+ TestBnBFail (" Exhaust looking for smallest 8 of 18 unique UTXOs" , doppelganger_pool, /* selection_target=*/ 8 * CENT);
192
+ }
185
193
}
186
194
187
195
BOOST_AUTO_TEST_CASE (bnb_feerate_sensitivity_test)
0 commit comments