Inside Bitcoin
Opening the selected file…
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.
251if (read_out.first != expected_block_hash) {252LogError("previous block header belongs to unexpected block %s; expected %s",253read_out.first.ToString(), expected_block_hash.ToString());254return std::nullopt;255}257return read_out.second.header;258}260bool BlockFilterIndex::CustomAppend(const interfaces::BlockInfo& block)261{262BlockFilter filter(m_filter_type, *Assert(block.data), *Assert(block.undo_data));263const uint256& header = filter.ComputeHeader(m_last_header);264bool res = Write(filter, block.height, header);265if (res) m_last_header = header; // update last header266return res;267}269bool BlockFilterIndex::Write(const BlockFilter& filter, uint32_t block_height, const uint256& filter_header)270{271size_t bytes_written = WriteFilterToDisk(m_next_filter_pos, filter);272if (bytes_written == 0) return false;274std::pair<uint256, DBVal> value;275value.first = filter.GetBlockHash();276value.second.hash = filter.GetHash();277value.second.header = filter_header;278value.second.pos = m_next_filter_pos;280m_db->Write(index_util::DBHeightKey(block_height), value);282m_next_filter_pos.nPos += bytes_written;283return true;284}286bool BlockFilterIndex::CustomRemove(const interfaces::BlockInfo& block)287{288CDBBatch batch(*m_db);289std::unique_ptr<CDBIterator> db_it(m_db->NewIterator());291// During a reorg, we need to copy block filter that is getting disconnected from the292// height index to the hash index so we can still find it when the height index entry293// is overwritten.294if (!index_util::CopyHeightIndexToHashIndex<DBVal>(*db_it, batch, m_name, block.height)) {295return false;296}298// The latest filter position gets written in Commit by the call to the BaseIndex::Rewind.299// But since this creates new references to the filter, the position should get updated here300// atomically as well in case Commit fails.301batch.Write(DB_FILTER_POS, m_next_filter_pos);302m_db->WriteBatch(batch);304// Update cached header to the previous block hash305m_last_header = *Assert(ReadFilterHeader(block.height - 1, *Assert(block.prev_hash)));306return true;307}309static bool LookupRange(CDBWrapper& db, const std::string& index_name, int start_height,310const CBlockIndex* stop_index, std::vector<DBVal>& results)311{312if (start_height < 0) {313LogError("start height (%d) is negative", start_height);314return false;315}316if (start_height > stop_index->nHeight) {317LogError("start height (%d) is greater than stop height (%d)",318start_height, stop_index->nHeight);319return false;320}322size_t results_size = static_cast<size_t>(stop_index->nHeight - start_height + 1);323std::vector<std::pair<uint256, DBVal>> values(results_size);325index_util::DBHeightKey key(start_height);326std::unique_ptr<CDBIterator> db_it(db.NewIterator());327db_it->Seek(index_util::DBHeightKey(start_height));328for (int height = start_height; height <= stop_index->nHeight; ++height) {329if (!db_it->Valid() || !db_it->GetKey(key) || key.height != height) {330return false;331}333size_t i = static_cast<size_t>(height - start_height);334if (!db_it->GetValue(values[i])) {335LogError("unable to read value in %s at key (%c, %d)",336index_name, index_util::DB_BLOCK_HEIGHT, height);337return false;338}340db_it->Next();341}343results.resize(results_size);345// Iterate backwards through block indexes collecting results in order to access the block hash346// of each entry in case we need to look it up in the hash index.347for (const CBlockIndex* block_index = stop_index;348block_index && block_index->nHeight >= start_height;349block_index = block_index->pprev) {350uint256 block_hash = block_index->GetBlockHash();352size_t i = static_cast<size_t>(block_index->nHeight - start_height);353if (block_hash == values[i].first) {354results[i] = std::move(values[i].second);355continue;356}358if (!db.Read(index_util::DBHashKey(block_hash), results[i])) {359LogError("unable to read value in %s at key (%c, %s)",360index_name, index_util::DB_BLOCK_HASH, block_hash.ToString());361return false;362}363}365return true;366}368bool BlockFilterIndex::LookupFilter(const CBlockIndex* block_index, BlockFilter& filter_out) const369{370DBVal entry;371if (!index_util::LookUpOne(*m_db, {block_index->GetBlockHash(), block_index->nHeight}, entry)) {372return false;373}375return ReadFilterFromDisk(entry.pos, entry.hash, filter_out);376}378bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out)379{380LOCK(m_cs_headers_cache);382bool is_checkpoint{block_index->nHeight % CFCHECKPT_INTERVAL == 0};384if (is_checkpoint) {385// Try to find the block in the headers cache if this is a checkpoint height.386auto header = m_headers_cache.find(block_index->GetBlockHash());387if (header != m_headers_cache.end()) {388header_out = header->second;389return true;390}391}393DBVal entry;394if (!index_util::LookUpOne(*m_db, {block_index->GetBlockHash(), block_index->nHeight}, entry)) {395return false;396}398if (is_checkpoint &&399m_headers_cache.size() < CF_HEADERS_CACHE_MAX_SZ) {400// Add to the headers cache if this is a checkpoint height.401m_headers_cache.emplace(block_index->GetBlockHash(), entry.header);402}404header_out = entry.header;405return true;406}408bool BlockFilterIndex::LookupFilterRange(int start_height, const CBlockIndex* stop_index,409std::vector<BlockFilter>& filters_out) const410{411std::vector<DBVal> entries;412if (!LookupRange(*m_db, m_name, start_height, stop_index, entries)) {413return false;414}416filters_out.resize(entries.size());417auto filter_pos_it = filters_out.begin();418for (const auto& entry : entries) {419if (!ReadFilterFromDisk(entry.pos, entry.hash, *filter_pos_it)) {420return false;421}422++filter_pos_it;423}425return true;426}428bool BlockFilterIndex::LookupFilterHashRange(int start_height, const CBlockIndex* stop_index,429std::vector<uint256>& hashes_out) const431{432std::vector<DBVal> entries;433if (!LookupRange(*m_db, m_name, start_height, stop_index, entries)) {434return false;435}437hashes_out.clear();438hashes_out.reserve(entries.size());439for (const auto& entry : entries) {440hashes_out.push_back(entry.hash);441}442return true;443}445BlockFilterIndex* GetBlockFilterIndex(BlockFilterType filter_type)446{447auto it = g_filter_indexes.find(filter_type);448return it != g_filter_indexes.end() ? &it->second : nullptr;449}451void ForEachBlockFilterIndex(std::function<void (BlockFilterIndex&)> fn)452{453for (auto& entry : g_filter_indexes) fn(entry.second);454}456bool InitBlockFilterIndex(std::function<std::unique_ptr<interfaces::Chain>()> make_chain, BlockFilterType filter_type,457size_t n_cache_size, bool f_memory, bool f_wipe)458{459auto result = g_filter_indexes.emplace(std::piecewise_construct,460std::forward_as_tuple(filter_type),461std::forward_as_tuple(make_chain(), filter_type,462n_cache_size, f_memory, f_wipe));463return result.second;464}466bool DestroyBlockFilterIndex(BlockFilterType filter_type)467{468return g_filter_indexes.erase(filter_type);469}471void DestroyAllBlockFilterIndexes()472{473g_filter_indexes.clear();474}