Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
wallet_create_tx.cpp
Go to the documentation of this file.
1// Copyright (c) 2022 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 <bench/bench.h>
6#include <chainparams.h>
8#include <consensus/merkle.h>
9#include <kernel/chain.h>
10#include <node/context.h>
12#include <validation.h>
13#include <wallet/spend.h>
14#include <wallet/test/util.h>
15#include <wallet/wallet.h>
16
17using wallet::CWallet;
20
27
28TipBlock getTip(const CChainParams& params, const node::NodeContext& context)
29{
30 auto tip = WITH_LOCK(::cs_main, return context.chainman->ActiveTip());
31 return (tip) ? TipBlock{tip->GetBlockHash(), tip->GetBlockTime(), tip->nHeight} :
32 TipBlock{params.GenesisBlock().GetHash(), params.GenesisBlock().GetBlockTime(), 0};
33}
34
36 const node::NodeContext& context,
38 const CScript& coinbase_out_script)
39{
40 TipBlock tip{getTip(params, context)};
41
42 // Create block
43 CBlock block;
44 CMutableTransaction coinbase_tx;
45 coinbase_tx.vin.resize(1);
46 coinbase_tx.vin[0].prevout.SetNull();
47 coinbase_tx.vout.resize(2);
48 coinbase_tx.vout[0].scriptPubKey = coinbase_out_script;
49 coinbase_tx.vout[0].nValue = 49 * COIN;
50 coinbase_tx.vin[0].scriptSig = CScript() << ++tip.tip_height << OP_0;
51 coinbase_tx.vout[1].scriptPubKey = coinbase_out_script; // extra output
52 coinbase_tx.vout[1].nValue = 1 * COIN;
53 block.vtx = {MakeTransactionRef(std::move(coinbase_tx))};
54
56 block.hashPrevBlock = tip.prev_block_hash;
57 block.hashMerkleRoot = BlockMerkleRoot(block);
58 block.nTime = ++tip.prev_block_time;
59 block.nBits = params.GenesisBlock().nBits;
60 block.nNonce = 0;
61
62 {
64 // Add it to the index
65 CBlockIndex* pindex{context.chainman->m_blockman.AddToBlockIndex(block, context.chainman->m_best_header)};
66 // add it to the chain
67 context.chainman->ActiveChain().SetTip(*pindex);
68 }
69
70 // notify wallet
71 const auto& pindex = WITH_LOCK(::cs_main, return context.chainman->ActiveChain().Tip());
72 wallet.blockConnected(ChainstateRole::NORMAL, kernel::MakeBlockInfo(pindex, &block));
73}
74
76 // How many coins from the wallet the process should select
78 // future: this could have external inputs as well.
79};
80
81static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type, bool allow_other_inputs, std::optional<PreSelectInputs> preset_inputs)
82{
83 const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
84
85 // Set clock to genesis block, so the descriptors/keys creation time don't interfere with the blocks scanning process.
86 SetMockTime(test_setup->m_node.chainman->GetParams().GenesisBlock().nTime);
87 CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
88 {
89 LOCK(wallet.cs_wallet);
90 wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
91 wallet.SetupDescriptorScriptPubKeyMans();
92 }
93
94 // Generate destinations
95 const auto dest{getNewDestination(wallet, output_type)};
96
97 // Generate chain; each coinbase will have two outputs to fill-up the wallet
98 const auto& params = Params();
99 const CScript coinbase_out{GetScriptForDestination(dest)};
100 unsigned int chain_size = 5000; // 5k blocks means 10k UTXO for the wallet (minus 200 due COINBASE_MATURITY)
101 for (unsigned int i = 0; i < chain_size; ++i) {
102 generateFakeBlock(params, test_setup->m_node, wallet, coinbase_out);
103 }
104
105 // Check available balance
106 auto bal = WITH_LOCK(wallet.cs_wallet, return wallet::AvailableCoins(wallet).GetTotalAmount()); // Cache
107 assert(bal == 50 * COIN * (chain_size - COINBASE_MATURITY));
108
109 wallet::CCoinControl coin_control;
110 coin_control.m_allow_other_inputs = allow_other_inputs;
111
112 CAmount target = 0;
113 if (preset_inputs) {
114 // Select inputs, each has 49 BTC
115 wallet::CoinFilterParams filter_coins;
116 filter_coins.max_count = preset_inputs->num_of_internal_inputs;
117 const auto& res = WITH_LOCK(wallet.cs_wallet,
118 return wallet::AvailableCoins(wallet, /*coinControl=*/nullptr, /*feerate=*/std::nullopt, filter_coins));
119 for (int i=0; i < preset_inputs->num_of_internal_inputs; i++) {
120 const auto& coin{res.coins.at(output_type)[i]};
121 target += coin.txout.nValue;
122 coin_control.Select(coin.outpoint);
123 }
124 }
125
126 // If automatic coin selection is enabled, add the value of another UTXO to the target
127 if (coin_control.m_allow_other_inputs) target += 50 * COIN;
128 std::vector<wallet::CRecipient> recipients = {{dest, target, true}};
129
130 bench.epochIterations(5).run([&] {
131 LOCK(wallet.cs_wallet);
132 const auto& tx_res = CreateTransaction(wallet, recipients, /*change_pos=*/std::nullopt, coin_control);
133 assert(tx_res);
134 });
135}
136
137static void AvailableCoins(benchmark::Bench& bench, const std::vector<OutputType>& output_type)
138{
139 const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
140 // Set clock to genesis block, so the descriptors/keys creation time don't interfere with the blocks scanning process.
141 SetMockTime(test_setup->m_node.chainman->GetParams().GenesisBlock().nTime);
142 CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
143 {
144 LOCK(wallet.cs_wallet);
145 wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
146 wallet.SetupDescriptorScriptPubKeyMans();
147 }
148
149 // Generate destinations
150 std::vector<CScript> dest_wallet;
151 dest_wallet.reserve(output_type.size());
152 for (auto type : output_type) {
153 dest_wallet.emplace_back(GetScriptForDestination(getNewDestination(wallet, type)));
154 }
155
156 // Generate chain; each coinbase will have two outputs to fill-up the wallet
157 const auto& params = Params();
158 unsigned int chain_size = 1000;
159 for (unsigned int i = 0; i < chain_size / dest_wallet.size(); ++i) {
160 for (const auto& dest : dest_wallet) {
161 generateFakeBlock(params, test_setup->m_node, wallet, dest);
162 }
163 }
164
165 // Check available balance
166 auto bal = WITH_LOCK(wallet.cs_wallet, return wallet::AvailableCoins(wallet).GetTotalAmount()); // Cache
167 assert(bal == 50 * COIN * (chain_size - COINBASE_MATURITY));
168
169 bench.epochIterations(2).run([&] {
170 LOCK(wallet.cs_wallet);
171 const auto& res = wallet::AvailableCoins(wallet);
172 assert(res.All().size() == (chain_size - COINBASE_MATURITY) * 2);
173 });
174}
175
176static void WalletCreateTxUseOnlyPresetInputs(benchmark::Bench& bench) { WalletCreateTx(bench, OutputType::BECH32, /*allow_other_inputs=*/false,
177 {{/*num_of_internal_inputs=*/4}}); }
178
180 {{/*num_of_internal_inputs=*/4}}); }
181
183
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
#define BENCHMARK(n, priority_level)
Definition bench.h:79
const CChainParams & Params()
Return the currently selected parameters.
uint32_t nNonce
Definition block.h:30
uint32_t nBits
Definition block.h:29
uint32_t nTime
Definition block.h:28
int64_t GetBlockTime() const
Definition block.h:61
int32_t nVersion
Definition block.h:25
uint256 hashPrevBlock
Definition block.h:26
uint256 hashMerkleRoot
Definition block.h:27
uint256 GetHash() const
Definition block.cpp:11
Definition block.h:69
std::vector< CTransactionRef > vtx
Definition block.h:72
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition chain.h:141
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition chainparams.h:81
const CBlock & GenesisBlock() const
Definition chainparams.h:98
Serialized script, used inside transaction inputs and outputs.
Definition script.h:414
Main entry point to nanobench's benchmarking facility.
Definition nanobench.h:627
Bench & run(char const *benchmarkName, Op &&op)
Repeatedly calls op() based on the configuration, and performs measurements.
Definition nanobench.h:1234
Bench & epochIterations(uint64_t numIters) noexcept
Sets exactly the number of iterations for each epoch.
256-bit opaque blob.
Definition uint256.h:178
Coin Control Features.
Definition coincontrol.h:81
PreselectedInput & Select(const COutPoint &outpoint)
Lock-in the given output for spending.
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:91
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition wallet.h:300
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
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition merkle.cpp:65
@ LOW
Definition bench.h:46
interfaces::BlockInfo MakeBlockInfo(const CBlockIndex *index, const CBlock *data)
Return data from block index.
Definition chain.cpp:14
std::unique_ptr< WalletDatabase > CreateMockableWalletDatabase(MockableData records)
Definition util.cpp:185
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Definition walletutil.h:74
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:309
OutputType
Definition outputtype.h:17
static CTransactionRef MakeTransactionRef(Tx &&txIn)
@ OP_0
Definition script.h:75
std::unique_ptr< T > MakeNoLogFileContext(const ChainType chain_type=ChainType::REGTEST, TestOpts opts={})
Make a test setup that has disk access to the debug.log file disabled.
A mutable version of CTransaction.
std::vector< CTxOut > vout
std::vector< CTxIn > vin
uint256 prev_block_hash
int64_t prev_block_time
NodeContext struct containing references to chain state and connection state.
Definition context.h:55
std::unique_ptr< ChainstateManager > chainman
Definition context.h:69
CAmount GetTotalAmount()
Definition spend.h:56
#define LOCK(cs)
Definition sync.h:257
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition sync.h:301
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition time.cpp:32
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:14
static void WalletCreateTxUsePresetInputsAndCoinSelection(benchmark::Bench &bench)
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)