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