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