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.
751auto transactions_copy{transactions};752BOOST_CHECK(transactions.begin() == transactions_copy.begin());753BOOST_CHECK(transactions.begin() == block_tx.Transactions().begin());754auto transaction_it{transactions.begin()};755BOOST_CHECK((*transaction_it).Txid() == block_tx.GetTransaction(0).Txid());756auto invalid_data = hex_string_to_byte_vec("012300");757BOOST_CHECK_THROW(Block{invalid_data}, std::runtime_error);758auto empty_data = hex_string_to_byte_vec("");759BOOST_CHECK_THROW(Block{empty_data}, std::runtime_error);760}762Context create_context(std::shared_ptr<TestKernelNotifications> notifications, ChainType chain_type, std::shared_ptr<TestValidationInterface> validation_interface = nullptr)763{764ContextOptions options{};765ChainParams params{chain_type};766options.SetChainParams(params);767options.SetNotifications(notifications);768if (validation_interface) {769options.SetValidationInterface(validation_interface);770}771auto context{Context{options}};772return context;773}775BOOST_AUTO_TEST_CASE(btck_chainman_tests)776{777Logger logger{std::make_unique<TestLog>()};778auto test_directory{TestDirectory{"chainman_test_bitcoin_kernel"}};780{ // test with default context781Context context{};782ChainstateManagerOptions chainman_opts{context, PathToString(test_directory.m_directory), PathToString(test_directory.m_directory / "blocks")};783ChainMan chainman{context, chainman_opts};784}786{ // test with default context options787ContextOptions options{};788Context context{options};789ChainstateManagerOptions chainman_opts{context, PathToString(test_directory.m_directory), PathToString(test_directory.m_directory / "blocks")};790ChainMan chainman{context, chainman_opts};791}792{ // null or empty data_directory or blocks_directory are not allowed793Context context{};794auto valid_dir{PathToString(test_directory.m_directory)};795std::vector<std::pair<std::string_view, std::string_view>> illegal_cases{796{"", valid_dir},797{valid_dir, {nullptr, 0}},798{"", ""},799{{nullptr, 0}, {nullptr, 0}},800};801for (auto& [data_dir, blocks_dir] : illegal_cases) {802BOOST_CHECK_THROW(ChainstateManagerOptions(context, data_dir, blocks_dir),803std::runtime_error);804};805}807auto notifications{std::make_shared<TestKernelNotifications>()};808auto context{create_context(notifications, ChainType::MAINNET)};810ChainstateManagerOptions chainman_opts{context, PathToString(test_directory.m_directory), PathToString(test_directory.m_directory / "blocks")};811chainman_opts.SetWorkerThreads(4);812BOOST_CHECK(!chainman_opts.SetDatabaseCacheBytes(4_MiB - 1));813if constexpr (sizeof(void*) == 4) BOOST_CHECK(!chainman_opts.SetDatabaseCacheBytes(2_GiB));814BOOST_CHECK(chainman_opts.SetDatabaseCacheBytes(4_MiB));815BOOST_CHECK(!chainman_opts.SetWipeDbs(/*wipe_block_tree=*/true, /*wipe_chainstate=*/false));816BOOST_CHECK(chainman_opts.SetWipeDbs(/*wipe_block_tree=*/true, /*wipe_chainstate=*/true));817BOOST_CHECK(chainman_opts.SetWipeDbs(/*wipe_block_tree=*/false, /*wipe_chainstate=*/true));818BOOST_CHECK(chainman_opts.SetWipeDbs(/*wipe_block_tree=*/false, /*wipe_chainstate=*/false));819ChainMan chainman{context, chainman_opts};820}822std::unique_ptr<ChainMan> create_chainman(TestDirectory& test_directory,823bool reindex,824bool wipe_chainstate,825bool block_tree_db_in_memory,826bool chainstate_db_in_memory,827Context& context)828{829ChainstateManagerOptions chainman_opts{context, PathToString(test_directory.m_directory), PathToString(test_directory.m_directory / "blocks")};831if (reindex) {832chainman_opts.SetWipeDbs(/*wipe_block_tree=*/reindex, /*wipe_chainstate=*/reindex);833}834if (wipe_chainstate) {835chainman_opts.SetWipeDbs(/*wipe_block_tree=*/false, /*wipe_chainstate=*/wipe_chainstate);836}837if (block_tree_db_in_memory) {838chainman_opts.UpdateBlockTreeDbInMemory(block_tree_db_in_memory);839}840if (chainstate_db_in_memory) {841chainman_opts.UpdateChainstateDbInMemory(chainstate_db_in_memory);842}844auto chainman{std::make_unique<ChainMan>(context, chainman_opts)};845return chainman;846}848void chainman_reindex_test(TestDirectory& test_directory)849{850auto notifications{std::make_shared<TestKernelNotifications>()};851auto context{create_context(notifications, ChainType::MAINNET)};852auto chainman{create_chainman(853test_directory, /*reindex=*/true, /*wipe_chainstate=*/false,854/*block_tree_db_in_memory=*/false, /*chainstate_db_in_memory=*/false, context)};856std::vector<std::string> import_files;857BOOST_CHECK(chainman->ImportBlocks(import_files));859// Sanity check some block retrievals860auto chain{chainman->GetChain()};861BOOST_CHECK_THROW(chain.GetByHeight(1000), std::runtime_error);862auto genesis_index{chain.Entries().front()};863BOOST_CHECK(!genesis_index.GetPrevious());864auto genesis_block_raw{chainman->ReadBlock(genesis_index).value().ToBytes()};865auto first_index{chain.GetByHeight(0)};866auto first_block_raw{chainman->ReadBlock(genesis_index).value().ToBytes()};867check_equal(genesis_block_raw, first_block_raw);868auto height{first_index.GetHeight()};869BOOST_CHECK_EQUAL(height, 0);871auto next_index{chain.GetByHeight(first_index.GetHeight() + 1)};872BOOST_CHECK(chain.Contains(next_index));873auto next_block_data{chainman->ReadBlock(next_index).value().ToBytes()};874auto tip_index{chain.Entries().back()};875auto tip_block_data{chainman->ReadBlock(tip_index).value().ToBytes()};876auto second_index{chain.GetByHeight(1)};877auto second_block{chainman->ReadBlock(second_index).value()};878auto second_block_data{second_block.ToBytes()};879auto second_height{second_index.GetHeight()};880BOOST_CHECK_EQUAL(second_height, 1);881check_equal(next_block_data, tip_block_data);882check_equal(next_block_data, second_block_data);884auto second_hash{second_index.GetHash()};885auto another_second_index{chainman->GetBlockTreeEntry(second_hash)};886BOOST_CHECK(another_second_index);887auto another_second_height{another_second_index->GetHeight()};888auto second_block_hash{second_block.GetHash()};889check_equal(second_block_hash.ToBytes(), second_hash.ToBytes());890BOOST_CHECK_EQUAL(second_height, another_second_height);891}893void chainman_reindex_chainstate_test(TestDirectory& test_directory)894{895auto notifications{std::make_shared<TestKernelNotifications>()};896auto context{create_context(notifications, ChainType::MAINNET)};897auto chainman{create_chainman(898test_directory, /*reindex=*/false, /*wipe_chainstate=*/true,899/*block_tree_db_in_memory=*/false, /*chainstate_db_in_memory=*/false, context)};901std::vector<std::string> import_files;902import_files.push_back(PathToString(test_directory.m_directory / "blocks" / "blk00000.dat"));903BOOST_CHECK(chainman->ImportBlocks(import_files));904}906void chainman_mainnet_validation_test(TestDirectory& test_directory)907{908auto notifications{std::make_shared<TestKernelNotifications>()};909auto validation_interface{std::make_shared<TestValidationInterface>()};910auto context{create_context(notifications, ChainType::MAINNET, validation_interface)};911auto chainman{create_chainman(912test_directory, /*reindex=*/false, /*wipe_chainstate=*/false,913/*block_tree_db_in_memory=*/false, /*chainstate_db_in_memory=*/false, context)};915// mainnet block 1916auto raw_block = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000");917Block block{raw_block};918BlockHeader header{block.GetHeader()};919TransactionView tx{block.GetTransaction(block.CountTransactions() - 1)};920BOOST_CHECK_EQUAL(byte_span_to_hex_string_reversed(tx.Txid().ToBytes()), "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");921BOOST_CHECK_EQUAL(header.Version(), 1);922BOOST_CHECK_EQUAL(header.Timestamp(), 1231469665);923BOOST_CHECK_EQUAL(header.Bits(), 0x1d00ffff);924BOOST_CHECK_EQUAL(header.Nonce(), 2573394689);925BOOST_CHECK_EQUAL(tx.CountInputs(), 1);926Transaction tx2 = tx;927BOOST_CHECK_EQUAL(tx2.CountInputs(), 1);928for (auto transaction : block.Transactions()) {929BOOST_CHECK_EQUAL(transaction.CountInputs(), 1);930}931auto output_counts = *(block.Transactions() | std::views::transform([](const auto& tx) {932return tx.CountOutputs();933})).begin();934BOOST_CHECK_EQUAL(output_counts, 1);936validation_interface->m_expected_valid_block.emplace(raw_block);937auto ser_block{block.ToBytes()};938check_equal(ser_block, raw_block);939bool new_block = false;940BOOST_CHECK(chainman->ProcessBlock(block, &new_block));941BOOST_CHECK(new_block);943validation_interface->m_expected_valid_block = std::nullopt;944new_block = false;945Block invalid_block{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[REGTEST_BLOCK_DATA.size() - 1])};946BOOST_CHECK(!chainman->ProcessBlock(invalid_block, &new_block));947BOOST_CHECK(!new_block);949auto chain{chainman->GetChain()};950BOOST_CHECK_EQUAL(chain.Height(), 1);951auto tip{chain.Entries().back()};952auto read_block{chainman->ReadBlock(tip)};953BOOST_REQUIRE(read_block);954check_equal(read_block.value().ToBytes(), raw_block);956// Check that we can read the previous block957BlockTreeEntry tip_2{*tip.GetPrevious()};958Block read_block_2{*chainman->ReadBlock(tip_2)};959BOOST_CHECK_EQUAL(chainman->ReadBlockSpentOutputs(tip_2).Count(), 0);960BOOST_CHECK_EQUAL(chainman->ReadBlockSpentOutputs(tip).Count(), 0);962// It should be an error if we go another block back, since the genesis has no ancestor963BOOST_CHECK(!tip_2.GetPrevious());965// If we try to validate it again, it should be a duplicate966BOOST_CHECK(chainman->ProcessBlock(block, &new_block));967BOOST_CHECK(!new_block);968}970BOOST_AUTO_TEST_CASE(btck_check_block_context_free)971{972constexpr size_t MERKLE_ROOT_OFFSET{4 + 32};973constexpr size_t NBITS_OFFSET{4 + 32 + 32 + 4};974constexpr size_t COINBASE_PREVOUT_N_OFFSET{4 + 32 + 32 + 4 + 4 + 4 + 1 + 4 + 1 + 32};976// Mainnet block 1977auto raw_block = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000");979// Context-free block checks still need consensus params for the optional980// proof-of-work validation path.981ChainParams mainnet_params{ChainType::MAINNET};982auto consensus_params = mainnet_params.GetConsensusParams();984Block block{raw_block};985BlockValidationState state;987BOOST_CHECK(block.Check(consensus_params, BlockCheckFlags::BASE, state));988BOOST_CHECK(state.GetValidationMode() == ValidationMode::VALID);990BOOST_CHECK(block.Check(consensus_params, BlockCheckFlags::ALL, state));991BOOST_CHECK(state.GetValidationMode() == ValidationMode::VALID);993auto bad_merkle_block_data = raw_block;994bad_merkle_block_data[MERKLE_ROOT_OFFSET] ^= std::byte{0x01};995Block bad_merkle_block{bad_merkle_block_data};997BOOST_CHECK(!bad_merkle_block.Check(consensus_params, BlockCheckFlags::MERKLE, state));998BOOST_CHECK(state.GetValidationMode() == ValidationMode::INVALID);999BOOST_CHECK(state.GetBlockValidationResult() == BlockValidationResult::MUTATED);
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.