Skip to main content

reth_storage_overlay/
builder.rs

1use crate::{
2    manager::{OverlayCacheConfig, StateTrieOverlayError},
3    OverlayManager,
4};
5use alloy_eips::BlockNumHash;
6use alloy_primitives::{
7    map::{AddressMap, AddressSet, B256Map, U256Map},
8    Address, BlockHash, BlockNumber, B256, U256,
9};
10use metrics::{Counter, Histogram};
11use reth_chain_state::{BlockState, ExecutedBlock};
12use reth_errors::{ProviderError, ProviderResult};
13use reth_ethereum_primitives::EthPrimitives;
14use reth_metrics::Metrics;
15use reth_primitives_traits::{AlloyBlockHeader, NodePrimitives};
16use reth_prune_types::PruneSegment;
17use reth_stages_types::StageId;
18use reth_storage_api::{
19    BlockNumReader, ChangeSetReader, DBProvider, PruneCheckpointReader, StageCheckpointReader,
20    StorageChangeSetReader, StorageSettingsCache,
21};
22use reth_trie::{updates::TrieUpdatesSorted, HashedPostStateSorted, TrieInputSorted};
23use reth_trie_db::DatabaseHashedPostState;
24use revm::{bytecode::Bytecode, database::BundleState, state::AccountInfo};
25use std::{
26    ops::RangeInclusive,
27    sync::Arc,
28    time::{Duration, Instant},
29};
30use tracing::{debug, debug_span, instrument};
31
32/// Contains the trie and hashed-state data required to initialize an overlay state provider.
33#[derive(Debug, Clone)]
34pub struct StateTrieOverlay {
35    input: TrieInputSorted,
36    /// Whether construction was skipped because a reused sparse trie covers this range.
37    skipped_for_reused_sparse_trie: bool,
38}
39
40impl StateTrieOverlay {
41    pub(crate) const fn new(input: TrieInputSorted) -> Self {
42        Self { input, skipped_for_reused_sparse_trie: false }
43    }
44
45    fn empty() -> Self {
46        Self { input: TrieInputSorted::default(), skipped_for_reused_sparse_trie: true }
47    }
48
49    /// Returns the trie input represented by this overlay.
50    pub const fn input(&self) -> &TrieInputSorted {
51        &self.input
52    }
53
54    pub(crate) const fn skipped_for_reused_sparse_trie(&self) -> bool {
55        self.skipped_for_reused_sparse_trie
56    }
57}
58
59/// Execution state required to initialize an overlay state provider.
60///
61/// Account entries preserve known non-existence, while storage and code entries contain only data
62/// explicitly observed during execution. Accounts never retain database-context-local lookup IDs.
63#[derive(Clone, Debug, Default)]
64pub struct ExecutionOverlay {
65    /// In-memory block hashes in ascending block-number order.
66    block_hashes: Vec<BlockNumHash>,
67    /// Account state by address, without database-context-local [`AccountInfo::account_id`] hints.
68    accounts: AddressMap<Option<AccountInfo>>,
69    /// Storage values by address and slot.
70    storage: AddressMap<U256Map<U256>>,
71    /// Accounts whose storage was wiped during execution.
72    ///
73    /// An absent slot for one of these accounts is known to be zero, rather than falling back to
74    /// the durable database state.
75    storage_wipes: AddressSet,
76    /// Bytecode by code hash.
77    code_hashes: B256Map<Bytecode>,
78}
79
80impl ExecutionOverlay {
81    /// Returns the in-memory block hashes in ascending block-number order.
82    pub const fn block_hashes(&self) -> &[BlockNumHash] {
83        self.block_hashes.as_slice()
84    }
85
86    /// Returns the account state by address.
87    pub const fn accounts(&self) -> &AddressMap<Option<AccountInfo>> {
88        &self.accounts
89    }
90
91    /// Returns the storage values by address and slot.
92    pub const fn storage(&self) -> &AddressMap<U256Map<U256>> {
93        &self.storage
94    }
95
96    /// Returns an explicitly observed storage value, or zero when the account's storage was
97    /// wiped.
98    pub(crate) fn storage_value(&self, address: Address, slot: U256) -> Option<U256> {
99        self.storage
100            .get(&address)
101            .and_then(|storage| storage.get(&slot))
102            .copied()
103            .or_else(|| self.storage_wipes.contains(&address).then_some(U256::ZERO))
104    }
105
106    /// Returns the bytecode by code hash.
107    pub const fn code_hashes(&self) -> &B256Map<Bytecode> {
108        &self.code_hashes
109    }
110
111    #[cfg(test)]
112    pub(crate) const fn block_hashes_mut(&mut self) -> &mut Vec<BlockNumHash> {
113        &mut self.block_hashes
114    }
115
116    #[cfg(test)]
117    pub(crate) const fn accounts_mut(&mut self) -> &mut AddressMap<Option<AccountInfo>> {
118        &mut self.accounts
119    }
120
121    #[cfg(test)]
122    pub(crate) const fn storage_mut(&mut self) -> &mut AddressMap<U256Map<U256>> {
123        &mut self.storage
124    }
125
126    #[cfg(test)]
127    pub(crate) const fn code_hashes_mut(&mut self) -> &mut B256Map<Bytecode> {
128        &mut self.code_hashes
129    }
130
131    /// Extends this overlay with the execution state of a later block.
132    pub(crate) fn extend_block<N: NodePrimitives>(&mut self, block: &ExecutedBlock<N>) {
133        self.block_hashes.push(block.recovered_block().num_hash());
134        self.extend_state(&block.execution_output.state);
135    }
136
137    /// Extends this overlay with a later bundle state.
138    ///
139    /// [`AccountInfo::account_id`] is a lookup hint owned by the database context that assigned it
140    /// and cannot be reused by the overlay's database context. All other account fields are
141    /// preserved.
142    fn extend_state(&mut self, state: &BundleState) {
143        let (accounts, storage, storage_wipes, code_hashes) =
144            (&mut self.accounts, &mut self.storage, &mut self.storage_wipes, &mut self.code_hashes);
145
146        #[allow(unused_mut)]
147        let mut extend_accounts_and_storage = || {
148            for (address, account) in state.state() {
149                accounts.insert(*address, Self::normalized_account_info(account.info.clone()));
150                if account.was_destroyed() {
151                    storage_wipes.insert(*address);
152                    storage.remove(address);
153                }
154                let account_storage = storage.entry(*address).or_default();
155                for (slot, value) in &account.storage {
156                    account_storage.insert(*slot, value.present_value);
157                }
158            }
159        };
160        #[allow(unused_mut)]
161        let mut extend_code_hashes = || {
162            code_hashes.extend(state.contracts.iter().map(|(hash, code)| (*hash, code.clone())));
163        };
164
165        #[cfg(feature = "rayon")]
166        rayon::join(extend_accounts_and_storage, extend_code_hashes);
167
168        #[cfg(not(feature = "rayon"))]
169        {
170            extend_accounts_and_storage();
171            extend_code_hashes();
172        }
173    }
174
175    #[cfg(test)]
176    fn extend_overlay(&mut self, other: &Self) {
177        self.block_hashes.extend_from_slice(&other.block_hashes);
178        self.accounts.extend(
179            other
180                .accounts
181                .iter()
182                .map(|(address, info)| (*address, Self::normalized_account_info(info.clone()))),
183        );
184        for address in &other.storage_wipes {
185            self.storage.remove(address);
186        }
187        for (address, slots) in &other.storage {
188            self.storage
189                .entry(*address)
190                .or_default()
191                .extend(slots.iter().map(|(slot, value)| (*slot, *value)));
192        }
193        self.storage_wipes.extend(other.storage_wipes.iter().copied());
194        self.code_hashes.extend(other.code_hashes.iter().map(|(hash, code)| (*hash, code.clone())));
195    }
196
197    /// Removes the database-local account lookup hint before caching account state.
198    ///
199    /// `account_id` indexes the database or BAL context that produced the [`AccountInfo`]. A later
200    /// execution context can assign that ID to a different account, so it must not cross the
201    /// execution-overlay boundary.
202    const fn normalized_account_info(mut info: Option<AccountInfo>) -> Option<AccountInfo> {
203        if let Some(info) = &mut info {
204            info.account_id = None;
205        }
206        info
207    }
208}
209
210/// Builder for calculating trie and hashed-state overlays.
211///
212/// This stores the overlay manager, overlay configuration, and the logic for resolving overlays
213/// and collecting reverts.
214#[derive(Debug, Clone)]
215pub struct OverlayBuilder<N: NodePrimitives = EthPrimitives> {
216    /// Parent hash requested by the caller.
217    parent_hash: B256,
218    /// Manager used for cached changesets and overlays.
219    overlay_manager: OverlayManager<N>,
220    /// Snapshot of the in-memory chain ending at the requested parent.
221    ///
222    /// This is shared with the caller so that a chain that is already maintained elsewhere (for
223    /// example the canonical in-memory chain) can be reused instead of rebuilt.
224    parent_state: Option<Arc<BlockState<N>>>,
225    /// Anchor hash of the reused sparse trie, if this task reused one.
226    reused_sparse_trie_anchor_hash: Option<B256>,
227    /// Whether building the overlay may query revert changesets.
228    no_reverts: bool,
229    /// Cache behavior for the overlays this builder resolves.
230    overlay_cache_config: OverlayCacheConfig,
231    /// Metrics for overlay construction.
232    metrics: OverlayBuilderMetrics,
233}
234
235impl<N: NodePrimitives> OverlayBuilder<N> {
236    /// Create a new manager-backed overlay builder.
237    pub(crate) fn new(
238        parent_hash: B256,
239        parent_state: Option<Arc<BlockState<N>>>,
240        overlay_manager: OverlayManager<N>,
241    ) -> Self {
242        Self {
243            parent_hash,
244            overlay_manager,
245            parent_state,
246            reused_sparse_trie_anchor_hash: None,
247            no_reverts: false,
248            overlay_cache_config: OverlayCacheConfig::default(),
249            metrics: OverlayBuilderMetrics::default(),
250        }
251    }
252
253    /// Skips managed overlay construction when the sparse trie was reused and the DB tip is
254    /// already covered by its anchor-to-parent range.
255    pub(crate) const fn with_skip_overlay_for_reused_sparse_trie(
256        mut self,
257        anchor_hash: B256,
258    ) -> Self {
259        self.reused_sparse_trie_anchor_hash = Some(anchor_hash);
260        self
261    }
262
263    /// Returns an error instead of querying revert changesets when reverts are required.
264    pub(crate) const fn with_no_reverts(mut self) -> Self {
265        self.no_reverts = true;
266        self
267    }
268
269    /// Appends an executed block to this builder's in-memory parent state.
270    pub fn with_appended_block(mut self, block: ExecutedBlock<N>) -> Self {
271        debug_assert_eq!(block.recovered_block().parent_hash(), self.parent_hash);
272        self.parent_hash = block.recovered_block().hash();
273        self.parent_state =
274            Some(Arc::new(BlockState::with_parent(block, self.parent_state.take())));
275        self.reused_sparse_trie_anchor_hash = None;
276        self.overlay_cache_config.write_to_cache = false;
277        self
278    }
279
280    /// Returns the durable anchor to use for this builder's parent.
281    #[cfg(test)]
282    fn anchor_at_parent<Provider>(&self, provider: &Provider) -> ProviderResult<AnchorForParent>
283    where
284        Provider: StageCheckpointReader + BlockNumReader + PruneCheckpointReader,
285    {
286        let (partial_state_trie, finish) = database_state_frontiers(provider)?;
287        self.anchor_at_parent_with_frontiers(provider, partial_state_trie, finish)
288    }
289
290    /// Returns the durable anchor to use for this builder's parent using known frontiers.
291    fn anchor_at_parent_with_frontiers<Provider>(
292        &self,
293        provider: &Provider,
294        partial_state_trie: BlockNumHash,
295        finish: BlockNumHash,
296    ) -> ProviderResult<AnchorForParent>
297    where
298        Provider: BlockNumReader + PruneCheckpointReader,
299    {
300        use std::io::Error;
301
302        let mut in_mem_chain = self
303            .parent_state
304            .iter()
305            .flat_map(|state| state.chain())
306            .map(BlockState::block_ref)
307            .peekable();
308        let persisted_parent = match in_mem_chain
309            .peek()
310            .filter(|block| block.recovered_block().hash() == self.parent_hash)
311            .map(|block| block.recovered_block().number())
312        {
313            Some(parent_number) if parent_number > partial_state_trie.number => None,
314            Some(parent_number)
315                if parent_number == partial_state_trie.number &&
316                    self.parent_hash == partial_state_trie.hash =>
317            {
318                Some(parent_number)
319            }
320            Some(parent_number) => (provider.block_hash(parent_number)? == Some(self.parent_hash))
321                .then_some(parent_number),
322            None if self.parent_hash == partial_state_trie.hash => Some(partial_state_trie.number),
323            None => provider
324                .block_number(self.parent_hash)?
325                .filter(|&number| number <= partial_state_trie.number),
326        };
327
328        let mut finish_seen = self.parent_hash == finish.hash;
329        let anchor = if let Some(parent_number) = persisted_parent {
330            BlockNumHash::new(parent_number, self.parent_hash)
331        } else {
332            let mut in_mem_chain = in_mem_chain.inspect(|block| {
333                finish_seen |= block.recovered_block().hash() == finish.hash;
334            });
335
336            if let Some(anchor) =
337                anchor_for_parent_in(self.parent_hash, &mut in_mem_chain, partial_state_trie)
338            {
339                anchor
340            } else {
341                let anchor_number = provider
342                    .convert_hash_or_number(self.parent_hash.into())?
343                    .ok_or(ProviderError::BlockHashNotFound(self.parent_hash))?;
344                BlockNumHash::new(anchor_number, self.parent_hash)
345            }
346        };
347
348        finish_seen |= anchor.hash == finish.hash;
349
350        if anchor.number > partial_state_trie.number {
351            return Err(ProviderError::other(Error::other(format!(
352                "overlay anchor #{} ({}) is after partial state trie frontier #{} ({}); missing trie updates for blocks #{}..=#{}",
353                anchor.number,
354                anchor.hash,
355                partial_state_trie.number,
356                partial_state_trie.hash,
357                partial_state_trie.number + 1,
358                anchor.number,
359            ))))
360        }
361
362        // If the Finish block (db tip) was seen in the in-memory chain then we know that anchor is
363        // on the same chain as partial_state_trie as well. Given that anchor <= partial_state_trie,
364        // we can be sure that the in-memory chain is a superset of partial_state_trie+1..finish,
365        // and therefore can be used without reverts.
366        if finish_seen {
367            return Ok(AnchorForParent::NoReverts { anchor })
368        }
369
370        // Reverts reconstruct canonical state by block number, so they cannot recover an anchor
371        // from another fork even if its number is covered by the available changesets.
372        if provider.block_hash(anchor.number)? != Some(anchor.hash) {
373            return Err(ProviderError::BlockHashNotFound(anchor.hash))
374        }
375
376        // Otherwise reverts are required; we check the changesets to make sure they are actually
377        // available before signaling that they are required.
378        let account_history = provider
379            .get_prune_checkpoint(PruneSegment::AccountHistory)?
380            .and_then(|checkpoint| checkpoint.block_number);
381        let storage_history = provider
382            .get_prune_checkpoint(PruneSegment::StorageHistory)?
383            .and_then(|checkpoint| checkpoint.block_number);
384        let lower_bound = account_history.max(storage_history).unwrap_or_default();
385        let available_range = lower_bound..=finish.number;
386        if !available_range.contains(&anchor.number) {
387            return Err(ProviderError::InsufficientChangesets {
388                requested: anchor.number,
389                available: available_range,
390            })
391        }
392
393        Ok(AnchorForParent::RevertsRequired { anchor, finish })
394    }
395
396    /// Builds the effective state trie overlay for the given provider.
397    ///
398    /// Set `trie_changesets` only for consumers that produce [`TrieUpdates`], such as
399    /// [`StateRootProvider::state_root_with_updates`]. Other consumers, including roots, proofs,
400    /// multiproofs, and witnesses, should leave it false: complete the cached trie at Finish and
401    /// invalidate hashed-state revert prefixes instead of querying trie changesets.
402    #[cfg(test)]
403    #[instrument(level = "debug", target = "storage::overlay", skip_all)]
404    fn build_state_trie_overlay<Provider>(
405        &self,
406        provider: &Provider,
407        trie_changesets: bool,
408    ) -> ProviderResult<StateTrieOverlay>
409    where
410        Provider: StageCheckpointReader
411            + PruneCheckpointReader
412            + ChangeSetReader
413            + StorageChangeSetReader
414            + DBProvider
415            + BlockNumReader
416            + StorageSettingsCache,
417    {
418        let (state_trie_tip_block, finish_tip_block) = database_state_frontiers(provider)?;
419        self.build_state_trie_overlay_at_frontiers(
420            provider,
421            state_trie_tip_block,
422            finish_tip_block,
423            trie_changesets,
424        )
425    }
426
427    /// Builds the effective state trie overlay using frontiers already read from the provider.
428    ///
429    /// This is useful for callers that key an overlay cache by the durable frontiers.
430    #[instrument(
431        level = "debug",
432        target = "storage::overlay",
433        skip_all,
434        fields(?state_trie_tip_block, ?finish_tip_block, parent_hash = ?self.parent_hash)
435    )]
436    pub(crate) fn build_state_trie_overlay_at_frontiers<Provider>(
437        &self,
438        provider: &Provider,
439        state_trie_tip_block: BlockNumHash,
440        finish_tip_block: BlockNumHash,
441        trie_changesets: bool,
442    ) -> ProviderResult<StateTrieOverlay>
443    where
444        Provider: ChangeSetReader
445            + StorageChangeSetReader
446            + DBProvider
447            + BlockNumReader
448            + StageCheckpointReader
449            + PruneCheckpointReader
450            + StorageSettingsCache,
451    {
452        let retrieve_trie_reverts_duration;
453        let retrieve_hashed_state_reverts_duration;
454        let trie_updates_total_len;
455        let hashed_state_updates_total_len;
456
457        let anchor_for_parent =
458            self.anchor_at_parent_with_frontiers(provider, state_trie_tip_block, finish_tip_block)?;
459
460        // Collect any reverts which are required to bring the DB view back to the anchor hash.
461        let (trie_updates, hashed_post_state, prefix_sets) = match &anchor_for_parent {
462            AnchorForParent::RevertsRequired { anchor, .. } => {
463                let revert_blocks =
464                    self.revert_blocks(&anchor_for_parent)?.expect("reverts are required");
465
466                debug!(
467                    target: "storage::overlay",
468                    ?revert_blocks,
469                    ?anchor,
470                    "Collecting trie reverts for overlay state provider"
471                );
472
473                let trie_reverts = if trie_changesets {
474                    let _guard = debug_span!(target: "storage::overlay", "retrieving_trie_reverts")
475                        .entered();
476                    let start = Instant::now();
477                    let accumulated_reverts =
478                        self.overlay_manager.get_or_compute_cached_changesets_range_at_frontiers(
479                            provider,
480                            revert_blocks.clone(),
481                            state_trie_tip_block,
482                            finish_tip_block,
483                        )?;
484                    retrieve_trie_reverts_duration = start.elapsed();
485                    accumulated_reverts
486                } else if state_trie_tip_block == finish_tip_block {
487                    retrieve_trie_reverts_duration = Duration::ZERO;
488                    Arc::default()
489                } else {
490                    // Revert prefixes describe changes since the anchor, not stale hashes in
491                    // masked DB rows. Complete the trie at Finish before applying those prefixes.
492                    let start = Instant::now();
493                    let finish_state = self
494                        .overlay_manager
495                        .block_state(finish_tip_block.hash)
496                        .ok_or_else(|| {
497                            ProviderError::other(StateTrieOverlayError {
498                                tip_hash: finish_tip_block.hash,
499                                anchor_hash: state_trie_tip_block.hash,
500                            })
501                        })?;
502                    let (nodes, _) = self
503                        .overlay_manager
504                        .overlay_for_parent(
505                            &finish_state,
506                            state_trie_tip_block.hash,
507                            OverlayCacheConfig::default(),
508                        )
509                        .map_err(ProviderError::other)?;
510                    retrieve_trie_reverts_duration = start.elapsed();
511                    nodes
512                };
513
514                let mut hashed_state_reverts = {
515                    let _guard =
516                        debug_span!(target: "storage::overlay", "retrieving_hashed_state_reverts")
517                            .entered();
518                    let start = Instant::now();
519                    let res = HashedPostStateSorted::from_reverts(provider, revert_blocks)?;
520                    retrieve_hashed_state_reverts_duration = start.elapsed();
521                    res
522                };
523
524                let prefix_sets = if trie_changesets {
525                    Default::default()
526                } else {
527                    hashed_state_reverts.construct_prefix_sets()
528                };
529
530                // Resolve overlays and extend reverts with them. If reverts are empty, use overlays
531                // directly to avoid cloning.
532                let (overlay_trie, overlay_state) =
533                    self.resolve_state_trie_overlays(anchor.hash)?;
534
535                let trie_updates = if trie_reverts.is_empty() {
536                    overlay_trie
537                } else if !overlay_trie.is_empty() {
538                    let mut trie_reverts = (*trie_reverts).clone();
539                    trie_reverts.extend_ref_and_sort(&overlay_trie);
540                    Arc::new(trie_reverts)
541                } else {
542                    trie_reverts
543                };
544
545                let hashed_state_updates = if hashed_state_reverts.is_empty() {
546                    overlay_state
547                } else if !overlay_state.is_empty() {
548                    hashed_state_reverts.extend_ref_and_sort(&overlay_state);
549                    Arc::new(hashed_state_reverts)
550                } else {
551                    Arc::new(hashed_state_reverts)
552                };
553
554                trie_updates_total_len = trie_updates.total_len();
555                hashed_state_updates_total_len = hashed_state_updates.total_len();
556
557                debug!(
558                    target: "storage::overlay",
559                    num_trie_updates = ?trie_updates_total_len,
560                    num_state_updates = ?hashed_state_updates_total_len,
561                    ?anchor,
562                    "Reverted to anchor block",
563                );
564
565                (trie_updates, hashed_state_updates, prefix_sets)
566            }
567            AnchorForParent::NoReverts { anchor } => {
568                // If no reverts are needed, use the manager overlay directly unless the reused
569                // sparse trie already covers both durable frontiers through the
570                // requested parent.
571                if self.should_skip_overlay_for_reused_sparse_trie(
572                    state_trie_tip_block.hash,
573                    finish_tip_block.hash,
574                ) {
575                    self.metrics.sparse_trie_overlay_skips.increment(1);
576
577                    return Ok(StateTrieOverlay::empty())
578                }
579
580                let (trie_updates, hashed_post_state) =
581                    self.resolve_state_trie_overlays(anchor.hash)?;
582
583                retrieve_trie_reverts_duration = Duration::ZERO;
584                retrieve_hashed_state_reverts_duration = Duration::ZERO;
585                trie_updates_total_len = trie_updates.total_len();
586                hashed_state_updates_total_len = hashed_post_state.total_len();
587
588                debug!(
589                    target: "storage::overlay",
590                    num_trie_updates = trie_updates_total_len,
591                    num_state_updates = hashed_state_updates_total_len,
592                    ?anchor,
593                    "Built overlay directly from durable frontier"
594                );
595
596                (trie_updates, hashed_post_state, Default::default())
597            }
598        };
599
600        self.metrics
601            .retrieve_trie_reverts_duration
602            .record(retrieve_trie_reverts_duration.as_secs_f64());
603        self.metrics
604            .retrieve_hashed_state_reverts_duration
605            .record(retrieve_hashed_state_reverts_duration.as_secs_f64());
606        self.metrics.trie_updates_size.record(trie_updates_total_len as f64);
607        self.metrics.hashed_state_size.record(hashed_state_updates_total_len as f64);
608
609        Ok(StateTrieOverlay::new(TrieInputSorted::new(
610            trie_updates,
611            hashed_post_state,
612            prefix_sets,
613        )))
614    }
615
616    /// Returns the in-memory execution overlay and the block for historical fallback reads.
617    #[cfg(test)]
618    #[instrument(level = "debug", target = "storage::overlay", skip_all)]
619    fn execution_overlay<Provider>(
620        &self,
621        provider: &Provider,
622    ) -> ProviderResult<(Arc<ExecutionOverlay>, Option<BlockNumber>)>
623    where
624        Provider: StageCheckpointReader
625            + PruneCheckpointReader
626            + ChangeSetReader
627            + StorageChangeSetReader
628            + DBProvider
629            + BlockNumReader,
630    {
631        let (state_trie_tip_block, finish_tip_block) = database_state_frontiers(provider)?;
632        self.execution_overlay_at_frontiers(provider, state_trie_tip_block, finish_tip_block)
633    }
634
635    /// Returns the in-memory execution overlay using frontiers already read from the provider.
636    #[instrument(
637        level = "trace",
638        target = "storage::overlay",
639        skip_all,
640        fields(?state_trie_tip_block, ?finish_tip_block, parent_hash = ?self.parent_hash)
641    )]
642    pub(crate) fn execution_overlay_at_frontiers<Provider>(
643        &self,
644        provider: &Provider,
645        state_trie_tip_block: BlockNumHash,
646        finish_tip_block: BlockNumHash,
647    ) -> ProviderResult<(Arc<ExecutionOverlay>, Option<BlockNumber>)>
648    where
649        Provider: ChangeSetReader
650            + StorageChangeSetReader
651            + DBProvider
652            + BlockNumReader
653            + PruneCheckpointReader,
654    {
655        let anchor_for_parent =
656            self.anchor_at_parent_with_frontiers(provider, state_trie_tip_block, finish_tip_block)?;
657        let (anchor_hash, fallback_block_number) = match anchor_for_parent {
658            AnchorForParent::RevertsRequired { anchor, .. } => {
659                (anchor.hash, Some(anchor.number + 1))
660            }
661            AnchorForParent::NoReverts { anchor } => (anchor.hash, None),
662        };
663        Ok((self.resolve_execution_overlay(anchor_hash)?, fallback_block_number))
664    }
665
666    /// Resolves the effective overlay (trie updates, hashed state).
667    fn resolve_state_trie_overlays(
668        &self,
669        anchor_hash: BlockHash,
670    ) -> ProviderResult<(Arc<TrieUpdatesSorted>, Arc<HashedPostStateSorted>)> {
671        if anchor_hash == self.parent_hash {
672            Ok((Arc::new(TrieUpdatesSorted::default()), Arc::new(HashedPostStateSorted::default())))
673        } else {
674            let parent_state = self.parent_state.as_deref().ok_or_else(|| {
675                ProviderError::other(std::io::Error::other(
676                    "state trie overlay cannot be anchored without in-memory parent state",
677                ))
678            })?;
679            self.overlay_manager
680                .overlay_for_parent(parent_state, anchor_hash, self.overlay_cache_config)
681                .map_err(ProviderError::other)
682        }
683    }
684
685    /// Resolves the execution overlay for the configured in-memory source.
686    fn resolve_execution_overlay(
687        &self,
688        anchor_hash: BlockHash,
689    ) -> ProviderResult<Arc<ExecutionOverlay>> {
690        if anchor_hash == self.parent_hash {
691            Ok(Arc::new(ExecutionOverlay::default()))
692        } else {
693            let parent_state = self.parent_state.as_deref().ok_or_else(|| {
694                ProviderError::other(std::io::Error::other("missing in-memory parent state"))
695            })?;
696            self.overlay_manager
697                .execution_overlay_for_block_state(
698                    parent_state,
699                    anchor_hash,
700                    self.overlay_cache_config,
701                )
702                .map_err(ProviderError::other)
703        }
704    }
705
706    /// Returns the blocks to revert from Finish to the selected anchor, if any.
707    fn revert_blocks(
708        &self,
709        anchor_for_parent: &AnchorForParent,
710    ) -> ProviderResult<Option<RangeInclusive<BlockNumber>>> {
711        match anchor_for_parent {
712            AnchorForParent::NoReverts { .. } => Ok(None),
713            AnchorForParent::RevertsRequired { anchor, finish, .. } => {
714                if self.no_reverts {
715                    return Err(ProviderError::other(std::io::Error::other(format!(
716                        "reverts are disabled, but overlay for parent {} requires reverting Finish #{} ({}) to anchor #{} ({})",
717                        self.parent_hash, finish.number, finish.hash, anchor.number, anchor.hash,
718                    ))))
719                }
720                Ok(Some(anchor.number + 1..=finish.number))
721            }
722        }
723    }
724
725    /// Returns true if managed overlay resolution can be skipped for this builder.
726    fn should_skip_overlay_for_reused_sparse_trie(
727        &self,
728        state_trie_tip_hash: B256,
729        finish_tip_hash: B256,
730    ) -> bool {
731        let Some(anchor_hash) = self.reused_sparse_trie_anchor_hash else { return false };
732
733        self.contains_hash(anchor_hash, state_trie_tip_hash) &&
734            self.contains_hash(anchor_hash, finish_tip_hash)
735    }
736
737    fn contains_hash(&self, anchor_hash: B256, hash: B256) -> bool {
738        let mut current_hash = self.parent_hash;
739        let mut blocks = self.parent_state.iter().flat_map(|state| state.chain());
740
741        loop {
742            if current_hash == hash {
743                return true
744            }
745            if current_hash == anchor_hash {
746                return false
747            }
748
749            let Some(block) = blocks.next() else { return false };
750            current_hash = block.block_ref().recovered_block().parent_hash();
751        }
752    }
753}
754
755/// Returns the highest blocks whose state/trie data and non-state/trie data are durably
756/// available in the database.
757pub(crate) fn database_state_frontiers<Provider>(
758    provider: &Provider,
759) -> ProviderResult<(BlockNumHash, BlockNumHash)>
760where
761    Provider: StageCheckpointReader + BlockNumReader,
762{
763    let checkpoint = provider
764        .get_stage_checkpoint(StageId::Finish)?
765        .ok_or_else(|| ProviderError::InsufficientChangesets { requested: 0, available: 0..=0 })?;
766    let state_trie_tip_number = checkpoint
767        .finish_stage_checkpoint()
768        .and_then(|finish| finish.partial_state_trie())
769        .unwrap_or(checkpoint.block_number);
770    let state_trie_tip_hash = provider
771        .convert_number(state_trie_tip_number.into())?
772        .ok_or_else(|| ProviderError::HeaderNotFound(state_trie_tip_number.into()))?;
773    let finish_tip_number = checkpoint.block_number;
774    let finish_tip_hash = provider
775        .convert_number(finish_tip_number.into())?
776        .ok_or_else(|| ProviderError::HeaderNotFound(finish_tip_number.into()))?;
777
778    Ok((
779        BlockNumHash::new(state_trie_tip_number, state_trie_tip_hash),
780        BlockNumHash::new(finish_tip_number, finish_tip_hash),
781    ))
782}
783
784/// Metrics for overlay construction.
785#[derive(Clone, Metrics)]
786#[metrics(scope = "storage.overlay.builder")]
787struct OverlayBuilderMetrics {
788    /// Duration of retrieving trie updates from the database.
789    retrieve_trie_reverts_duration: Histogram,
790    /// Duration of retrieving hashed state from the database.
791    retrieve_hashed_state_reverts_duration: Histogram,
792    /// Size of trie updates (number of entries).
793    trie_updates_size: Histogram,
794    /// Size of hashed state (number of entries).
795    hashed_state_size: Histogram,
796    /// Number of managed overlay creations skipped because the reused sparse trie already covers
797    /// the DB tip to parent range.
798    sparse_trie_overlay_skips: Counter,
799}
800
801fn anchor_for_parent_in<'a, N: NodePrimitives + 'a>(
802    parent_hash: B256,
803    in_mem_chain: impl Iterator<Item = &'a ExecutedBlock<N>>,
804    preferred_anchor: BlockNumHash,
805) -> Option<BlockNumHash> {
806    if parent_hash == preferred_anchor.hash {
807        return Some(preferred_anchor)
808    }
809
810    let mut anchor = None;
811
812    for block in in_mem_chain {
813        let block_parent = block.recovered_block().parent_num_hash();
814
815        if block_parent.hash == preferred_anchor.hash {
816            return Some(preferred_anchor)
817        }
818        anchor = Some(block_parent);
819    }
820
821    anchor
822}
823
824/// Describes whether an overlay must revert the database before using its anchor.
825#[derive(Debug)]
826enum AnchorForParent {
827    /// The in-memory chain covers the durable frontiers through this anchor.
828    NoReverts {
829        /// Block to anchor the overlay to.
830        anchor: BlockNumHash,
831    },
832    /// The database must be reverted from `finish` to `anchor` first.
833    RevertsRequired {
834        /// Block to anchor the overlay to.
835        anchor: BlockNumHash,
836        /// Current Finish frontier.
837        finish: BlockNumHash,
838    },
839}
840
841#[cfg(test)]
842mod tests {
843    use super::*;
844    use alloy_primitives::{map::HashMap, Address, U256};
845    use reth_chain_state::{
846        test_utils::TestBlockBuilder, CanonicalInMemoryState, ExecutedBlock, NewCanonicalChain,
847    };
848    use reth_db::{
849        models::{AccountBeforeTx, BlockNumberAddress},
850        tables,
851        transaction::DbTxMut,
852    };
853    use reth_primitives_traits::{Account, StorageEntry};
854    use reth_provider::{
855        test_utils::{create_test_provider_factory, MockNodeTypesWithDB},
856        BlockWriter, ProviderFactory,
857    };
858    use reth_stages_types::{FinishCheckpoint, StageCheckpoint};
859    use reth_storage_api::StageCheckpointWriter;
860    use reth_trie::{BranchNodeCompact, HashedPostState, HashedStorage, Nibbles};
861    use revm::{
862        bytecode::Bytecode,
863        database::{AccountStatus, BundleAccount, BundleState},
864        state::{AccountId, AccountInfo},
865    };
866
867    fn with_unique_trie_data(
868        block: &ExecutedBlock<EthPrimitives>,
869        id: u8,
870    ) -> ExecutedBlock<EthPrimitives> {
871        let hashed_address = B256::with_last_byte(id);
872        let hashed_slot = B256::with_last_byte(id.saturating_add(32));
873        let hashed_state = HashedPostState::default()
874            .with_accounts([(hashed_address, Some(Account::default()))])
875            .with_storages([(
876                hashed_address,
877                HashedStorage::from_iter([(hashed_slot, U256::from(id))]),
878            )])
879            .into_sorted();
880        let trie_updates = TrieUpdatesSorted::new(
881            vec![(
882                Nibbles::from_nibbles([id]),
883                Some(BranchNodeCompact::new(0, 0, 0, vec![], None)),
884            )],
885            Default::default(),
886        );
887        let address = Address::with_last_byte(id);
888        let slot = U256::from(id);
889        let code_hash = B256::with_last_byte(id.saturating_add(64));
890        let state = BundleState::builder(block.block_number()..=block.block_number())
891            .state_present_account_info(
892                address,
893                AccountInfo {
894                    nonce: id as u64,
895                    balance: U256::from(id),
896                    account_id: AccountId::new(id as usize),
897                    ..Default::default()
898                },
899            )
900            .state_storage(address, HashMap::from_iter([(slot, (U256::ZERO, U256::from(id)))]))
901            .contract(code_hash, Bytecode::new_raw(vec![id].into()))
902            .build();
903        let mut execution_output = (*block.execution_output).clone();
904        execution_output.state = state;
905
906        ExecutedBlock::new(
907            Arc::clone(&block.recovered_block),
908            Arc::new(execution_output),
909            Arc::new(hashed_state),
910            Arc::new(trie_updates),
911        )
912    }
913
914    fn test_blocks() -> Vec<ExecutedBlock<EthPrimitives>> {
915        TestBlockBuilder::eth()
916            .get_executed_blocks(0..5)
917            .enumerate()
918            .map(|(index, block)| with_unique_trie_data(&block, index as u8 + 1))
919            .collect()
920    }
921
922    fn setup_frontiers(
923        state_trie_tip_index: usize,
924        finish_tip_index: usize,
925    ) -> (ProviderFactory<MockNodeTypesWithDB>, Vec<ExecutedBlock<EthPrimitives>>) {
926        let factory = create_test_provider_factory();
927        let blocks = test_blocks();
928        let provider_rw = factory.provider_rw().unwrap();
929        for block in &blocks[..=finish_tip_index] {
930            provider_rw.insert_block(block.recovered_block()).unwrap();
931        }
932        provider_rw
933            .save_stage_checkpoint(
934                StageId::Finish,
935                StageCheckpoint::new(blocks[finish_tip_index].block_number())
936                    .with_finish_stage_checkpoint(FinishCheckpoint {
937                        partial_state_trie: Some(blocks[state_trie_tip_index].block_number()),
938                    }),
939            )
940            .unwrap();
941        provider_rw.commit().unwrap();
942
943        (factory, blocks)
944    }
945
946    /// Tracks `blocks` in a [`CanonicalInMemoryState`] the way the engine does, so the resulting
947    /// `Arc<BlockState>` chain matches what state providers hold.
948    fn canonical_in_memory_state(
949        blocks: &[ExecutedBlock<EthPrimitives>],
950    ) -> CanonicalInMemoryState<EthPrimitives> {
951        let state = CanonicalInMemoryState::empty();
952        state.update_chain(NewCanonicalChain::Commit { new: blocks.to_vec() });
953        state
954    }
955
956    const fn anchor_num_hash(anchor: &AnchorForParent) -> BlockNumHash {
957        match anchor {
958            AnchorForParent::NoReverts { anchor } |
959            AnchorForParent::RevertsRequired { anchor, .. } => *anchor,
960        }
961    }
962
963    fn account_keys(overlay: &StateTrieOverlay) -> Vec<B256> {
964        overlay.input().state.accounts.iter().map(|(key, _)| *key).collect()
965    }
966
967    fn account_node_paths(overlay: &StateTrieOverlay) -> Vec<Nibbles> {
968        overlay.input().nodes.account_nodes_ref().iter().map(|(path, _)| *path).collect()
969    }
970
971    #[test]
972    fn execution_overlay_extends_bundle_state_without_account_ids() {
973        let address = Address::with_last_byte(1);
974        let slot = U256::from(2);
975        let value = U256::from(3);
976        let code = Bytecode::new_raw(vec![0x60, 0x00].into());
977        let code_hash = code.hash_slow();
978        let account = AccountInfo {
979            account_id: AccountId::new(6),
980            ..AccountInfo::new(U256::from(5), 4, code_hash, code.clone())
981        };
982        let state = BundleState::builder(0..=0)
983            .state_present_account_info(address, account.clone())
984            .state_storage(address, HashMap::from_iter([(slot, (U256::ZERO, value))]))
985            .contract(code_hash, code.clone())
986            .build();
987        assert!(state.state()[&address].info.as_ref().unwrap().account_id.is_some());
988
989        let mut overlay = ExecutionOverlay::default();
990        overlay.extend_state(&state);
991
992        let stored_account = overlay.accounts[&address].as_ref().unwrap();
993        assert_eq!(stored_account.account_id, None);
994        assert_eq!(
995            stored_account,
996            &AccountInfo { account_id: None, ..account },
997            "normalization must preserve durable account fields"
998        );
999        assert_eq!(stored_account.code, Some(code.clone()));
1000        assert_eq!(overlay.storage[&address][&slot], value);
1001        assert_eq!(overlay.code_hashes[&code_hash], code);
1002    }
1003
1004    #[test]
1005    fn execution_overlay_zeroes_unobserved_storage_for_destroyed_accounts() {
1006        let address = Address::with_last_byte(1);
1007        let mut state = BundleState::default();
1008        state.state.insert(
1009            address,
1010            BundleAccount::new(
1011                Some(AccountInfo::default()),
1012                None,
1013                Default::default(),
1014                AccountStatus::Destroyed,
1015            ),
1016        );
1017
1018        let mut overlay = ExecutionOverlay::default();
1019        overlay.extend_state(&state);
1020
1021        assert_eq!(overlay.storage_value(address, U256::ZERO), Some(U256::ZERO));
1022    }
1023
1024    #[test]
1025    fn execution_overlay_composition_uses_later_values_and_normalizes_accounts() {
1026        let address = Address::with_last_byte(1);
1027        let retained_address = Address::with_last_byte(2);
1028        let slot = U256::from(3);
1029        let retained_slot = U256::from(4);
1030        let first_code_hash = B256::with_last_byte(5);
1031        let later_code_hash = B256::with_last_byte(6);
1032        let first_block = BlockNumHash::new(1, B256::with_last_byte(7));
1033        let later_block = BlockNumHash::new(2, B256::with_last_byte(8));
1034
1035        let mut overlay = ExecutionOverlay::default();
1036        overlay.block_hashes.push(first_block);
1037        overlay.accounts.insert(address, Some(AccountInfo { nonce: 1, ..Default::default() }));
1038        overlay.accounts.insert(retained_address, Some(AccountInfo::default()));
1039        overlay.storage.entry(address).or_default().insert(slot, U256::from(9));
1040        overlay.storage.entry(address).or_default().insert(retained_slot, U256::from(10));
1041        overlay.code_hashes.insert(first_code_hash, Bytecode::new_raw(vec![1].into()));
1042
1043        let mut later = ExecutionOverlay::default();
1044        later.block_hashes.push(later_block);
1045        later.accounts.insert(
1046            address,
1047            Some(AccountInfo { nonce: 11, account_id: AccountId::new(12), ..Default::default() }),
1048        );
1049        later.storage.entry(address).or_default().insert(slot, U256::from(13));
1050        later.storage_wipes.insert(address);
1051        later.code_hashes.insert(later_code_hash, Bytecode::new_raw(vec![2].into()));
1052
1053        overlay.extend_overlay(&later);
1054
1055        assert!(later.accounts[&address].as_ref().unwrap().account_id.is_some());
1056        assert_eq!(overlay.block_hashes, vec![first_block, later_block]);
1057        assert_eq!(overlay.accounts[&address].as_ref().unwrap().nonce, 11);
1058        assert_eq!(overlay.accounts[&address].as_ref().unwrap().account_id, None);
1059        assert!(overlay.accounts.contains_key(&retained_address));
1060        assert_eq!(overlay.storage[&address][&slot], U256::from(13));
1061        assert!(!overlay.storage[&address].contains_key(&retained_slot));
1062        assert_eq!(overlay.storage_value(address, U256::from(14)), Some(U256::ZERO));
1063        assert!(overlay.code_hashes.contains_key(&first_code_hash));
1064        assert!(overlay.code_hashes.contains_key(&later_code_hash));
1065    }
1066
1067    #[test]
1068    fn overlay_builder_for_state_matches_hash_lookup() {
1069        // The state trie frontier sits at block 1 while Finish is at block 3, so the in-memory
1070        // chain straddles the state-masking frontier.
1071        let (factory, blocks) = setup_frontiers(1, 3);
1072        let manager = OverlayManager::default();
1073        for block in &blocks[2..=4] {
1074            manager.insert_block(block.clone());
1075        }
1076        let canonical = canonical_in_memory_state(&blocks[2..=4]);
1077        let provider = factory.provider().unwrap();
1078
1079        // The head, a block below the head, and the oldest in-memory block, whose chain no longer
1080        // covers the Finish frontier and therefore anchors below it.
1081        for index in [4usize, 3, 2] {
1082            let hash = blocks[index].recovered_block().hash();
1083            let state = canonical.state_by_hash(hash).expect("canonical state for in-memory block");
1084            assert_eq!(state.hash(), hash);
1085
1086            let from_hash = manager.overlay_builder(hash);
1087            let from_state = manager.overlay_builder_for_state(state);
1088
1089            let anchor = anchor_num_hash(&from_hash.anchor_at_parent(&provider).unwrap());
1090            assert_eq!(
1091                anchor_num_hash(&from_state.anchor_at_parent(&provider).unwrap()),
1092                anchor,
1093                "block {index} must resolve the same anchor from both builders"
1094            );
1095
1096            let (hash_overlay, hash_fallback) = from_hash.execution_overlay(&provider).unwrap();
1097            let (state_overlay, state_fallback) = from_state.execution_overlay(&provider).unwrap();
1098            assert_eq!(state_fallback, hash_fallback);
1099            assert!(
1100                Arc::ptr_eq(&hash_overlay, &state_overlay),
1101                "block {index} must resolve the cached execution overlay"
1102            );
1103
1104            let (hash_nodes, hash_state) =
1105                from_hash.resolve_state_trie_overlays(anchor.hash).unwrap();
1106            let (state_nodes, state_state) =
1107                from_state.resolve_state_trie_overlays(anchor.hash).unwrap();
1108            assert!(
1109                Arc::ptr_eq(&hash_nodes, &state_nodes),
1110                "block {index} must resolve the cached trie overlay nodes"
1111            );
1112            assert!(
1113                Arc::ptr_eq(&hash_state, &state_state),
1114                "block {index} must resolve the cached trie overlay state"
1115            );
1116
1117            // The fully built overlay folds in database reverts for anchors below Finish, so
1118            // compare it by value rather than by pointer.
1119            let hash_trie = from_hash.build_state_trie_overlay(&provider, false).unwrap();
1120            let state_trie = from_state.build_state_trie_overlay(&provider, false).unwrap();
1121            assert_eq!(account_keys(&state_trie), account_keys(&hash_trie), "block {index}");
1122            assert_eq!(
1123                account_node_paths(&state_trie),
1124                account_node_paths(&hash_trie),
1125                "block {index}"
1126            );
1127        }
1128    }
1129
1130    #[test]
1131    fn managed_overlay_starts_at_state_trie_frontier() {
1132        let (factory, blocks) = setup_frontiers(1, 3);
1133        let manager = OverlayManager::default();
1134        for block in &blocks[2..=4] {
1135            manager.insert_block(block.clone());
1136        }
1137        let provider = factory.provider().unwrap();
1138
1139        for (parent_index, expected_ids) in [(3, vec![3, 4]), (4, vec![3, 4, 5])] {
1140            let overlay = manager
1141                .overlay_builder(blocks[parent_index].recovered_block().hash())
1142                .build_state_trie_overlay(&provider, true)
1143                .unwrap();
1144
1145            assert_eq!(
1146                account_keys(&overlay),
1147                expected_ids.iter().copied().map(B256::with_last_byte).collect::<Vec<_>>()
1148            );
1149            assert_eq!(
1150                account_node_paths(&overlay),
1151                expected_ids
1152                    .iter()
1153                    .copied()
1154                    .map(|id| Nibbles::from_nibbles([id]))
1155                    .collect::<Vec<_>>()
1156            );
1157        }
1158    }
1159
1160    #[test]
1161    fn managed_overlay_skips_when_finish_is_the_anchor() {
1162        let (factory, blocks) = setup_frontiers(3, 3);
1163        let manager = OverlayManager::default();
1164        manager.insert_block(blocks[4].clone());
1165        let provider = factory.provider().unwrap();
1166
1167        let overlay = manager
1168            .overlay_builder(blocks[4].recovered_block().hash())
1169            .with_skip_overlay_for_reused_sparse_trie(blocks[3].recovered_block().hash())
1170            .build_state_trie_overlay(&provider, true)
1171            .unwrap();
1172
1173        assert!(overlay.input().state.is_empty());
1174        assert!(overlay.input().nodes.is_empty());
1175    }
1176
1177    #[test]
1178    fn no_reverts_errors_when_reverts_are_required() {
1179        let (factory, blocks) = setup_frontiers(2, 3);
1180        let provider = factory.provider().unwrap();
1181
1182        let builder = OverlayManager::<EthPrimitives>::default()
1183            .overlay_builder(blocks[1].recovered_block().hash())
1184            .with_no_reverts();
1185        let error = builder.build_state_trie_overlay(&provider, true).unwrap_err();
1186
1187        assert!(error.to_string().contains("reverts are disabled"));
1188    }
1189
1190    #[test]
1191    fn appended_overlay_rejects_noncanonical_anchor_when_reverts_are_required() {
1192        let (factory, blocks) = setup_frontiers(1, 3);
1193        let provider = factory.provider().unwrap();
1194        let parent_hash = B256::with_last_byte(100);
1195        assert_ne!(parent_hash, blocks[1].recovered_block().hash());
1196        let block = TestBlockBuilder::eth()
1197            .get_executed_block_with_number(blocks[2].block_number(), parent_hash);
1198        let builder =
1199            OverlayManager::default().overlay_builder(parent_hash).with_appended_block(block);
1200
1201        assert!(matches!(
1202            builder.execution_overlay(&provider),
1203            Err(ProviderError::BlockHashNotFound(hash)) if hash == parent_hash
1204        ));
1205        assert!(matches!(
1206            builder.build_state_trie_overlay(&provider, true),
1207            Err(ProviderError::BlockHashNotFound(hash)) if hash == parent_hash
1208        ));
1209    }
1210
1211    #[test]
1212    fn state_trie_overlay_uses_revert_prefix_sets_without_trie_changesets() {
1213        let (factory, blocks) = setup_frontiers(3, 3);
1214        let provider_rw = factory.provider_rw().unwrap();
1215        provider_rw
1216            .tx_ref()
1217            .put::<tables::AccountChangeSets>(
1218                3,
1219                AccountBeforeTx {
1220                    address: Address::with_last_byte(1),
1221                    info: Some(Account::default()),
1222                },
1223            )
1224            .unwrap();
1225        provider_rw.commit().unwrap();
1226
1227        let provider = factory.provider().unwrap();
1228        let overlay = OverlayManager::<EthPrimitives>::default()
1229            .overlay_builder(blocks[1].recovered_block().hash())
1230            .build_state_trie_overlay(&provider, false)
1231            .unwrap();
1232
1233        assert!(overlay.input().nodes.is_empty());
1234        assert!(!overlay.input().prefix_sets.is_empty());
1235    }
1236
1237    #[test]
1238    fn execution_overlay_marks_historical_fallback() {
1239        let (factory, blocks) = setup_frontiers(1, 3);
1240        let provider_rw = factory.provider_rw().unwrap();
1241        let address = Address::with_last_byte(1);
1242        let slot = U256::from(5);
1243
1244        provider_rw
1245            .tx_ref()
1246            .put::<tables::AccountChangeSets>(
1247                2,
1248                AccountBeforeTx {
1249                    address,
1250                    info: Some(Account { balance: U256::from(10), ..Default::default() }),
1251                },
1252            )
1253            .unwrap();
1254        provider_rw
1255            .tx_ref()
1256            .put::<tables::AccountChangeSets>(
1257                3,
1258                AccountBeforeTx {
1259                    address,
1260                    info: Some(Account { balance: U256::from(20), ..Default::default() }),
1261                },
1262            )
1263            .unwrap();
1264        for (block_number, value) in [(2, 10), (3, 15)] {
1265            provider_rw
1266                .tx_ref()
1267                .put::<tables::StorageChangeSets>(
1268                    BlockNumberAddress((block_number, address)),
1269                    StorageEntry { key: B256::from(slot), value: U256::from(value) },
1270                )
1271                .unwrap();
1272        }
1273        provider_rw.commit().unwrap();
1274
1275        let provider = factory.provider().unwrap();
1276        let (overlay, fallback_block_number) = OverlayManager::<EthPrimitives>::default()
1277            .overlay_builder(blocks[1].recovered_block().hash())
1278            .execution_overlay(&provider)
1279            .unwrap();
1280
1281        assert_eq!(fallback_block_number, Some(2));
1282        assert!(overlay.accounts.is_empty());
1283        assert!(overlay.storage.is_empty());
1284        assert!(overlay.code_hashes.is_empty());
1285    }
1286
1287    #[test]
1288    fn execution_overlay_uses_managed_blocks_after_the_anchor() {
1289        let (factory, blocks) = setup_frontiers(1, 3);
1290        let manager = OverlayManager::default();
1291        for block in &blocks[2..=4] {
1292            manager.insert_block(block.clone());
1293        }
1294        let provider = factory.provider().unwrap();
1295
1296        let (overlay, fallback_block_number) = manager
1297            .overlay_builder(blocks[3].recovered_block().hash())
1298            .execution_overlay(&provider)
1299            .unwrap();
1300
1301        assert_eq!(fallback_block_number, None);
1302
1303        for id in [3, 4] {
1304            let address = Address::with_last_byte(id);
1305            let slot = U256::from(id);
1306            assert_eq!(overlay.accounts[&address].as_ref().unwrap().balance, U256::from(id));
1307            assert_eq!(overlay.accounts[&address].as_ref().unwrap().account_id, None);
1308            assert_eq!(overlay.storage[&address][&slot], U256::from(id));
1309            assert!(overlay.code_hashes.contains_key(&B256::with_last_byte(id + 64)));
1310        }
1311        assert_eq!(
1312            overlay.block_hashes,
1313            blocks[2..=3]
1314                .iter()
1315                .map(|block| block.recovered_block().num_hash())
1316                .collect::<Vec<_>>()
1317        );
1318    }
1319
1320    #[test]
1321    fn execution_overlay_marks_historical_fallback_for_managed_fork() {
1322        let (factory, blocks) = setup_frontiers(1, 3);
1323        let address = Address::with_last_byte(1);
1324        let slot = U256::from(1);
1325        let provider_rw = factory.provider_rw().unwrap();
1326        for (block_number, balance, storage_value) in [(2u64, 10u64, 10u64), (3u64, 20u64, 15u64)] {
1327            provider_rw
1328                .tx_ref()
1329                .put::<tables::AccountChangeSets>(
1330                    block_number,
1331                    AccountBeforeTx {
1332                        address,
1333                        info: Some(Account { balance: U256::from(balance), ..Default::default() }),
1334                    },
1335                )
1336                .unwrap();
1337            provider_rw
1338                .tx_ref()
1339                .put::<tables::StorageChangeSets>(
1340                    BlockNumberAddress((block_number, address)),
1341                    StorageEntry { key: B256::from(slot), value: U256::from(storage_value) },
1342                )
1343                .unwrap();
1344        }
1345        provider_rw.commit().unwrap();
1346
1347        let mut side_chain_builder = TestBlockBuilder::eth();
1348        let side_block_two = side_chain_builder.get_executed_block_with_number(
1349            blocks[2].block_number(),
1350            blocks[1].recovered_block().hash(),
1351        );
1352        let side_block_two = with_unique_trie_data(&side_block_two, 1);
1353        let side_block_three = side_chain_builder.get_executed_block_with_number(
1354            blocks[3].block_number(),
1355            side_block_two.recovered_block().hash(),
1356        );
1357        let side_block_three = with_unique_trie_data(&side_block_three, 1);
1358        assert_ne!(
1359            side_block_three.recovered_block().hash(),
1360            blocks[3].recovered_block().hash(),
1361            "the managed chain must not contain the durable Finish block"
1362        );
1363
1364        let manager = OverlayManager::default();
1365        manager.insert_block(side_block_two.clone());
1366        manager.insert_block(side_block_three.clone());
1367        let provider = factory.provider().unwrap();
1368
1369        let (overlay, fallback_block_number) = manager
1370            .overlay_builder(side_block_three.recovered_block().hash())
1371            .execution_overlay(&provider)
1372            .unwrap();
1373
1374        assert_eq!(fallback_block_number, Some(2));
1375
1376        assert_eq!(overlay.accounts[&address].as_ref().unwrap().balance, U256::from(1));
1377        assert_eq!(overlay.accounts[&address].as_ref().unwrap().account_id, None);
1378        assert_eq!(overlay.storage[&address][&slot], U256::from(1));
1379        assert_eq!(
1380            overlay.block_hashes,
1381            [side_block_two, side_block_three]
1382                .iter()
1383                .map(|block| block.recovered_block().num_hash())
1384                .collect::<Vec<_>>()
1385        );
1386    }
1387
1388    #[test]
1389    fn execution_overlay_no_revert_path_discards_account_ids() {
1390        let (factory, blocks) = setup_frontiers(1, 1);
1391        let manager = OverlayManager::default();
1392        for block in &blocks[2..=3] {
1393            manager.insert_block(block.clone());
1394        }
1395        let provider = factory.provider().unwrap();
1396
1397        let (overlay, fallback_block_number) = manager
1398            .overlay_builder(blocks[3].recovered_block().hash())
1399            .execution_overlay(&provider)
1400            .unwrap();
1401
1402        assert_eq!(fallback_block_number, None);
1403        assert_eq!(overlay.accounts.len(), 2);
1404        assert!(overlay.accounts.values().flatten().all(|account| account.account_id.is_none()));
1405    }
1406
1407    #[test]
1408    fn managed_overlay_uses_persisted_parent_even_if_retained() {
1409        let (factory, blocks) = setup_frontiers(2, 3);
1410        let manager = OverlayManager::default();
1411        manager.insert_block(blocks[1].clone());
1412        let provider = factory.provider().unwrap();
1413        let builder = manager.overlay_builder(blocks[1].recovered_block().hash());
1414        match builder.anchor_at_parent(&provider).unwrap() {
1415            AnchorForParent::RevertsRequired { anchor, finish } => {
1416                assert_eq!(anchor, blocks[1].recovered_block().num_hash());
1417                assert_eq!(finish, blocks[3].recovered_block().num_hash());
1418            }
1419            AnchorForParent::NoReverts { .. } => {
1420                panic!("persisted parent below Finish must require reverts")
1421            }
1422        }
1423    }
1424
1425    #[test]
1426    fn builder_appends_block_to_parent_state() {
1427        let manager = OverlayManager::default();
1428        let blocks = test_blocks();
1429        for block in &blocks[2..=4] {
1430            manager.insert_block(block.clone());
1431        }
1432
1433        let block = TestBlockBuilder::eth().get_executed_block_with_number(
1434            blocks[4].recovered_block().number() + 1,
1435            blocks[4].recovered_block().hash(),
1436        );
1437        let builder = manager
1438            .overlay_builder(block.recovered_block().parent_hash())
1439            .with_appended_block(block.clone());
1440
1441        assert_eq!(builder.parent_hash, block.recovered_block().hash());
1442        assert_eq!(
1443            builder.parent_state.unwrap().chain().map(BlockState::hash).collect::<Vec<_>>(),
1444            std::iter::once(&block)
1445                .chain(blocks[2..=4].iter().rev())
1446                .map(|block| block.recovered_block().hash())
1447                .collect::<Vec<_>>(),
1448        );
1449    }
1450
1451    #[test]
1452    fn overlay_after_state_trie_frontier_requires_managed_coverage() {
1453        let (factory, blocks) = setup_frontiers(1, 3);
1454        let provider = factory.provider().unwrap();
1455        let error = OverlayManager::<EthPrimitives>::default()
1456            .overlay_builder(blocks[3].recovered_block().hash())
1457            .build_state_trie_overlay(&provider, true)
1458            .unwrap_err();
1459
1460        assert!(
1461            error.to_string().contains("is after partial state trie frontier"),
1462            "unexpected error: {error}"
1463        );
1464    }
1465
1466    #[test]
1467    fn managed_overlay_errors_if_parent_is_not_persisted_or_managed_across_frontiers() {
1468        let (factory, blocks) = setup_frontiers(1, 3);
1469        let provider = factory.provider().unwrap();
1470        let parent_hash = blocks[3].recovered_block().hash();
1471        let error = OverlayManager::<EthPrimitives>::default()
1472            .overlay_builder(parent_hash)
1473            .build_state_trie_overlay(&provider, true)
1474            .unwrap_err();
1475
1476        assert!(error.to_string().contains("is after partial state trie frontier"));
1477    }
1478
1479    #[test]
1480    fn managed_overlay_skips_manager_for_persisted_parent() {
1481        let parent_hash = B256::with_last_byte(1);
1482        let builder = OverlayManager::<EthPrimitives>::default().overlay_builder(parent_hash);
1483
1484        let (trie, state) = builder.resolve_state_trie_overlays(parent_hash).unwrap();
1485        assert!(trie.is_empty());
1486        assert!(state.is_empty());
1487    }
1488
1489    #[test]
1490    fn managed_overlay_errors_if_parent_is_not_persisted_or_managed() {
1491        let parent_hash = B256::with_last_byte(1);
1492        let anchor_hash = B256::with_last_byte(2);
1493        let builder = OverlayManager::<EthPrimitives>::default().overlay_builder(parent_hash);
1494
1495        let err = builder.resolve_state_trie_overlays(anchor_hash).unwrap_err();
1496
1497        assert!(err.to_string().contains("cannot be anchored"));
1498    }
1499
1500    #[test]
1501    fn managed_overlay_skip_requires_both_frontiers() {
1502        let parent_hash = B256::with_last_byte(1);
1503        let builder = OverlayManager::<EthPrimitives>::default().overlay_builder(parent_hash);
1504        assert!(!builder.should_skip_overlay_for_reused_sparse_trie(parent_hash, parent_hash));
1505
1506        let builder = builder.with_skip_overlay_for_reused_sparse_trie(parent_hash);
1507        assert!(builder.should_skip_overlay_for_reused_sparse_trie(parent_hash, parent_hash));
1508        assert!(!builder
1509            .should_skip_overlay_for_reused_sparse_trie(B256::with_last_byte(3), parent_hash,));
1510
1511        let blocks = test_blocks();
1512        let manager = OverlayManager::default();
1513        for block in &blocks[2..=4] {
1514            manager.insert_block(block.clone());
1515        }
1516        let builder = manager
1517            .overlay_builder(blocks[4].recovered_block().hash())
1518            .with_skip_overlay_for_reused_sparse_trie(blocks[1].recovered_block().hash());
1519        assert!(builder.should_skip_overlay_for_reused_sparse_trie(
1520            blocks[1].recovered_block().hash(),
1521            blocks[3].recovered_block().hash(),
1522        ));
1523
1524        let builder =
1525            builder.with_skip_overlay_for_reused_sparse_trie(blocks[2].recovered_block().hash());
1526        assert!(!builder.should_skip_overlay_for_reused_sparse_trie(
1527            blocks[1].recovered_block().hash(),
1528            blocks[3].recovered_block().hash(),
1529        ));
1530    }
1531}