Bitcoin ABC  0.26.3
P2P Digital Currency
coinselection.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017 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 <common/system.h>
8 #include <consensus/amount.h>
9 #include <feerate.h>
10 #include <logging.h>
11 #include <util/insert.h>
12 #include <util/moneystr.h>
13 
14 #include <optional>
15 
16 // Descending order comparator
17 struct {
18  bool operator()(const OutputGroup &a, const OutputGroup &b) const {
19  return a.effective_value > b.effective_value;
20  }
22 
23 static const size_t TOTAL_TRIES = 100000;
24 
73 bool SelectCoinsBnB(std::vector<OutputGroup> &utxo_pool,
74  const Amount &target_value, const Amount &cost_of_change,
75  std::set<CInputCoin> &out_set, Amount &value_ret,
76  const Amount not_input_fees) {
77  out_set.clear();
78  Amount curr_value = Amount::zero();
79 
80  // select the utxo at this index
81  std::vector<bool> curr_selection;
82  curr_selection.reserve(utxo_pool.size());
83  Amount actual_target = not_input_fees + target_value;
84 
85  // Calculate curr_available_value
86  Amount curr_available_value = Amount::zero();
87  for (const OutputGroup &utxo : utxo_pool) {
88  // Assert that this utxo is not negative. It should never be negative,
89  // effective value calculation should have removed it
90  assert(utxo.effective_value > Amount::zero());
91  curr_available_value += utxo.effective_value;
92  }
93  if (curr_available_value < actual_target) {
94  return false;
95  }
96 
97  // Sort the utxo_pool
98  std::sort(utxo_pool.begin(), utxo_pool.end(), descending);
99 
100  Amount curr_waste = Amount::zero();
101  std::vector<bool> best_selection;
102  Amount best_waste = MAX_MONEY;
103 
104  // Depth First search loop for choosing the UTXOs
105  for (size_t i = 0; i < TOTAL_TRIES; ++i) {
106  // Conditions for starting a backtrack
107  bool backtrack = false;
108  if (curr_value + curr_available_value <
109  actual_target || // Cannot possibly reach target with the amount
110  // remaining in the curr_available_value.
111  curr_value >
112  actual_target +
113  cost_of_change || // Selected value is out of range, go back
114  // and try other branch
115  (curr_waste > best_waste &&
116  (utxo_pool.at(0).fee - utxo_pool.at(0).long_term_fee) >
117  Amount::zero())) {
118  // Don't select things which we know will be more wasteful if the
119  // waste is increasing
120  backtrack = true;
121  }
122 
123  // Selected value is within range
124  else if (curr_value >= actual_target) {
125  // This is the excess value which is added to the waste for the
126  // below comparison. Adding another UTXO after this check could
127  // bring the waste down if the long term fee is higher than the
128  // current fee. However we are not going to explore that because
129  // this optimization for the waste is only done when we have hit our
130  // target value. Adding any more UTXOs will be just burning the
131  // UTXO; it will go entirely to fees. Thus we aren't going to
132  // explore any more UTXOs to avoid burning money like that.
133  curr_waste += (curr_value - actual_target);
134 
135  if (curr_waste <= best_waste) {
136  best_selection = curr_selection;
137  best_selection.resize(utxo_pool.size());
138  best_waste = curr_waste;
139  if (best_waste == Amount::zero()) {
140  break;
141  }
142  }
143  // Remove the excess value as we will be selecting different coins
144  // now
145  curr_waste -= (curr_value - actual_target);
146  backtrack = true;
147  }
148 
149  // Backtracking, moving backwards
150  if (backtrack) {
151  // Walk backwards to find the last included UTXO that still needs to
152  // have its omission branch traversed.
153  while (!curr_selection.empty() && !curr_selection.back()) {
154  curr_selection.pop_back();
155  curr_available_value +=
156  utxo_pool.at(curr_selection.size()).effective_value;
157  }
158 
159  if (curr_selection.empty()) {
160  // We have walked back to the first utxo and no branch is
161  // untraversed. All solutions searched
162  break;
163  }
164 
165  // Output was included on previous iterations, try excluding now.
166  curr_selection.back() = false;
167  OutputGroup &utxo = utxo_pool.at(curr_selection.size() - 1);
168  curr_value -= utxo.effective_value;
169  curr_waste -= utxo.fee - utxo.long_term_fee;
170  }
171 
172  // Moving forwards, continuing down this branch
173  else {
174  OutputGroup &utxo = utxo_pool.at(curr_selection.size());
175 
176  // Remove this utxo from the curr_available_value utxo amount
177  curr_available_value -= utxo.effective_value;
178 
179  // Avoid searching a branch if the previous UTXO has the same value
180  // and same waste and was excluded. Since the ratio of fee to long
181  // term fee is the same, we only need to check if one of those
182  // values match in order to know that the waste is the same.
183  if (!curr_selection.empty() && !curr_selection.back() &&
184  utxo.effective_value ==
185  utxo_pool.at(curr_selection.size() - 1).effective_value &&
186  utxo.fee == utxo_pool.at(curr_selection.size() - 1).fee) {
187  curr_selection.push_back(false);
188  } else {
189  // Inclusion branch first (Largest First Exploration)
190  curr_selection.push_back(true);
191  curr_value += utxo.effective_value;
192  curr_waste += utxo.fee - utxo.long_term_fee;
193  }
194  }
195  }
196 
197  // Check for solution
198  if (best_selection.empty()) {
199  return false;
200  }
201 
202  // Set output set
203  value_ret = Amount::zero();
204  for (size_t i = 0; i < best_selection.size(); ++i) {
205  if (best_selection.at(i)) {
206  util::insert(out_set, utxo_pool.at(i).m_outputs);
207  value_ret += utxo_pool.at(i).m_value;
208  }
209  }
210 
211  return true;
212 }
213 
214 static void ApproximateBestSubset(const std::vector<OutputGroup> &groups,
215  const Amount &nTotalLower,
216  const Amount &nTargetValue,
217  std::vector<char> &vfBest, Amount &nBest,
218  int iterations = 1000) {
219  std::vector<char> vfIncluded;
220 
221  vfBest.assign(groups.size(), true);
222  nBest = nTotalLower;
223 
224  FastRandomContext insecure_rand;
225 
226  for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) {
227  vfIncluded.assign(groups.size(), false);
228  Amount nTotal = Amount::zero();
229  bool fReachedTarget = false;
230  for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) {
231  for (size_t i = 0; i < groups.size(); i++) {
232  // The solver here uses a randomized algorithm, the randomness
233  // serves no real security purpose but is just needed to prevent
234  // degenerate behavior and it is important that the rng is fast.
235  // We do not use a constant random sequence, because there may
236  // be some privacy improvement by making the selection random.
237  if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i]) {
238  nTotal += groups[i].m_value;
239  vfIncluded[i] = true;
240  if (nTotal >= nTargetValue) {
241  fReachedTarget = true;
242  if (nTotal < nBest) {
243  nBest = nTotal;
244  vfBest = vfIncluded;
245  }
246 
247  nTotal -= groups[i].m_value;
248  vfIncluded[i] = false;
249  }
250  }
251  }
252  }
253  }
254 }
255 
256 bool KnapsackSolver(const Amount nTargetValue, std::vector<OutputGroup> &groups,
257  std::set<CInputCoin> &setCoinsRet, Amount &nValueRet) {
258  setCoinsRet.clear();
259  nValueRet = Amount::zero();
260 
261  // List of values less than target
262  std::optional<OutputGroup> lowest_larger;
263  std::vector<OutputGroup> applicable_groups;
264  Amount nTotalLower = Amount::zero();
265 
266  Shuffle(groups.begin(), groups.end(), FastRandomContext());
267 
268  for (const OutputGroup &group : groups) {
269  if (group.m_value == nTargetValue) {
270  util::insert(setCoinsRet, group.m_outputs);
271  nValueRet += group.m_value;
272  return true;
273  } else if (group.m_value < nTargetValue + MIN_CHANGE) {
274  applicable_groups.push_back(group);
275  nTotalLower += group.m_value;
276  } else if (!lowest_larger || group.m_value < lowest_larger->m_value) {
277  lowest_larger = group;
278  }
279  }
280 
281  if (nTotalLower == nTargetValue) {
282  for (const auto &group : applicable_groups) {
283  util::insert(setCoinsRet, group.m_outputs);
284  nValueRet += group.m_value;
285  }
286  return true;
287  }
288 
289  if (nTotalLower < nTargetValue) {
290  if (!lowest_larger) {
291  return false;
292  }
293  util::insert(setCoinsRet, lowest_larger->m_outputs);
294  nValueRet += lowest_larger->m_value;
295  return true;
296  }
297 
298  // Solve subset sum by stochastic approximation
299  std::sort(applicable_groups.begin(), applicable_groups.end(), descending);
300  std::vector<char> vfBest;
301  Amount nBest;
302 
303  ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue, vfBest,
304  nBest);
305  if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) {
306  ApproximateBestSubset(applicable_groups, nTotalLower,
307  nTargetValue + MIN_CHANGE, vfBest, nBest);
308  }
309 
310  // If we have a bigger coin and (either the stochastic approximation didn't
311  // find a good solution, or the next bigger coin is closer), return the
312  // bigger coin
313  if (lowest_larger &&
314  ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) ||
315  lowest_larger->m_value <= nBest)) {
316  util::insert(setCoinsRet, lowest_larger->m_outputs);
317  nValueRet += lowest_larger->m_value;
318  } else {
319  for (size_t i = 0; i < applicable_groups.size(); i++) {
320  if (vfBest[i]) {
321  util::insert(setCoinsRet, applicable_groups[i].m_outputs);
322  nValueRet += applicable_groups[i].m_value;
323  }
324  }
325 
327  /* Continued */
329  "SelectCoins() best subset: ");
330  for (size_t i = 0; i < applicable_groups.size(); i++) {
331  if (vfBest[i]) {
332  /* Continued */
334  BCLog::SELECTCOINS, "%s ",
335  FormatMoney(applicable_groups[i].m_value));
336  }
337  }
338  LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest));
339  }
340  }
341 
342  return true;
343 }
344 
345 /******************************************************************************
346 
347  OutputGroup
348 
349  ******************************************************************************/
350 
351 void OutputGroup::Insert(const CInputCoin &output, int depth, bool from_me,
352  bool positive_only) {
353  // Compute the effective value first
354  const Amount coin_fee =
355  output.m_input_bytes < 0
356  ? Amount::zero()
358  const Amount ev = output.txout.nValue - coin_fee;
359 
360  // Filter for positive only here before adding the coin
361  if (positive_only && ev <= Amount::zero()) {
362  return;
363  }
364 
365  m_outputs.push_back(output);
366  CInputCoin &coin = m_outputs.back();
367 
368  coin.m_fee = coin_fee;
369  fee += coin.m_fee;
370 
371  coin.m_long_term_fee = coin.m_input_bytes < 0
372  ? Amount::zero()
375 
376  coin.effective_value = ev;
378 
379  m_from_me &= from_me;
380  m_value += output.txout.nValue;
381  m_depth = std::min(m_depth, depth);
382 }
383 
385  const CoinEligibilityFilter &eligibility_filter) const {
386  return m_depth >= (m_from_me ? eligibility_filter.conf_mine
387  : eligibility_filter.conf_theirs);
388 }
static constexpr Amount MAX_MONEY
No amount larger than this (in satoshi) is valid.
Definition: amount.h:165
Amount GetFee(size_t nBytes) const
Return the fee in satoshis for the given size in bytes.
Definition: feerate.cpp:49
int m_input_bytes
Pre-computed estimated size of this output as a fully-signed input in a transaction.
Definition: coinselection.h:47
Amount m_fee
Definition: coinselection.h:40
Amount m_long_term_fee
Definition: coinselection.h:41
CTxOut txout
Definition: coinselection.h:38
Amount effective_value
Definition: coinselection.h:39
Amount nValue
Definition: transaction.h:130
Fast randomness source.
Definition: random.h:156
bool randbool() noexcept
Generate a random boolean.
Definition: random.h:256
static void ApproximateBestSubset(const std::vector< OutputGroup > &groups, const Amount &nTotalLower, const Amount &nTargetValue, std::vector< char > &vfBest, Amount &nBest, int iterations=1000)
static const size_t TOTAL_TRIES
bool KnapsackSolver(const Amount nTargetValue, std::vector< OutputGroup > &groups, std::set< CInputCoin > &setCoinsRet, Amount &nValueRet)
bool SelectCoinsBnB(std::vector< OutputGroup > &utxo_pool, const Amount &target_value, const Amount &cost_of_change, std::set< CInputCoin > &out_set, Amount &value_ret, const Amount not_input_fees)
This is the Branch and Bound Coin Selection algorithm designed by Murch.
struct @19 descending
static constexpr Amount MIN_CHANGE
target minimum change amount
Definition: coinselection.h:13
static bool LogAcceptCategory(BCLog::LogFlags category)
Return true if log accepts specified category.
Definition: logging.h:177
#define LogPrint(category,...)
Definition: logging.h:211
#define LogPrintToBeContinued
Definition: logging.h:224
std::string FormatMoney(const Amount amt)
Do not use these functions to represent or parse monetary amounts to or from JSON but use AmountFromV...
Definition: moneystr.cpp:13
@ SELECTCOINS
Definition: logging.h:50
void insert(Tdst &dst, const Tsrc &src)
Simplification of std insertion.
Definition: insert.h:14
void Shuffle(I first, I last, R &&rng)
More efficient than using std::shuffle on a FastRandomContext.
Definition: random.h:291
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32
const int conf_theirs
Minimum number of confirmations for outputs received from a different wallet.
Definition: coinselection.h:68
std::vector< CInputCoin > m_outputs
Definition: coinselection.h:82
Amount m_value
Definition: coinselection.h:84
CFeeRate m_long_term_feerate
Definition: coinselection.h:90
Amount long_term_fee
Definition: coinselection.h:89
CFeeRate m_effective_feerate
Definition: coinselection.h:88
void Insert(const CInputCoin &output, int depth, bool from_me, bool positive_only)
Amount effective_value
Definition: coinselection.h:86
bool EligibleForSpending(const CoinEligibilityFilter &eligibility_filter) const
assert(!tx.IsCoinBase())