Bitcoin Core 31.99.0
P2P Digital Currency
wallet_create_tx.cpp
Go to the documentation of this file.
1// Copyright (c) 2022-present The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or https://www.opensource.org/licenses/mit-license.php.
4
5#include <addresstype.h>
6#include <bench/bench.h>
7#include <chain.h>
8#include <chainparams.h>
9#include <consensus/amount.h>
10#include <consensus/consensus.h>
11#include <consensus/merkle.h>
12#include <kernel/chain.h>
13#include <kernel/types.h>
14#include <node/blockstorage.h>
15#include <outputtype.h>
16#include <policy/feerate.h>
17#include <primitives/block.h>
19#include <script/script.h>
20#include <sync.h>
22#include <test/util/time.h>
23#include <uint256.h>
24#include <util/check.h>
25#include <util/result.h>
26#include <validation.h>
27#include <versionbits.h>
28#include <wallet/coincontrol.h>
30#include <wallet/db.h>
31#include <wallet/spend.h>
32#include <wallet/test/util.h>
33#include <wallet/types.h>
34#include <wallet/wallet.h>
35#include <wallet/walletutil.h>
36
37#include <cstdint>
38#include <map>
39#include <memory>
40#include <optional>
41#include <string>
42#include <utility>
43#include <vector>
44
46using wallet::CWallet;
49
51{
55};
56
57TipBlock getTip(const CChainParams& params, const node::NodeContext& context)
58{
59 auto tip = WITH_LOCK(::cs_main, return context.chainman->ActiveTip());
60 return (tip) ? TipBlock{tip->GetBlockHash(), tip->GetBlockTime(), tip->nHeight} :
61 TipBlock{params.GenesisBlock().GetHash(), params.GenesisBlock().GetBlockTime(), 0};
62}
63
65 const node::NodeContext& context,
67 const CScript& coinbase_out_script)
68{
69 TipBlock tip{getTip(params, context)};
70
71 // Create block
72 CBlock block;
73 CMutableTransaction coinbase_tx;
74 coinbase_tx.vin.resize(1);
75 coinbase_tx.vin[0].prevout.SetNull();
76 coinbase_tx.vout.resize(2);
77 coinbase_tx.vout[0].scriptPubKey = coinbase_out_script;
78 coinbase_tx.vout[0].nValue = 48 * COIN;
79 coinbase_tx.vin[0].scriptSig = CScript() << ++tip.tip_height << OP_0;
80 coinbase_tx.vout[1].scriptPubKey = coinbase_out_script; // extra output
81 coinbase_tx.vout[1].nValue = 1 * COIN;
82
83 // Fill the coinbase with outputs that don't belong to the wallet in order to benchmark
84 // AvailableCoins' behavior with unnecessary TXOs
85 for (int i = 0; i < 50; ++i) {
86 coinbase_tx.vout.emplace_back(1 * COIN / 50, CScript(OP_TRUE));
87 }
88
89 block.vtx = {MakeTransactionRef(std::move(coinbase_tx))};
90
92 block.hashPrevBlock = tip.prev_block_hash;
93 block.hashMerkleRoot = BlockMerkleRoot(block);
94 block.nTime = ++tip.prev_block_time;
95 block.nBits = params.GenesisBlock().nBits;
96 block.nNonce = 0;
97
98 {
100 // Add it to the index
101 CBlockIndex* pindex{context.chainman->m_blockman.AddToBlockIndex(block, context.chainman->m_best_header)};
102 // add it to the chain
103 context.chainman->ActiveChain().SetTip(*pindex);
104 }
105
106 // notify wallet
107 const auto& pindex = WITH_LOCK(::cs_main, return context.chainman->ActiveChain().Tip());
108 wallet.blockConnected(ChainstateRole{}, kernel::MakeBlockInfo(pindex, &block));
109}
110
112 // How many coins from the wallet the process should select
114 // future: this could have external inputs as well.
115};
116
117static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type, bool allow_other_inputs, std::optional<PreSelectInputs> preset_inputs)
118{
119 const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
120
121 // Set clock to genesis block, so the descriptors/keys creation time don't interfere with the blocks scanning process.
122 FakeNodeClock clock{test_setup->m_node.chainman->GetParams().GenesisBlock().Time()};
123 CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
124 {
125 LOCK(wallet.cs_wallet);
126 wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
127 wallet.SetupDescriptorScriptPubKeyMans();
128 }
129
130 // Generate destinations
131 const auto dest{getNewDestination(wallet, output_type)};
132
133 // Generate chain; each coinbase will have two outputs to fill-up the wallet
134 const auto& params = Params();
135 const CScript coinbase_out{GetScriptForDestination(dest)};
136 unsigned int chain_size = 5000; // 5k blocks means 10k UTXO for the wallet (minus 200 due COINBASE_MATURITY)
137 for (unsigned int i = 0; i < chain_size; ++i) {
138 generateFakeBlock(params, test_setup->m_node, wallet, coinbase_out);
139 }
140
141 // Check available balance
142 auto bal = WITH_LOCK(wallet.cs_wallet, return wallet::AvailableCoins(wallet).GetTotalAmount()); // Cache
143 assert(bal == 49 * COIN * (chain_size - COINBASE_MATURITY));
144
145 wallet::CCoinControl coin_control;
146 coin_control.m_allow_other_inputs = allow_other_inputs;
147
148 CAmount target = 0;
149 if (preset_inputs) {
150 // Select inputs, each has 48 BTC
151 wallet::CoinFilterParams filter_coins;
152 filter_coins.max_count = preset_inputs->num_of_internal_inputs;
153 const auto& res = WITH_LOCK(wallet.cs_wallet,
154 return wallet::AvailableCoins(wallet, /*coinControl=*/nullptr, /*feerate=*/std::nullopt, filter_coins));
155 for (int i=0; i < preset_inputs->num_of_internal_inputs; i++) {
156 const auto& coin{res.coins.at(output_type)[i]};
157 target += coin.txout.nValue;
158 coin_control.Select(coin.outpoint);
159 }
160 }
161
162 // If automatic coin selection is enabled, add the value of another UTXO to the target
163 if (coin_control.m_allow_other_inputs) target += 50 * COIN;
164 std::vector<wallet::CRecipient> recipients = {{dest, target, true}};
165
166 bench.run([&] {
167 LOCK(wallet.cs_wallet);
168 const auto& tx_res = CreateTransaction(wallet, recipients, /*change_pos=*/std::nullopt, coin_control);
169 assert(tx_res);
170 });
171}
172
173static void AvailableCoins(benchmark::Bench& bench, const std::vector<OutputType>& output_type)
174{
175 const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
176 // Set clock to genesis block, so the descriptors/keys creation time don't interfere with the blocks scanning process.
177 FakeNodeClock clock{test_setup->m_node.chainman->GetParams().GenesisBlock().Time()};
178 CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
179 {
180 LOCK(wallet.cs_wallet);
181 wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
182 wallet.SetupDescriptorScriptPubKeyMans();
183 }
184
185 // Generate destinations
186 std::vector<CScript> dest_wallet;
187 dest_wallet.reserve(output_type.size());
188 for (auto type : output_type) {
189 dest_wallet.emplace_back(GetScriptForDestination(getNewDestination(wallet, type)));
190 }
191
192 // Generate chain; each coinbase will have two outputs to fill-up the wallet
193 const auto& params = Params();
194 unsigned int chain_size = 1000;
195 for (unsigned int i = 0; i < chain_size / dest_wallet.size(); ++i) {
196 for (const auto& dest : dest_wallet) {
197 generateFakeBlock(params, test_setup->m_node, wallet, dest);
198 }
199 }
200
201 // Check available balance
202 auto bal = WITH_LOCK(wallet.cs_wallet, return wallet::AvailableCoins(wallet).GetTotalAmount()); // Cache
203 assert(bal == 49 * COIN * (chain_size - COINBASE_MATURITY));
204
205 bench.run([&] {
206 LOCK(wallet.cs_wallet);
207 const auto& res = wallet::AvailableCoins(wallet);
208 assert(res.All().size() == (chain_size - COINBASE_MATURITY) * 2);
209 });
210}
211
212static void WalletCreateTxUseOnlyPresetInputs(benchmark::Bench& bench) { WalletCreateTx(bench, OutputType::BECH32, /*allow_other_inputs=*/false,
213 {{/*num_of_internal_inputs=*/4}}); }
214
216 {{/*num_of_internal_inputs=*/4}}); }
217
219
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
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
const CChainParams & Params()
Return the currently selected parameters.
uint32_t nNonce
Definition: block.h:35
uint32_t nBits
Definition: block.h:34
uint32_t nTime
Definition: block.h:33
int64_t GetBlockTime() const
Definition: block.h:66
int32_t nVersion
Definition: block.h:30
uint256 hashPrevBlock
Definition: block.h:31
uint256 hashMerkleRoot
Definition: block.h:32
uint256 GetHash() const
Definition: block.cpp:14
Definition: block.h:74
std::vector< CTransactionRef > vtx
Definition: block.h:77
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:94
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition: chainparams.h:77
const CBlock & GenesisBlock() const
Definition: chainparams.h:94
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:406
Helper to initialize the global NodeClock, let a duration elapse, and reset it after use in a test.
Definition: time.h:54
Main entry point to nanobench's benchmarking facility.
Definition: nanobench.h:649
Bench & run(char const *benchmarkName, Op &&op)
Repeatedly calls op() based on the configuration, and performs measurements.
Definition: nanobench.h:1308
256-bit opaque blob.
Definition: uint256.h:196
Coin Control Features.
Definition: coincontrol.h:84
PreselectedInput & Select(const COutPoint &outpoint)
Lock-in the given output for spending.
Definition: coincontrol.cpp:40
bool m_allow_other_inputs
If true, the selection process can add extra unselected inputs from the wallet while requires all sel...
Definition: coincontrol.h:94
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:309
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition: merkle.cpp:66
static const int COINBASE_MATURITY
Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
Definition: consensus.h:19
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
is a home for simple enum and struct type definitions that can be used internally by functions in the...
interfaces::BlockInfo MakeBlockInfo(const CBlockIndex *index, const CBlock *data)
Return data from block index.
Definition: chain.cpp:18
std::unique_ptr< WalletDatabase > CreateMockableWalletDatabase()
Definition: util.cpp:122
util::Result< CreatedTransactionResult > CreateTransaction(CWallet &wallet, const std::vector< CRecipient > &vecSend, std::optional< unsigned int > change_pos, const CCoinControl &coin_control, bool sign)
Create a new transaction paying the recipients with a set of coins selected by SelectCoins(); Also cr...
Definition: spend.cpp:1447
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Definition: walletutil.h:53
CTxDestination getNewDestination(CWallet &w, OutputType output_type)
Returns a new destination, of an specific type, from the wallet.
Definition: util.cpp:113
CoinsResult AvailableCoins(const CWallet &wallet, const CCoinControl *coinControl, std::optional< CFeeRate > feerate, const CoinFilterParams &params)
Populate the CoinsResult struct with vectors of available COutputs, organized by OutputType.
Definition: spend.cpp:316
OutputType
Definition: outputtype.h:18
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:404
@ OP_TRUE
Definition: script.h:85
@ OP_0
Definition: script.h:77
A mutable version of CTransaction.
Definition: transaction.h:358
std::vector< CTxOut > vout
Definition: transaction.h:360
std::vector< CTxIn > vin
Definition: transaction.h:359
uint256 prev_block_hash
int64_t prev_block_time
Information about chainstate that notifications are sent from.
Definition: types.h:18
NodeContext struct containing references to chain state and connection state.
Definition: context.h:59
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:76
uint64_t max_count
Definition: spend.h:83
CAmount GetTotalAmount() const
Definition: spend.h:61
#define LOCK(cs)
Definition: sync.h:268
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:299
assert(!tx.IsCoinBase())
static const int32_t VERSIONBITS_LAST_OLD_BLOCK_VERSION
What block version to use for new blocks (pre versionbits)
Definition: versionbits.h:19
is a home for public enum and struct type definitions that are used by internally by wallet code,...
static void WalletCreateTxUsePresetInputsAndCoinSelection(benchmark::Bench &bench)
BENCHMARK(WalletCreateTxUseOnlyPresetInputs)
static void WalletAvailableCoins(benchmark::Bench &bench)
TipBlock getTip(const CChainParams &params, const node::NodeContext &context)
void generateFakeBlock(const CChainParams &params, const node::NodeContext &context, CWallet &wallet, const CScript &coinbase_out_script)
static void WalletCreateTx(benchmark::Bench &bench, const OutputType output_type, bool allow_other_inputs, std::optional< PreSelectInputs > preset_inputs)
static void AvailableCoins(benchmark::Bench &bench, const std::vector< OutputType > &output_type)
static void WalletCreateTxUseOnlyPresetInputs(benchmark::Bench &bench)