Bitcoin Core 31.99.0
P2P Digital Currency
coin_selection.cpp
Go to the documentation of this file.
1// Copyright (c) 2012-present 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 <bench/bench.h>
6#include <consensus/amount.h>
7#include <interfaces/chain.h>
8#include <node/context.h>
9#include <outputtype.h>
10#include <policy/feerate.h>
11#include <policy/policy.h>
13#include <random.h>
14#include <sync.h>
16#include <util/result.h>
18#include <wallet/context.h>
19#include <wallet/spend.h>
20#include <wallet/test/util.h>
21#include <wallet/transaction.h>
22#include <wallet/wallet.h>
23
24#include <cassert>
25#include <map>
26#include <memory>
27#include <set>
28#include <utility>
29#include <vector>
30
31namespace wallet {
32static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<std::unique_ptr<CWalletTx>>& wtxs)
33{
34 static int nextLockTime = 0;
36 tx.nLockTime = nextLockTime++; // so all transactions get different hashes
37 tx.vout.resize(1);
38 tx.vout[0].nValue = nValue;
39 wtxs.push_back(std::make_unique<CWalletTx>(MakeTransactionRef(std::move(tx)), TxStateInactive{}));
40}
41
42// Simple benchmark for wallet coin selection. Note that it maybe be necessary
43// to build up more complicated scenarios in order to get meaningful
44// measurements of performance. From laanwj, "Wallet coin selection is probably
45// the hardest, as you need a wider selection of scenarios, just testing the
46// same one over and over isn't too useful. Generating random isn't useful
47// either for measurements."
48// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
50{
51 const auto test_setup = MakeNoLogFileContext<TestingSetup>();
52 CWallet wallet(test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase());
53 std::vector<std::unique_ptr<CWalletTx>> wtxs;
54 LOCK(wallet.cs_wallet);
55
56 // Add coins.
57 for (int i = 0; i < 1000; ++i) {
58 addCoin(1000 * COIN, wallet, wtxs);
59 }
60 addCoin(3 * COIN, wallet, wtxs);
61
62 // Create coins
63 wallet::CoinsResult available_coins;
64 for (const auto& wtx : wtxs) {
65 const auto txout = wtx->tx->vout.at(0);
66 available_coins.coins[OutputType::BECH32].emplace_back(COutPoint(wtx->GetHash(), 0), txout, /*depth=*/6 * 24, CalculateMaximumSignedInputSize(txout, &wallet, /*coin_control=*/nullptr), /*solvable=*/true, /*safe=*/true, wtx->GetTxTime(), /*from_me=*/true, /*fees=*/ 0);
67 }
68
70 FastRandomContext rand{};
71 const CoinSelectionParams coin_selection_params{
72 rand,
73 /*change_output_size=*/ 34,
74 /*change_spend_size=*/ 148,
75 /*min_change_target=*/ CHANGE_LOWER,
76 /*effective_feerate=*/ CFeeRate(20'000),
77 /*long_term_feerate=*/ CFeeRate(10'000),
78 /*discard_feerate=*/ CFeeRate(3000),
79 /*tx_noinputs_size=*/ 0,
80 /*avoid_partial=*/ false,
81 };
82 auto group = wallet::GroupOutputs(wallet, available_coins, coin_selection_params, {{filter_standard}})[filter_standard];
83 bench.run([&] {
84 auto result = AttemptSelection(wallet.chain(), 1002.99 * COIN, group, coin_selection_params, /*allow_mixed_output_types=*/true);
85 assert(result);
86 assert(result->GetSelectedValue() == 1003 * COIN);
87 assert(result->GetInputSet().size() == 2);
88 });
89}
90
91// Copied from src/wallet/test/coinselector_tests.cpp
92static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>& set)
93{
95 tx.vout.resize(nInput + 1);
96 tx.vout[nInput].nValue = nValue;
97 COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/0, /*input_bytes=*/-1, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/true, /*fees=*/0);
98 set.emplace_back();
99 set.back().Insert(std::make_shared<COutput>(output), /*ancestors=*/ 0, /*cluster_count=*/ 0);
100}
101// Copied from src/wallet/test/coinselector_tests.cpp
102static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
103{
104 utxo_pool.clear();
105 CAmount target = 0;
106 for (int i = 0; i < utxos; ++i) {
107 target += CAmount{1} << (utxos+i);
108 add_coin(CAmount{1} << (utxos+i), 2*i, utxo_pool);
109 add_coin((CAmount{1} << (utxos+i)) + (CAmount{1} << (utxos-1-i)), 2*i + 1, utxo_pool);
110 }
111 return target;
112}
113
115{
116 std::vector<OutputGroup> utxo_pool;
117 CAmount target;
118 bench.epochIterations(1)
119 .setup([&] { target = make_hard_case(17, utxo_pool); })
120 .run([&] {
121 auto res{SelectCoinsBnB(utxo_pool, target, /*cost_of_change=*/0, MAX_STANDARD_TX_WEIGHT)}; // Should exhaust
123 });
124}
125
128}; // namespace wallet
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
static constexpr CAmount COIN
The amount of satoshis in one BTC.
Definition: amount.h:15
Fee rate in satoshis per virtualbyte: CAmount / vB the feerate is represented internally as FeeFrac.
Definition: feerate.h:32
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:29
Fast randomness source.
Definition: random.h:386
Main entry point to nanobench's benchmarking facility.
Definition: nanobench.h:632
Bench & run(char const *benchmarkName, Op &&op)
Repeatedly calls op() based on the configuration, and performs measurements.
Definition: nanobench.h:1288
detail::SetupRunner< SetupOp > setup(SetupOp setupOp)
Configure an untimed setup step per epoch (fluent API).
Definition: nanobench.h:1282
Bench & epochIterations(uint64_t numIters) noexcept
Sets exactly the number of iterations for each epoch.
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:309
void doNotOptimizeAway(Arg &&arg)
Makes sure none of the given arguments are optimized away by the compiler.
Definition: nanobench.h:1333
static const CoinEligibilityFilter filter_standard(1, 6, 0)
std::unique_ptr< WalletDatabase > CreateMockableWalletDatabase()
Definition: util.cpp:144
FilteredOutputGroups GroupOutputs(const CWallet &wallet, const CoinsResult &coins, const CoinSelectionParams &coin_sel_params, const std::vector< SelectionFilter > &filters, std::vector< OutputGroup > &ret_discarded_groups)
Definition: spend.cpp:572
static constexpr CAmount CHANGE_LOWER
lower bound for randomly-chosen target change amount
Definition: coinselection.h:23
util::Result< SelectionResult > SelectCoinsBnB(std::vector< OutputGroup > &utxo_pool, const CAmount &selection_target, const CAmount &cost_of_change, int max_selection_weight)
static void addCoin(const CAmount &nValue, const CWallet &wallet, std::vector< std::unique_ptr< CWalletTx > > &wtxs)
util::Result< SelectionResult > AttemptSelection(interfaces::Chain &chain, const CAmount &nTargetValue, OutputGroupTypeMap &groups, const CoinSelectionParams &coin_selection_params, bool allow_mixed_output_types)
Attempt to find a valid input set that preserves privacy by not mixing OutputTypes.
Definition: spend.cpp:702
static void CoinSelection(benchmark::Bench &bench)
static void BnBExhaustion(benchmark::Bench &bench)
BENCHMARK(CoinSelection)
static void add_coin(const CAmount &nValue, int nInput, std::vector< OutputGroup > &set)
static CAmount make_hard_case(int utxos, std::vector< OutputGroup > &utxo_pool)
static int nextLockTime
int CalculateMaximumSignedInputSize(const CTxOut &txout, const COutPoint outpoint, const SigningProvider *provider, bool can_grind_r, const CCoinControl *coin_control)
Definition: spend.cpp:92
static constexpr int32_t MAX_STANDARD_TX_WEIGHT
The maximum weight for transactions we're willing to relay/mine.
Definition: policy.h:38
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:404
A mutable version of CTransaction.
Definition: transaction.h:358
std::vector< CTxOut > vout
Definition: transaction.h:360
Txid GetHash() const
Compute the hash of this CMutableTransaction.
Definition: transaction.cpp:69
A UTXO under consideration for use in funding a new transaction.
Definition: coinselection.h:28
Parameters for filtering which OutputGroups we may use in coin selection.
Parameters for one iteration of Coin Selection.
COutputs available for spending, stored by OutputType.
Definition: spend.h:46
std::map< OutputType, std::vector< COutput > > coins
Definition: spend.h:47
State of transaction not confirmed or conflicting with a known block and not in the mempool.
Definition: transaction.h:59
#define LOCK(cs)
Definition: sync.h:268
assert(!tx.IsCoinBase())