Skip to main content

reth_chain_state/
memory_overlay.rs

1use super::ExecutedBlock;
2use alloy_consensus::BlockHeader;
3use alloy_primitives::{keccak256, Address, BlockNumber, Bytes, StorageKey, StorageValue, B256};
4use reth_errors::ProviderResult;
5use reth_primitives_traits::{Account, Bytecode, NodePrimitives};
6use reth_storage_api::{
7    AccountReader, BlockHashReader, BytecodeReader, HashedPostStateProvider, StateProofProvider,
8    StateProvider, StateProviderBox, StateRootProvider, StorageRootProvider,
9};
10use reth_trie::{
11    updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof,
12    MultiProofTargets, StorageMultiProof, TrieInput,
13};
14use revm_database::BundleState;
15use std::{borrow::Cow, sync::OnceLock};
16
17/// A state provider that stores references to in-memory blocks along with their state as well as a
18/// reference of the historical state provider for fallback lookups.
19#[expect(missing_debug_implementations)]
20pub struct MemoryOverlayStateProviderRef<
21    'a,
22    N: NodePrimitives = reth_ethereum_primitives::EthPrimitives,
23> {
24    /// Historical state provider for state lookups that are not found in memory blocks.
25    pub(crate) historical: Box<dyn StateProvider + 'a>,
26    /// The collection of executed parent blocks. Expected order is newest to oldest.
27    pub(crate) in_memory: Cow<'a, [ExecutedBlock<N>]>,
28    /// Lazy-loaded in-memory trie data.
29    pub(crate) trie_input: OnceLock<TrieInput>,
30}
31
32impl<'a, N: NodePrimitives> MemoryOverlayStateProviderRef<'a, N> {
33    /// Create new memory overlay state provider.
34    ///
35    /// ## Arguments
36    ///
37    /// - `in_memory` - the collection of executed ancestor blocks in reverse.
38    /// - `historical` - a historical state provider for the latest ancestor block stored in the
39    ///   database.
40    pub fn new(historical: Box<dyn StateProvider + 'a>, in_memory: Vec<ExecutedBlock<N>>) -> Self {
41        Self { historical, in_memory: Cow::Owned(in_memory), trie_input: OnceLock::new() }
42    }
43
44    /// Turn this state provider into a state provider
45    pub fn boxed(self) -> Box<dyn StateProvider + 'a> {
46        Box::new(self)
47    }
48
49    /// Return lazy-loaded trie state aggregated from in-memory blocks.
50    fn trie_input(&self) -> &TrieInput {
51        self.trie_input.get_or_init(|| {
52            let mut input = TrieInput::default();
53            // Iterate from oldest to newest
54            for block in self.in_memory.iter().rev() {
55                let data = block.trie_data();
56                input.nodes.extend_from_sorted(&data.trie_updates);
57                input.state.extend_from_sorted(&data.hashed_state);
58            }
59            input
60        })
61    }
62
63    fn merged_hashed_storage(&self, address: Address, storage: HashedStorage) -> HashedStorage {
64        let state = &self.trie_input().state;
65        let mut hashed = state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
66        hashed.extend(&storage);
67        hashed
68    }
69}
70
71impl<N: NodePrimitives> BlockHashReader for MemoryOverlayStateProviderRef<'_, N> {
72    fn block_hash(&self, number: BlockNumber) -> ProviderResult<Option<B256>> {
73        for block in self.in_memory.iter() {
74            if block.recovered_block().number() == number {
75                return Ok(Some(block.recovered_block().hash()));
76            }
77        }
78
79        self.historical.block_hash(number)
80    }
81
82    fn canonical_hashes_range(
83        &self,
84        start: BlockNumber,
85        end: BlockNumber,
86    ) -> ProviderResult<Vec<B256>> {
87        let range = start..end;
88        let mut earliest_block_number = None;
89        let mut in_memory_hashes = Vec::with_capacity(range.size_hint().0);
90
91        // iterate in ascending order (oldest to newest = low to high)
92        for block in self.in_memory.iter() {
93            let block_num = block.recovered_block().number();
94            if range.contains(&block_num) {
95                in_memory_hashes.push(block.recovered_block().hash());
96                earliest_block_number = Some(block_num);
97            }
98        }
99
100        // `self.in_memory` stores executed blocks in ascending order (oldest to newest).
101        // However, `in_memory_hashes` should be constructed in descending order (newest to oldest),
102        // so we reverse the vector after collecting the hashes.
103        in_memory_hashes.reverse();
104
105        let mut hashes =
106            self.historical.canonical_hashes_range(start, earliest_block_number.unwrap_or(end))?;
107        hashes.append(&mut in_memory_hashes);
108        Ok(hashes)
109    }
110}
111
112impl<N: NodePrimitives> AccountReader for MemoryOverlayStateProviderRef<'_, N> {
113    fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
114        for block in self.in_memory.iter() {
115            if let Some(account) = block.execution_output.account(address) {
116                return Ok(account);
117            }
118        }
119
120        self.historical.basic_account(address)
121    }
122}
123
124impl<N: NodePrimitives> StateRootProvider for MemoryOverlayStateProviderRef<'_, N> {
125    fn state_root(&self, state: HashedPostState) -> ProviderResult<B256> {
126        self.state_root_from_nodes(TrieInput::from_state(state))
127    }
128
129    fn state_root_from_nodes(&self, mut input: TrieInput) -> ProviderResult<B256> {
130        input.prepend_self(self.trie_input().clone());
131        self.historical.state_root_from_nodes(input)
132    }
133
134    fn state_root_with_updates(
135        &self,
136        state: HashedPostState,
137    ) -> ProviderResult<(B256, TrieUpdates)> {
138        self.state_root_from_nodes_with_updates(TrieInput::from_state(state))
139    }
140
141    fn state_root_from_nodes_with_updates(
142        &self,
143        mut input: TrieInput,
144    ) -> ProviderResult<(B256, TrieUpdates)> {
145        input.prepend_self(self.trie_input().clone());
146        self.historical.state_root_from_nodes_with_updates(input)
147    }
148}
149
150impl<N: NodePrimitives> StorageRootProvider for MemoryOverlayStateProviderRef<'_, N> {
151    // TODO: Currently this does not reuse available in-memory trie nodes.
152    fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult<B256> {
153        let merged = self.merged_hashed_storage(address, storage);
154        self.historical.storage_root(address, merged)
155    }
156
157    // TODO: Currently this does not reuse available in-memory trie nodes.
158    fn storage_proof(
159        &self,
160        address: Address,
161        slot: B256,
162        storage: HashedStorage,
163    ) -> ProviderResult<reth_trie::StorageProof> {
164        let merged = self.merged_hashed_storage(address, storage);
165        self.historical.storage_proof(address, slot, merged)
166    }
167
168    // TODO: Currently this does not reuse available in-memory trie nodes.
169    fn storage_multiproof(
170        &self,
171        address: Address,
172        slots: &[B256],
173        storage: HashedStorage,
174    ) -> ProviderResult<StorageMultiProof> {
175        let merged = self.merged_hashed_storage(address, storage);
176        self.historical.storage_multiproof(address, slots, merged)
177    }
178}
179
180impl<N: NodePrimitives> StateProofProvider for MemoryOverlayStateProviderRef<'_, N> {
181    fn proof(
182        &self,
183        mut input: TrieInput,
184        address: Address,
185        slots: &[B256],
186    ) -> ProviderResult<AccountProof> {
187        input.prepend_self(self.trie_input().clone());
188        self.historical.proof(input, address, slots)
189    }
190
191    fn multiproof(
192        &self,
193        mut input: TrieInput,
194        targets: MultiProofTargets,
195    ) -> ProviderResult<MultiProof> {
196        input.prepend_self(self.trie_input().clone());
197        self.historical.multiproof(input, targets)
198    }
199
200    fn witness(&self, mut input: TrieInput, target: HashedPostState) -> ProviderResult<Vec<Bytes>> {
201        input.prepend_self(self.trie_input().clone());
202        self.historical.witness(input, target)
203    }
204}
205
206impl<N: NodePrimitives> HashedPostStateProvider for MemoryOverlayStateProviderRef<'_, N> {
207    fn hashed_post_state(&self, bundle_state: &BundleState) -> HashedPostState {
208        self.historical.hashed_post_state(bundle_state)
209    }
210}
211
212impl<N: NodePrimitives> StateProvider for MemoryOverlayStateProviderRef<'_, N> {
213    fn storage(
214        &self,
215        address: Address,
216        storage_key: StorageKey,
217    ) -> ProviderResult<Option<StorageValue>> {
218        for block in self.in_memory.iter() {
219            if let Some(value) = block.execution_output.storage(&address, storage_key.into()) {
220                return Ok(Some(value));
221            }
222        }
223
224        self.historical.storage(address, storage_key)
225    }
226
227    fn storage_by_hashed_key(
228        &self,
229        address: Address,
230        hashed_storage_key: StorageKey,
231    ) -> ProviderResult<Option<StorageValue>> {
232        let hashed_address = keccak256(address);
233        let state = &self.trie_input().state;
234
235        if let Some(hs) = state.storages.get(&hashed_address) {
236            if let Some(value) = hs.storage.get(&hashed_storage_key) {
237                return Ok(Some(*value));
238            }
239            if hs.wiped {
240                return Ok(Some(StorageValue::ZERO));
241            }
242        }
243
244        self.historical.storage_by_hashed_key(address, hashed_storage_key)
245    }
246}
247
248impl<N: NodePrimitives> BytecodeReader for MemoryOverlayStateProviderRef<'_, N> {
249    fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
250        for block in self.in_memory.iter() {
251            if let Some(contract) = block.execution_output.bytecode(code_hash) {
252                return Ok(Some(contract));
253            }
254        }
255
256        self.historical.bytecode_by_hash(code_hash)
257    }
258}
259
260/// An owned state provider that stores references to in-memory blocks along with their state as
261/// well as a reference of the historical state provider for fallback lookups.
262#[expect(missing_debug_implementations)]
263pub struct MemoryOverlayStateProvider<N: NodePrimitives = reth_ethereum_primitives::EthPrimitives> {
264    /// Historical state provider for state lookups that are not found in memory blocks.
265    pub(crate) historical: StateProviderBox,
266    /// The collection of executed parent blocks. Expected order is newest to oldest.
267    pub(crate) in_memory: Vec<ExecutedBlock<N>>,
268    /// Lazy-loaded in-memory trie data.
269    pub(crate) trie_input: OnceLock<TrieInput>,
270}
271
272impl<N: NodePrimitives> MemoryOverlayStateProvider<N> {
273    /// Create new memory overlay state provider.
274    ///
275    /// ## Arguments
276    ///
277    /// - `in_memory` - the collection of executed ancestor blocks in reverse.
278    /// - `historical` - a historical state provider for the latest ancestor block stored in the
279    ///   database.
280    pub fn new(historical: StateProviderBox, in_memory: Vec<ExecutedBlock<N>>) -> Self {
281        Self { historical, in_memory, trie_input: OnceLock::new() }
282    }
283
284    /// Returns a new provider that takes the `TX` as reference
285    #[inline(always)]
286    fn as_ref(&self) -> MemoryOverlayStateProviderRef<'_, N> {
287        MemoryOverlayStateProviderRef {
288            historical: Box::new(self.historical.as_ref()),
289            in_memory: Cow::Borrowed(&self.in_memory),
290            trie_input: self.trie_input.clone(),
291        }
292    }
293
294    /// Wraps the [`Self`] in a `Box`.
295    pub fn boxed(self) -> StateProviderBox {
296        Box::new(self)
297    }
298}
299
300// Delegates all provider impls to [`MemoryOverlayStateProviderRef`]
301reth_storage_api::macros::delegate_provider_impls!(MemoryOverlayStateProvider<N> where [N: NodePrimitives]);