Skip to content

Commit 0187d4c

Browse files
committed
[indexes] Add compact block filter headers cache
Cache block filter headers at heights of multiples of 1000 in memory. Block filter headers at height 1000x are checkpointed, and will be the most frequently requested. Cache them in memory to avoid costly disk reads.
1 parent 8da1e43 commit 0187d4c

File tree

3 files changed

+40
-6
lines changed

3 files changed

+40
-6
lines changed

src/index/blockfilterindex.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ constexpr char DB_FILTER_POS = 'P';
3131
constexpr unsigned int MAX_FLTR_FILE_SIZE = 0x1000000; // 16 MiB
3232
/** The pre-allocation chunk size for fltr?????.dat files */
3333
constexpr unsigned int FLTR_FILE_CHUNK_SIZE = 0x100000; // 1 MiB
34+
/** Maximum size of the cfheaders cache
35+
* We have a limit to prevent a bug in filling this cache
36+
* potentially turning into an OOM. At 2000 entries, this cache
37+
* is big enough for a 2,000,000 length block chain, which
38+
* we should be enough until ~2047. */
39+
constexpr size_t CF_HEADERS_CACHE_MAX_SZ{2000};
3440

3541
namespace {
3642

@@ -387,13 +393,32 @@ bool BlockFilterIndex::LookupFilter(const CBlockIndex* block_index, BlockFilter&
387393
return ReadFilterFromDisk(entry.pos, filter_out);
388394
}
389395

390-
bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) const
396+
bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out)
391397
{
398+
LOCK(m_cs_headers_cache);
399+
400+
bool is_checkpoint{block_index->nHeight % CFCHECKPT_INTERVAL == 0};
401+
402+
if (is_checkpoint) {
403+
// Try to find the block in the headers cache if this is a checkpoint height.
404+
auto header = m_headers_cache.find(block_index->GetBlockHash());
405+
if (header != m_headers_cache.end()) {
406+
header_out = header->second;
407+
return true;
408+
}
409+
}
410+
392411
DBVal entry;
393412
if (!LookupOne(*m_db, block_index, entry)) {
394413
return false;
395414
}
396415

416+
if (is_checkpoint &&
417+
m_headers_cache.size() < CF_HEADERS_CACHE_MAX_SZ) {
418+
// Add to the headers cache if this is a checkpoint height.
419+
m_headers_cache.emplace(block_index->GetBlockHash(), entry.header);
420+
}
421+
397422
header_out = entry.header;
398423
return true;
399424
}

src/index/blockfilterindex.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
#include <flatfile.h>
1111
#include <index/base.h>
1212

13+
/** Interval between compact filter checkpoints. See BIP 157. */
14+
static constexpr int CFCHECKPT_INTERVAL = 1000;
15+
16+
struct FilterHeaderHasher
17+
{
18+
size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
19+
};
20+
1321
/**
1422
* BlockFilterIndex is used to store and retrieve block filters, hashes, and headers for a range of
1523
* blocks by height. An index is constructed for each supported filter type with its own database
@@ -30,6 +38,9 @@ class BlockFilterIndex final : public BaseIndex
3038
bool ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const;
3139
size_t WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& filter);
3240

41+
Mutex m_cs_headers_cache;
42+
std::unordered_map<uint256, uint256, FilterHeaderHasher> m_headers_cache GUARDED_BY(m_cs_headers_cache);
43+
3344
protected:
3445
bool Init() override;
3546

@@ -54,7 +65,7 @@ class BlockFilterIndex final : public BaseIndex
5465
bool LookupFilter(const CBlockIndex* block_index, BlockFilter& filter_out) const;
5566

5667
/** Get a single filter header by block. */
57-
bool LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) const;
68+
bool LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out);
5869

5970
/** Get a range of filters between two heights on a chain. */
6071
bool LookupFilterRange(int start_height, const CBlockIndex* stop_index,

src/net_processing.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,6 @@ static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_
129129
static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
130130
/** Maximum feefilter broadcast delay after significant change. */
131131
static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
132-
/** Interval between compact filter checkpoints. See BIP 157. */
133-
static constexpr int CFCHECKPT_INTERVAL = 1000;
134132

135133
struct COrphanTx {
136134
// When modifying, adapt the copy of this definition in tests/DoS_tests.
@@ -1990,7 +1988,7 @@ static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_pa
19901988
BlockFilterType filter_type,
19911989
const uint256& stop_hash,
19921990
const CBlockIndex*& stop_index,
1993-
const BlockFilterIndex*& filter_index)
1991+
BlockFilterIndex*& filter_index)
19941992
{
19951993
const bool supported_filter_type =
19961994
(filter_type == BlockFilterType::BASIC &&
@@ -2045,7 +2043,7 @@ static void ProcessGetCFCheckPt(CNode* pfrom, CDataStream& vRecv, const CChainPa
20452043
const BlockFilterType filter_type = static_cast<BlockFilterType>(filter_type_ser);
20462044

20472045
const CBlockIndex* stop_index;
2048-
const BlockFilterIndex* filter_index;
2046+
BlockFilterIndex* filter_index;
20492047
if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, stop_hash,
20502048
stop_index, filter_index)) {
20512049
return;

0 commit comments

Comments
 (0)