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