@@ -146,6 +146,42 @@ BOOST_AUTO_TEST_CASE(bnb_test)
146146 AddCoins (clone_pool, {2 * CENT, 7 * CENT, 7 * CENT});
147147 AddDuplicateCoins (clone_pool, 50'000 , 5 * CENT);
148148 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);
176+ }
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);
181+
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);
149185}
150186
151187BOOST_AUTO_TEST_CASE (bnb_feerate_sensitivity_test)
0 commit comments