diff --git a/src/wallet/test/coinselection_tests.cpp b/src/wallet/test/coinselection_tests.cpp index 0445c7ec45b4a1..853ee1836e00c7 100644 --- a/src/wallet/test/coinselection_tests.cpp +++ b/src/wallet/test/coinselection_tests.cpp @@ -140,6 +140,28 @@ BOOST_AUTO_TEST_CASE(bnb_test) AddCoins(clone_pool, {2 * CENT, 7 * CENT, 7 * CENT}); AddDuplicateCoins(clone_pool, 50'000, 5 * CENT); TestBnBSuccess("Skip equivalent input sets", clone_pool, /*selection_target=*/16 * CENT, /*expected_input_amounts=*/{2 * CENT, 7 * CENT, 7 * CENT}); + + // Test BnB attempt limit + std::vector doppelganger_pool; + std::vector doppelgangers; + std::vector expected_inputs; + for (int i = 0; i < 17; ++i) { + if (i < 8) { + // 8 smallest UTXOs can be combined to create expected_result + doppelgangers.push_back(1 * CENT + i); + expected_inputs.push_back(doppelgangers[i]); + } else { + // Any 8 UTXOs including at least one UTXO with the added cost_of_change will exceed target window + doppelgangers.push_back(1 * CENT + default_cs_params.m_cost_of_change + i); + } + } + AddCoins(doppelganger_pool, doppelgangers); + // Among up to 17 unique UTXOs of similar effective value we will find a solution composed of the eight smallest + TestBnBSuccess("Combine smallest 8 of 17 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT, /*expected_input_amounts=*/expected_inputs); + + // Starting with 18 unique UTXOs of similar effective value we will not find the solution + AddCoins(doppelganger_pool, {1 * CENT + default_cs_params.m_cost_of_change + 17}); + TestBnBFail("Exhaust looking for smallest 8 of 18 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT); } BOOST_AUTO_TEST_CASE(bnb_feerate_sensitivity_test) diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index fd79934d5d45c4..8ba1cc3a0227f2 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -37,15 +37,6 @@ static const CoinEligibilityFilter filter_confirmed(1, 1, 0); static const CoinEligibilityFilter filter_standard_extra(6, 6, 0); static int nextLockTime = 0; -static void add_coin(const CAmount& nValue, int nInput, std::vector& set) -{ - CMutableTransaction tx; - tx.vout.resize(nInput + 1); - tx.vout[nInput].nValue = nValue; - tx.nLockTime = nextLockTime++; // so all transactions get different hashes - set.emplace_back(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0); -} - static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result) { CMutableTransaction tx; @@ -133,18 +124,6 @@ static bool EqualResult(const SelectionResult& a, const SelectionResult& b) return ret.first == a.GetInputSet().end() && ret.second == b.GetInputSet().end(); } -static CAmount make_hard_case(int utxos, std::vector& utxo_pool) -{ - utxo_pool.clear(); - CAmount target = 0; - for (int i = 0; i < utxos; ++i) { - target += CAmount{1} << (utxos+i); - add_coin(CAmount{1} << (utxos+i), 2*i, utxo_pool); - add_coin((CAmount{1} << (utxos+i)) + (CAmount{1} << (utxos-1-i)), 2*i + 1, utxo_pool); - } - return target; -} - inline std::vector& GroupCoins(const std::vector& available_coins, bool subtract_fee_outputs = false) { static std::vector static_groups; @@ -195,17 +174,6 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) std::vector utxo_pool; SelectionResult expected_result(CAmount(0), SelectionAlgorithm::BNB); - ///////////////////////// - // Known Outcome tests // - ///////////////////////// - - // Iteration exhaustion test - CAmount target = make_hard_case(17, utxo_pool); - BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), target, 1)); // Should exhaust - target = make_hard_case(14, utxo_pool); - const auto result7 = SelectCoinsBnB(GroupCoins(utxo_pool), target, 1); // Should not exhaust - BOOST_CHECK(result7); - //////////////////// // Behavior tests // ////////////////////