aboutsummaryrefslogtreecommitdiff
path: root/src/bench/wallet_create_tx.cpp
diff options
context:
space:
mode:
authorfurszy <matiasfurszyfer@protonmail.com>2022-08-03 14:59:55 -0300
committerfurszy <matiasfurszyfer@protonmail.com>2022-10-26 15:54:31 -0300
commit3fcb545ab26be3e785b5e5654be0bdc77099d827 (patch)
treeb6e5d4e817e08fbf941ecc4c20fb1b96526ddd9b /src/bench/wallet_create_tx.cpp
parenta8a75346d7e7247596c8a580d65ceaad49c97b97 (diff)
bench: benchmark transaction creation process
Goal 1: Benchmark the transaction creation process for pre-selected-inputs only. Setting `m_allow_other_inputs=false` to disallow the wallet to include coins automatically. Goal 2: Benchmark the transaction creation process for pre-selected-inputs and coin selection. ----------------------- Benchmark Setup: 1) Generates a 5k blockchain, loading the wallet with 5k transactions with two outputs each. 2) Fetch 4 random UTXO from the wallet's available coins and pre-select them as inputs inside CoinControl. Benchmark (Goal 1): Call `CreateTransaction` providing the coin control, who has set `m_allow_other_inputs=false` and the manually selected coins. Benchmark (Goal 2): Call `CreateTransaction` providing the coin control, who has set `m_allow_other_inputs=true` and the manually selected coins.
Diffstat (limited to 'src/bench/wallet_create_tx.cpp')
-rw-r--r--src/bench/wallet_create_tx.cpp142
1 files changed, 142 insertions, 0 deletions
diff --git a/src/bench/wallet_create_tx.cpp b/src/bench/wallet_create_tx.cpp
new file mode 100644
index 0000000000..207b22c584
--- /dev/null
+++ b/src/bench/wallet_create_tx.cpp
@@ -0,0 +1,142 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or https://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+#include <chainparams.h>
+#include <wallet/coincontrol.h>
+#include <consensus/merkle.h>
+#include <kernel/chain.h>
+#include <node/context.h>
+#include <test/util/setup_common.h>
+#include <test/util/wallet.h>
+#include <validation.h>
+#include <wallet/spend.h>
+#include <wallet/wallet.h>
+
+using wallet::CWallet;
+using wallet::CreateMockWalletDatabase;
+using wallet::DBErrors;
+using wallet::WALLET_FLAG_DESCRIPTORS;
+
+struct TipBlock
+{
+ uint256 prev_block_hash;
+ int64_t prev_block_time;
+ int tip_height;
+};
+
+TipBlock getTip(const CChainParams& params, const node::NodeContext& context)
+{
+ auto tip = WITH_LOCK(::cs_main, return context.chainman->ActiveTip());
+ return (tip) ? TipBlock{tip->GetBlockHash(), tip->GetBlockTime(), tip->nHeight} :
+ TipBlock{params.GenesisBlock().GetHash(), params.GenesisBlock().GetBlockTime(), 0};
+}
+
+void generateFakeBlock(const CChainParams& params,
+ const node::NodeContext& context,
+ CWallet& wallet,
+ const CScript& coinbase_out_script)
+{
+ TipBlock tip{getTip(params, context)};
+
+ // Create block
+ CBlock block;
+ CMutableTransaction coinbase_tx;
+ coinbase_tx.vin.resize(1);
+ coinbase_tx.vin[0].prevout.SetNull();
+ coinbase_tx.vout.resize(2);
+ coinbase_tx.vout[0].scriptPubKey = coinbase_out_script;
+ coinbase_tx.vout[0].nValue = 49 * COIN;
+ coinbase_tx.vin[0].scriptSig = CScript() << ++tip.tip_height << OP_0;
+ coinbase_tx.vout[1].scriptPubKey = coinbase_out_script; // extra output
+ coinbase_tx.vout[1].nValue = 1 * COIN;
+ block.vtx = {MakeTransactionRef(std::move(coinbase_tx))};
+
+ block.nVersion = VERSIONBITS_LAST_OLD_BLOCK_VERSION;
+ block.hashPrevBlock = tip.prev_block_hash;
+ block.hashMerkleRoot = BlockMerkleRoot(block);
+ block.nTime = ++tip.prev_block_time;
+ block.nBits = params.GenesisBlock().nBits;
+ block.nNonce = 0;
+
+ {
+ LOCK(::cs_main);
+ // Add it to the index
+ CBlockIndex* pindex{context.chainman->m_blockman.AddToBlockIndex(block, context.chainman->m_best_header)};
+ // add it to the chain
+ context.chainman->ActiveChain().SetTip(*pindex);
+ }
+
+ // notify wallet
+ const auto& pindex = WITH_LOCK(::cs_main, return context.chainman->ActiveChain().Tip());
+ wallet.blockConnected(kernel::MakeBlockInfo(pindex, &block));
+}
+
+struct PreSelectInputs {
+ // How many coins from the wallet the process should select
+ int num_of_internal_inputs;
+ // future: this could have external inputs as well.
+};
+
+static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type, bool allow_other_inputs, std::optional<PreSelectInputs> preset_inputs)
+{
+ const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
+
+ CWallet wallet{test_setup->m_node.chain.get(), "", gArgs, CreateMockWalletDatabase()};
+ {
+ LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet.SetupDescriptorScriptPubKeyMans();
+ if (wallet.LoadWallet() != DBErrors::LOAD_OK) assert(false);
+ }
+
+ // Generate destinations
+ CScript dest = GetScriptForDestination(getNewDestination(wallet, output_type));
+
+ // Generate chain; each coinbase will have two outputs to fill-up the wallet
+ const auto& params = Params();
+ unsigned int chain_size = 5000; // 5k blocks means 10k UTXO for the wallet (minus 200 due COINBASE_MATURITY)
+ for (unsigned int i = 0; i < chain_size; ++i) {
+ generateFakeBlock(params, test_setup->m_node, wallet, dest);
+ }
+
+ // Check available balance
+ auto bal = wallet::GetAvailableBalance(wallet); // Cache
+ assert(bal == 50 * COIN * (chain_size - COINBASE_MATURITY));
+
+ wallet::CCoinControl coin_control;
+ coin_control.m_allow_other_inputs = allow_other_inputs;
+
+ CAmount target = 0;
+ if (preset_inputs) {
+ // Select inputs, each has 49 BTC
+ const auto& res = WITH_LOCK(wallet.cs_wallet,
+ return wallet::AvailableCoins(wallet, nullptr, std::nullopt, 1, MAX_MONEY,
+ MAX_MONEY, preset_inputs->num_of_internal_inputs));
+ for (int i=0; i < preset_inputs->num_of_internal_inputs; i++) {
+ const auto& coin{res.coins.at(output_type)[i]};
+ target += coin.txout.nValue;
+ coin_control.Select(coin.outpoint);
+ }
+ }
+
+ // If automatic coin selection is enabled, add the value of another UTXO to the target
+ if (coin_control.m_allow_other_inputs) target += 50 * COIN;
+ std::vector<wallet::CRecipient> recipients = {{dest, target, true}};
+
+ bench.epochIterations(5).run([&] {
+ LOCK(wallet.cs_wallet);
+ const auto& tx_res = CreateTransaction(wallet, recipients, -1, coin_control);
+ assert(tx_res);
+ });
+}
+
+static void WalletCreateTxUseOnlyPresetInputs(benchmark::Bench& bench) { WalletCreateTx(bench, OutputType::BECH32, /*allow_other_inputs=*/false,
+ {{/*num_of_internal_inputs=*/4}}); }
+
+static void WalletCreateTxUsePresetInputsAndCoinSelection(benchmark::Bench& bench) { WalletCreateTx(bench, OutputType::BECH32, /*allow_other_inputs=*/true,
+ {{/*num_of_internal_inputs=*/4}}); }
+
+BENCHMARK(WalletCreateTxUseOnlyPresetInputs, benchmark::PriorityLevel::LOW)
+BENCHMARK(WalletCreateTxUsePresetInputsAndCoinSelection, benchmark::PriorityLevel::LOW)