Skip to content

Commit 3fcb545

Browse files
committed
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.
1 parent a8a7534 commit 3fcb545

File tree

4 files changed

+153
-2
lines changed

4 files changed

+153
-2
lines changed

src/Makefile.bench.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ if ENABLE_WALLET
7979
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
8080
bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
8181
bench_bench_bitcoin_SOURCES += bench/wallet_loading.cpp
82+
bench_bench_bitcoin_SOURCES += bench/wallet_create_tx.cpp
8283
bench_bench_bitcoin_LDADD += $(BDB_LIBS) $(SQLITE_LIBS)
8384
endif
8485

src/bench/wallet_create_tx.cpp

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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 <test/util/wallet.h>
13+
#include <validation.h>
14+
#include <wallet/spend.h>
15+
#include <wallet/wallet.h>
16+
17+
using wallet::CWallet;
18+
using wallet::CreateMockWalletDatabase;
19+
using wallet::DBErrors;
20+
using wallet::WALLET_FLAG_DESCRIPTORS;
21+
22+
struct TipBlock
23+
{
24+
uint256 prev_block_hash;
25+
int64_t prev_block_time;
26+
int tip_height;
27+
};
28+
29+
TipBlock getTip(const CChainParams& params, const node::NodeContext& context)
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,
37+
const node::NodeContext& context,
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+
56+
block.nVersion = VERSIONBITS_LAST_OLD_BLOCK_VERSION;
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(kernel::MakeBlockInfo(pindex, &block));
74+
}
75+
76+
struct PreSelectInputs {
77+
// How many coins from the wallet the process should select
78+
int num_of_internal_inputs;
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+
CWallet wallet{test_setup->m_node.chain.get(), "", gArgs, CreateMockWalletDatabase()};
87+
{
88+
LOCK(wallet.cs_wallet);
89+
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
90+
wallet.SetupDescriptorScriptPubKeyMans();
91+
if (wallet.LoadWallet() != DBErrors::LOAD_OK) assert(false);
92+
}
93+
94+
// Generate destinations
95+
CScript dest = GetScriptForDestination(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+
unsigned int chain_size = 5000; // 5k blocks means 10k UTXO for the wallet (minus 200 due COINBASE_MATURITY)
100+
for (unsigned int i = 0; i < chain_size; ++i) {
101+
generateFakeBlock(params, test_setup->m_node, wallet, dest);
102+
}
103+
104+
// Check available balance
105+
auto bal = wallet::GetAvailableBalance(wallet); // Cache
106+
assert(bal == 50 * COIN * (chain_size - COINBASE_MATURITY));
107+
108+
wallet::CCoinControl coin_control;
109+
coin_control.m_allow_other_inputs = allow_other_inputs;
110+
111+
CAmount target = 0;
112+
if (preset_inputs) {
113+
// Select inputs, each has 49 BTC
114+
const auto& res = WITH_LOCK(wallet.cs_wallet,
115+
return wallet::AvailableCoins(wallet, nullptr, std::nullopt, 1, MAX_MONEY,
116+
MAX_MONEY, preset_inputs->num_of_internal_inputs));
117+
for (int i=0; i < preset_inputs->num_of_internal_inputs; i++) {
118+
const auto& coin{res.coins.at(output_type)[i]};
119+
target += coin.txout.nValue;
120+
coin_control.Select(coin.outpoint);
121+
}
122+
}
123+
124+
// If automatic coin selection is enabled, add the value of another UTXO to the target
125+
if (coin_control.m_allow_other_inputs) target += 50 * COIN;
126+
std::vector<wallet::CRecipient> recipients = {{dest, target, true}};
127+
128+
bench.epochIterations(5).run([&] {
129+
LOCK(wallet.cs_wallet);
130+
const auto& tx_res = CreateTransaction(wallet, recipients, -1, coin_control);
131+
assert(tx_res);
132+
});
133+
}
134+
135+
static void WalletCreateTxUseOnlyPresetInputs(benchmark::Bench& bench) { WalletCreateTx(bench, OutputType::BECH32, /*allow_other_inputs=*/false,
136+
{{/*num_of_internal_inputs=*/4}}); }
137+
138+
static void WalletCreateTxUsePresetInputsAndCoinSelection(benchmark::Bench& bench) { WalletCreateTx(bench, OutputType::BECH32, /*allow_other_inputs=*/true,
139+
{{/*num_of_internal_inputs=*/4}}); }
140+
141+
BENCHMARK(WalletCreateTxUseOnlyPresetInputs, benchmark::PriorityLevel::LOW)
142+
BENCHMARK(WalletCreateTxUsePresetInputsAndCoinSelection, benchmark::PriorityLevel::LOW)

src/test/util/wallet.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqq
2121
std::string getnewaddress(CWallet& w)
2222
{
2323
constexpr auto output_type = OutputType::BECH32;
24-
return EncodeDestination(*Assert(w.GetNewDestination(output_type, "")));
24+
return EncodeDestination(getNewDestination(w, output_type));
25+
}
26+
27+
CTxDestination getNewDestination(CWallet& w, OutputType output_type)
28+
{
29+
return *Assert(w.GetNewDestination(output_type, ""));
2530
}
2631

2732
#endif // ENABLE_WALLET

src/test/util/wallet.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef BITCOIN_TEST_UTIL_WALLET_H
66
#define BITCOIN_TEST_UTIL_WALLET_H
77

8+
#include <outputtype.h>
89
#include <string>
910

1011
namespace wallet {
@@ -19,8 +20,10 @@ extern const std::string ADDRESS_BCRT1_UNSPENDABLE;
1920

2021
/** Import the address to the wallet */
2122
void importaddress(wallet::CWallet& wallet, const std::string& address);
22-
/** Returns a new address from the wallet */
23+
/** Returns a new encoded destination from the wallet (hardcoded to BECH32) */
2324
std::string getnewaddress(wallet::CWallet& w);
25+
/** Returns a new destination, of an specific type, from the wallet */
26+
CTxDestination getNewDestination(wallet::CWallet& w, OutputType output_type);
2427

2528

2629
#endif // BITCOIN_TEST_UTIL_WALLET_H

0 commit comments

Comments
 (0)