1use super::ExecutedBlock;
2use alloy_consensus::BlockHeader;
3use alloy_primitives::{
4 keccak256, Address, BlockNumber, Bytes, StorageKey, StorageValue, B256, U256,
5};
6use reth_errors::ProviderResult;
7use reth_primitives_traits::{Account, Bytecode, NodePrimitives};
8use reth_storage_api::{
9 AccountReader, BlockHashReader, BytecodeReader, HashedPostStateProvider, StateProofProvider,
10 StateProvider, StateProviderBox, StateRootProvider, StorageRootProvider,
11};
12use reth_trie::{
13 updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof,
14 MultiProofTargets, StorageMultiProof, TrieInput,
15};
16use revm::database::BundleState;
17use std::{borrow::Cow, sync::OnceLock};
18
19#[expect(missing_debug_implementations)]
22pub struct MemoryOverlayStateProviderRef<
23 'a,
24 N: NodePrimitives = reth_ethereum_primitives::EthPrimitives,
25> {
26 pub(crate) historical: Box<dyn StateProvider + 'a>,
28 pub(crate) in_memory: Cow<'a, [ExecutedBlock<N>]>,
30 pub(crate) trie_input: OnceLock<TrieInput>,
32}
33
34impl<'a, N: NodePrimitives> MemoryOverlayStateProviderRef<'a, N> {
35 pub fn new(historical: Box<dyn StateProvider + 'a>, in_memory: Vec<ExecutedBlock<N>>) -> Self {
43 Self { historical, in_memory: Cow::Owned(in_memory), trie_input: OnceLock::new() }
44 }
45
46 pub fn boxed(self) -> Box<dyn StateProvider + 'a> {
48 Box::new(self)
49 }
50
51 fn trie_input(&self) -> &TrieInput {
53 self.trie_input.get_or_init(|| {
54 let mut input = TrieInput::default();
55 for block in self.in_memory.iter().rev() {
57 let data = block.trie_data();
58 input.nodes.extend_from_sorted(&data.sorted.trie_updates);
59 input.state.extend_from_sorted(&data.sorted.hashed_state);
60 }
61 input
62 })
63 }
64
65 fn merged_hashed_storage(&self, address: Address, storage: HashedStorage) -> HashedStorage {
66 let state = &self.trie_input().state;
67 let mut hashed = state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
68 hashed.extend(&storage);
69 hashed
70 }
71}
72
73impl<N: NodePrimitives> BlockHashReader for MemoryOverlayStateProviderRef<'_, N> {
74 fn block_hash(&self, number: BlockNumber) -> ProviderResult<Option<B256>> {
75 for block in self.in_memory.iter() {
76 if block.recovered_block().number() == number {
77 return Ok(Some(block.recovered_block().hash()));
78 }
79 }
80
81 self.historical.block_hash(number)
82 }
83
84 fn canonical_hashes_range(
85 &self,
86 start: BlockNumber,
87 end: BlockNumber,
88 ) -> ProviderResult<Vec<B256>> {
89 let range = start..end;
90 let mut earliest_block_number = None;
91 let mut in_memory_hashes = Vec::with_capacity(range.size_hint().0);
92
93 for block in self.in_memory.iter() {
95 let block_num = block.recovered_block().number();
96 if range.contains(&block_num) {
97 in_memory_hashes.push(block.recovered_block().hash());
98 earliest_block_number = Some(block_num);
99 }
100 }
101
102 in_memory_hashes.reverse();
106
107 let mut hashes =
108 self.historical.canonical_hashes_range(start, earliest_block_number.unwrap_or(end))?;
109 hashes.append(&mut in_memory_hashes);
110 Ok(hashes)
111 }
112}
113
114impl<N: NodePrimitives> AccountReader for MemoryOverlayStateProviderRef<'_, N> {
115 fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
116 for block in self.in_memory.iter() {
117 if let Some(account) = block.execution_output.account(address) {
118 return Ok(account);
119 }
120 }
121
122 self.historical.basic_account(address)
123 }
124}
125
126impl<N: NodePrimitives> StateRootProvider for MemoryOverlayStateProviderRef<'_, N> {
127 fn state_root(&self, state: HashedPostState) -> ProviderResult<B256> {
128 self.state_root_from_nodes(TrieInput::from_state(state))
129 }
130
131 fn state_root_from_nodes(&self, mut input: TrieInput) -> ProviderResult<B256> {
132 input.prepend_self(self.trie_input().clone());
133 self.historical.state_root_from_nodes(input)
134 }
135
136 fn state_root_with_updates(
137 &self,
138 state: HashedPostState,
139 ) -> ProviderResult<(B256, TrieUpdates)> {
140 self.state_root_from_nodes_with_updates(TrieInput::from_state(state))
141 }
142
143 fn state_root_from_nodes_with_updates(
144 &self,
145 mut input: TrieInput,
146 ) -> ProviderResult<(B256, TrieUpdates)> {
147 input.prepend_self(self.trie_input().clone());
148 self.historical.state_root_from_nodes_with_updates(input)
149 }
150}
151
152impl<N: NodePrimitives> StorageRootProvider for MemoryOverlayStateProviderRef<'_, N> {
153 fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult<B256> {
155 let merged = self.merged_hashed_storage(address, storage);
156 self.historical.storage_root(address, merged)
157 }
158
159 fn storage_proof(
161 &self,
162 address: Address,
163 slot: B256,
164 storage: HashedStorage,
165 ) -> ProviderResult<reth_trie::StorageProof> {
166 let merged = self.merged_hashed_storage(address, storage);
167 self.historical.storage_proof(address, slot, merged)
168 }
169
170 fn storage_multiproof(
172 &self,
173 address: Address,
174 slots: &[B256],
175 storage: HashedStorage,
176 ) -> ProviderResult<StorageMultiProof> {
177 let merged = self.merged_hashed_storage(address, storage);
178 self.historical.storage_multiproof(address, slots, merged)
179 }
180}
181
182impl<N: NodePrimitives> StateProofProvider for MemoryOverlayStateProviderRef<'_, N> {
183 fn proof(
184 &self,
185 mut input: TrieInput,
186 address: Address,
187 slots: &[B256],
188 ) -> ProviderResult<AccountProof> {
189 input.prepend_self(self.trie_input().clone());
190 self.historical.proof(input, address, slots)
191 }
192
193 fn multiproof(
194 &self,
195 mut input: TrieInput,
196 targets: MultiProofTargets,
197 ) -> ProviderResult<MultiProof> {
198 input.prepend_self(self.trie_input().clone());
199 self.historical.multiproof(input, targets)
200 }
201
202 fn witness(
203 &self,
204 mut input: TrieInput,
205 target: HashedPostState,
206 mode: reth_trie::ExecutionWitnessMode,
207 ) -> ProviderResult<Vec<Bytes>> {
208 input.prepend_self(self.trie_input().clone());
209 self.historical.witness(input, target, mode)
210 }
211}
212
213impl<N: NodePrimitives> HashedPostStateProvider for MemoryOverlayStateProviderRef<'_, N> {
214 fn hashed_post_state(&self, bundle_state: &BundleState) -> ProviderResult<HashedPostState> {
215 let mut hashed_state = self.historical.hashed_post_state(bundle_state)?;
216
217 for (address, account) in bundle_state.state() {
218 if !account.was_destroyed() || account.original_info.is_none() {
220 continue
221 }
222
223 let hashed_address = keccak256(address);
224 let Some(parent_storage) = self.trie_input().state.storages.get(&hashed_address) else {
225 continue
226 };
227 let storage = &mut hashed_state.storages.entry(hashed_address).or_default().storage;
228 for hashed_slot in parent_storage.storage.keys() {
229 storage.entry(*hashed_slot).or_insert(U256::ZERO);
230 }
231 }
232
233 Ok(hashed_state)
234 }
235}
236
237impl<N: NodePrimitives> StateProvider for MemoryOverlayStateProviderRef<'_, N> {
238 fn storage(
239 &self,
240 address: Address,
241 storage_key: StorageKey,
242 ) -> ProviderResult<Option<StorageValue>> {
243 for block in self.in_memory.iter() {
244 if let Some(value) = block.execution_output.storage(&address, storage_key.into()) {
245 return Ok(Some(value));
246 }
247 }
248
249 self.historical.storage(address, storage_key)
250 }
251}
252
253impl<N: NodePrimitives> BytecodeReader for MemoryOverlayStateProviderRef<'_, N> {
254 fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
255 for block in self.in_memory.iter() {
256 if let Some(contract) = block.execution_output.bytecode(code_hash) {
257 return Ok(Some(contract));
258 }
259 }
260
261 self.historical.bytecode_by_hash(code_hash)
262 }
263}
264
265#[expect(missing_debug_implementations)]
268pub struct MemoryOverlayStateProvider<N: NodePrimitives = reth_ethereum_primitives::EthPrimitives> {
269 pub(crate) historical: StateProviderBox,
271 pub(crate) in_memory: Vec<ExecutedBlock<N>>,
273 pub(crate) trie_input: OnceLock<TrieInput>,
275}
276
277impl<N: NodePrimitives> MemoryOverlayStateProvider<N> {
278 pub fn new(historical: StateProviderBox, in_memory: Vec<ExecutedBlock<N>>) -> Self {
286 Self { historical, in_memory, trie_input: OnceLock::new() }
287 }
288
289 #[inline(always)]
291 fn as_ref(&self) -> MemoryOverlayStateProviderRef<'_, N> {
292 MemoryOverlayStateProviderRef {
293 historical: Box::new(self.historical.as_ref()),
294 in_memory: Cow::Borrowed(&self.in_memory),
295 trie_input: self.trie_input.clone(),
296 }
297 }
298
299 pub fn boxed(self) -> StateProviderBox {
301 Box::new(self)
302 }
303}
304
305reth_storage_api::macros::delegate_provider_impls!(MemoryOverlayStateProvider<N> where [N: NodePrimitives]);
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311 use reth_ethereum_primitives::EthPrimitives;
312 use reth_storage_api::noop::NoopProvider;
313 use revm::database::{AccountStatus, BundleAccount};
314
315 #[test]
316 fn created_and_destroyed_account_skips_in_memory_trie_aggregation() {
317 let address = Address::with_last_byte(1);
318 let provider = MemoryOverlayStateProviderRef::<EthPrimitives>::new(
319 Box::new(NoopProvider::default()),
320 Vec::new(),
321 );
322 let mut bundle_state = BundleState::default();
323 bundle_state.state.insert(
324 address,
325 BundleAccount::new(None, None, Default::default(), AccountStatus::Destroyed),
326 );
327
328 provider.hashed_post_state(&bundle_state).unwrap();
329
330 assert!(provider.trie_input.get().is_none());
331 }
332}