Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2025-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <addresstype.h>6#include <bench/bench.h>7#include <chain.h>8#include <coins.h>9#include <consensus/amount.h>10#include <consensus/validation.h>11#include <key.h>12#include <node/blockstorage.h>13#include <policy/feerate.h>14#include <primitives/block.h>15#include <primitives/transaction.h>16#include <pubkey.h>17#include <script/script.h>18#include <sync.h>19#include <test/util/setup_common.h>20#include <util/check.h>21#include <validation.h>23#include <cstddef>24#include <memory>25#include <optional>26#include <utility>27#include <vector>29/*30* Creates a test block containing transactions with the following properties:31* - Each transaction has the same number of inputs and outputs32* - All Taproot inputs use simple key path spends (no script path spends)33* - All signatures use SIGHASH_ALL (default sighash)34* - Each transaction spends all outputs from the previous transaction35*/36CBlock CreateTestBlock(37TestChain100Setup& test_setup,38const std::vector<CKey>& keys,39const std::vector<CTxOut>& outputs,40int num_txs = 1000)41{42Chainstate& chainstate{test_setup.m_node.chainman->ActiveChainstate()};44const WitnessV1Taproot coinbase_taproot{XOnlyPubKey(test_setup.coinbaseKey.GetPubKey())};46// Create the outputs that will be spent in the first transaction of the test block47// Doing this in a separate block excludes the validation of its inputs from the benchmark48auto& coinbase_to_spend{test_setup.m_coinbase_txns[0]};49const auto [first_tx, _]{test_setup.CreateValidTransaction(50{coinbase_to_spend},51{COutPoint(coinbase_to_spend->GetHash(), 0)},52chainstate.m_chain.Height() + 1, keys, outputs, {}, {})};53const CScript coinbase_spk{GetScriptForDestination(coinbase_taproot)};54test_setup.CreateAndProcessBlock({first_tx}, coinbase_spk);56std::vector<CMutableTransaction> txs;57txs.reserve(num_txs);58CTransactionRef tx_to_spend{MakeTransactionRef(first_tx)};59for (int i{0}; i < num_txs; i++) {60std::vector<COutPoint> inputs;61inputs.reserve(outputs.size());63for (size_t j{0}; j < outputs.size(); j++) {64inputs.emplace_back(tx_to_spend->GetHash(), j);65}67const auto [tx, _]{test_setup.CreateValidTransaction(68{tx_to_spend}, inputs, chainstate.m_chain.Height() + 1, keys, outputs, {}, {})};69txs.emplace_back(tx);70tx_to_spend = MakeTransactionRef(tx);71}73// Coinbase output can use any output type as it is not spent and will not change the benchmark74return test_setup.CreateBlock(txs, coinbase_spk);75}77/*78* Creates key pairs and corresponding outputs for the benchmark transactions.79* - For Schnorr signatures: Creates simple key path spendable outputs80* - For Ecdsa signatures: Creates P2WPKH (native SegWit v0) outputs81* - All outputs have value of 1 BTC82*/83std::pair<std::vector<CKey>, std::vector<CTxOut>> CreateKeysAndOutputs(const CKey& coinbaseKey, size_t num_schnorr, size_t num_ecdsa)84{85std::vector<CKey> keys{coinbaseKey};86keys.reserve(num_schnorr + num_ecdsa + 1);88std::vector<CTxOut> outputs;89outputs.reserve(num_schnorr + num_ecdsa);91for (size_t i{0}; i < num_ecdsa; ++i) {92keys.emplace_back(GenerateRandomKey());93outputs.emplace_back(COIN, GetScriptForDestination(WitnessV0KeyHash{keys.back().GetPubKey()}));94}96for (size_t i{0}; i < num_schnorr; ++i) {97keys.emplace_back(GenerateRandomKey());98outputs.emplace_back(COIN, GetScriptForDestination(WitnessV1Taproot{XOnlyPubKey(keys.back().GetPubKey())}));99}101return {keys, outputs};102}104void BenchmarkConnectBlock(benchmark::Bench& bench, std::vector<CKey>& keys, std::vector<CTxOut>& outputs, TestChain100Setup& test_setup)105{106const auto& test_block{CreateTestBlock(test_setup, keys, outputs)};107bench.unit("block").run([&] {108LOCK(cs_main);109auto& chainman{test_setup.m_node.chainman};110auto& chainstate{chainman->ActiveChainstate()};111BlockValidationState test_block_state;112auto* pindex{chainman->m_blockman.AddToBlockIndex(test_block, chainman->m_best_header)}; // Doing this here doesn't impact the benchmark113CCoinsViewCache viewNew{&chainstate.CoinsTip()};115assert(chainstate.ConnectBlock(test_block, test_block_state, pindex, viewNew));116});117}119static void ConnectBlockAllSchnorr(benchmark::Bench& bench)120{121const auto test_setup{MakeNoLogFileContext<TestChain100Setup>()};122auto [keys, outputs]{CreateKeysAndOutputs(test_setup->coinbaseKey, /*num_schnorr=*/5, /*num_ecdsa=*/0)};123BenchmarkConnectBlock(bench, keys, outputs, *test_setup);124}126static void ConnectBlockMixedEcdsaSchnorr(benchmark::Bench& bench)127{128const auto test_setup{MakeNoLogFileContext<TestChain100Setup>()};129// Blocks in range 848000 to 868000 have a roughly 20 to 80 ratio of schnorr to ecdsa inputs130auto [keys, outputs]{CreateKeysAndOutputs(test_setup->coinbaseKey, /*num_schnorr=*/1, /*num_ecdsa=*/4)};131BenchmarkConnectBlock(bench, keys, outputs, *test_setup);132}134static void ConnectBlockAllEcdsa(benchmark::Bench& bench)135{136const auto test_setup{MakeNoLogFileContext<TestChain100Setup>()};137auto [keys, outputs]{CreateKeysAndOutputs(test_setup->coinbaseKey, /*num_schnorr=*/0, /*num_ecdsa=*/5)};138BenchmarkConnectBlock(bench, keys, outputs, *test_setup);139}141BENCHMARK(ConnectBlockAllSchnorr);142BENCHMARK(ConnectBlockMixedEcdsaSchnorr);143BENCHMARK(ConnectBlockAllEcdsa);
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.