Skip to main content

forest/chain_sync/
bad_block_cache.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use std::num::NonZeroUsize;
5
6use nonzero_ext::nonzero;
7
8use crate::prelude::*;
9use crate::utils::cache::SizeTrackingCache;
10
11/// Default capacity for CID caches (32768 entries).
12/// That's about 4 MiB.
13const DEFAULT_CID_CACHE_CAPACITY: NonZeroUsize = nonzero!(1usize << 15);
14
15/// Thread-safe cache for tracking bad blocks.
16/// This cache is checked before validating a block, to ensure no duplicate
17/// work.
18#[derive(Debug, derive_more::Deref)]
19pub struct BadBlockCache {
20    cache: SizeTrackingCache<CidWrapper, ()>,
21}
22
23impl Default for BadBlockCache {
24    fn default() -> Self {
25        Self::new(DEFAULT_CID_CACHE_CAPACITY)
26    }
27}
28
29impl ShallowClone for BadBlockCache {
30    fn shallow_clone(&self) -> Self {
31        Self {
32            cache: self.cache.shallow_clone(),
33        }
34    }
35}
36
37impl BadBlockCache {
38    pub fn new(cap: NonZeroUsize) -> Self {
39        Self {
40            cache: SizeTrackingCache::new_with_metrics("bad_block", cap),
41        }
42    }
43
44    pub fn push(&self, c: Cid) {
45        self.cache.insert(c.into(), ());
46        tracing::warn!("Marked bad block: {c}");
47    }
48}
49
50/// Thread-safe cache for tracking recently seen gossip block CIDs.
51/// Used to de-duplicate gossip blocks before expensive message fetching.
52#[derive(Debug, derive_more::Deref)]
53pub struct SeenBlockCache {
54    cache: SizeTrackingCache<CidWrapper, ()>,
55}
56
57impl ShallowClone for SeenBlockCache {
58    fn shallow_clone(&self) -> Self {
59        Self {
60            cache: self.cache.shallow_clone(),
61        }
62    }
63}
64
65impl Default for SeenBlockCache {
66    fn default() -> Self {
67        Self::new(DEFAULT_CID_CACHE_CAPACITY)
68    }
69}
70
71impl SeenBlockCache {
72    pub fn new(cap: NonZeroUsize) -> Self {
73        Self {
74            cache: SizeTrackingCache::new_with_metrics("seen_gossip_block", cap),
75        }
76    }
77
78    /// Returns `true` if the CID was already present (duplicate).
79    /// Always inserts/refreshes the entry.
80    pub fn test_and_insert(&self, c: &Cid) -> bool {
81        self.cache.push_and_get_prev((*c).into(), ()).is_some()
82    }
83}