revm_database/states/
block_hash_cache.rs

1use primitives::{alloy_primitives::B256, BLOCK_HASH_HISTORY};
2use std::boxed::Box;
3
4const BLOCK_HASH_HISTORY_USIZE: usize = BLOCK_HASH_HISTORY as usize;
5
6/// A fixed-size cache for the 256 most recent block hashes.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct BlockHashCache {
9    /// A fixed-size array holding the block hashes.
10    /// Since we only store the most recent 256 block hashes, this array has a length of 256.
11    /// The reason we store block number alongside its hash is to handle the case where it wraps around,
12    /// so we can verify the block number. Uses `Option<u64>` to distinguish between "not cached"
13    /// (`None`) and "cached with value" (`Some(block_number)`).
14    hashes: Box<[(Option<u64>, B256); BLOCK_HASH_HISTORY_USIZE]>,
15}
16
17impl Default for BlockHashCache {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl BlockHashCache {
24    /// Creates a new empty BlockHashCache of length [BLOCK_HASH_HISTORY].
25    #[inline]
26    pub fn new() -> Self {
27        Self {
28            hashes: Box::new([(None, B256::ZERO); BLOCK_HASH_HISTORY_USIZE]),
29        }
30    }
31
32    /// Inserts a block hash for the given block number.
33    #[inline]
34    pub const fn insert(&mut self, block_number: u64, block_hash: B256) {
35        let index = (block_number % BLOCK_HASH_HISTORY) as usize;
36        self.hashes[index] = (Some(block_number), block_hash);
37    }
38
39    /// Retrieves the block hash for the given block number, if it exists in the cache.
40    #[inline]
41    pub fn get(&self, block_number: u64) -> Option<B256> {
42        let index = (block_number % BLOCK_HASH_HISTORY) as usize;
43        let (stored_block_number, stored_hash) = self.hashes[index];
44        if Some(block_number) == stored_block_number {
45            Some(stored_hash)
46        } else {
47            None
48        }
49    }
50}