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