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