Bitcoin Core  22.99.0
P2P Digital Currency
coinselector_tests.cpp
Go to the documentation of this file.
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>
8 #include <random.h>
10 #include <util/translation.h>
11 #include <wallet/coincontrol.h>
12 #include <wallet/coinselection.h>
13 #include <wallet/spend.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 #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 #define RANDOM_REPEATS 5
30 
31 typedef std::set<CInputCoin> 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<CInputCoin>& set)
39 {
41  tx.vout.resize(nInput + 1);
42  tx.vout[nInput].nValue = nValue;
43  tx.nLockTime = nextLockTime++; // so all transactions get different hashes
44  set.emplace_back(MakeTransactionRef(tx), nInput);
45 }
46 
47 static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
48 {
50  tx.vout.resize(nInput + 1);
51  tx.vout[nInput].nValue = nValue;
52  tx.nLockTime = nextLockTime++; // so all transactions get different hashes
53  CInputCoin coin(MakeTransactionRef(tx), nInput);
54  OutputGroup group;
55  group.Insert(coin, 1, false, 0, 0, true);
56  result.AddInput(group);
57 }
58 
59 static void add_coin(const CAmount& nValue, int nInput, CoinSet& set, CAmount fee = 0, CAmount long_term_fee = 0)
60 {
62  tx.vout.resize(nInput + 1);
63  tx.vout[nInput].nValue = nValue;
64  tx.nLockTime = nextLockTime++; // so all transactions get different hashes
65  CInputCoin coin(MakeTransactionRef(tx), nInput);
66  coin.effective_value = nValue - fee;
67  coin.m_fee = fee;
68  coin.m_long_term_fee = long_term_fee;
69  set.insert(coin);
70 }
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 {
75  tx.nLockTime = nextLockTime++; // so all transactions get different hashes
76  tx.vout.resize(nInput + 1);
77  tx.vout[nInput].nValue = nValue;
78  if (spendable) {
79  CTxDestination dest;
81  const bool destination_ok = wallet.GetNewDestination(OutputType::BECH32, "", dest, error);
82  assert(destination_ok);
83  tx.vout[nInput].scriptPubKey = GetScriptForDestination(dest);
84  }
85  if (fIsFromMe) {
86  // IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(),
87  // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
88  tx.vin.resize(1);
89  }
90  uint256 txid = tx.GetHash();
91 
92  LOCK(wallet.cs_wallet);
93  auto ret = wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(txid), std::forward_as_tuple(MakeTransactionRef(std::move(tx)), TxStateInactive{}));
94  assert(ret.second);
95  CWalletTx& wtx = (*ret.first).second;
96  if (fIsFromMe)
97  {
99  wtx.m_is_cache_empty = false;
100  }
101  COutput output(wallet, wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
102  coins.push_back(output);
103 }
104 
107 static bool EquivalentResult(const SelectionResult& a, const SelectionResult& b)
108 {
109  std::vector<CAmount> a_amts;
110  std::vector<CAmount> b_amts;
111  for (const auto& coin : a.GetInputSet()) {
112  a_amts.push_back(coin.txout.nValue);
113  }
114  for (const auto& coin : b.GetInputSet()) {
115  b_amts.push_back(coin.txout.nValue);
116  }
117  std::sort(a_amts.begin(), a_amts.end());
118  std::sort(b_amts.begin(), b_amts.end());
119 
120  std::pair<std::vector<CAmount>::iterator, std::vector<CAmount>::iterator> ret = std::mismatch(a_amts.begin(), a_amts.end(), b_amts.begin());
121  return ret.first == a_amts.end() && ret.second == b_amts.end();
122 }
123 
125 static bool EqualResult(const SelectionResult& a, const SelectionResult& b)
126 {
127  std::pair<CoinSet::iterator, CoinSet::iterator> ret = std::mismatch(a.GetInputSet().begin(), a.GetInputSet().end(), b.GetInputSet().begin());
128  return ret.first == a.GetInputSet().end() && ret.second == b.GetInputSet().end();
129 }
130 
131 static CAmount make_hard_case(int utxos, std::vector<CInputCoin>& utxo_pool)
132 {
133  utxo_pool.clear();
134  CAmount target = 0;
135  for (int i = 0; i < utxos; ++i) {
136  target += (CAmount)1 << (utxos+i);
137  add_coin((CAmount)1 << (utxos+i), 2*i, utxo_pool);
138  add_coin(((CAmount)1 << (utxos+i)) + ((CAmount)1 << (utxos-1-i)), 2*i + 1, utxo_pool);
139  }
140  return target;
141 }
142 
143 inline std::vector<OutputGroup>& GroupCoins(const std::vector<CInputCoin>& coins)
144 {
145  static std::vector<OutputGroup> static_groups;
146  static_groups.clear();
147  for (auto& coin : coins) {
148  static_groups.emplace_back();
149  static_groups.back().Insert(coin, 0, true, 0, 0, false);
150  }
151  return static_groups;
152 }
153 
154 inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
155 {
156  static std::vector<OutputGroup> static_groups;
157  static_groups.clear();
158  for (auto& coin : coins) {
159  static_groups.emplace_back();
160  static_groups.back().Insert(coin.GetInputCoin(), coin.nDepth, coin.tx->m_amounts[CWalletTx::DEBIT].m_cached[ISMINE_SPENDABLE] && coin.tx->m_amounts[CWalletTx::DEBIT].m_value[ISMINE_SPENDABLE] == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0, false);
161  }
162  return static_groups;
163 }
164 
165 inline std::vector<OutputGroup>& KnapsackGroupOutputs(const std::vector<COutput>& coins, CWallet& wallet, const CoinEligibilityFilter& filter)
166 {
167  CoinSelectionParams coin_selection_params(/* change_output_size= */ 0,
168  /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0),
169  /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
170  /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
171  static std::vector<OutputGroup> static_groups;
172  static_groups = GroupOutputs(wallet, coins, coin_selection_params, filter, /*positive_only=*/false);
173  return static_groups;
174 }
175 
176 // Branch and bound coin selection tests
177 BOOST_AUTO_TEST_CASE(bnb_search_test)
178 {
179  // Setup
180  std::vector<CInputCoin> utxo_pool;
181  SelectionResult expected_result(CAmount(0));
182 
184  // Known Outcome tests //
186 
187  // Empty utxo pool
188  BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT));
189 
190  // Add utxos
191  add_coin(1 * CENT, 1, utxo_pool);
192  add_coin(2 * CENT, 2, utxo_pool);
193  add_coin(3 * CENT, 3, utxo_pool);
194  add_coin(4 * CENT, 4, utxo_pool);
195 
196  // Select 1 Cent
197  add_coin(1 * CENT, 1, expected_result);
198  const auto result1 = SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT);
199  BOOST_CHECK(result1);
200  BOOST_CHECK(EquivalentResult(expected_result, *result1));
201  BOOST_CHECK_EQUAL(result1->GetSelectedValue(), 1 * CENT);
202  expected_result.Clear();
203 
204  // Select 2 Cent
205  add_coin(2 * CENT, 2, expected_result);
206  const auto result2 = SelectCoinsBnB(GroupCoins(utxo_pool), 2 * CENT, 0.5 * CENT);
207  BOOST_CHECK(result2);
208  BOOST_CHECK(EquivalentResult(expected_result, *result2));
209  BOOST_CHECK_EQUAL(result2->GetSelectedValue(), 2 * CENT);
210  expected_result.Clear();
211 
212  // Select 5 Cent
213  add_coin(4 * CENT, 4, expected_result);
214  add_coin(1 * CENT, 1, expected_result);
215  const auto result3 = SelectCoinsBnB(GroupCoins(utxo_pool), 5 * CENT, 0.5 * CENT);
216  BOOST_CHECK(result3);
217  BOOST_CHECK(EquivalentResult(expected_result, *result3));
218  BOOST_CHECK_EQUAL(result3->GetSelectedValue(), 5 * CENT);
219  expected_result.Clear();
220 
221  // Select 11 Cent, not possible
222  BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 11 * CENT, 0.5 * CENT));
223  expected_result.Clear();
224 
225  // Cost of change is greater than the difference between target value and utxo sum
226  add_coin(1 * CENT, 1, expected_result);
227  const auto result4 = SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0.5 * CENT);
228  BOOST_CHECK(result4);
229  BOOST_CHECK_EQUAL(result4->GetSelectedValue(), 1 * CENT);
230  BOOST_CHECK(EquivalentResult(expected_result, *result4));
231  expected_result.Clear();
232 
233  // Cost of change is less than the difference between target value and utxo sum
234  BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0));
235  expected_result.Clear();
236 
237  // Select 10 Cent
238  add_coin(5 * CENT, 5, utxo_pool);
239  add_coin(5 * CENT, 5, expected_result);
240  add_coin(4 * CENT, 4, expected_result);
241  add_coin(1 * CENT, 1, expected_result);
242  const auto result5 = SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 0.5 * CENT);
243  BOOST_CHECK(result5);
244  BOOST_CHECK(EquivalentResult(expected_result, *result5));
245  BOOST_CHECK_EQUAL(result5->GetSelectedValue(), 10 * CENT);
246  expected_result.Clear();
247 
248  // Negative effective value
249  // Select 10 Cent but have 1 Cent not be possible because too small
250  add_coin(5 * CENT, 5, expected_result);
251  add_coin(3 * CENT, 3, expected_result);
252  add_coin(2 * CENT, 2, expected_result);
253  const auto result6 = SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 5000);
254  BOOST_CHECK(result6);
255  BOOST_CHECK_EQUAL(result6->GetSelectedValue(), 10 * CENT);
256  // FIXME: this test is redundant with the above, because 1 Cent is selected, not "too small"
257  // BOOST_CHECK(EquivalentResult(expected_result, *result));
258 
259  // Select 0.25 Cent, not possible
260  BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.25 * CENT, 0.5 * CENT));
261  expected_result.Clear();
262 
263  // Iteration exhaustion test
264  CAmount target = make_hard_case(17, utxo_pool);
265  BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), target, 0)); // Should exhaust
266  target = make_hard_case(14, utxo_pool);
267  const auto result7 = SelectCoinsBnB(GroupCoins(utxo_pool), target, 0); // Should not exhaust
268  BOOST_CHECK(result7);
269 
270  // Test same value early bailout optimization
271  utxo_pool.clear();
272  add_coin(7 * CENT, 7, expected_result);
273  add_coin(7 * CENT, 7, expected_result);
274  add_coin(7 * CENT, 7, expected_result);
275  add_coin(7 * CENT, 7, expected_result);
276  add_coin(2 * CENT, 7, expected_result);
277  add_coin(7 * CENT, 7, utxo_pool);
278  add_coin(7 * CENT, 7, utxo_pool);
279  add_coin(7 * CENT, 7, utxo_pool);
280  add_coin(7 * CENT, 7, utxo_pool);
281  add_coin(2 * CENT, 7, utxo_pool);
282  for (int i = 0; i < 50000; ++i) {
283  add_coin(5 * CENT, 7, utxo_pool);
284  }
285  const auto result8 = SelectCoinsBnB(GroupCoins(utxo_pool), 30 * CENT, 5000);
286  BOOST_CHECK(result8);
287  BOOST_CHECK_EQUAL(result8->GetSelectedValue(), 30 * CENT);
288  BOOST_CHECK(EquivalentResult(expected_result, *result8));
289 
291  // Behavior tests //
293  // Select 1 Cent with pool of only greater than 5 Cent
294  utxo_pool.clear();
295  for (int i = 5; i <= 20; ++i) {
296  add_coin(i * CENT, i, utxo_pool);
297  }
298  // Run 100 times, to make sure it is never finding a solution
299  for (int i = 0; i < 100; ++i) {
300  BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 2 * CENT));
301  }
302 
303  // Make sure that effective value is working in AttemptSelection when BnB is used
304  CoinSelectionParams coin_selection_params_bnb(/* change_output_size= */ 0,
305  /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000),
306  /* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000),
307  /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
308  {
309  std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
310  wallet->LoadWallet();
311  LOCK(wallet->cs_wallet);
312  wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
313  wallet->SetupDescriptorScriptPubKeyMans();
314 
315  std::vector<COutput> coins;
316 
317  add_coin(coins, *wallet, 1);
318  coins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
319  BOOST_CHECK(!SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change));
320 
321  // Test fees subtracted from output:
322  coins.clear();
323  add_coin(coins, *wallet, 1 * CENT);
324  coins.at(0).nInputBytes = 40;
325  coin_selection_params_bnb.m_subtract_fee_outputs = true;
326  const auto result9 = SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change);
327  BOOST_CHECK(result9);
328  BOOST_CHECK_EQUAL(result9->GetSelectedValue(), 1 * CENT);
329  }
330 
331  {
332  std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
333  wallet->LoadWallet();
334  LOCK(wallet->cs_wallet);
335  wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
336  wallet->SetupDescriptorScriptPubKeyMans();
337 
338  std::vector<COutput> coins;
339 
340  add_coin(coins, *wallet, 5 * CENT, 6 * 24, false, 0, true);
341  add_coin(coins, *wallet, 3 * CENT, 6 * 24, false, 0, true);
342  add_coin(coins, *wallet, 2 * CENT, 6 * 24, false, 0, true);
343  CCoinControl coin_control;
344  coin_control.fAllowOtherInputs = true;
345  coin_control.Select(COutPoint(coins.at(0).tx->GetHash(), coins.at(0).i));
346  coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
347  const auto result10 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
348  BOOST_CHECK(result10);
349  }
350 }
351 
352 BOOST_AUTO_TEST_CASE(knapsack_solver_test)
353 {
354  std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
355  wallet->LoadWallet();
356  LOCK(wallet->cs_wallet);
357  wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
358  wallet->SetupDescriptorScriptPubKeyMans();
359 
360  std::vector<COutput> coins;
361 
362  // test multiple times to allow for differences in the shuffle order
363  for (int i = 0; i < RUN_TESTS; i++)
364  {
365  coins.clear();
366 
367  // with an empty wallet we can't even pay one cent
369 
370  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
374 
375  // but we can find a new 1 cent
376  const auto result1 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 1 * CENT);
377  BOOST_CHECK(result1);
378  BOOST_CHECK_EQUAL(result1->GetSelectedValue(), 1 * CENT);
379 
380  add_coin(coins, *wallet, 2*CENT); // add a mature 2 cent coin
381 
382  // we can't make 3 cents of mature coins
384 
385  // we can make 3 cents of new coins
386  const auto result2 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 3 * CENT);
387  BOOST_CHECK(result2);
388  BOOST_CHECK_EQUAL(result2->GetSelectedValue(), 3 * CENT);
389 
390  add_coin(coins, *wallet, 5*CENT); // add a mature 5 cent coin,
391  add_coin(coins, *wallet, 10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
392  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:
398  // we can't even make 37 cents if we don't allow new coins even if they're from us
400  // but we can make 37 cents if we accept new coins from ourself
401  const auto result3 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 37 * CENT);
402  BOOST_CHECK(result3);
403  BOOST_CHECK_EQUAL(result3->GetSelectedValue(), 37 * CENT);
404  // and we can make 38 cents if we accept all new coins
405  const auto result4 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 38 * CENT);
406  BOOST_CHECK(result4);
407  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  const auto result5 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 34 * CENT);
411  BOOST_CHECK(result5);
412  BOOST_CHECK_EQUAL(result5->GetSelectedValue(), 35 * CENT); // but 35 cents is closest
413  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  const auto result6 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 7 * CENT);
417  BOOST_CHECK(result6);
418  BOOST_CHECK_EQUAL(result6->GetSelectedValue(), 7 * CENT);
419  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  const auto result7 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 8 * CENT);
423  BOOST_CHECK(result7);
424  BOOST_CHECK(result7->GetSelectedValue() == 8 * CENT);
425  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  const auto result8 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 9 * CENT);
429  BOOST_CHECK(result8);
430  BOOST_CHECK_EQUAL(result8->GetSelectedValue(), 10 * CENT);
431  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  coins.clear();
435 
436  add_coin(coins, *wallet, 6*CENT);
437  add_coin(coins, *wallet, 7*CENT);
438  add_coin(coins, *wallet, 8*CENT);
439  add_coin(coins, *wallet, 20*CENT);
440  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  const auto result9 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 71 * CENT);
444  BOOST_CHECK(result9);
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  const auto result10 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 16 * CENT);
449  BOOST_CHECK(result10);
450  BOOST_CHECK_EQUAL(result10->GetSelectedValue(), 20 * CENT); // we should get 20 in one coin
451  BOOST_CHECK_EQUAL(result10->GetInputSet().size(), 1U);
452 
453  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  const auto result11 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 16 * CENT);
457  BOOST_CHECK(result11);
458  BOOST_CHECK_EQUAL(result11->GetSelectedValue(), 18 * CENT); // we should get 18 in 3 coins
459  BOOST_CHECK_EQUAL(result11->GetInputSet().size(), 3U);
460 
461  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  const auto result12 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 16 * CENT);
465  BOOST_CHECK(result12);
466  BOOST_CHECK_EQUAL(result12->GetSelectedValue(), 18 * CENT); // we should get 18 in 1 coin
467  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  const auto result13 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 11 * CENT);
471  BOOST_CHECK(result13);
472  BOOST_CHECK_EQUAL(result13->GetSelectedValue(), 11 * CENT);
473  BOOST_CHECK_EQUAL(result13->GetInputSet().size(), 2U);
474 
475  // check that the smallest bigger coin is used
476  add_coin(coins, *wallet, 1*COIN);
477  add_coin(coins, *wallet, 2*COIN);
478  add_coin(coins, *wallet, 3*COIN);
479  add_coin(coins, *wallet, 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
480  const auto result14 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 95 * CENT);
481  BOOST_CHECK(result14);
482  BOOST_CHECK_EQUAL(result14->GetSelectedValue(), 1 * COIN); // we should get 1 BTC in 1 coin
483  BOOST_CHECK_EQUAL(result14->GetInputSet().size(), 1U);
484 
485  const auto result15 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 195 * CENT);
486  BOOST_CHECK(result15);
487  BOOST_CHECK_EQUAL(result15->GetSelectedValue(), 2 * COIN); // we should get 2 BTC in 1 coin
488  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  coins.clear();
493  add_coin(coins, *wallet, MIN_CHANGE * 1 / 10);
494  add_coin(coins, *wallet, MIN_CHANGE * 2 / 10);
495  add_coin(coins, *wallet, MIN_CHANGE * 3 / 10);
496  add_coin(coins, *wallet, MIN_CHANGE * 4 / 10);
497  add_coin(coins, *wallet, MIN_CHANGE * 5 / 10);
498 
499  // try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
500  // we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
501  const auto result16 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), MIN_CHANGE);
502  BOOST_CHECK(result16);
503  BOOST_CHECK_EQUAL(result16->GetSelectedValue(), MIN_CHANGE);
504 
505  // but if we add a bigger coin, small change is avoided
506  add_coin(coins, *wallet, 1111*MIN_CHANGE);
507 
508  // try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
509  const auto result17 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 1 * MIN_CHANGE);
510  BOOST_CHECK(result17);
511  BOOST_CHECK_EQUAL(result17->GetSelectedValue(), 1 * MIN_CHANGE); // we should get the exact amount
512 
513  // if we add more small coins:
514  add_coin(coins, *wallet, MIN_CHANGE * 6 / 10);
515  add_coin(coins, *wallet, MIN_CHANGE * 7 / 10);
516 
517  // and try again to make 1.0 * MIN_CHANGE
518  const auto result18 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 1 * MIN_CHANGE);
519  BOOST_CHECK(result18);
520  BOOST_CHECK_EQUAL(result18->GetSelectedValue(), 1 * MIN_CHANGE); // 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  coins.clear();
525  for (int j = 0; j < 20; j++)
526  add_coin(coins, *wallet, 50000 * COIN);
527 
528  const auto result19 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 500000 * COIN);
529  BOOST_CHECK(result19);
530  BOOST_CHECK_EQUAL(result19->GetSelectedValue(), 500000 * COIN); // we should get the exact amount
531  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 * MIN_CHANGE 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  coins.clear();
538  add_coin(coins, *wallet, MIN_CHANGE * 5 / 10);
539  add_coin(coins, *wallet, MIN_CHANGE * 6 / 10);
540  add_coin(coins, *wallet, MIN_CHANGE * 7 / 10);
541  add_coin(coins, *wallet, 1111 * MIN_CHANGE);
542  const auto result20 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 1 * MIN_CHANGE);
543  BOOST_CHECK(result20);
544  BOOST_CHECK_EQUAL(result20->GetSelectedValue(), 1111 * MIN_CHANGE); // we get the bigger coin
545  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  coins.clear();
549  add_coin(coins, *wallet, MIN_CHANGE * 4 / 10);
550  add_coin(coins, *wallet, MIN_CHANGE * 6 / 10);
551  add_coin(coins, *wallet, MIN_CHANGE * 8 / 10);
552  add_coin(coins, *wallet, 1111 * MIN_CHANGE);
553  const auto result21 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), MIN_CHANGE);
554  BOOST_CHECK(result21);
555  BOOST_CHECK_EQUAL(result21->GetSelectedValue(), MIN_CHANGE); // we should get the exact amount
556  BOOST_CHECK_EQUAL(result21->GetInputSet().size(), 2U); // in two coins 0.4+0.6
557 
558  // test avoiding small change
559  coins.clear();
560  add_coin(coins, *wallet, MIN_CHANGE * 5 / 100);
561  add_coin(coins, *wallet, MIN_CHANGE * 1);
562  add_coin(coins, *wallet, MIN_CHANGE * 100);
563 
564  // trying to make 100.01 from these three coins
565  const auto result22 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), MIN_CHANGE * 10001 / 100);
566  BOOST_CHECK(result22);
567  BOOST_CHECK_EQUAL(result22->GetSelectedValue(), MIN_CHANGE * 10105 / 100); // we should get all coins
568  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  const auto result23 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), MIN_CHANGE * 9990 / 100);
572  BOOST_CHECK(result23);
573  BOOST_CHECK_EQUAL(result23->GetSelectedValue(), 101 * MIN_CHANGE);
574  BOOST_CHECK_EQUAL(result23->GetInputSet().size(), 2U);
575  }
576 
577  // test with many inputs
578  for (CAmount amt=1500; amt < COIN; amt*=10) {
579  coins.clear();
580  // Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
581  for (uint16_t j = 0; j < 676; j++)
582  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  for (int i = 0; i < RUN_TESTS; i++) {
586  const auto result24 = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_confirmed), 2000);
587  BOOST_CHECK(result24);
588 
589  if (amt - 2000 < MIN_CHANGE) {
590  // needs more than one input:
591  uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt);
592  CAmount returnValue = amt * returnSize;
593  BOOST_CHECK_EQUAL(result24->GetSelectedValue(), returnValue);
594  BOOST_CHECK_EQUAL(result24->GetInputSet().size(), returnSize);
595  } else {
596  // one input is sufficient:
597  BOOST_CHECK_EQUAL(result24->GetSelectedValue(), amt);
598  BOOST_CHECK_EQUAL(result24->GetInputSet().size(), 1U);
599  }
600  }
601  }
602 
603  // test randomness
604  {
605  coins.clear();
606  for (int i2 = 0; i2 < 100; i2++)
607  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  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  const auto result25 = KnapsackSolver(GroupCoins(coins), 50 * COIN);
614  BOOST_CHECK(result25);
615  const auto result26 = KnapsackSolver(GroupCoins(coins), 50 * COIN);
616  BOOST_CHECK(result26);
617  BOOST_CHECK(!EqualResult(*result25, *result26));
618 
619  int fails = 0;
620  for (int j = 0; j < RANDOM_REPEATS; j++)
621  {
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  const auto result27 = KnapsackSolver(GroupCoins(coins), COIN);
627  BOOST_CHECK(result27);
628  const auto result28 = KnapsackSolver(GroupCoins(coins), COIN);
629  BOOST_CHECK(result28);
630  if (EqualResult(*result27, *result28))
631  fails++;
632  }
633  BOOST_CHECK_NE(fails, RANDOM_REPEATS);
634  }
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  add_coin(coins, *wallet, 5 * CENT);
640  add_coin(coins, *wallet, 10 * CENT);
641  add_coin(coins, *wallet, 15 * CENT);
642  add_coin(coins, *wallet, 20 * CENT);
643  add_coin(coins, *wallet, 25 * CENT);
644 
645  for (int i = 0; i < RUN_TESTS; i++) {
646  int fails = 0;
647  for (int j = 0; j < RANDOM_REPEATS; j++)
648  {
649  const auto result29 = KnapsackSolver(GroupCoins(coins), 90 * CENT);
650  BOOST_CHECK(result29);
651  const auto result30 = KnapsackSolver(GroupCoins(coins), 90 * CENT);
652  BOOST_CHECK(result30);
653  if (EqualResult(*result29, *result30))
654  fails++;
655  }
656  BOOST_CHECK_NE(fails, RANDOM_REPEATS);
657  }
658  }
659 }
660 
662 {
663  std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
664  wallet->LoadWallet();
665  LOCK(wallet->cs_wallet);
666  wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
667  wallet->SetupDescriptorScriptPubKeyMans();
668 
669  std::vector<COutput> coins;
670 
671  // Test vValue sort order
672  for (int i = 0; i < 1000; i++)
673  add_coin(coins, *wallet, 1000 * COIN);
674  add_coin(coins, *wallet, 3 * COIN);
675 
676  const auto result = KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 1003 * COIN);
677  BOOST_CHECK(result);
678  BOOST_CHECK_EQUAL(result->GetSelectedValue(), 1003 * COIN);
679  BOOST_CHECK_EQUAL(result->GetInputSet().size(), 2U);
680 }
681 
682 // Tests that with the ideal conditions, the coin selector will always be able to find a solution that can pay the target value
683 BOOST_AUTO_TEST_CASE(SelectCoins_test)
684 {
685  std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
686  wallet->LoadWallet();
687  LOCK(wallet->cs_wallet);
688  wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
689  wallet->SetupDescriptorScriptPubKeyMans();
690 
691  // Random generator stuff
692  std::default_random_engine generator;
693  std::exponential_distribution<double> distribution (100);
694  FastRandomContext rand;
695 
696  // Run this test 100 times
697  for (int i = 0; i < 100; ++i)
698  {
699  std::vector<COutput> coins;
700  CAmount balance{0};
701 
702  // Make a wallet with 1000 exponentially distributed random inputs
703  for (int j = 0; j < 1000; ++j)
704  {
705  CAmount val = distribution(generator)*10000000;
706  add_coin(coins, *wallet, val);
707  balance += val;
708  }
709 
710  // Generate a random fee rate in the range of 100 - 400
711  CFeeRate rate(rand.randrange(300) + 100);
712 
713  // Generate a random target value between 1000 and wallet balance
714  CAmount target = rand.randrange(balance - 1000) + 1000;
715 
716  // Perform selection
717  CoinSelectionParams cs_params(/* change_output_size= */ 34,
718  /* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
719  /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
720  /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
721  CCoinControl cc;
722  const auto result = SelectCoins(*wallet, coins, target, cc, cs_params);
723  BOOST_CHECK(result);
724  BOOST_CHECK_GE(result->GetSelectedValue(), target);
725  }
726 }
727 
729 {
730  CoinSet selection;
731  const CAmount fee{100};
732  const CAmount change_cost{125};
733  const CAmount fee_diff{40};
734  const CAmount in_amt{3 * COIN};
735  const CAmount target{2 * COIN};
736  const CAmount excess{in_amt - fee * 2 - target};
737 
738  // Waste with change is the change cost and difference between fee and long term fee
739  add_coin(1 * COIN, 1, selection, fee, fee - fee_diff);
740  add_coin(2 * COIN, 2, selection, fee, fee - fee_diff);
741  const CAmount waste1 = GetSelectionWaste(selection, change_cost, target);
742  BOOST_CHECK_EQUAL(fee_diff * 2 + change_cost, waste1);
743  selection.clear();
744 
745  // Waste without change is the excess and difference between fee and long term fee
746  add_coin(1 * COIN, 1, selection, fee, fee - fee_diff);
747  add_coin(2 * COIN, 2, selection, fee, fee - fee_diff);
748  const CAmount waste_nochange1 = GetSelectionWaste(selection, 0, target);
749  BOOST_CHECK_EQUAL(fee_diff * 2 + excess, waste_nochange1);
750  selection.clear();
751 
752  // Waste with change and fee == long term fee is just cost of change
753  add_coin(1 * COIN, 1, selection, fee, fee);
754  add_coin(2 * COIN, 2, selection, fee, fee);
755  BOOST_CHECK_EQUAL(change_cost, GetSelectionWaste(selection, change_cost, target));
756  selection.clear();
757 
758  // Waste without change and fee == long term fee is just the excess
759  add_coin(1 * COIN, 1, selection, fee, fee);
760  add_coin(2 * COIN, 2, selection, fee, fee);
761  BOOST_CHECK_EQUAL(excess, GetSelectionWaste(selection, 0, target));
762  selection.clear();
763 
764  // Waste will be greater when fee is greater, but long term fee is the same
765  add_coin(1 * COIN, 1, selection, fee * 2, fee - fee_diff);
766  add_coin(2 * COIN, 2, selection, fee * 2, fee - fee_diff);
767  const CAmount waste2 = GetSelectionWaste(selection, change_cost, target);
768  BOOST_CHECK_GT(waste2, waste1);
769  selection.clear();
770 
771  // Waste with change is the change cost and difference between fee and long term fee
772  // With long term fee greater than fee, waste should be less than when long term fee is less than fee
773  add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
774  add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
775  const CAmount waste3 = GetSelectionWaste(selection, change_cost, target);
776  BOOST_CHECK_EQUAL(fee_diff * -2 + change_cost, waste3);
777  BOOST_CHECK_LT(waste3, waste1);
778  selection.clear();
779 
780  // Waste without change is the excess and difference between fee and long term fee
781  // With long term fee greater than fee, waste should be less than when long term fee is less than fee
782  add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
783  add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
784  const CAmount waste_nochange2 = GetSelectionWaste(selection, 0, target);
785  BOOST_CHECK_EQUAL(fee_diff * -2 + excess, waste_nochange2);
786  BOOST_CHECK_LT(waste_nochange2, waste_nochange1);
787  selection.clear();
788 
789  // No Waste when fee == long_term_fee, no change, and no excess
790  add_coin(1 * COIN, 1, selection, fee, fee);
791  add_coin(2 * COIN, 2, selection, fee, fee);
792  const CAmount exact_target{in_amt - fee * 2};
793  BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, /*change_cost=*/0, exact_target));
794  selection.clear();
795 
796  // No Waste when (fee - long_term_fee) == (-cost_of_change), and no excess
797  const CAmount new_change_cost{fee_diff * 2};
798  add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
799  add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
800  BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, new_change_cost, target));
801  selection.clear();
802 
803  // No Waste when (fee - long_term_fee) == (-excess), no change cost
804  const CAmount new_target{in_amt - fee * 2 - fee_diff * 2};
805  add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
806  add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
807  BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, /* change cost */ 0, new_target));
808 }
809 
811 } // namespace wallet
CMutableTransaction::vin
std::vector< CTxIn > vin
Definition: transaction.h:346
assert
assert(!tx.IsCoinBase())
wallet::CachableAmount::Set
void Set(isminefilter filter, CAmount value)
Definition: ismine.h:65
wallet.h
wallet::CCoinControl::fAllowOtherInputs
bool fAllowOtherInputs
If false, allows unselected inputs, but requires all selected inputs be used.
Definition: coincontrol.h:41
wallet::GroupOutputs
std::vector< OutputGroup > GroupOutputs(const CWallet &wallet, const std::vector< COutput > &outputs, const CoinSelectionParams &coin_sel_params, const CoinEligibilityFilter &filter, bool positive_only)
Definition: spend.cpp:291
setup_common.h
wallet::nextLockTime
static int nextLockTime
Definition: coinselector_tests.cpp:36
transaction.h
GetScriptForDestination
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:310
wallet::filter_standard_extra
static const CoinEligibilityFilter filter_standard_extra(6, 6, 0)
wallet::SelectionResult::Clear
void Clear()
Definition: coinselection.cpp:402
bilingual_str
Bilingual messages:
Definition: translation.h:16
RANDOM_REPEATS
#define RANDOM_REPEATS
Definition: coinselector_tests.cpp:29
m_node
node::NodeContext m_node
Definition: bitcoin-gui.cpp:36
wallet::EqualResult
static bool EqualResult(const SelectionResult &a, const SelectionResult &b)
Check if this selection is equal to another one.
Definition: coinselector_tests.cpp:125
wallet::CWalletTx::m_amounts
CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS]
Definition: transaction.h:190
wallet::OutputGroup::Insert
void Insert(const CInputCoin &output, int depth, bool from_me, size_t ancestors, size_t descendants, bool positive_only)
Definition: coinselection.cpp:316
wallet::WALLET_FLAG_DESCRIPTORS
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Definition: walletutil.h:66
MakeTransactionRef
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:387
wallet::GetSelectionWaste
CAmount GetSelectionWaste(const std::set< CInputCoin > &inputs, CAmount change_cost, CAmount target, bool use_effective_value)
Compute the waste for this result given the cost of change and the opportunity cost of spending these...
Definition: coinselection.cpp:360
wallet
Definition: node.h:38
wallet::SelectionResult::GetInputSet
const std::set< CInputCoin > & GetInputSet() const
Get m_selected_inputs.
Definition: coinselection.cpp:414
BOOST_FIXTURE_TEST_SUITE
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
wallet::KnapsackSolver
std::optional< SelectionResult > KnapsackSolver(std::vector< OutputGroup > &groups, const CAmount &nTargetValue)
Definition: coinselection.cpp:238
context.h
wallet::CWalletTx
A transaction with a bunch of additional info that only the owner cares about.
Definition: transaction.h:137
wallet::CoinSelectionParams::m_cost_of_change
CAmount m_cost_of_change
Cost of creating the change output + cost of spending the change output in the future.
Definition: coinselection.h:85
BOOST_AUTO_TEST_SUITE_END
BOOST_AUTO_TEST_SUITE_END()
CFeeRate
Fee rate in satoshis per kilobyte: CAmount / kB.
Definition: feerate.h:29
wallet::filter_confirmed
static const CoinEligibilityFilter filter_confirmed(1, 1, 0)
random.h
CMutableTransaction::nLockTime
uint32_t nLockTime
Definition: transaction.h:349
coinselection.h
CTxDestination
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:157
wallet::CoinSelectionParams::m_effective_feerate
CFeeRate m_effective_feerate
The targeted feerate of the transaction being built.
Definition: coinselection.h:87
wallet::CWallet
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:232
CENT
static constexpr CAmount CENT
Definition: setup_common.h:78
CAmount
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
wallet::CoinSet
std::set< CInputCoin > CoinSet
Definition: coinselector_tests.cpp:31
wallet::CCoinControl::Select
void Select(const COutPoint &output)
Definition: coincontrol.h:92
wallet::OutputGroup
A group of UTXOs paid to the same output script.
Definition: coinselection.h:139
uint256
256-bit opaque blob.
Definition: uint256.h:124
wallet::CreateMockWalletDatabase
std::unique_ptr< WalletDatabase > CreateMockWalletDatabase()
Return object for accessing temporary in-memory database.
Definition: walletdb.cpp:1190
wallet::CInputCoin
A UTXO under consideration for use in funding a new transaction.
Definition: coinselection.h:22
wallet::SelectCoinsBnB
std::optional< SelectionResult > SelectCoinsBnB(std::vector< OutputGroup > &utxo_pool, const CAmount &selection_target, const CAmount &cost_of_change)
Definition: coinselection.cpp:65
wallet_test_fixture.h
wallet::BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(bnb_search_test)
Definition: coinselector_tests.cpp:177
wallet::ISMINE_SPENDABLE
@ ISMINE_SPENDABLE
Definition: ismine.h:44
wallet::CInputCoin::m_long_term_fee
CAmount m_long_term_fee
Definition: coinselection.h:57
wallet::TxStateInactive
State of transaction not confirmed or conflicting with a known block and not in the mempool.
Definition: transaction.h:48
OutputType::BECH32
@ BECH32
CMutableTransaction::vout
std::vector< CTxOut > vout
Definition: transaction.h:347
wallet::CInputCoin::effective_value
CAmount effective_value
Definition: coinselection.h:55
RUN_TESTS
#define RUN_TESTS
Definition: coinselector_tests.cpp:25
wallet::MIN_CHANGE
static constexpr CAmount MIN_CHANGE
target minimum change amount
Definition: coinselection.h:17
wallet::ApproximateBestSubset
static void ApproximateBestSubset(const std::vector< OutputGroup > &groups, const CAmount &nTotalLower, const CAmount &nTargetValue, std::vector< char > &vfBest, CAmount &nBest, int iterations=1000)
Definition: coinselection.cpp:192
translation.h
wallet::add_coin
static void add_coin(const CAmount &nValue, int nInput, std::vector< CInputCoin > &set)
Definition: coinselector_tests.cpp:38
LOCK
#define LOCK(cs)
Definition: sync.h:226
wallet::EquivalentResult
static bool EquivalentResult(const SelectionResult &a, const SelectionResult &b)
Check if SelectionResult a is equivalent to SelectionResult b.
Definition: coinselector_tests.cpp:107
wallet::CoinSelectionParams
Parameters for one iteration of Coin Selection.
Definition: coinselection.h:76
wallet::COutput
Definition: spend.h:17
wallet::SelectCoins
std::optional< SelectionResult > SelectCoins(const CWallet &wallet, const std::vector< COutput > &vAvailableCoins, const CAmount &nTargetValue, const CCoinControl &coin_control, const CoinSelectionParams &coin_selection_params)
Select a set of coins such that nTargetValue is met and at least all coins from coin_control are sele...
Definition: spend.cpp:418
wallet::CoinSelectionParams::m_subtract_fee_outputs
bool m_subtract_fee_outputs
Indicate that we are subtracting the fee from outputs.
Definition: coinselection.h:97
wallet::SelectionResult::AddInput
void AddInput(const OutputGroup &group)
Definition: coinselection.cpp:408
FastRandomContext::randrange
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
Definition: random.h:190
wallet::make_hard_case
static CAmount make_hard_case(int utxos, std::vector< CInputCoin > &utxo_pool)
Definition: coinselector_tests.cpp:131
wallet::GroupCoins
std::vector< OutputGroup > & GroupCoins(const std::vector< CInputCoin > &coins)
Definition: coinselector_tests.cpp:143
COutPoint
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:26
wallet::CWalletTx::m_is_cache_empty
bool m_is_cache_empty
This flag is true if all m_amounts caches are empty.
Definition: transaction.h:197
wallet::CInputCoin::m_fee
CAmount m_fee
Definition: coinselection.h:56
CMutableTransaction::GetHash
uint256 GetHash() const
Compute the hash of this CMutableTransaction.
Definition: transaction.cpp:63
wallet::KnapsackGroupOutputs
std::vector< OutputGroup > & KnapsackGroupOutputs(const std::vector< COutput > &coins, CWallet &wallet, const CoinEligibilityFilter &filter)
Definition: coinselector_tests.cpp:165
error
bool error(const char *fmt, const Args &... args)
Definition: system.h:49
COIN
static constexpr CAmount COIN
The amount of satoshis in one BTC.
Definition: amount.h:15
CMutableTransaction
A mutable version of CTransaction.
Definition: transaction.h:344
coincontrol.h
wallet::SelectionResult
Definition: coinselection.h:203
amount.h
wallet::CoinEligibilityFilter
Parameters for filtering which OutputGroups we may use in coin selection.
Definition: coinselection.h:119
wallet::CCoinControl
Coin Control Features.
Definition: coincontrol.h:29
spend.h
wallet::CWalletTx::DEBIT
@ DEBIT
Definition: transaction.h:189
wallet::filter_standard
static const CoinEligibilityFilter filter_standard(1, 6, 0)
FastRandomContext
Fast randomness source.
Definition: random.h:119
BOOST_CHECK
#define BOOST_CHECK(expr)
Definition: object.cpp:17
BOOST_CHECK_EQUAL
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
node::NodeContext::chain
std::unique_ptr< interfaces::Chain > chain
Definition: context.h:51