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, DecodedMultiProofV2, HashedPostState, HashedStorage,
14 MultiProof, MultiProofTargets, MultiProofTargetsV2, 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 multiproof_v2(
203 &self,
204 mut input: TrieInput,
205 targets: MultiProofTargetsV2,
206 ) -> ProviderResult<DecodedMultiProofV2> {
207 input.prepend_self(self.trie_input().clone());
208 self.historical.multiproof_v2(input, targets)
209 }
210
211 fn witness(
212 &self,
213 mut input: TrieInput,
214 target: HashedPostState,
215 mode: reth_trie::ExecutionWitnessMode,
216 ) -> ProviderResult<Vec<Bytes>> {
217 input.prepend_self(self.trie_input().clone());
218 self.historical.witness(input, target, mode)
219 }
220}
221
222impl<N: NodePrimitives> HashedPostStateProvider for MemoryOverlayStateProviderRef<'_, N> {
223 fn hashed_post_state(&self, bundle_state: &BundleState) -> ProviderResult<HashedPostState> {
224 let mut hashed_state = self.historical.hashed_post_state(bundle_state)?;
225
226 for (address, account) in bundle_state.state() {
227 if !account.was_destroyed() || account.original_info.is_none() {
229 continue
230 }
231
232 let hashed_address = keccak256(address);
233 let Some(parent_storage) = self.trie_input().state.storages.get(&hashed_address) else {
234 continue
235 };
236 let storage = &mut hashed_state.storages.entry(hashed_address).or_default().storage;
237 for hashed_slot in parent_storage.storage.keys() {
238 storage.entry(*hashed_slot).or_insert(U256::ZERO);
239 }
240 }
241
242 Ok(hashed_state)
243 }
244}
245
246impl<N: NodePrimitives> StateProvider for MemoryOverlayStateProviderRef<'_, N> {
247 fn storage(
248 &self,
249 address: Address,
250 storage_key: StorageKey,
251 ) -> ProviderResult<Option<StorageValue>> {
252 for block in self.in_memory.iter() {
253 if let Some(value) = block.execution_output.storage(&address, storage_key.into()) {
254 return Ok(Some(value));
255 }
256 }
257
258 self.historical.storage(address, storage_key)
259 }
260}
261
262impl<N: NodePrimitives> BytecodeReader for MemoryOverlayStateProviderRef<'_, N> {
263 fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
264 for block in self.in_memory.iter() {
265 if let Some(contract) = block.execution_output.bytecode(code_hash) {
266 return Ok(Some(contract));
267 }
268 }
269
270 self.historical.bytecode_by_hash(code_hash)
271 }
272}
273
274#[expect(missing_debug_implementations)]
277pub struct MemoryOverlayStateProvider<N: NodePrimitives = reth_ethereum_primitives::EthPrimitives> {
278 pub(crate) historical: StateProviderBox,
280 pub(crate) in_memory: Vec<ExecutedBlock<N>>,
282 pub(crate) trie_input: OnceLock<TrieInput>,
284}
285
286impl<N: NodePrimitives> MemoryOverlayStateProvider<N> {
287 pub fn new(historical: StateProviderBox, in_memory: Vec<ExecutedBlock<N>>) -> Self {
295 Self { historical, in_memory, trie_input: OnceLock::new() }
296 }
297
298 #[inline(always)]
300 fn as_ref(&self) -> MemoryOverlayStateProviderRef<'_, N> {
301 MemoryOverlayStateProviderRef {
302 historical: Box::new(self.historical.as_ref()),
303 in_memory: Cow::Borrowed(&self.in_memory),
304 trie_input: self.trie_input.clone(),
305 }
306 }
307
308 pub fn boxed(self) -> StateProviderBox {
310 Box::new(self)
311 }
312}
313
314reth_storage_api::macros::delegate_provider_impls!(MemoryOverlayStateProvider<N> where [N: NodePrimitives]);
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320 use reth_ethereum_primitives::EthPrimitives;
321 use reth_storage_api::noop::NoopProvider;
322 use revm::database::{AccountStatus, BundleAccount};
323
324 #[test]
325 fn created_and_destroyed_account_skips_in_memory_trie_aggregation() {
326 let address = Address::with_last_byte(1);
327 let provider = MemoryOverlayStateProviderRef::<EthPrimitives>::new(
328 Box::new(NoopProvider::default()),
329 Vec::new(),
330 );
331 let mut bundle_state = BundleState::default();
332 bundle_state.state.insert(
333 address,
334 BundleAccount::new(None, None, Default::default(), AccountStatus::Destroyed),
335 );
336
337 provider.hashed_post_state(&bundle_state).unwrap();
338
339 assert!(provider.trie_input.get().is_none());
340 }
341}