LCOV - code coverage report
Current view: top level - src/wallet - coinselection.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 193 199 97.0 %
Date: 2021-06-29 14:35:33 Functions: 7 7 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: 117 128 91.4 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2017-2020 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 <wallet/coinselection.h>
#       6                 :            : 
#       7                 :            : #include <policy/feerate.h>
#       8                 :            : #include <util/system.h>
#       9                 :            : #include <util/moneystr.h>
#      10                 :            : 
#      11                 :            : #include <optional>
#      12                 :            : 
#      13                 :            : // Descending order comparator
#      14                 :            : struct {
#      15                 :            :     bool operator()(const OutputGroup& a, const OutputGroup& b) const
#      16                 :    3041257 :     {
#      17                 :    3041257 :         return a.GetSelectionAmount() > b.GetSelectionAmount();
#      18                 :    3041257 :     }
#      19                 :            : } descending;
#      20                 :            : 
#      21                 :            : /*
#      22                 :            :  * This is the Branch and Bound Coin Selection algorithm designed by Murch. It searches for an input
#      23                 :            :  * set that can pay for the spending target and does not exceed the spending target by more than the
#      24                 :            :  * cost of creating and spending a change output. The algorithm uses a depth-first search on a binary
#      25                 :            :  * tree. In the binary tree, each node corresponds to the inclusion or the omission of a UTXO. UTXOs
#      26                 :            :  * are sorted by their effective values and the trees is explored deterministically per the inclusion
#      27                 :            :  * branch first. At each node, the algorithm checks whether the selection is within the target range.
#      28                 :            :  * While the selection has not reached the target range, more UTXOs are included. When a selection's
#      29                 :            :  * value exceeds the target range, the complete subtree deriving from this selection can be omitted.
#      30                 :            :  * At that point, the last included UTXO is deselected and the corresponding omission branch explored
#      31                 :            :  * instead. The search ends after the complete tree has been searched or after a limited number of tries.
#      32                 :            :  *
#      33                 :            :  * The search continues to search for better solutions after one solution has been found. The best
#      34                 :            :  * solution is chosen by minimizing the waste metric. The waste metric is defined as the cost to
#      35                 :            :  * spend the current inputs at the given fee rate minus the long term expected cost to spend the
#      36                 :            :  * inputs, plus the amount the selection exceeds the spending target:
#      37                 :            :  *
#      38                 :            :  * waste = selectionTotal - target + inputs × (currentFeeRate - longTermFeeRate)
#      39                 :            :  *
#      40                 :            :  * The algorithm uses two additional optimizations. A lookahead keeps track of the total value of
#      41                 :            :  * the unexplored UTXOs. A subtree is not explored if the lookahead indicates that the target range
#      42                 :            :  * cannot be reached. Further, it is unnecessary to test equivalent combinations. This allows us
#      43                 :            :  * to skip testing the inclusion of UTXOs that match the effective value and waste of an omitted
#      44                 :            :  * predecessor.
#      45                 :            :  *
#      46                 :            :  * The Branch and Bound algorithm is described in detail in Murch's Master Thesis:
#      47                 :            :  * https://murch.one/wp-content/uploads/2016/11/erhardt2016coinselection.pdf
#      48                 :            :  *
#      49                 :            :  * @param const std::vector<CInputCoin>& utxo_pool The set of UTXOs that we are choosing from.
#      50                 :            :  *        These UTXOs will be sorted in descending order by effective value and the CInputCoins'
#      51                 :            :  *        values are their effective values.
#      52                 :            :  * @param const CAmount& selection_target This is the value that we want to select. It is the lower
#      53                 :            :  *        bound of the range.
#      54                 :            :  * @param const CAmount& cost_of_change This is the cost of creating and spending a change output.
#      55                 :            :  *        This plus selection_target is the upper bound of the range.
#      56                 :            :  * @param std::set<CInputCoin>& out_set -> This is an output parameter for the set of CInputCoins
#      57                 :            :  *        that have been selected.
#      58                 :            :  * @param CAmount& value_ret -> This is an output parameter for the total value of the CInputCoins
#      59                 :            :  *        that were selected.
#      60                 :            :  */
#      61                 :            : 
#      62                 :            : static const size_t TOTAL_TRIES = 100000;
#      63                 :            : 
#      64                 :            : bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret)
#      65                 :      10794 : {
#      66                 :      10794 :     out_set.clear();
#      67                 :      10794 :     CAmount curr_value = 0;
#      68                 :            : 
#      69                 :      10794 :     std::vector<bool> curr_selection; // select the utxo at this index
#      70                 :      10794 :     curr_selection.reserve(utxo_pool.size());
#      71                 :            : 
#      72                 :            :     // Calculate curr_available_value
#      73                 :      10794 :     CAmount curr_available_value = 0;
#      74         [ +  + ]:     665860 :     for (const OutputGroup& utxo : utxo_pool) {
#      75                 :            :         // Assert that this utxo is not negative. It should never be negative, effective value calculation should have removed it
#      76                 :     665860 :         assert(utxo.GetSelectionAmount() > 0);
#      77                 :     665860 :         curr_available_value += utxo.GetSelectionAmount();
#      78                 :     665860 :     }
#      79         [ +  + ]:      10794 :     if (curr_available_value < selection_target) {
#      80                 :       3242 :         return false;
#      81                 :       3242 :     }
#      82                 :            : 
#      83                 :            :     // Sort the utxo_pool
#      84                 :       7552 :     std::sort(utxo_pool.begin(), utxo_pool.end(), descending);
#      85                 :            : 
#      86                 :       7552 :     CAmount curr_waste = 0;
#      87                 :       7552 :     std::vector<bool> best_selection;
#      88                 :       7552 :     CAmount best_waste = MAX_MONEY;
#      89                 :            : 
#      90                 :            :     // Depth First search loop for choosing the UTXOs
#      91         [ +  + ]:   14236912 :     for (size_t i = 0; i < TOTAL_TRIES; ++i) {
#      92                 :            :         // Conditions for starting a backtrack
#      93                 :   14236786 :         bool backtrack = false;
#      94         [ +  + ]:   14236786 :         if (curr_value + curr_available_value < selection_target ||                // Cannot possibly reach target with the amount remaining in the curr_available_value.
#      95         [ +  + ]:   14236786 :             curr_value > selection_target + cost_of_change ||    // Selected value is out of range, go back and try other branch
#      96 [ +  + ][ -  + ]:   14236786 :             (curr_waste > best_waste && (utxo_pool.at(0).fee - utxo_pool.at(0).long_term_fee) > 0)) { // Don't select things which we know will be more wasteful if the waste is increasing
#      97                 :    4742355 :             backtrack = true;
#      98         [ +  + ]:    9494431 :         } else if (curr_value >= selection_target) {       // Selected value is within range
#      99                 :       1942 :             curr_waste += (curr_value - selection_target); // This is the excess value which is added to the waste for the below comparison
#     100                 :            :             // Adding another UTXO after this check could bring the waste down if the long term fee is higher than the current fee.
#     101                 :            :             // However we are not going to explore that because this optimization for the waste is only done when we have hit our target
#     102                 :            :             // value. Adding any more UTXOs will be just burning the UTXO; it will go entirely to fees. Thus we aren't going to
#     103                 :            :             // explore any more UTXOs to avoid burning money like that.
#     104         [ +  - ]:       1942 :             if (curr_waste <= best_waste) {
#     105                 :       1942 :                 best_selection = curr_selection;
#     106                 :       1942 :                 best_selection.resize(utxo_pool.size());
#     107                 :       1942 :                 best_waste = curr_waste;
#     108         [ +  + ]:       1942 :                 if (best_waste == 0) {
#     109                 :       1466 :                     break;
#     110                 :       1466 :                 }
#     111                 :        476 :             }
#     112                 :        476 :             curr_waste -= (curr_value - selection_target); // Remove the excess value as we will be selecting different coins now
#     113                 :        476 :             backtrack = true;
#     114                 :        476 :         }
#     115                 :            : 
#     116                 :            :         // Backtracking, moving backwards
#     117         [ +  + ]:   14236786 :         if (backtrack) {
#     118                 :            :             // Walk backwards to find the last included UTXO that still needs to have its omission branch traversed.
#     119 [ +  + ][ +  + ]:   14060972 :             while (!curr_selection.empty() && !curr_selection.back()) {
#                 [ +  + ]
#     120                 :    9318141 :                 curr_selection.pop_back();
#     121                 :    9318141 :                 curr_available_value += utxo_pool.at(curr_selection.size()).GetSelectionAmount();
#     122                 :    9318141 :             }
#     123                 :            : 
#     124         [ +  + ]:    4742831 :             if (curr_selection.empty()) { // We have walked back to the first utxo and no branch is untraversed. All solutions searched
#     125                 :       5960 :                 break;
#     126                 :       5960 :             }
#     127                 :            : 
#     128                 :            :             // Output was included on previous iterations, try excluding now.
#     129                 :    4736871 :             curr_selection.back() = false;
#     130                 :    4736871 :             OutputGroup& utxo = utxo_pool.at(curr_selection.size() - 1);
#     131                 :    4736871 :             curr_value -= utxo.GetSelectionAmount();
#     132                 :    4736871 :             curr_waste -= utxo.fee - utxo.long_term_fee;
#     133                 :    9492489 :         } else { // Moving forwards, continuing down this branch
#     134                 :    9492489 :             OutputGroup& utxo = utxo_pool.at(curr_selection.size());
#     135                 :            : 
#     136                 :            :             // Remove this utxo from the curr_available_value utxo amount
#     137                 :    9492489 :             curr_available_value -= utxo.GetSelectionAmount();
#     138                 :            : 
#     139                 :            :             // Avoid searching a branch if the previous UTXO has the same value and same waste and was excluded. Since the ratio of fee to
#     140                 :            :             // 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.
#     141 [ +  + ][ +  + ]:    9492489 :             if (!curr_selection.empty() && !curr_selection.back() &&
#                 [ +  + ]
#     142         [ +  + ]:    9492489 :                 utxo.GetSelectionAmount() == utxo_pool.at(curr_selection.size() - 1).GetSelectionAmount() &&
#     143         [ +  - ]:    9492489 :                 utxo.fee == utxo_pool.at(curr_selection.size() - 1).fee) {
#     144                 :    4724143 :                 curr_selection.push_back(false);
#     145                 :    4768346 :             } else {
#     146                 :            :                 // Inclusion branch first (Largest First Exploration)
#     147                 :    4768346 :                 curr_selection.push_back(true);
#     148                 :    4768346 :                 curr_value += utxo.GetSelectionAmount();
#     149                 :    4768346 :                 curr_waste += utxo.fee - utxo.long_term_fee;
#     150                 :    4768346 :             }
#     151                 :    9492489 :         }
#     152                 :   14235320 :     }
#     153                 :            : 
#     154                 :            :     // Check for solution
#     155         [ +  + ]:       7552 :     if (best_selection.empty()) {
#     156                 :       6036 :         return false;
#     157                 :       6036 :     }
#     158                 :            : 
#     159                 :            :     // Set output set
#     160                 :       1516 :     value_ret = 0;
#     161         [ +  + ]:     164257 :     for (size_t i = 0; i < best_selection.size(); ++i) {
#     162         [ +  + ]:     162741 :         if (best_selection.at(i)) {
#     163                 :      32253 :             util::insert(out_set, utxo_pool.at(i).m_outputs);
#     164                 :      32253 :             value_ret += utxo_pool.at(i).m_value;
#     165                 :      32253 :         }
#     166                 :     162741 :     }
#     167                 :            : 
#     168                 :       1516 :     return true;
#     169                 :       1516 : }
#     170                 :            : 
#     171                 :            : static void ApproximateBestSubset(const std::vector<OutputGroup>& groups, const CAmount& nTotalLower, const CAmount& nTargetValue,
#     172                 :            :                                   std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
#     173                 :       2890 : {
#     174                 :       2890 :     std::vector<char> vfIncluded;
#     175                 :            : 
#     176                 :       2890 :     vfBest.assign(groups.size(), true);
#     177                 :       2890 :     nBest = nTotalLower;
#     178                 :            : 
#     179                 :       2890 :     FastRandomContext insecure_rand;
#     180                 :            : 
#     181 [ +  + ][ +  + ]:    2493448 :     for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++)
#     182                 :    2490558 :     {
#     183                 :    2490558 :         vfIncluded.assign(groups.size(), false);
#     184                 :    2490558 :         CAmount nTotal = 0;
#     185                 :    2490558 :         bool fReachedTarget = false;
#     186 [ +  + ][ +  + ]:    6252378 :         for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
#     187                 :    3761820 :         {
#     188         [ +  + ]:  381664306 :             for (unsigned int i = 0; i < groups.size(); i++)
#     189                 :  377902486 :             {
#     190                 :            :                 //The solver here uses a randomized algorithm,
#     191                 :            :                 //the randomness serves no real security purpose but is just
#     192                 :            :                 //needed to prevent degenerate behavior and it is important
#     193                 :            :                 //that the rng is fast. We do not use a constant random sequence,
#     194                 :            :                 //because there may be some privacy improvement by making
#     195                 :            :                 //the selection random.
#     196 [ +  + ][ +  + ]:  377902486 :                 if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i])
#     197                 :  189501083 :                 {
#     198                 :  189501083 :                     nTotal += groups[i].m_value;
#     199                 :  189501083 :                     vfIncluded[i] = true;
#     200         [ +  + ]:  189501083 :                     if (nTotal >= nTargetValue)
#     201                 :  172517119 :                     {
#     202                 :  172517119 :                         fReachedTarget = true;
#     203         [ +  + ]:  172517119 :                         if (nTotal < nBest)
#     204                 :       5499 :                         {
#     205                 :       5499 :                             nBest = nTotal;
#     206                 :       5499 :                             vfBest = vfIncluded;
#     207                 :       5499 :                         }
#     208                 :  172517119 :                         nTotal -= groups[i].m_value;
#     209                 :  172517119 :                         vfIncluded[i] = false;
#     210                 :  172517119 :                     }
#     211                 :  189501083 :                 }
#     212                 :  377902486 :             }
#     213                 :    3761820 :         }
#     214                 :    2490558 :     }
#     215                 :       2890 : }
#     216                 :            : 
#     217                 :            : bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet)
#     218                 :      11373 : {
#     219                 :      11373 :     setCoinsRet.clear();
#     220                 :      11373 :     nValueRet = 0;
#     221                 :            : 
#     222                 :            :     // List of values less than target
#     223                 :      11373 :     std::optional<OutputGroup> lowest_larger;
#     224                 :      11373 :     std::vector<OutputGroup> applicable_groups;
#     225                 :      11373 :     CAmount nTotalLower = 0;
#     226                 :            : 
#     227                 :      11373 :     Shuffle(groups.begin(), groups.end(), FastRandomContext());
#     228                 :            : 
#     229         [ +  + ]:     627473 :     for (const OutputGroup& group : groups) {
#     230         [ +  + ]:     627473 :         if (group.GetSelectionAmount() == nTargetValue) {
#     231                 :       1000 :             util::insert(setCoinsRet, group.m_outputs);
#     232                 :       1000 :             nValueRet += group.m_value;
#     233                 :       1000 :             return true;
#     234         [ +  + ]:     626473 :         } else if (group.GetSelectionAmount() < nTargetValue + MIN_CHANGE) {
#     235                 :     247518 :             applicable_groups.push_back(group);
#     236                 :     247518 :             nTotalLower += group.GetSelectionAmount();
#     237 [ +  + ][ +  + ]:     378955 :         } else if (!lowest_larger || group.GetSelectionAmount() < lowest_larger->GetSelectionAmount()) {
#     238                 :       9603 :             lowest_larger = group;
#     239                 :       9603 :         }
#     240                 :     627473 :     }
#     241                 :            : 
#     242         [ -  + ]:      11373 :     if (nTotalLower == nTargetValue) {
#     243         [ #  # ]:          0 :         for (const auto& group : applicable_groups) {
#     244                 :          0 :             util::insert(setCoinsRet, group.m_outputs);
#     245                 :          0 :             nValueRet += group.m_value;
#     246                 :          0 :         }
#     247                 :          0 :         return true;
#     248                 :          0 :     }
#     249                 :            : 
#     250         [ +  + ]:      10373 :     if (nTotalLower < nTargetValue) {
#     251         [ +  + ]:       8756 :         if (!lowest_larger) return false;
#     252                 :       5516 :         util::insert(setCoinsRet, lowest_larger->m_outputs);
#     253                 :       5516 :         nValueRet += lowest_larger->m_value;
#     254                 :       5516 :         return true;
#     255                 :       5516 :     }
#     256                 :            : 
#     257                 :            :     // Solve subset sum by stochastic approximation
#     258                 :       1617 :     std::sort(applicable_groups.begin(), applicable_groups.end(), descending);
#     259                 :       1617 :     std::vector<char> vfBest;
#     260                 :       1617 :     CAmount nBest;
#     261                 :            : 
#     262                 :       1617 :     ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue, vfBest, nBest);
#     263 [ +  + ][ +  + ]:       1617 :     if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) {
#     264                 :       1273 :         ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest);
#     265                 :       1273 :     }
#     266                 :            : 
#     267                 :            :     // If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
#     268                 :            :     //                                   or the next bigger coin is closer), return the bigger coin
#     269         [ +  + ]:       1617 :     if (lowest_larger &&
#     270 [ +  - ][ +  + ]:       1617 :         ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || lowest_larger->GetSelectionAmount() <= nBest)) {
#                 [ +  + ]
#     271                 :        320 :         util::insert(setCoinsRet, lowest_larger->m_outputs);
#     272                 :        320 :         nValueRet += lowest_larger->m_value;
#     273                 :       1297 :     } else {
#     274         [ +  + ]:     237931 :         for (unsigned int i = 0; i < applicable_groups.size(); i++) {
#     275         [ +  + ]:     236634 :             if (vfBest[i]) {
#     276                 :      89335 :                 util::insert(setCoinsRet, applicable_groups[i].m_outputs);
#     277                 :      89335 :                 nValueRet += applicable_groups[i].m_value;
#     278                 :      89335 :             }
#     279                 :     236634 :         }
#     280                 :            : 
#     281         [ +  - ]:       1297 :         if (LogAcceptCategory(BCLog::SELECTCOINS)) {
#     282         [ +  - ]:       1297 :             LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: "); /* Continued */
#     283         [ +  + ]:     237931 :             for (unsigned int i = 0; i < applicable_groups.size(); i++) {
#     284         [ +  + ]:     236634 :                 if (vfBest[i]) {
#     285         [ +  - ]:      89335 :                     LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(applicable_groups[i].m_value)); /* Continued */
#     286                 :      89335 :                 }
#     287                 :     236634 :             }
#     288         [ +  - ]:       1297 :             LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest));
#     289                 :       1297 :         }
#     290                 :       1297 :     }
#     291                 :            : 
#     292                 :       1617 :     return true;
#     293                 :       1617 : }
#     294                 :            : 
#     295                 :            : /******************************************************************************
#     296                 :            : 
#     297                 :            :  OutputGroup
#     298                 :            : 
#     299                 :            :  ******************************************************************************/
#     300                 :            : 
#     301                 :    1980692 : void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants, bool positive_only) {
#     302                 :            :     // Compute the effective value first
#     303         [ +  + ]:    1980692 :     const CAmount coin_fee = output.m_input_bytes < 0 ? 0 : m_effective_feerate.GetFee(output.m_input_bytes);
#     304                 :    1980692 :     const CAmount ev = output.txout.nValue - coin_fee;
#     305                 :            : 
#     306                 :            :     // Filter for positive only here before adding the coin
#     307 [ +  + ][ +  + ]:    1980692 :     if (positive_only && ev <= 0) return;
#     308                 :            : 
#     309                 :    1980669 :     m_outputs.push_back(output);
#     310                 :    1980669 :     CInputCoin& coin = m_outputs.back();
#     311                 :            : 
#     312                 :    1980669 :     coin.m_fee = coin_fee;
#     313                 :    1980669 :     fee += coin.m_fee;
#     314                 :            : 
#     315         [ +  + ]:    1980669 :     coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 : m_long_term_feerate.GetFee(coin.m_input_bytes);
#     316                 :    1980669 :     long_term_fee += coin.m_long_term_fee;
#     317                 :            : 
#     318                 :    1980669 :     coin.effective_value = ev;
#     319                 :    1980669 :     effective_value += coin.effective_value;
#     320                 :            : 
#     321                 :    1980669 :     m_from_me &= from_me;
#     322                 :    1980669 :     m_value += output.txout.nValue;
#     323                 :    1980669 :     m_depth = std::min(m_depth, depth);
#     324                 :            :     // ancestors here express the number of ancestors the new coin will end up having, which is
#     325                 :            :     // the sum, rather than the max; this will overestimate in the cases where multiple inputs
#     326                 :            :     // have common ancestors
#     327                 :    1980669 :     m_ancestors += ancestors;
#     328                 :            :     // descendants is the count as seen from the top ancestor, not the descendants as seen from the
#     329                 :            :     // coin itself; thus, this value is counted as the max, not the sum
#     330                 :    1980669 :     m_descendants = std::max(m_descendants, descendants);
#     331                 :    1980669 : }
#     332                 :            : 
#     333                 :            : bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const
#     334                 :    1459823 : {
#     335 [ +  + ][ +  + ]:    1459823 :     return m_depth >= (m_from_me ? eligibility_filter.conf_mine : eligibility_filter.conf_theirs)
#     336         [ +  + ]:    1459823 :         && m_ancestors <= eligibility_filter.max_ancestors
#     337         [ +  + ]:    1459823 :         && m_descendants <= eligibility_filter.max_descendants;
#     338                 :    1459823 : }
#     339                 :            : 
#     340                 :            : CAmount OutputGroup::GetSelectionAmount() const
#     341                 :   53455258 : {
#     342         [ +  + ]:   53455258 :     return m_subtract_fee_outputs ? m_value : effective_value;
#     343                 :   53455258 : }

Generated by: LCOV version 1.14