mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 06:52:36 +01:00
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.
This commit is contained in:
parent
a8a75346d7
commit
3fcb545ab2
4 changed files with 153 additions and 2 deletions
|
@ -79,6 +79,7 @@ if ENABLE_WALLET
|
|||
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
|
||||
bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
|
||||
bench_bench_bitcoin_SOURCES += bench/wallet_loading.cpp
|
||||
bench_bench_bitcoin_SOURCES += bench/wallet_create_tx.cpp
|
||||
bench_bench_bitcoin_LDADD += $(BDB_LIBS) $(SQLITE_LIBS)
|
||||
endif
|
||||
|
||||
|
|
142
src/bench/wallet_create_tx.cpp
Normal file
142
src/bench/wallet_create_tx.cpp
Normal file
|
@ -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)
|
|
@ -21,7 +21,12 @@ const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqq
|
|||
std::string getnewaddress(CWallet& w)
|
||||
{
|
||||
constexpr auto output_type = OutputType::BECH32;
|
||||
return EncodeDestination(*Assert(w.GetNewDestination(output_type, "")));
|
||||
return EncodeDestination(getNewDestination(w, output_type));
|
||||
}
|
||||
|
||||
CTxDestination getNewDestination(CWallet& w, OutputType output_type)
|
||||
{
|
||||
return *Assert(w.GetNewDestination(output_type, ""));
|
||||
}
|
||||
|
||||
#endif // ENABLE_WALLET
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef BITCOIN_TEST_UTIL_WALLET_H
|
||||
#define BITCOIN_TEST_UTIL_WALLET_H
|
||||
|
||||
#include <outputtype.h>
|
||||
#include <string>
|
||||
|
||||
namespace wallet {
|
||||
|
@ -19,8 +20,10 @@ extern const std::string ADDRESS_BCRT1_UNSPENDABLE;
|
|||
|
||||
/** Import the address to the wallet */
|
||||
void importaddress(wallet::CWallet& wallet, const std::string& address);
|
||||
/** Returns a new address from the wallet */
|
||||
/** Returns a new encoded destination from the wallet (hardcoded to BECH32) */
|
||||
std::string getnewaddress(wallet::CWallet& w);
|
||||
/** Returns a new destination, of an specific type, from the wallet */
|
||||
CTxDestination getNewDestination(wallet::CWallet& w, OutputType output_type);
|
||||
|
||||
|
||||
#endif // BITCOIN_TEST_UTIL_WALLET_H
|
||||
|
|
Loading…
Add table
Reference in a new issue