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.
251stats.total_prevout_spent_amount = entry.total_prevout_spent_amount;252stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;253stats.total_coinbase_amount = entry.total_coinbase_amount;254stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block;255stats.total_unspendables_bip30 = entry.total_unspendables_bip30;256stats.total_unspendables_scripts = entry.total_unspendables_scripts;257stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;259return stats;260}262bool CoinStatsIndex::CustomInit(const std::optional<interfaces::BlockRef>& block)263{264if (!m_db->Read(DB_MUHASH, m_muhash)) {265// Check that the cause of the read failure is that the key does not266// exist. Any other errors indicate database corruption or a disk267// failure, and starting the index would cause further corruption.268if (m_db->Exists(DB_MUHASH)) {269LogError("Cannot read current %s state; index may be corrupted",270GetName());271return false;272}273}275if (block) {276DBVal entry;277if (!index_util::LookUpOne(*m_db, *block, entry)) {278LogError("Cannot read current %s state; index may be corrupted",279GetName());280return false;281}283uint256 out;284m_muhash.Finalize(out);285if (entry.muhash != out) {286LogError("Cannot read current %s state; index may be corrupted",287GetName());288return false;289}291m_transaction_output_count = entry.transaction_output_count;292m_bogo_size = entry.bogo_size;293m_total_amount = entry.total_amount;294m_total_subsidy = entry.total_subsidy;295m_total_prevout_spent_amount = entry.total_prevout_spent_amount;296m_total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;297m_total_coinbase_amount = entry.total_coinbase_amount;298m_total_unspendables_genesis_block = entry.total_unspendables_genesis_block;299m_total_unspendables_bip30 = entry.total_unspendables_bip30;300m_total_unspendables_scripts = entry.total_unspendables_scripts;301m_total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;302m_current_block_hash = block->hash;303}305return true;306}308bool CoinStatsIndex::CustomCommit(CDBBatch& batch)309{310// DB_MUHASH should always be committed in a batch together with DB_BEST_BLOCK311// to prevent an inconsistent state of the DB.312batch.Write(DB_MUHASH, m_muhash);313return true;314}316interfaces::Chain::NotifyOptions CoinStatsIndex::CustomOptions()317{318interfaces::Chain::NotifyOptions options;319options.connect_undo_data = true;320options.disconnect_data = true;321options.disconnect_undo_data = true;322return options;323}325// Revert a single block as part of a reorg326bool CoinStatsIndex::RevertBlock(const interfaces::BlockInfo& block)327{328std::pair<uint256, DBVal> read_out;330// Ignore genesis block331if (block.height > 0) {332if (!m_db->Read(index_util::DBHeightKey(block.height - 1), read_out)) {333return false;334}336uint256 expected_block_hash{*block.prev_hash};337if (read_out.first != expected_block_hash) {338LogWarning("previous block header belongs to unexpected block %s; expected %s",339read_out.first.ToString(), expected_block_hash.ToString());341if (!m_db->Read(index_util::DBHashKey(expected_block_hash), read_out)) {342LogError("previous block header not found; expected %s",343expected_block_hash.ToString());344return false;345}346}347}349// Roll back muhash by removing the new UTXOs that were created by the350// block and reapplying the old UTXOs that were spent by the block351assert(block.data);352assert(block.undo_data);353for (size_t i = 0; i < block.data->vtx.size(); ++i) {354const auto& tx{block.data->vtx.at(i)};355const bool is_coinbase{tx->IsCoinBase()};357if (is_coinbase && IsBIP30Unspendable(block.hash, block.height)) {358continue;359}361for (uint32_t j = 0; j < tx->vout.size(); ++j) {362const CTxOut& out{tx->vout[j]};363const COutPoint outpoint{tx->GetHash(), j};364const Coin coin{out, block.height, is_coinbase};366if (!coin.out.scriptPubKey.IsUnspendable()) {367RemoveCoinHash(m_muhash, outpoint, coin);368}369}371// The coinbase tx has no undo data since no former output is spent372if (!is_coinbase) {373const auto& tx_undo{block.undo_data->vtxundo.at(i - 1)};375for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {376const Coin& coin{tx_undo.vprevout[j]};377const COutPoint outpoint{tx->vin[j].prevout.hash, tx->vin[j].prevout.n};378ApplyCoinHash(m_muhash, outpoint, coin);379}380}381}383// Check that the rolled back muhash is consistent with the DB read out384uint256 out;385m_muhash.Finalize(out);386Assert(read_out.second.muhash == out);388// Apply the other values from the DB to the member variables389m_transaction_output_count = read_out.second.transaction_output_count;390m_total_amount = read_out.second.total_amount;391m_bogo_size = read_out.second.bogo_size;392m_total_subsidy = read_out.second.total_subsidy;393m_total_prevout_spent_amount = read_out.second.total_prevout_spent_amount;394m_total_new_outputs_ex_coinbase_amount = read_out.second.total_new_outputs_ex_coinbase_amount;395m_total_coinbase_amount = read_out.second.total_coinbase_amount;396m_total_unspendables_genesis_block = read_out.second.total_unspendables_genesis_block;397m_total_unspendables_bip30 = read_out.second.total_unspendables_bip30;398m_total_unspendables_scripts = read_out.second.total_unspendables_scripts;399m_total_unspendables_unclaimed_rewards = read_out.second.total_unspendables_unclaimed_rewards;400m_current_block_hash = *block.prev_hash;402return true;403}