LCOV - code coverage report
Current view: top level - src/wallet/test - coinselector_tests.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 553 556 99.5 %
Date: 2022-04-21 14:51:19 Functions: 16 16 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 53 56 94.6 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2017-2021 The Bitcoin Core developers
#       2                 :            : // Distributed under the MIT software license, see the accompanying
#       3                 :            : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#       4                 :            : 
#       5                 :            : #include <consensus/amount.h>
#       6                 :            : #include <node/context.h>
#       7                 :            : #include <primitives/transaction.h>
#       8                 :            : #include <random.h>
#       9                 :            : #include <test/util/setup_common.h>
#      10                 :            : #include <util/translation.h>
#      11                 :            : #include <wallet/coincontrol.h>
#      12                 :            : #include <wallet/coinselection.h>
#      13                 :            : #include <wallet/spend.h>
#      14                 :            : #include <wallet/test/wallet_test_fixture.h>
#      15                 :            : #include <wallet/wallet.h>
#      16                 :            : 
#      17                 :            : #include <algorithm>
#      18                 :            : #include <boost/test/unit_test.hpp>
#      19                 :            : #include <random>
#      20                 :            : 
#      21                 :            : namespace wallet {
#      22                 :            : BOOST_FIXTURE_TEST_SUITE(coinselector_tests, WalletTestingSetup)
#      23                 :            : 
#      24                 :            : // how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles
#      25                 :        808 : #define RUN_TESTS 100
#      26                 :            : 
#      27                 :            : // some tests fail 1% of the time due to bad luck.
#      28                 :            : // we repeat those tests this many times and only complain if all iterations of the test fail
#      29                 :       1200 : #define RANDOM_REPEATS 5
#      30                 :            : 
#      31                 :            : typedef std::set<COutput> CoinSet;
#      32                 :            : 
#      33                 :            : static const CoinEligibilityFilter filter_standard(1, 6, 0);
#      34                 :            : static const CoinEligibilityFilter filter_confirmed(1, 1, 0);
#      35                 :            : static const CoinEligibilityFilter filter_standard_extra(6, 6, 0);
#      36                 :            : static int nextLockTime = 0;
#      37                 :            : 
#      38                 :            : static void add_coin(const CAmount& nValue, int nInput, std::vector<COutput>& set)
#      39                 :      50088 : {
#      40                 :      50088 :     CMutableTransaction tx;
#      41                 :      50088 :     tx.vout.resize(nInput + 1);
#      42                 :      50088 :     tx.vout[nInput].nValue = nValue;
#      43                 :      50088 :     tx.nLockTime = nextLockTime++;        // so all transactions get different hashes
#      44                 :      50088 :     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);
#      45                 :      50088 : }
#      46                 :            : 
#      47                 :            : static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
#      48                 :         16 : {
#      49                 :         16 :     CMutableTransaction tx;
#      50                 :         16 :     tx.vout.resize(nInput + 1);
#      51                 :         16 :     tx.vout[nInput].nValue = nValue;
#      52                 :         16 :     tx.nLockTime = nextLockTime++;        // so all transactions get different hashes
#      53                 :         16 :     COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
#      54                 :         16 :     OutputGroup group;
#      55                 :         16 :     group.Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ true);
#      56                 :         16 :     result.AddInput(group);
#      57                 :         16 : }
#      58                 :            : 
#      59                 :            : static void add_coin(const CAmount& nValue, int nInput, CoinSet& set, CAmount fee = 0, CAmount long_term_fee = 0)
#      60                 :         20 : {
#      61                 :         20 :     CMutableTransaction tx;
#      62                 :         20 :     tx.vout.resize(nInput + 1);
#      63                 :         20 :     tx.vout[nInput].nValue = nValue;
#      64                 :         20 :     tx.nLockTime = nextLockTime++;        // so all transactions get different hashes
#      65                 :         20 :     COutput coin(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
#      66                 :         20 :     coin.effective_value = nValue - fee;
#      67                 :         20 :     coin.fee = fee;
#      68                 :         20 :     coin.long_term_fee = long_term_fee;
#      69                 :         20 :     set.insert(coin);
#      70                 :         20 : }
#      71                 :            : 
#      72                 :            : static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
#      73                 :     109991 : {
#      74                 :     109991 :     CMutableTransaction tx;
#      75                 :     109991 :     tx.nLockTime = nextLockTime++;        // so all transactions get different hashes
#      76                 :     109991 :     tx.vout.resize(nInput + 1);
#      77                 :     109991 :     tx.vout[nInput].nValue = nValue;
#      78         [ +  + ]:     109991 :     if (spendable) {
#      79                 :          3 :         CTxDestination dest;
#      80                 :          3 :         bilingual_str error;
#      81                 :          3 :         const bool destination_ok = wallet.GetNewDestination(OutputType::BECH32, "", dest, error);
#      82                 :          3 :         assert(destination_ok);
#      83                 :          0 :         tx.vout[nInput].scriptPubKey = GetScriptForDestination(dest);
#      84                 :          3 :     }
#      85                 :          0 :     uint256 txid = tx.GetHash();
#      86                 :            : 
#      87                 :     109991 :     LOCK(wallet.cs_wallet);
#      88                 :     109991 :     auto ret = wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(txid), std::forward_as_tuple(MakeTransactionRef(std::move(tx)), TxStateInactive{}));
#      89                 :     109991 :     assert(ret.second);
#      90                 :          0 :     CWalletTx& wtx = (*ret.first).second;
#      91                 :     109991 :     coins.emplace_back(COutPoint(wtx.GetHash(), nInput), wtx.tx->vout.at(nInput), nAge, GetTxSpendSize(wallet, wtx, nInput), /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, wtx.GetTxTime(), fIsFromMe);
#      92                 :     109991 : }
#      93                 :            : 
#      94                 :            : /** Check if SelectionResult a is equivalent to SelectionResult b.
#      95                 :            :  * Equivalent means same input values, but maybe different inputs (i.e. same value, different prevout) */
#      96                 :            : static bool EquivalentResult(const SelectionResult& a, const SelectionResult& b)
#      97                 :          6 : {
#      98                 :          6 :     std::vector<CAmount> a_amts;
#      99                 :          6 :     std::vector<CAmount> b_amts;
#     100         [ +  + ]:         13 :     for (const auto& coin : a.GetInputSet()) {
#     101                 :         13 :         a_amts.push_back(coin.txout.nValue);
#     102                 :         13 :     }
#     103         [ +  + ]:         13 :     for (const auto& coin : b.GetInputSet()) {
#     104                 :         13 :         b_amts.push_back(coin.txout.nValue);
#     105                 :         13 :     }
#     106                 :          6 :     std::sort(a_amts.begin(), a_amts.end());
#     107                 :          6 :     std::sort(b_amts.begin(), b_amts.end());
#     108                 :            : 
#     109                 :          6 :     std::pair<std::vector<CAmount>::iterator, std::vector<CAmount>::iterator> ret = std::mismatch(a_amts.begin(), a_amts.end(), b_amts.begin());
#     110 [ +  - ][ +  - ]:          6 :     return ret.first == a_amts.end() && ret.second == b_amts.end();
#     111                 :          6 : }
#     112                 :            : 
#     113                 :            : /** Check if this selection is equal to another one. Equal means same inputs (i.e same value and prevout) */
#     114                 :            : static bool EqualResult(const SelectionResult& a, const SelectionResult& b)
#     115                 :       1100 : {
#     116                 :       1100 :     std::pair<CoinSet::iterator, CoinSet::iterator> ret = std::mismatch(a.GetInputSet().begin(), a.GetInputSet().end(), b.GetInputSet().begin(),
#     117                 :       1154 :         [](const COutput& a, const COutput& b) {
#     118                 :       1154 :             return a.outpoint == b.outpoint;
#     119                 :       1154 :         });
#     120 [ +  + ][ +  - ]:       1100 :     return ret.first == a.GetInputSet().end() && ret.second == b.GetInputSet().end();
#     121                 :       1100 : }
#     122                 :            : 
#     123                 :            : static CAmount make_hard_case(int utxos, std::vector<COutput>& utxo_pool)
#     124                 :          2 : {
#     125                 :          2 :     utxo_pool.clear();
#     126                 :          2 :     CAmount target = 0;
#     127         [ +  + ]:         33 :     for (int i = 0; i < utxos; ++i) {
#     128                 :         31 :         target += (CAmount)1 << (utxos+i);
#     129                 :         31 :         add_coin((CAmount)1 << (utxos+i), 2*i, utxo_pool);
#     130                 :         31 :         add_coin(((CAmount)1 << (utxos+i)) + ((CAmount)1 << (utxos-1-i)), 2*i + 1, utxo_pool);
#     131                 :         31 :     }
#     132                 :          2 :     return target;
#     133                 :          2 : }
#     134                 :            : 
#     135                 :            : inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
#     136                 :       2315 : {
#     137                 :       2315 :     static std::vector<OutputGroup> static_groups;
#     138                 :       2315 :     static_groups.clear();
#     139         [ +  + ]:     276708 :     for (auto& coin : coins) {
#     140                 :     276708 :         static_groups.emplace_back();
#     141                 :     276708 :         static_groups.back().Insert(coin, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
#     142                 :     276708 :     }
#     143                 :       2315 :     return static_groups;
#     144                 :       2315 : }
#     145                 :            : 
#     146                 :            : inline std::vector<OutputGroup>& KnapsackGroupOutputs(const std::vector<COutput>& coins, CWallet& wallet, const CoinEligibilityFilter& filter)
#     147                 :       3401 : {
#     148                 :       3401 :     FastRandomContext rand{};
#     149                 :       3401 :     CoinSelectionParams coin_selection_params{
#     150                 :       3401 :         rand,
#     151                 :       3401 :         /*change_output_size=*/ 0,
#     152                 :       3401 :         /*change_spend_size=*/ 0,
#     153                 :       3401 :         /*min_change_target=*/ CENT,
#     154                 :       3401 :         /*effective_feerate=*/ CFeeRate(0),
#     155                 :       3401 :         /*long_term_feerate=*/ CFeeRate(0),
#     156                 :       3401 :         /*discard_feerate=*/ CFeeRate(0),
#     157                 :       3401 :         /*tx_noinputs_size=*/ 0,
#     158                 :       3401 :         /*avoid_partial=*/ false,
#     159                 :       3401 :     };
#     160                 :       3401 :     static std::vector<OutputGroup> static_groups;
#     161                 :       3401 :     static_groups = GroupOutputs(wallet, coins, coin_selection_params, filter, /*positive_only=*/false);
#     162                 :       3401 :     return static_groups;
#     163                 :       3401 : }
#     164                 :            : 
#     165                 :            : // Branch and bound coin selection tests
#     166                 :            : BOOST_AUTO_TEST_CASE(bnb_search_test)
#     167                 :          1 : {
#     168                 :          1 :     FastRandomContext rand{};
#     169                 :            :     // Setup
#     170                 :          1 :     std::vector<COutput> utxo_pool;
#     171                 :          1 :     SelectionResult expected_result(CAmount(0));
#     172                 :            : 
#     173                 :            :     /////////////////////////
#     174                 :            :     // Known Outcome tests //
#     175                 :            :     /////////////////////////
#     176                 :            : 
#     177                 :            :     // Empty utxo pool
#     178                 :          1 :     BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT));
#     179                 :            : 
#     180                 :            :     // Add utxos
#     181                 :          1 :     add_coin(1 * CENT, 1, utxo_pool);
#     182                 :          1 :     add_coin(2 * CENT, 2, utxo_pool);
#     183                 :          1 :     add_coin(3 * CENT, 3, utxo_pool);
#     184                 :          1 :     add_coin(4 * CENT, 4, utxo_pool);
#     185                 :            : 
#     186                 :            :     // Select 1 Cent
#     187                 :          1 :     add_coin(1 * CENT, 1, expected_result);
#     188                 :          1 :     const auto result1 = SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT);
#     189                 :          1 :     BOOST_CHECK(result1);
#     190                 :          1 :     BOOST_CHECK(EquivalentResult(expected_result, *result1));
#     191                 :          1 :     BOOST_CHECK_EQUAL(result1->GetSelectedValue(), 1 * CENT);
#     192                 :          1 :     expected_result.Clear();
#     193                 :            : 
#     194                 :            :     // Select 2 Cent
#     195                 :          1 :     add_coin(2 * CENT, 2, expected_result);
#     196                 :          1 :     const auto result2 = SelectCoinsBnB(GroupCoins(utxo_pool), 2 * CENT, 0.5 * CENT);
#     197                 :          1 :     BOOST_CHECK(result2);
#     198                 :          1 :     BOOST_CHECK(EquivalentResult(expected_result, *result2));
#     199                 :          1 :     BOOST_CHECK_EQUAL(result2->GetSelectedValue(), 2 * CENT);
#     200                 :          1 :     expected_result.Clear();
#     201                 :            : 
#     202                 :            :     // Select 5 Cent
#     203                 :          1 :     add_coin(4 * CENT, 4, expected_result);
#     204                 :          1 :     add_coin(1 * CENT, 1, expected_result);
#     205                 :          1 :     const auto result3 = SelectCoinsBnB(GroupCoins(utxo_pool), 5 * CENT, 0.5 * CENT);
#     206                 :          1 :     BOOST_CHECK(result3);
#     207                 :          1 :     BOOST_CHECK(EquivalentResult(expected_result, *result3));
#     208                 :          1 :     BOOST_CHECK_EQUAL(result3->GetSelectedValue(), 5 * CENT);
#     209                 :          1 :     expected_result.Clear();
#     210                 :            : 
#     211                 :            :     // Select 11 Cent, not possible
#     212                 :          1 :     BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 11 * CENT, 0.5 * CENT));
#     213                 :          1 :     expected_result.Clear();
#     214                 :            : 
#     215                 :            :     // Cost of change is greater than the difference between target value and utxo sum
#     216                 :          1 :     add_coin(1 * CENT, 1, expected_result);
#     217                 :          1 :     const auto result4 = SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0.5 * CENT);
#     218                 :          1 :     BOOST_CHECK(result4);
#     219                 :          1 :     BOOST_CHECK_EQUAL(result4->GetSelectedValue(), 1 * CENT);
#     220                 :          1 :     BOOST_CHECK(EquivalentResult(expected_result, *result4));
#     221                 :          1 :     expected_result.Clear();
#     222                 :            : 
#     223                 :            :     // Cost of change is less than the difference between target value and utxo sum
#     224                 :          1 :     BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0));
#     225                 :          1 :     expected_result.Clear();
#     226                 :            : 
#     227                 :            :     // Select 10 Cent
#     228                 :          1 :     add_coin(5 * CENT, 5, utxo_pool);
#     229                 :          1 :     add_coin(5 * CENT, 5, expected_result);
#     230                 :          1 :     add_coin(4 * CENT, 4, expected_result);
#     231                 :          1 :     add_coin(1 * CENT, 1, expected_result);
#     232                 :          1 :     const auto result5 = SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 0.5 * CENT);
#     233                 :          1 :     BOOST_CHECK(result5);
#     234                 :          1 :     BOOST_CHECK(EquivalentResult(expected_result, *result5));
#     235                 :          1 :     BOOST_CHECK_EQUAL(result5->GetSelectedValue(), 10 * CENT);
#     236                 :          1 :     expected_result.Clear();
#     237                 :            : 
#     238                 :            :     // Negative effective value
#     239                 :            :     // Select 10 Cent but have 1 Cent not be possible because too small
#     240                 :          1 :     add_coin(5 * CENT, 5, expected_result);
#     241                 :          1 :     add_coin(3 * CENT, 3, expected_result);
#     242                 :          1 :     add_coin(2 * CENT, 2, expected_result);
#     243                 :          1 :     const auto result6 = SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 5000);
#     244                 :          1 :     BOOST_CHECK(result6);
#     245                 :          1 :     BOOST_CHECK_EQUAL(result6->GetSelectedValue(), 10 * CENT);
#     246                 :            :     // FIXME: this test is redundant with the above, because 1 Cent is selected, not "too small"
#     247                 :            :     // BOOST_CHECK(EquivalentResult(expected_result, *result));
#     248                 :            : 
#     249                 :            :     // Select 0.25 Cent, not possible
#     250                 :          1 :     BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.25 * CENT, 0.5 * CENT));
#     251                 :          1 :     expected_result.Clear();
#     252                 :            : 
#     253                 :            :     // Iteration exhaustion test
#     254                 :          1 :     CAmount target = make_hard_case(17, utxo_pool);
#     255                 :          1 :     BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), target, 0)); // Should exhaust
#     256                 :          1 :     target = make_hard_case(14, utxo_pool);
#     257                 :          1 :     const auto result7 = SelectCoinsBnB(GroupCoins(utxo_pool), target, 0); // Should not exhaust
#     258                 :          1 :     BOOST_CHECK(result7);
#     259                 :            : 
#     260                 :            :     // Test same value early bailout optimization
#     261                 :          1 :     utxo_pool.clear();
#     262                 :          1 :     add_coin(7 * CENT, 7, expected_result);
#     263                 :          1 :     add_coin(7 * CENT, 7, expected_result);
#     264                 :          1 :     add_coin(7 * CENT, 7, expected_result);
#     265                 :          1 :     add_coin(7 * CENT, 7, expected_result);
#     266                 :          1 :     add_coin(2 * CENT, 7, expected_result);
#     267                 :          1 :     add_coin(7 * CENT, 7, utxo_pool);
#     268                 :          1 :     add_coin(7 * CENT, 7, utxo_pool);
#     269                 :          1 :     add_coin(7 * CENT, 7, utxo_pool);
#     270                 :          1 :     add_coin(7 * CENT, 7, utxo_pool);
#     271                 :          1 :     add_coin(2 * CENT, 7, utxo_pool);
#     272         [ +  + ]:      50001 :     for (int i = 0; i < 50000; ++i) {
#     273                 :      50000 :         add_coin(5 * CENT, 7, utxo_pool);
#     274                 :      50000 :     }
#     275                 :          1 :     const auto result8 = SelectCoinsBnB(GroupCoins(utxo_pool), 30 * CENT, 5000);
#     276                 :          1 :     BOOST_CHECK(result8);
#     277                 :          1 :     BOOST_CHECK_EQUAL(result8->GetSelectedValue(), 30 * CENT);
#     278                 :          1 :     BOOST_CHECK(EquivalentResult(expected_result, *result8));
#     279                 :            : 
#     280                 :            :     ////////////////////
#     281                 :            :     // Behavior tests //
#     282                 :            :     ////////////////////
#     283                 :            :     // Select 1 Cent with pool of only greater than 5 Cent
#     284                 :          1 :     utxo_pool.clear();
#     285         [ +  + ]:         17 :     for (int i = 5; i <= 20; ++i) {
#     286                 :         16 :         add_coin(i * CENT, i, utxo_pool);
#     287                 :         16 :     }
#     288                 :            :     // Run 100 times, to make sure it is never finding a solution
#     289         [ +  + ]:        101 :     for (int i = 0; i < 100; ++i) {
#     290                 :        100 :         BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 2 * CENT));
#     291                 :        100 :     }
#     292                 :            : 
#     293                 :            :     // Make sure that effective value is working in AttemptSelection when BnB is used
#     294                 :          1 :     CoinSelectionParams coin_selection_params_bnb{
#     295                 :          1 :         rand,
#     296                 :          1 :         /*change_output_size=*/ 0,
#     297                 :          1 :         /*change_spend_size=*/ 0,
#     298                 :          1 :         /*min_change_target=*/ 0,
#     299                 :          1 :         /*effective_feerate=*/ CFeeRate(3000),
#     300                 :          1 :         /*long_term_feerate=*/ CFeeRate(1000),
#     301                 :          1 :         /*discard_feerate=*/ CFeeRate(1000),
#     302                 :          1 :         /*tx_noinputs_size=*/ 0,
#     303                 :          1 :         /*avoid_partial=*/ false,
#     304                 :          1 :     };
#     305                 :          1 :     {
#     306                 :          1 :         std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
#     307                 :          1 :         wallet->LoadWallet();
#     308                 :          1 :         LOCK(wallet->cs_wallet);
#     309                 :          1 :         wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
#     310                 :          1 :         wallet->SetupDescriptorScriptPubKeyMans();
#     311                 :            : 
#     312                 :          1 :         std::vector<COutput> coins;
#     313                 :            : 
#     314                 :          1 :         add_coin(coins, *wallet, 1);
#     315                 :          1 :         coins.at(0).input_bytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
#     316                 :          1 :         BOOST_CHECK(!SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change));
#     317                 :            : 
#     318                 :            :         // Test fees subtracted from output:
#     319                 :          1 :         coins.clear();
#     320                 :          1 :         add_coin(coins, *wallet, 1 * CENT);
#     321                 :          1 :         coins.at(0).input_bytes = 40;
#     322                 :          1 :         coin_selection_params_bnb.m_subtract_fee_outputs = true;
#     323                 :          1 :         const auto result9 = SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change);
#     324                 :          1 :         BOOST_CHECK(result9);
#     325                 :          1 :         BOOST_CHECK_EQUAL(result9->GetSelectedValue(), 1 * CENT);
#     326                 :          1 :     }
#     327                 :            : 
#     328                 :          1 :     {
#     329                 :          1 :         std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
#     330                 :          1 :         wallet->LoadWallet();
#     331                 :          1 :         LOCK(wallet->cs_wallet);
#     332                 :          1 :         wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
#     333                 :          1 :         wallet->SetupDescriptorScriptPubKeyMans();
#     334                 :            : 
#     335                 :          1 :         std::vector<COutput> coins;
#     336                 :            : 
#     337                 :          1 :         add_coin(coins, *wallet, 5 * CENT, 6 * 24, false, 0, true);
#     338                 :          1 :         add_coin(coins, *wallet, 3 * CENT, 6 * 24, false, 0, true);
#     339                 :          1 :         add_coin(coins, *wallet, 2 * CENT, 6 * 24, false, 0, true);
#     340                 :          1 :         CCoinControl coin_control;
#     341                 :          1 :         coin_control.fAllowOtherInputs = true;
#     342                 :          1 :         coin_control.Select(coins.at(0).outpoint);
#     343                 :          1 :         coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
#     344                 :          1 :         const auto result10 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
#     345                 :          1 :         BOOST_CHECK(result10);
#     346                 :          1 :     }
#     347                 :          1 : }
#     348                 :            : 
#     349                 :            : BOOST_AUTO_TEST_CASE(knapsack_solver_test)
#     350                 :          1 : {
#     351                 :          1 :     FastRandomContext rand{};
#     352                 :       5600 :     const auto temp1{[&rand](std::vector<OutputGroup>& g, const CAmount& v, CAmount c) { return KnapsackSolver(g, v, c, rand); }};
#     353                 :          1 :     const auto KnapsackSolver{temp1};
#     354                 :          1 :     std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
#     355                 :          1 :     wallet->LoadWallet();
#     356                 :          1 :     LOCK(wallet->cs_wallet);
#     357                 :          1 :     wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
#     358                 :          1 :     wallet->SetupDescriptorScriptPubKeyMans();
#     359                 :            : 
#     360                 :          1 :     std::vector<COutput> coins;
#     361                 :            : 
#     362                 :            :     // test multiple times to allow for differences in the shuffle order
#     363         [ +  + ]:        101 :     for (int i = 0; i < RUN_TESTS; i++)
#     364                 :        100 :     {
#     365                 :        100 :         coins.clear();
#     366                 :            : 
#     367                 :            :         // with an empty wallet we can't even pay one cent
#     368                 :        100 :         BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 1 * CENT, CENT));
#     369                 :            : 
#     370                 :        100 :         add_coin(coins, *wallet, 1*CENT, 4);        // add a new 1 cent coin
#     371                 :            : 
#     372                 :            :         // with a new 1 cent coin, we still can't find a mature 1 cent
#     373                 :        100 :         BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 1 * CENT, CENT));
#     374                 :            : 
#     375                 :            :         // but we can find a new 1 cent
#     376                 :        100 :         const auto result1 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 1 * CENT, CENT);
#     377                 :        100 :         BOOST_CHECK(result1);
#     378                 :        100 :         BOOST_CHECK_EQUAL(result1->GetSelectedValue(), 1 * CENT);
#     379                 :            : 
#     380                 :        100 :         add_coin(coins, *wallet, 2*CENT);           // add a mature 2 cent coin
#     381                 :            : 
#     382                 :            :         // we can't make 3 cents of mature coins
#     383                 :        100 :         BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 3 * CENT, CENT));
#     384                 :            : 
#     385                 :            :         // we can make 3 cents of new coins
#     386                 :        100 :         const auto result2 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 3 * CENT, CENT);
#     387                 :        100 :         BOOST_CHECK(result2);
#     388                 :        100 :         BOOST_CHECK_EQUAL(result2->GetSelectedValue(), 3 * CENT);
#     389                 :            : 
#     390                 :        100 :         add_coin(coins, *wallet, 5*CENT);           // add a mature 5 cent coin,
#     391                 :        100 :         add_coin(coins, *wallet, 10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
#     392                 :        100 :         add_coin(coins, *wallet, 20*CENT);          // and a mature 20 cent coin
#     393                 :            : 
#     394                 :            :         // now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27.  total = 38
#     395                 :            : 
#     396                 :            :         // we can't make 38 cents only if we disallow new coins:
#     397                 :        100 :         BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 38 * CENT, CENT));
#     398                 :            :         // we can't even make 37 cents if we don't allow new coins even if they're from us
#     399                 :        100 :         BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard_extra), 38 * CENT, CENT));
#     400                 :            :         // but we can make 37 cents if we accept new coins from ourself
#     401                 :        100 :         const auto result3 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 37 * CENT, CENT);
#     402                 :        100 :         BOOST_CHECK(result3);
#     403                 :        100 :         BOOST_CHECK_EQUAL(result3->GetSelectedValue(), 37 * CENT);
#     404                 :            :         // and we can make 38 cents if we accept all new coins
#     405                 :        100 :         const auto result4 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 38 * CENT, CENT);
#     406                 :        100 :         BOOST_CHECK(result4);
#     407                 :        100 :         BOOST_CHECK_EQUAL(result4->GetSelectedValue(), 38 * CENT);
#     408                 :            : 
#     409                 :            :         // try making 34 cents from 1,2,5,10,20 - we can't do it exactly
#     410                 :        100 :         const auto result5 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 34 * CENT, CENT);
#     411                 :        100 :         BOOST_CHECK(result5);
#     412                 :        100 :         BOOST_CHECK_EQUAL(result5->GetSelectedValue(), 35 * CENT);       // but 35 cents is closest
#     413                 :        100 :         BOOST_CHECK_EQUAL(result5->GetInputSet().size(), 3U);     // the best should be 20+10+5.  it's incredibly unlikely the 1 or 2 got included (but possible)
#     414                 :            : 
#     415                 :            :         // when we try making 7 cents, the smaller coins (1,2,5) are enough.  We should see just 2+5
#     416                 :        100 :         const auto result6 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 7 * CENT, CENT);
#     417                 :        100 :         BOOST_CHECK(result6);
#     418                 :        100 :         BOOST_CHECK_EQUAL(result6->GetSelectedValue(), 7 * CENT);
#     419                 :        100 :         BOOST_CHECK_EQUAL(result6->GetInputSet().size(), 2U);
#     420                 :            : 
#     421                 :            :         // when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
#     422                 :        100 :         const auto result7 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 8 * CENT, CENT);
#     423                 :        100 :         BOOST_CHECK(result7);
#     424                 :        100 :         BOOST_CHECK(result7->GetSelectedValue() == 8 * CENT);
#     425                 :        100 :         BOOST_CHECK_EQUAL(result7->GetInputSet().size(), 3U);
#     426                 :            : 
#     427                 :            :         // when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
#     428                 :        100 :         const auto result8 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 9 * CENT, CENT);
#     429                 :        100 :         BOOST_CHECK(result8);
#     430                 :        100 :         BOOST_CHECK_EQUAL(result8->GetSelectedValue(), 10 * CENT);
#     431                 :        100 :         BOOST_CHECK_EQUAL(result8->GetInputSet().size(), 1U);
#     432                 :            : 
#     433                 :            :         // now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin
#     434                 :        100 :         coins.clear();
#     435                 :            : 
#     436                 :        100 :         add_coin(coins, *wallet,  6*CENT);
#     437                 :        100 :         add_coin(coins, *wallet,  7*CENT);
#     438                 :        100 :         add_coin(coins, *wallet,  8*CENT);
#     439                 :        100 :         add_coin(coins, *wallet, 20*CENT);
#     440                 :        100 :         add_coin(coins, *wallet, 30*CENT); // now we have 6+7+8+20+30 = 71 cents total
#     441                 :            : 
#     442                 :            :         // check that we have 71 and not 72
#     443                 :        100 :         const auto result9 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 71 * CENT, CENT);
#     444                 :        100 :         BOOST_CHECK(result9);
#     445                 :        100 :         BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 72 * CENT, CENT));
#     446                 :            : 
#     447                 :            :         // now try making 16 cents.  the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20
#     448                 :        100 :         const auto result10 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 16 * CENT, CENT);
#     449                 :        100 :         BOOST_CHECK(result10);
#     450                 :        100 :         BOOST_CHECK_EQUAL(result10->GetSelectedValue(), 20 * CENT); // we should get 20 in one coin
#     451                 :        100 :         BOOST_CHECK_EQUAL(result10->GetInputSet().size(), 1U);
#     452                 :            : 
#     453                 :        100 :         add_coin(coins, *wallet,  5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
#     454                 :            : 
#     455                 :            :         // now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20
#     456                 :        100 :         const auto result11 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 16 * CENT, CENT);
#     457                 :        100 :         BOOST_CHECK(result11);
#     458                 :        100 :         BOOST_CHECK_EQUAL(result11->GetSelectedValue(), 18 * CENT); // we should get 18 in 3 coins
#     459                 :        100 :         BOOST_CHECK_EQUAL(result11->GetInputSet().size(), 3U);
#     460                 :            : 
#     461                 :        100 :         add_coin(coins, *wallet,  18*CENT); // now we have 5+6+7+8+18+20+30
#     462                 :            : 
#     463                 :            :         // and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18
#     464                 :        100 :         const auto result12 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 16 * CENT, CENT);
#     465                 :        100 :         BOOST_CHECK(result12);
#     466                 :        100 :         BOOST_CHECK_EQUAL(result12->GetSelectedValue(), 18 * CENT);  // we should get 18 in 1 coin
#     467                 :        100 :         BOOST_CHECK_EQUAL(result12->GetInputSet().size(), 1U); // because in the event of a tie, the biggest coin wins
#     468                 :            : 
#     469                 :            :         // now try making 11 cents.  we should get 5+6
#     470                 :        100 :         const auto result13 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 11 * CENT, CENT);
#     471                 :        100 :         BOOST_CHECK(result13);
#     472                 :        100 :         BOOST_CHECK_EQUAL(result13->GetSelectedValue(), 11 * CENT);
#     473                 :        100 :         BOOST_CHECK_EQUAL(result13->GetInputSet().size(), 2U);
#     474                 :            : 
#     475                 :            :         // check that the smallest bigger coin is used
#     476                 :        100 :         add_coin(coins, *wallet,  1*COIN);
#     477                 :        100 :         add_coin(coins, *wallet,  2*COIN);
#     478                 :        100 :         add_coin(coins, *wallet,  3*COIN);
#     479                 :        100 :         add_coin(coins, *wallet,  4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
#     480                 :        100 :         const auto result14 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 95 * CENT, CENT);
#     481                 :        100 :         BOOST_CHECK(result14);
#     482                 :        100 :         BOOST_CHECK_EQUAL(result14->GetSelectedValue(), 1 * COIN);  // we should get 1 BTC in 1 coin
#     483                 :        100 :         BOOST_CHECK_EQUAL(result14->GetInputSet().size(), 1U);
#     484                 :            : 
#     485                 :        100 :         const auto result15 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 195 * CENT, CENT);
#     486                 :        100 :         BOOST_CHECK(result15);
#     487                 :        100 :         BOOST_CHECK_EQUAL(result15->GetSelectedValue(), 2 * COIN);  // we should get 2 BTC in 1 coin
#     488                 :        100 :         BOOST_CHECK_EQUAL(result15->GetInputSet().size(), 1U);
#     489                 :            : 
#     490                 :            :         // empty the wallet and start again, now with fractions of a cent, to test small change avoidance
#     491                 :            : 
#     492                 :        100 :         coins.clear();
#     493                 :        100 :         add_coin(coins, *wallet, CENT * 1 / 10);
#     494                 :        100 :         add_coin(coins, *wallet, CENT * 2 / 10);
#     495                 :        100 :         add_coin(coins, *wallet, CENT * 3 / 10);
#     496                 :        100 :         add_coin(coins, *wallet, CENT * 4 / 10);
#     497                 :        100 :         add_coin(coins, *wallet, CENT * 5 / 10);
#     498                 :            : 
#     499                 :            :         // try making 1 * CENT from the 1.5 * CENT
#     500                 :            :         // we'll get change smaller than CENT whatever happens, so can expect CENT exactly
#     501                 :        100 :         const auto result16 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), CENT, CENT);
#     502                 :        100 :         BOOST_CHECK(result16);
#     503                 :        100 :         BOOST_CHECK_EQUAL(result16->GetSelectedValue(), CENT);
#     504                 :            : 
#     505                 :            :         // but if we add a bigger coin, small change is avoided
#     506                 :        100 :         add_coin(coins, *wallet, 1111*CENT);
#     507                 :            : 
#     508                 :            :         // try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
#     509                 :        100 :         const auto result17 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 1 * CENT, CENT);
#     510                 :        100 :         BOOST_CHECK(result17);
#     511                 :        100 :         BOOST_CHECK_EQUAL(result17->GetSelectedValue(), 1 * CENT); // we should get the exact amount
#     512                 :            : 
#     513                 :            :         // if we add more small coins:
#     514                 :        100 :         add_coin(coins, *wallet, CENT * 6 / 10);
#     515                 :        100 :         add_coin(coins, *wallet, CENT * 7 / 10);
#     516                 :            : 
#     517                 :            :         // and try again to make 1.0 * CENT
#     518                 :        100 :         const auto result18 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 1 * CENT, CENT);
#     519                 :        100 :         BOOST_CHECK(result18);
#     520                 :        100 :         BOOST_CHECK_EQUAL(result18->GetSelectedValue(), 1 * CENT); // we should get the exact amount
#     521                 :            : 
#     522                 :            :         // run the 'mtgox' test (see https://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
#     523                 :            :         // they tried to consolidate 10 50k coins into one 500k coin, and ended up with 50k in change
#     524                 :        100 :         coins.clear();
#     525         [ +  + ]:       2100 :         for (int j = 0; j < 20; j++)
#     526                 :       2000 :             add_coin(coins, *wallet, 50000 * COIN);
#     527                 :            : 
#     528                 :        100 :         const auto result19 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 500000 * COIN, CENT);
#     529                 :        100 :         BOOST_CHECK(result19);
#     530                 :        100 :         BOOST_CHECK_EQUAL(result19->GetSelectedValue(), 500000 * COIN); // we should get the exact amount
#     531                 :        100 :         BOOST_CHECK_EQUAL(result19->GetInputSet().size(), 10U); // in ten coins
#     532                 :            : 
#     533                 :            :         // if there's not enough in the smaller coins to make at least 1 * CENT change (0.5+0.6+0.7 < 1.0+1.0),
#     534                 :            :         // we need to try finding an exact subset anyway
#     535                 :            : 
#     536                 :            :         // sometimes it will fail, and so we use the next biggest coin:
#     537                 :        100 :         coins.clear();
#     538                 :        100 :         add_coin(coins, *wallet, CENT * 5 / 10);
#     539                 :        100 :         add_coin(coins, *wallet, CENT * 6 / 10);
#     540                 :        100 :         add_coin(coins, *wallet, CENT * 7 / 10);
#     541                 :        100 :         add_coin(coins, *wallet, 1111 * CENT);
#     542                 :        100 :         const auto result20 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 1 * CENT, CENT);
#     543                 :        100 :         BOOST_CHECK(result20);
#     544                 :        100 :         BOOST_CHECK_EQUAL(result20->GetSelectedValue(), 1111 * CENT); // we get the bigger coin
#     545                 :        100 :         BOOST_CHECK_EQUAL(result20->GetInputSet().size(), 1U);
#     546                 :            : 
#     547                 :            :         // but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0)
#     548                 :        100 :         coins.clear();
#     549                 :        100 :         add_coin(coins, *wallet, CENT * 4 / 10);
#     550                 :        100 :         add_coin(coins, *wallet, CENT * 6 / 10);
#     551                 :        100 :         add_coin(coins, *wallet, CENT * 8 / 10);
#     552                 :        100 :         add_coin(coins, *wallet, 1111 * CENT);
#     553                 :        100 :         const auto result21 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), CENT, CENT);
#     554                 :        100 :         BOOST_CHECK(result21);
#     555                 :        100 :         BOOST_CHECK_EQUAL(result21->GetSelectedValue(), CENT);   // we should get the exact amount
#     556                 :        100 :         BOOST_CHECK_EQUAL(result21->GetInputSet().size(), 2U); // in two coins 0.4+0.6
#     557                 :            : 
#     558                 :            :         // test avoiding small change
#     559                 :        100 :         coins.clear();
#     560                 :        100 :         add_coin(coins, *wallet, CENT * 5 / 100);
#     561                 :        100 :         add_coin(coins, *wallet, CENT * 1);
#     562                 :        100 :         add_coin(coins, *wallet, CENT * 100);
#     563                 :            : 
#     564                 :            :         // trying to make 100.01 from these three coins
#     565                 :        100 :         const auto result22 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), CENT * 10001 / 100, CENT);
#     566                 :        100 :         BOOST_CHECK(result22);
#     567                 :        100 :         BOOST_CHECK_EQUAL(result22->GetSelectedValue(), CENT * 10105 / 100); // we should get all coins
#     568                 :        100 :         BOOST_CHECK_EQUAL(result22->GetInputSet().size(), 3U);
#     569                 :            : 
#     570                 :            :         // but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
#     571                 :        100 :         const auto result23 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), CENT * 9990 / 100, CENT);
#     572                 :        100 :         BOOST_CHECK(result23);
#     573                 :        100 :         BOOST_CHECK_EQUAL(result23->GetSelectedValue(), 101 * CENT);
#     574                 :        100 :         BOOST_CHECK_EQUAL(result23->GetInputSet().size(), 2U);
#     575                 :        100 :     }
#     576                 :            : 
#     577                 :            :     // test with many inputs
#     578         [ +  + ]:          6 :     for (CAmount amt=1500; amt < COIN; amt*=10) {
#     579                 :          5 :         coins.clear();
#     580                 :            :         // Create 676 inputs (=  (old MAX_STANDARD_TX_SIZE == 100000)  / 148 bytes per input)
#     581         [ +  + ]:       3385 :         for (uint16_t j = 0; j < 676; j++)
#     582                 :       3380 :             add_coin(coins, *wallet, amt);
#     583                 :            : 
#     584                 :            :         // We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
#     585         [ +  + ]:        505 :         for (int i = 0; i < RUN_TESTS; i++) {
#     586                 :        500 :             const auto result24 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 2000, CENT);
#     587                 :        500 :             BOOST_CHECK(result24);
#     588                 :            : 
#     589         [ +  + ]:        500 :             if (amt - 2000 < CENT) {
#     590                 :            :                 // needs more than one input:
#     591                 :        300 :                 uint16_t returnSize = std::ceil((2000.0 + CENT)/amt);
#     592                 :        300 :                 CAmount returnValue = amt * returnSize;
#     593                 :        300 :                 BOOST_CHECK_EQUAL(result24->GetSelectedValue(), returnValue);
#     594                 :        300 :                 BOOST_CHECK_EQUAL(result24->GetInputSet().size(), returnSize);
#     595                 :        300 :             } else {
#     596                 :            :                 // one input is sufficient:
#     597                 :        200 :                 BOOST_CHECK_EQUAL(result24->GetSelectedValue(), amt);
#     598                 :        200 :                 BOOST_CHECK_EQUAL(result24->GetInputSet().size(), 1U);
#     599                 :        200 :             }
#     600                 :        500 :         }
#     601                 :          5 :     }
#     602                 :            : 
#     603                 :            :     // test randomness
#     604                 :          1 :     {
#     605                 :          1 :         coins.clear();
#     606         [ +  + ]:        101 :         for (int i2 = 0; i2 < 100; i2++)
#     607                 :        100 :             add_coin(coins, *wallet, COIN);
#     608                 :            : 
#     609                 :            :         // Again, we only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
#     610         [ +  + ]:        101 :         for (int i = 0; i < RUN_TESTS; i++) {
#     611                 :            :             // picking 50 from 100 coins doesn't depend on the shuffle,
#     612                 :            :             // but does depend on randomness in the stochastic approximation code
#     613                 :        100 :             const auto result25 = KnapsackSolver(GroupCoins(coins), 50 * COIN, CENT);
#     614                 :        100 :             BOOST_CHECK(result25);
#     615                 :        100 :             const auto result26 = KnapsackSolver(GroupCoins(coins), 50 * COIN, CENT);
#     616                 :        100 :             BOOST_CHECK(result26);
#     617                 :        100 :             BOOST_CHECK(!EqualResult(*result25, *result26));
#     618                 :            : 
#     619                 :        100 :             int fails = 0;
#     620         [ +  + ]:        600 :             for (int j = 0; j < RANDOM_REPEATS; j++)
#     621                 :        500 :             {
#     622                 :            :                 // Test that the KnapsackSolver selects randomly from equivalent coins (same value and same input size).
#     623                 :            :                 // When choosing 1 from 100 identical coins, 1% of the time, this test will choose the same coin twice
#     624                 :            :                 // which will cause it to fail.
#     625                 :            :                 // To avoid that issue, run the test RANDOM_REPEATS times and only complain if all of them fail
#     626                 :        500 :                 const auto result27 = KnapsackSolver(GroupCoins(coins), COIN, CENT);
#     627                 :        500 :                 BOOST_CHECK(result27);
#     628                 :        500 :                 const auto result28 = KnapsackSolver(GroupCoins(coins), COIN, CENT);
#     629                 :        500 :                 BOOST_CHECK(result28);
#     630         [ +  + ]:        500 :                 if (EqualResult(*result27, *result28))
#     631                 :          7 :                     fails++;
#     632                 :        500 :             }
#     633                 :        100 :             BOOST_CHECK_NE(fails, RANDOM_REPEATS);
#     634                 :        100 :         }
#     635                 :            : 
#     636                 :            :         // add 75 cents in small change.  not enough to make 90 cents,
#     637                 :            :         // then try making 90 cents.  there are multiple competing "smallest bigger" coins,
#     638                 :            :         // one of which should be picked at random
#     639                 :          1 :         add_coin(coins, *wallet, 5 * CENT);
#     640                 :          1 :         add_coin(coins, *wallet, 10 * CENT);
#     641                 :          1 :         add_coin(coins, *wallet, 15 * CENT);
#     642                 :          1 :         add_coin(coins, *wallet, 20 * CENT);
#     643                 :          1 :         add_coin(coins, *wallet, 25 * CENT);
#     644                 :            : 
#     645         [ +  + ]:        101 :         for (int i = 0; i < RUN_TESTS; i++) {
#     646                 :        100 :             int fails = 0;
#     647         [ +  + ]:        600 :             for (int j = 0; j < RANDOM_REPEATS; j++)
#     648                 :        500 :             {
#     649                 :        500 :                 const auto result29 = KnapsackSolver(GroupCoins(coins), 90 * CENT, CENT);
#     650                 :        500 :                 BOOST_CHECK(result29);
#     651                 :        500 :                 const auto result30 = KnapsackSolver(GroupCoins(coins), 90 * CENT, CENT);
#     652                 :        500 :                 BOOST_CHECK(result30);
#     653         [ +  + ]:        500 :                 if (EqualResult(*result29, *result30))
#     654                 :          3 :                     fails++;
#     655                 :        500 :             }
#     656                 :        100 :             BOOST_CHECK_NE(fails, RANDOM_REPEATS);
#     657                 :        100 :         }
#     658                 :          1 :     }
#     659                 :          1 : }
#     660                 :            : 
#     661                 :            : BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
#     662                 :          1 : {
#     663                 :          1 :     FastRandomContext rand{};
#     664                 :          1 :     std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
#     665                 :          1 :     wallet->LoadWallet();
#     666                 :          1 :     LOCK(wallet->cs_wallet);
#     667                 :          1 :     wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
#     668                 :          1 :     wallet->SetupDescriptorScriptPubKeyMans();
#     669                 :            : 
#     670                 :          1 :     std::vector<COutput> coins;
#     671                 :            : 
#     672                 :            :     // Test vValue sort order
#     673         [ +  + ]:       1001 :     for (int i = 0; i < 1000; i++)
#     674                 :       1000 :         add_coin(coins, *wallet, 1000 * COIN);
#     675                 :          1 :     add_coin(coins, *wallet, 3 * COIN);
#     676                 :            : 
#     677                 :          1 :     const auto result = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 1003 * COIN, CENT, rand);
#     678                 :          1 :     BOOST_CHECK(result);
#     679                 :          1 :     BOOST_CHECK_EQUAL(result->GetSelectedValue(), 1003 * COIN);
#     680                 :          1 :     BOOST_CHECK_EQUAL(result->GetInputSet().size(), 2U);
#     681                 :          1 : }
#     682                 :            : 
#     683                 :            : // Tests that with the ideal conditions, the coin selector will always be able to find a solution that can pay the target value
#     684                 :            : BOOST_AUTO_TEST_CASE(SelectCoins_test)
#     685                 :          1 : {
#     686                 :          1 :     std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
#     687                 :          1 :     wallet->LoadWallet();
#     688                 :          1 :     LOCK(wallet->cs_wallet);
#     689                 :          1 :     wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
#     690                 :          1 :     wallet->SetupDescriptorScriptPubKeyMans();
#     691                 :            : 
#     692                 :            :     // Random generator stuff
#     693                 :          1 :     std::default_random_engine generator;
#     694                 :          1 :     std::exponential_distribution<double> distribution (100);
#     695                 :          1 :     FastRandomContext rand;
#     696                 :            : 
#     697                 :            :     // Run this test 100 times
#     698         [ +  + ]:        101 :     for (int i = 0; i < 100; ++i)
#     699                 :        100 :     {
#     700                 :        100 :         std::vector<COutput> coins;
#     701                 :        100 :         CAmount balance{0};
#     702                 :            : 
#     703                 :            :         // Make a wallet with 1000 exponentially distributed random inputs
#     704         [ +  + ]:     100100 :         for (int j = 0; j < 1000; ++j)
#     705                 :     100000 :         {
#     706                 :     100000 :             CAmount val = distribution(generator)*10000000;
#     707                 :     100000 :             add_coin(coins, *wallet, val);
#     708                 :     100000 :             balance += val;
#     709                 :     100000 :         }
#     710                 :            : 
#     711                 :            :         // Generate a random fee rate in the range of 100 - 400
#     712                 :        100 :         CFeeRate rate(rand.randrange(300) + 100);
#     713                 :            : 
#     714                 :            :         // Generate a random target value between 1000 and wallet balance
#     715                 :        100 :         CAmount target = rand.randrange(balance - 1000) + 1000;
#     716                 :            : 
#     717                 :            :         // Perform selection
#     718                 :        100 :         CoinSelectionParams cs_params{
#     719                 :        100 :             rand,
#     720                 :        100 :             /*change_output_size=*/ 34,
#     721                 :        100 :             /*change_spend_size=*/ 148,
#     722                 :        100 :             /*min_change_target=*/ CENT,
#     723                 :        100 :             /*effective_feerate=*/ CFeeRate(0),
#     724                 :        100 :             /*long_term_feerate=*/ CFeeRate(0),
#     725                 :        100 :             /*discard_feerate=*/ CFeeRate(0),
#     726                 :        100 :             /*tx_noinputs_size=*/ 0,
#     727                 :        100 :             /*avoid_partial=*/ false,
#     728                 :        100 :         };
#     729                 :        100 :         CCoinControl cc;
#     730                 :        100 :         const auto result = SelectCoins(*wallet, coins, target, cc, cs_params);
#     731                 :        100 :         BOOST_CHECK(result);
#     732                 :        100 :         BOOST_CHECK_GE(result->GetSelectedValue(), target);
#     733                 :        100 :     }
#     734                 :          1 : }
#     735                 :            : 
#     736                 :            : BOOST_AUTO_TEST_CASE(waste_test)
#     737                 :          1 : {
#     738                 :          1 :     CoinSet selection;
#     739                 :          1 :     const CAmount fee{100};
#     740                 :          1 :     const CAmount change_cost{125};
#     741                 :          1 :     const CAmount fee_diff{40};
#     742                 :          1 :     const CAmount in_amt{3 * COIN};
#     743                 :          1 :     const CAmount target{2 * COIN};
#     744                 :          1 :     const CAmount excess{in_amt - fee * 2 - target};
#     745                 :            : 
#     746                 :            :     // Waste with change is the change cost and difference between fee and long term fee
#     747                 :          1 :     add_coin(1 * COIN, 1, selection, fee, fee - fee_diff);
#     748                 :          1 :     add_coin(2 * COIN, 2, selection, fee, fee - fee_diff);
#     749                 :          1 :     const CAmount waste1 = GetSelectionWaste(selection, change_cost, target);
#     750                 :          1 :     BOOST_CHECK_EQUAL(fee_diff * 2 + change_cost, waste1);
#     751                 :          1 :     selection.clear();
#     752                 :            : 
#     753                 :            :     // Waste without change is the excess and difference between fee and long term fee
#     754                 :          1 :     add_coin(1 * COIN, 1, selection, fee, fee - fee_diff);
#     755                 :          1 :     add_coin(2 * COIN, 2, selection, fee, fee - fee_diff);
#     756                 :          1 :     const CAmount waste_nochange1 = GetSelectionWaste(selection, 0, target);
#     757                 :          1 :     BOOST_CHECK_EQUAL(fee_diff * 2 + excess, waste_nochange1);
#     758                 :          1 :     selection.clear();
#     759                 :            : 
#     760                 :            :     // Waste with change and fee == long term fee is just cost of change
#     761                 :          1 :     add_coin(1 * COIN, 1, selection, fee, fee);
#     762                 :          1 :     add_coin(2 * COIN, 2, selection, fee, fee);
#     763                 :          1 :     BOOST_CHECK_EQUAL(change_cost, GetSelectionWaste(selection, change_cost, target));
#     764                 :          1 :     selection.clear();
#     765                 :            : 
#     766                 :            :     // Waste without change and fee == long term fee is just the excess
#     767                 :          1 :     add_coin(1 * COIN, 1, selection, fee, fee);
#     768                 :          1 :     add_coin(2 * COIN, 2, selection, fee, fee);
#     769                 :          1 :     BOOST_CHECK_EQUAL(excess, GetSelectionWaste(selection, 0, target));
#     770                 :          1 :     selection.clear();
#     771                 :            : 
#     772                 :            :     // Waste will be greater when fee is greater, but long term fee is the same
#     773                 :          1 :     add_coin(1 * COIN, 1, selection, fee * 2, fee - fee_diff);
#     774                 :          1 :     add_coin(2 * COIN, 2, selection, fee * 2, fee - fee_diff);
#     775                 :          1 :     const CAmount waste2 = GetSelectionWaste(selection, change_cost, target);
#     776                 :          1 :     BOOST_CHECK_GT(waste2, waste1);
#     777                 :          1 :     selection.clear();
#     778                 :            : 
#     779                 :            :     // Waste with change is the change cost and difference between fee and long term fee
#     780                 :            :     // With long term fee greater than fee, waste should be less than when long term fee is less than fee
#     781                 :          1 :     add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
#     782                 :          1 :     add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
#     783                 :          1 :     const CAmount waste3 = GetSelectionWaste(selection, change_cost, target);
#     784                 :          1 :     BOOST_CHECK_EQUAL(fee_diff * -2 + change_cost, waste3);
#     785                 :          1 :     BOOST_CHECK_LT(waste3, waste1);
#     786                 :          1 :     selection.clear();
#     787                 :            : 
#     788                 :            :     // Waste without change is the excess and difference between fee and long term fee
#     789                 :            :     // With long term fee greater than fee, waste should be less than when long term fee is less than fee
#     790                 :          1 :     add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
#     791                 :          1 :     add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
#     792                 :          1 :     const CAmount waste_nochange2 = GetSelectionWaste(selection, 0, target);
#     793                 :          1 :     BOOST_CHECK_EQUAL(fee_diff * -2 + excess, waste_nochange2);
#     794                 :          1 :     BOOST_CHECK_LT(waste_nochange2, waste_nochange1);
#     795                 :          1 :     selection.clear();
#     796                 :            : 
#     797                 :            :     // No Waste when fee == long_term_fee, no change, and no excess
#     798                 :          1 :     add_coin(1 * COIN, 1, selection, fee, fee);
#     799                 :          1 :     add_coin(2 * COIN, 2, selection, fee, fee);
#     800                 :          1 :     const CAmount exact_target{in_amt - fee * 2};
#     801                 :          1 :     BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, /*change_cost=*/0, exact_target));
#     802                 :          1 :     selection.clear();
#     803                 :            : 
#     804                 :            :     // No Waste when (fee - long_term_fee) == (-cost_of_change), and no excess
#     805                 :          1 :     const CAmount new_change_cost{fee_diff * 2};
#     806                 :          1 :     add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
#     807                 :          1 :     add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
#     808                 :          1 :     BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, new_change_cost, target));
#     809                 :          1 :     selection.clear();
#     810                 :            : 
#     811                 :            :     // No Waste when (fee - long_term_fee) == (-excess), no change cost
#     812                 :          1 :     const CAmount new_target{in_amt - fee * 2 - fee_diff * 2};
#     813                 :          1 :     add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
#     814                 :          1 :     add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
#     815                 :          1 :     BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, /* change cost */ 0, new_target));
#     816                 :          1 : }
#     817                 :            : 
#     818                 :            : BOOST_AUTO_TEST_SUITE_END()
#     819                 :            : } // namespace wallet

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a