1use crate::stages::MERKLE_STAGE_DEFAULT_INCREMENTAL_THRESHOLD;
2use alloy_consensus::BlockHeader;
3use alloy_eip7928::bal::Bal;
4use alloy_primitives::BlockNumber;
5use num_traits::Zero;
6use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
7use reth_config::config::ExecutionConfig;
8use reth_consensus::FullConsensus;
9use reth_db::{static_file::HeaderMask, tables};
10use reth_evm::{execute::Executor, metrics::ExecutorMetrics, ConfigureEvm};
11use reth_execution_types::Chain;
12use reth_exex::{ExExManagerHandle, ExExNotification, ExExNotificationSource};
13use reth_primitives_traits::{format_gas_throughput, BlockBody, NodePrimitives};
14use reth_provider::{
15 providers::{StaticFileProvider, StaticFileWriter},
16 BlockReader, DBProvider, EitherWriter, ExecutionOutcome, HashedPostStateProvider,
17 HeaderProvider, LatestStateProviderRef, OriginalValuesKnown, ProviderError, StateProvider,
18 StateWriteConfig, StateWriter, StaticFileProviderFactory, StatsReader, StoragePath,
19 StorageSettingsCache, TransactionVariant,
20};
21use reth_revm::database::StateProviderDatabase;
22use reth_stages_api::{
23 BlockErrorKind, CheckpointBlockRange, EntitiesCheckpoint, ExecInput, ExecOutput,
24 ExecutionCheckpoint, ExecutionStageThresholds, Stage, StageCheckpoint, StageError, StageId,
25 UnwindInput, UnwindOutput,
26};
27use reth_static_file_types::StaticFileSegment;
28use std::{
29 cmp::{max, Ordering},
30 collections::BTreeMap,
31 ops::RangeInclusive,
32 sync::Arc,
33 task::{ready, Context, Poll},
34 time::{Duration, Instant},
35};
36use tracing::*;
37
38use super::missing_static_data_error;
39
40pub mod slot_preimages;
43
44#[derive(Debug)]
73pub struct ExecutionStage<E>
74where
75 E: ConfigureEvm,
76{
77 evm_config: E,
79 consensus: Arc<dyn FullConsensus<E::Primitives>>,
81 thresholds: ExecutionStageThresholds,
83 external_clean_threshold: u64,
88 post_execute_commit_input: Option<Chain<E::Primitives>>,
92 post_unwind_commit_input: Option<Chain<E::Primitives>>,
96 exex_manager_handle: ExExManagerHandle<E::Primitives>,
98 metrics: ExecutorMetrics,
100}
101
102impl<E> ExecutionStage<E>
103where
104 E: ConfigureEvm,
105{
106 pub fn new(
108 evm_config: E,
109 consensus: Arc<dyn FullConsensus<E::Primitives>>,
110 thresholds: ExecutionStageThresholds,
111 external_clean_threshold: u64,
112 exex_manager_handle: ExExManagerHandle<E::Primitives>,
113 ) -> Self {
114 Self {
115 external_clean_threshold,
116 evm_config,
117 consensus,
118 thresholds,
119 post_execute_commit_input: None,
120 post_unwind_commit_input: None,
121 exex_manager_handle,
122 metrics: ExecutorMetrics::default(),
123 }
124 }
125
126 pub fn new_with_executor(
130 evm_config: E,
131 consensus: Arc<dyn FullConsensus<E::Primitives>>,
132 ) -> Self {
133 Self::new(
134 evm_config,
135 consensus,
136 ExecutionStageThresholds::default(),
137 MERKLE_STAGE_DEFAULT_INCREMENTAL_THRESHOLD,
138 ExExManagerHandle::empty(),
139 )
140 }
141
142 pub fn from_config(
144 evm_config: E,
145 consensus: Arc<dyn FullConsensus<E::Primitives>>,
146 config: ExecutionConfig,
147 external_clean_threshold: u64,
148 ) -> Self {
149 Self::new(
150 evm_config,
151 consensus,
152 config.into(),
153 external_clean_threshold,
154 ExExManagerHandle::empty(),
155 )
156 }
157
158 fn can_prune_changesets(
170 &self,
171 provider: impl StatsReader,
172 start_block: u64,
173 max_block: u64,
174 ) -> Result<bool, StageError> {
175 Ok(max_block - start_block > self.external_clean_threshold ||
178 provider.count_entries::<tables::AccountsTrie>()?.is_zero())
179 }
180
181 fn ensure_consistency<Provider>(
190 &self,
191 provider: &Provider,
192 checkpoint: u64,
193 unwind_to: Option<u64>,
194 ) -> Result<(), StageError>
195 where
196 Provider: StaticFileProviderFactory + DBProvider + BlockReader + StorageSettingsCache,
197 {
198 if EitherWriter::receipts_destination(provider).is_database() {
201 return Ok(())
202 }
203
204 let next_receipt_num =
206 provider.block_body_indices(checkpoint)?.map(|b| b.next_tx_num()).unwrap_or(0);
207
208 let static_file_provider = provider.static_file_provider();
209
210 let next_static_file_receipt_num = static_file_provider
212 .get_highest_static_file_tx(StaticFileSegment::Receipts)
213 .map(|num| num + 1)
214 .unwrap_or(0);
215
216 let static_file_block_num = static_file_provider
218 .get_highest_static_file_block(StaticFileSegment::Receipts)
219 .unwrap_or(0);
220
221 match static_file_block_num.cmp(&checkpoint) {
224 Ordering::Greater | Ordering::Equal => {
227 let mut static_file_producer =
228 static_file_provider.latest_writer(StaticFileSegment::Receipts)?;
229 static_file_producer.prune_receipts(
230 next_static_file_receipt_num.saturating_sub(next_receipt_num),
231 checkpoint,
232 )?;
233 static_file_producer.commit()?;
236 }
237 Ordering::Less => {
238 if let Some(unwind_to) = unwind_to &&
241 unwind_to <= static_file_block_num
242 {
243 return Ok(())
244 }
245
246 return Err(missing_static_data_error(
249 next_static_file_receipt_num.saturating_sub(1),
250 &static_file_provider,
251 provider,
252 StaticFileSegment::Receipts,
253 )?)
254 }
255 }
256
257 Ok(())
258 }
259}
260
261impl<E, Provider> Stage<Provider> for ExecutionStage<E>
262where
263 E: ConfigureEvm,
264 Provider: DBProvider
265 + BlockReader<
266 Block = <E::Primitives as NodePrimitives>::Block,
267 Header = <E::Primitives as NodePrimitives>::BlockHeader,
268 > + StaticFileProviderFactory<
269 Primitives: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
270 > + StatsReader
271 + StateWriter<Receipt = <E::Primitives as NodePrimitives>::Receipt>
272 + StorageSettingsCache
273 + StoragePath
274 + ChainSpecProvider<ChainSpec: EthereumHardforks>,
275{
276 fn id(&self) -> StageId {
278 StageId::Execution
279 }
280
281 fn poll_execute_ready(
282 &mut self,
283 cx: &mut Context<'_>,
284 _: ExecInput,
285 ) -> Poll<Result<(), StageError>> {
286 ready!(self.exex_manager_handle.poll_ready(cx));
287
288 Poll::Ready(Ok(()))
289 }
290
291 fn execute(&mut self, provider: &Provider, input: ExecInput) -> Result<ExecOutput, StageError> {
293 if input.target_reached() {
294 return Ok(ExecOutput::done(input.checkpoint()))
295 }
296
297 let start_block = input.next_block();
298 let max_block = input.target();
299 let static_file_provider = provider.static_file_provider();
300
301 self.ensure_consistency(provider, input.checkpoint().block_number, None)?;
302
303 let db =
304 StateProviderDatabase(LatestStateProviderRef::new(provider).into_evm_state_provider());
305 let mut executor = self.evm_config.batch_executor(db);
306
307 let mut stage_progress = start_block;
309 let mut stage_checkpoint = execution_checkpoint(
310 &static_file_provider,
311 start_block,
312 max_block,
313 input.checkpoint(),
314 )?;
315
316 let mut fetch_block_duration = Duration::default();
317 let mut execution_duration = Duration::default();
318
319 let mut last_block = start_block;
320 let mut last_execution_duration = Duration::default();
321 let mut last_cumulative_gas = 0;
322 let mut last_log_instant = Instant::now();
323 let log_duration = Duration::from_secs(10);
324
325 debug!(target: "sync::stages::execution", start = start_block, end = max_block, "Executing range");
326
327 let mut cumulative_gas = 0;
329 let batch_start = Instant::now();
330
331 let mut blocks = Vec::new();
332 let mut results = Vec::new();
333 let mut bal_buf = Vec::new();
335 for block_number in start_block..=max_block {
336 let fetch_block_start = Instant::now();
338
339 let block = provider
341 .recovered_block(block_number.into(), TransactionVariant::NoHash)?
342 .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?;
343
344 fetch_block_duration += fetch_block_start.elapsed();
345
346 cumulative_gas += block.header().gas_used();
347
348 trace!(target: "sync::stages::execution", number = block_number, txs = block.body().transactions().len(), "Executing block");
350
351 let execute_start = Instant::now();
353
354 let result = self.metrics.metered_one(&block, |input| {
355 executor.execute_one(input).map_err(|error| StageError::Block {
356 block: Box::new(block.block_with_parent()),
357 error: BlockErrorKind::Execution(error),
358 })
359 })?;
360
361 let built_bal = executor.take_bal().map(Bal::from);
362 if let Some(bal) = &built_bal &&
363 let Err(err) = bal.validate_gas_limit(block.header().gas_limit())
364 {
365 return Err(StageError::Block {
366 block: Box::new(block.block_with_parent()),
367 error: BlockErrorKind::Validation(err.into()),
368 })
369 }
370 let bal_hash = built_bal.as_ref().map(|bal| bal.compute_hash_with_buf(&mut bal_buf));
371
372 if let Err(err) =
373 self.consensus.validate_block_post_execution(&block, &result, None, bal_hash)
374 {
375 return Err(StageError::Block {
376 block: Box::new(block.block_with_parent()),
377 error: BlockErrorKind::Validation(err),
378 })
379 }
380 results.push(result);
381
382 execution_duration += execute_start.elapsed();
383
384 if last_log_instant.elapsed() >= log_duration {
386 info!(
387 target: "sync::stages::execution",
388 start = last_block,
389 end = block_number,
390 throughput = format_gas_throughput(cumulative_gas - last_cumulative_gas, execution_duration - last_execution_duration),
391 "Executed block range"
392 );
393
394 last_block = block_number + 1;
395 last_execution_duration = execution_duration;
396 last_cumulative_gas = cumulative_gas;
397 last_log_instant = Instant::now();
398 }
399
400 stage_progress = block_number;
401 stage_checkpoint.progress.processed += block.header().gas_used();
402
403 if self.exex_manager_handle.has_exexs() {
405 blocks.push(block);
406 }
407
408 if self.thresholds.is_end_of_batch(
410 block_number - start_block,
411 executor.size_hint() as u64,
412 cumulative_gas,
413 batch_start.elapsed(),
414 ) {
415 break
416 }
417 }
418
419 let time = Instant::now();
421 let mut state = ExecutionOutcome::from_blocks(
422 start_block,
423 executor.into_state().take_bundle(),
424 results,
425 );
426 let write_preparation_duration = time.elapsed();
427
428 debug!(
430 target: "sync::stages::execution",
431 start = start_block,
432 end = stage_progress,
433 throughput = format_gas_throughput(cumulative_gas, execution_duration),
434 "Finished executing block range"
435 );
436
437 let mut exex_state = None;
440
441 let time = Instant::now();
442
443 if self.can_prune_changesets(provider, start_block, max_block)? {
444 let prune_modes = provider.prune_modes_ref();
445
446 if !blocks.is_empty() &&
447 prune_modes.account_history.is_some() &&
448 prune_modes.storage_history.is_some()
449 {
450 exex_state = Some(state.clone());
451 }
452
453 for block_number in start_block..=max_block {
455 let Some(reverts) =
456 state.bundle.reverts.get_mut((block_number - start_block) as usize)
457 else {
458 break
459 };
460
461 if prune_modes
464 .account_history
465 .is_some_and(|m| m.should_prune(block_number, max_block)) &&
466 prune_modes
467 .storage_history
468 .is_some_and(|m| m.should_prune(block_number, max_block))
469 {
470 reverts.clear();
471 }
472 }
473 }
474
475 if provider.cached_storage_settings().use_hashed_state() {
482 let start_header = provider
483 .header_by_number(start_block)?
484 .ok_or_else(|| ProviderError::HeaderNotFound(start_block.into()))?;
485
486 let path = provider.storage_path().join("preimage");
487 if !provider.chain_spec().is_cancun_active_at_timestamp(start_header.timestamp()) {
488 if !blocks.is_empty() && exex_state.is_none() {
489 exex_state = Some(state.clone());
490 }
491 slot_preimages::inject_plain_wipe_slots(&path, provider, &mut state)?;
492 } else if path.exists() {
493 let _ = std::fs::remove_dir_all(&path);
495 }
496 }
497
498 provider.write_state(&state, OriginalValuesKnown::Yes, StateWriteConfig::default())?;
502
503 if provider.cached_storage_settings().use_hashed_state() {
504 let hashed_state =
505 LatestStateProviderRef::new(provider).hashed_post_state(&state.bundle)?;
506 provider.write_hashed_state(&hashed_state.into_sorted())?;
507 }
508
509 let db_write_duration = time.elapsed();
510 debug!(
511 target: "sync::stages::execution",
512 block_fetch = ?fetch_block_duration,
513 execution = ?execution_duration,
514 write_preparation = ?write_preparation_duration,
515 write = ?db_write_duration,
516 "Execution time"
517 );
518
519 if !blocks.is_empty() {
524 let previous_input = self.post_execute_commit_input.replace(Chain::new(
525 blocks,
526 exex_state.unwrap_or(state),
527 BTreeMap::new(),
528 ));
529
530 if previous_input.is_some() {
531 return Err(StageError::PostExecuteCommit(
534 "Previous post execute commit input wasn't processed",
535 ))
536 }
537 }
538
539 let done = stage_progress == max_block;
540 Ok(ExecOutput {
541 checkpoint: StageCheckpoint::new(stage_progress)
542 .with_execution_stage_checkpoint(stage_checkpoint),
543 done,
544 })
545 }
546
547 fn post_execute_commit(&mut self) -> Result<(), StageError> {
548 let Some(chain) = self.post_execute_commit_input.take() else { return Ok(()) };
549
550 let _ = self.exex_manager_handle.send(
553 ExExNotificationSource::Pipeline,
554 ExExNotification::ChainCommitted { new: Arc::new(chain) },
555 );
556
557 Ok(())
558 }
559
560 fn unwind(
562 &mut self,
563 provider: &Provider,
564 input: UnwindInput,
565 ) -> Result<UnwindOutput, StageError> {
566 let (range, unwind_to, _) =
567 input.unwind_block_range_with_threshold(self.thresholds.max_blocks.unwrap_or(u64::MAX));
568 if range.is_empty() {
569 return Ok(UnwindOutput {
570 checkpoint: input.checkpoint.with_block_number(input.unwind_to),
571 })
572 }
573
574 reject_cancun_boundary_unwind(provider, input.checkpoint.block_number, unwind_to)?;
575
576 self.ensure_consistency(provider, input.checkpoint.block_number, Some(unwind_to))?;
577
578 let bundle_state_with_receipts = provider.take_state_above(unwind_to)?;
582
583 if self.exex_manager_handle.has_exexs() {
585 let blocks = provider.recovered_block_range(range.clone())?;
587 let previous_input = self.post_unwind_commit_input.replace(Chain::new(
588 blocks,
589 bundle_state_with_receipts,
590 BTreeMap::new(),
591 ));
592
593 debug_assert!(
594 previous_input.is_none(),
595 "Previous post unwind commit input wasn't processed"
596 );
597 if let Some(previous_input) = previous_input {
598 tracing::debug!(target: "sync::stages::execution", ?previous_input, "Previous post unwind commit input wasn't processed");
599 }
600 }
601
602 let mut stage_checkpoint = input.checkpoint.execution_stage_checkpoint();
604 if let Some(stage_checkpoint) = stage_checkpoint.as_mut() {
605 for block_number in range {
606 stage_checkpoint.progress.processed -= provider
607 .header_by_number(block_number)?
608 .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?
609 .gas_used();
610 }
611 }
612 let checkpoint = if let Some(stage_checkpoint) = stage_checkpoint {
613 StageCheckpoint::new(unwind_to).with_execution_stage_checkpoint(stage_checkpoint)
614 } else {
615 StageCheckpoint::new(unwind_to)
616 };
617
618 Ok(UnwindOutput { checkpoint })
619 }
620
621 fn post_unwind_commit(&mut self) -> Result<(), StageError> {
622 let Some(chain) = self.post_unwind_commit_input.take() else { return Ok(()) };
623
624 let _ = self.exex_manager_handle.send(
627 ExExNotificationSource::Pipeline,
628 ExExNotification::ChainReverted { old: Arc::new(chain) },
629 );
630
631 Ok(())
632 }
633}
634
635fn reject_cancun_boundary_unwind<Provider>(
636 provider: &Provider,
637 checkpoint_block: u64,
638 unwind_to: u64,
639) -> Result<(), StageError>
640where
641 Provider: HeaderProvider + ChainSpecProvider<ChainSpec: EthereumHardforks>,
642{
643 let checkpoint_header = provider
644 .header_by_number(checkpoint_block)?
645 .ok_or_else(|| ProviderError::HeaderNotFound(checkpoint_block.into()))?;
646 let unwind_to_header = provider
647 .header_by_number(unwind_to)?
648 .ok_or_else(|| ProviderError::HeaderNotFound(unwind_to.into()))?;
649 let checkpoint_is_cancun =
650 provider.chain_spec().is_cancun_active_at_timestamp(checkpoint_header.timestamp());
651 let unwind_to_is_cancun =
652 provider.chain_spec().is_cancun_active_at_timestamp(unwind_to_header.timestamp());
653 if checkpoint_is_cancun && !unwind_to_is_cancun {
654 return Err(StageError::Fatal(
655 std::io::Error::other(format!(
656 "execution unwind across Cancun activation boundary is not allowed: checkpoint \
657 block #{checkpoint_block} (ts={}) is Cancun-active but unwind target \
658 #{unwind_to} (ts={}) is pre-Cancun",
659 checkpoint_header.timestamp(),
660 unwind_to_header.timestamp()
661 ))
662 .into(),
663 ))
664 }
665
666 Ok(())
667}
668
669fn execution_checkpoint<N>(
670 provider: &StaticFileProvider<N>,
671 start_block: BlockNumber,
672 max_block: BlockNumber,
673 checkpoint: StageCheckpoint,
674) -> Result<ExecutionCheckpoint, ProviderError>
675where
676 N: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
677{
678 Ok(match checkpoint.execution_stage_checkpoint() {
679 Some(stage_checkpoint @ ExecutionCheckpoint { block_range, .. })
682 if block_range == CheckpointBlockRange::from(start_block..=max_block) =>
683 {
684 stage_checkpoint
685 }
686 Some(ExecutionCheckpoint {
689 block_range: CheckpointBlockRange { to, .. },
690 progress: EntitiesCheckpoint { processed, total },
691 }) if to == start_block - 1 => ExecutionCheckpoint {
692 block_range: CheckpointBlockRange { from: start_block, to: max_block },
693 progress: EntitiesCheckpoint {
694 processed,
695 total: total + calculate_gas_used_from_headers(provider, start_block..=max_block)?,
696 },
697 },
698 Some(ExecutionCheckpoint { block_range: CheckpointBlockRange { to, .. }, progress })
701 if to == max_block =>
702 {
703 ExecutionCheckpoint {
704 block_range: CheckpointBlockRange { from: start_block, to: max_block },
705 progress,
706 }
707 }
708 Some(ExecutionCheckpoint { progress: EntitiesCheckpoint { processed, .. }, .. }) => {
711 let after_checkpoint_block_number =
712 calculate_gas_used_from_headers(provider, checkpoint.block_number + 1..=max_block)?;
713
714 ExecutionCheckpoint {
715 block_range: CheckpointBlockRange { from: start_block, to: max_block },
716 progress: EntitiesCheckpoint {
717 processed,
718 total: processed + after_checkpoint_block_number,
719 },
720 }
721 }
722 _ => {
725 let genesis_block_number = provider.genesis_block_number();
726 let processed = calculate_gas_used_from_headers(
727 provider,
728 genesis_block_number..=max(start_block - 1, genesis_block_number),
729 )?;
730
731 ExecutionCheckpoint {
732 block_range: CheckpointBlockRange { from: start_block, to: max_block },
733 progress: EntitiesCheckpoint {
734 processed,
735 total: processed +
736 calculate_gas_used_from_headers(provider, start_block..=max_block)?,
737 },
738 }
739 }
740 })
741}
742
743pub fn calculate_gas_used_from_headers<N>(
745 provider: &StaticFileProvider<N>,
746 range: RangeInclusive<BlockNumber>,
747) -> Result<u64, ProviderError>
748where
749 N: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
750{
751 debug!(target: "sync::stages::execution", ?range, "Calculating gas used from headers");
752
753 let mut gas_total = 0;
754
755 let start = Instant::now();
756
757 for entry in provider.fetch_range_iter(
758 StaticFileSegment::Headers,
759 *range.start()..*range.end() + 1,
760 |cursor, number| cursor.get_one::<HeaderMask<N::BlockHeader>>(number.into()),
761 )? {
762 if let Some(entry) = entry? {
763 gas_total += entry.gas_used();
764 }
765 }
766
767 let duration = start.elapsed();
768 debug!(target: "sync::stages::execution", ?range, ?duration, "Finished calculating gas used from headers");
769
770 Ok(gas_total)
771}
772
773#[cfg(test)]
774mod tests {
775 use super::*;
776 use crate::{stages::MERKLE_STAGE_DEFAULT_REBUILD_THRESHOLD, test_utils::TestStageDB};
777 use alloy_primitives::{address, hex_literal::hex, keccak256, Address, B256, U256};
778 use alloy_rlp::Decodable;
779 use assert_matches::assert_matches;
780 use reth_chainspec::{ChainSpecBuilder, EthereumHardfork, ForkCondition};
781 use reth_db_api::{
782 models::{metadata::StorageSettings, AccountBeforeTx},
783 transaction::{DbTx, DbTxMut},
784 };
785 use reth_ethereum_consensus::EthBeaconConsensus;
786 use reth_ethereum_primitives::Block;
787 use reth_evm_ethereum::EthEvmConfig;
788 use reth_primitives_traits::{Account, Block as _, Bytecode, SealedBlock, StorageEntry};
789 use reth_provider::{
790 test_utils::{create_test_provider_factory, create_test_provider_factory_with_chain_spec},
791 AccountReader, BlockWriter, DatabaseProviderFactory, ReceiptProvider,
792 StaticFileProviderFactory,
793 };
794 use reth_prune::PruneModes;
795 use reth_prune_types::{PruneMode, ReceiptsLogPruneConfig};
796 use reth_revm::revm::database::{AccountStatus, BundleAccount};
797 use reth_stages_api::StageUnitCheckpoint;
798 use reth_testing_utils::generators;
799 use std::collections::BTreeMap;
800
801 fn stage() -> ExecutionStage<EthEvmConfig> {
802 let evm_config =
803 EthEvmConfig::new(Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build()));
804 let consensus = Arc::new(EthBeaconConsensus::new(Arc::new(
805 ChainSpecBuilder::mainnet().berlin_activated().build(),
806 )));
807 ExecutionStage::new(
808 evm_config,
809 consensus,
810 ExecutionStageThresholds {
811 max_blocks: Some(100),
812 max_changes: None,
813 max_cumulative_gas: None,
814 max_duration: None,
815 },
816 MERKLE_STAGE_DEFAULT_REBUILD_THRESHOLD,
817 ExExManagerHandle::empty(),
818 )
819 }
820
821 #[test]
822 fn destroyed_storage_is_materialized_without_reverts() {
823 let factory = create_test_provider_factory();
824 let provider = factory.database_provider_rw().unwrap();
825 let address = Address::repeat_byte(0x11);
826 let hashed_address = keccak256(address);
827 let first_slot = B256::repeat_byte(0x22);
828 let second_slot = B256::repeat_byte(0x33);
829
830 provider
831 .tx_ref()
832 .put::<tables::HashedStorages>(
833 hashed_address,
834 StorageEntry { key: first_slot, value: U256::from(2) },
835 )
836 .unwrap();
837 provider
838 .tx_ref()
839 .put::<tables::HashedStorages>(
840 hashed_address,
841 StorageEntry { key: second_slot, value: U256::from(3) },
842 )
843 .unwrap();
844
845 let mut state = ExecutionOutcome::<()>::default();
846 state.bundle.state.insert(
847 address,
848 BundleAccount::new(
849 Some(Default::default()),
850 None,
851 Default::default(),
852 AccountStatus::Destroyed,
853 ),
854 );
855
856 let hashed_state = provider.latest().hashed_post_state(&state.bundle).unwrap();
857
858 let storage = &hashed_state.storages[&hashed_address];
859 assert_eq!(storage.storage[&first_slot], U256::ZERO);
860 assert_eq!(storage.storage[&second_slot], U256::ZERO);
861 assert!(state.bundle.reverts.is_empty());
862 }
863
864 #[test]
865 fn execution_checkpoint_matches() {
866 let factory = create_test_provider_factory();
867
868 let previous_stage_checkpoint = ExecutionCheckpoint {
869 block_range: CheckpointBlockRange { from: 0, to: 0 },
870 progress: EntitiesCheckpoint { processed: 1, total: 2 },
871 };
872 let previous_checkpoint = StageCheckpoint {
873 block_number: 0,
874 stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
875 };
876
877 let stage_checkpoint = execution_checkpoint(
878 &factory.static_file_provider(),
879 previous_stage_checkpoint.block_range.from,
880 previous_stage_checkpoint.block_range.to,
881 previous_checkpoint,
882 );
883
884 assert!(
885 matches!(stage_checkpoint, Ok(checkpoint) if checkpoint == previous_stage_checkpoint)
886 );
887 }
888
889 #[test]
890 fn execution_checkpoint_precedes() {
891 let factory = create_test_provider_factory();
892 let provider = factory.provider_rw().unwrap();
893
894 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
895 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
896 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
897 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
898 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
899 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
900 provider
901 .static_file_provider()
902 .latest_writer(StaticFileSegment::Headers)
903 .unwrap()
904 .commit()
905 .unwrap();
906 provider.commit().unwrap();
907
908 let previous_stage_checkpoint = ExecutionCheckpoint {
909 block_range: CheckpointBlockRange { from: 0, to: 0 },
910 progress: EntitiesCheckpoint { processed: 1, total: 1 },
911 };
912 let previous_checkpoint = StageCheckpoint {
913 block_number: 1,
914 stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
915 };
916
917 let stage_checkpoint =
918 execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
919
920 assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
921 block_range: CheckpointBlockRange { from: 1, to: 1 },
922 progress: EntitiesCheckpoint {
923 processed,
924 total
925 }
926 }) if processed == previous_stage_checkpoint.progress.processed &&
927 total == previous_stage_checkpoint.progress.total + block.gas_used);
928 }
929
930 #[test]
931 fn execution_checkpoint_recalculate_full_previous_some() {
932 let factory = create_test_provider_factory();
933 let provider = factory.provider_rw().unwrap();
934
935 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
936 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
937 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
938 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
939 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
940 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
941 provider
942 .static_file_provider()
943 .latest_writer(StaticFileSegment::Headers)
944 .unwrap()
945 .commit()
946 .unwrap();
947 provider.commit().unwrap();
948
949 let previous_stage_checkpoint = ExecutionCheckpoint {
950 block_range: CheckpointBlockRange { from: 0, to: 0 },
951 progress: EntitiesCheckpoint { processed: 1, total: 1 },
952 };
953 let previous_checkpoint = StageCheckpoint {
954 block_number: 1,
955 stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
956 };
957
958 let stage_checkpoint =
959 execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
960
961 assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
962 block_range: CheckpointBlockRange { from: 1, to: 1 },
963 progress: EntitiesCheckpoint {
964 processed,
965 total
966 }
967 }) if processed == previous_stage_checkpoint.progress.processed &&
968 total == previous_stage_checkpoint.progress.total + block.gas_used());
969 }
970
971 #[test]
972 fn execution_checkpoint_recalculate_full_previous_none() {
973 let factory = create_test_provider_factory();
974 let provider = factory.provider_rw().unwrap();
975
976 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
977 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
978 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
979 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
980 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
981 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
982 provider
983 .static_file_provider()
984 .latest_writer(StaticFileSegment::Headers)
985 .unwrap()
986 .commit()
987 .unwrap();
988 provider.commit().unwrap();
989
990 let previous_checkpoint = StageCheckpoint { block_number: 1, stage_checkpoint: None };
991
992 let stage_checkpoint =
993 execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
994
995 assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
996 block_range: CheckpointBlockRange { from: 1, to: 1 },
997 progress: EntitiesCheckpoint {
998 processed: 0,
999 total
1000 }
1001 }) if total == block.gas_used);
1002 }
1003
1004 #[tokio::test]
1005 async fn sanity_execution_of_block() {
1006 let factory = create_test_provider_factory();
1007 let provider = factory.provider_rw().unwrap();
1008 let input = ExecInput { target: Some(1), checkpoint: None };
1009 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
1010 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
1011 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
1012 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
1013 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
1014 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
1015 provider
1016 .static_file_provider()
1017 .latest_writer(StaticFileSegment::Headers)
1018 .unwrap()
1019 .commit()
1020 .unwrap();
1021 {
1022 let static_file_provider = provider.static_file_provider();
1023 let mut receipts_writer =
1024 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1025 receipts_writer.increment_block(0).unwrap();
1026 receipts_writer.commit().unwrap();
1027 }
1028 provider.commit().unwrap();
1029
1030 let provider = factory.provider_rw().unwrap();
1032
1033 let db_tx = provider.tx_ref();
1034 let acc1 = address!("0x1000000000000000000000000000000000000000");
1035 let acc2 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1036 let code = hex!("5a465a905090036002900360015500");
1037 let balance = U256::from(0x3635c9adc5dea00000u128);
1038 let code_hash = keccak256(code);
1039 db_tx
1040 .put::<tables::PlainAccountState>(
1041 acc1,
1042 Account { bytecode_hash: Some(code_hash), ..Default::default() },
1043 )
1044 .unwrap();
1045 db_tx
1046 .put::<tables::PlainAccountState>(acc2, Account { balance, ..Default::default() })
1047 .unwrap();
1048 db_tx.put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.into())).unwrap();
1049 provider.commit().unwrap();
1050
1051 let modes = [None, Some(PruneModes::default())];
1056 let random_filter = ReceiptsLogPruneConfig(BTreeMap::from([(
1057 Address::random(),
1058 PruneMode::Distance(100000),
1059 )]));
1060
1061 for mut mode in modes {
1063 let mut provider = factory.database_provider_rw().unwrap();
1064
1065 if let Some(mode) = &mut mode {
1066 mode.receipts_log_filter = random_filter.clone();
1068 }
1069
1070 let mut execution_stage = stage();
1071 provider.set_prune_modes(mode.clone().unwrap_or_default());
1072
1073 let output = execution_stage.execute(&provider, input).unwrap();
1074 provider.commit().unwrap();
1075
1076 assert_matches!(output, ExecOutput {
1077 checkpoint: StageCheckpoint {
1078 block_number: 1,
1079 stage_checkpoint: Some(StageUnitCheckpoint::Execution(ExecutionCheckpoint {
1080 block_range: CheckpointBlockRange {
1081 from: 1,
1082 to: 1,
1083 },
1084 progress: EntitiesCheckpoint {
1085 processed,
1086 total
1087 }
1088 }))
1089 },
1090 done: true
1091 } if processed == total && total == block.gas_used);
1092
1093 {
1094 let provider = factory.provider().unwrap();
1095
1096 let account1 = address!("0x1000000000000000000000000000000000000000");
1098 let account1_info =
1099 Account { bytecode_hash: Some(code_hash), ..Default::default() };
1100 let account2 = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
1101 let account2_info =
1102 Account { balance: U256::from(0x1bc16d674ece94bau128), ..Default::default() };
1103 let account3 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1104 let account3_info = Account {
1105 balance: U256::from(0x3635c9adc5de996b46u128),
1106 nonce: 0x01,
1107 ..Default::default()
1108 };
1109
1110 assert!(matches!(
1112 provider.basic_account(&account1),
1113 Ok(Some(acc)) if acc == account1_info
1114 ));
1115 assert!(matches!(
1116 provider.basic_account(&account2),
1117 Ok(Some(acc)) if acc == account2_info
1118 ));
1119 assert!(matches!(
1120 provider.basic_account(&account3),
1121 Ok(Some(acc)) if acc == account3_info
1122 ));
1123 assert!(matches!(
1126 provider.tx_ref().get::<tables::PlainStorageState>(account1),
1127 Ok(Some(entry)) if entry.key == B256::with_last_byte(1) && entry.value == U256::from(2)
1128 ));
1129 }
1130
1131 let mut provider = factory.database_provider_rw().unwrap();
1132 let mut stage = stage();
1133 provider.set_prune_modes(mode.unwrap_or_default());
1134
1135 let _result = stage
1136 .unwind(
1137 &provider,
1138 UnwindInput { checkpoint: output.checkpoint, unwind_to: 0, bad_block: None },
1139 )
1140 .unwrap();
1141 provider.commit().unwrap();
1142 }
1143 }
1144
1145 #[tokio::test]
1146 #[allow(clippy::clone_on_copy)]
1147 async fn sanity_execute_unwind() {
1148 let factory = create_test_provider_factory();
1149 let provider = factory.provider_rw().unwrap();
1150 let input = ExecInput { target: Some(1), checkpoint: None };
1151 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
1152 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
1153 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
1154 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
1155 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
1156 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
1157 provider
1158 .static_file_provider()
1159 .latest_writer(StaticFileSegment::Headers)
1160 .unwrap()
1161 .commit()
1162 .unwrap();
1163 {
1164 let static_file_provider = provider.static_file_provider();
1165 let mut receipts_writer =
1166 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1167 receipts_writer.increment_block(0).unwrap();
1168 receipts_writer.commit().unwrap();
1169 }
1170 provider.commit().unwrap();
1171
1172 let code = hex!("5a465a905090036002900360015500");
1174 let balance = U256::from(0x3635c9adc5dea00000u128);
1175 let code_hash = keccak256(code);
1176 let provider = factory.provider_rw().unwrap();
1178
1179 let db_tx = provider.tx_ref();
1180 let acc1 = address!("0x1000000000000000000000000000000000000000");
1181 let acc1_info = Account { bytecode_hash: Some(code_hash), ..Default::default() };
1182 let acc2 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1183 let acc2_info = Account { balance, ..Default::default() };
1184
1185 db_tx.put::<tables::PlainAccountState>(acc1, acc1_info.clone()).unwrap();
1186 db_tx.put::<tables::PlainAccountState>(acc2, acc2_info.clone()).unwrap();
1187 db_tx.put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.into())).unwrap();
1188 provider.commit().unwrap();
1189
1190 let mut provider = factory.database_provider_rw().unwrap();
1192
1193 let modes = [None, Some(PruneModes::default())];
1196 let random_filter = ReceiptsLogPruneConfig(BTreeMap::from([(
1197 Address::random(),
1198 PruneMode::Before(100000),
1199 )]));
1200
1201 for mut mode in modes {
1203 if let Some(mode) = &mut mode {
1204 mode.receipts_log_filter = random_filter.clone();
1206 }
1207
1208 let mut execution_stage = stage();
1210 provider.set_prune_modes(mode.clone().unwrap_or_default());
1211
1212 let result = execution_stage.execute(&provider, input).unwrap();
1213 provider.commit().unwrap();
1214
1215 provider = factory.database_provider_rw().unwrap();
1217 let mut stage = stage();
1218 provider.set_prune_modes(mode.unwrap_or_default());
1219
1220 let result = stage
1221 .unwind(
1222 &provider,
1223 UnwindInput { checkpoint: result.checkpoint, unwind_to: 0, bad_block: None },
1224 )
1225 .unwrap();
1226
1227 provider.static_file_provider().commit().unwrap();
1228
1229 assert_matches!(result, UnwindOutput {
1230 checkpoint: StageCheckpoint {
1231 block_number: 0,
1232 stage_checkpoint: Some(StageUnitCheckpoint::Execution(ExecutionCheckpoint {
1233 block_range: CheckpointBlockRange {
1234 from: 1,
1235 to: 1,
1236 },
1237 progress: EntitiesCheckpoint {
1238 processed: 0,
1239 total
1240 }
1241 }))
1242 }
1243 } if total == block.gas_used);
1244
1245 assert!(matches!(provider.basic_account(&acc1), Ok(Some(acc)) if acc == acc1_info));
1247 assert!(matches!(provider.basic_account(&acc2), Ok(Some(acc)) if acc == acc2_info));
1248
1249 let miner_acc = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
1250 assert!(matches!(provider.basic_account(&miner_acc), Ok(None)));
1251
1252 assert!(matches!(provider.receipt(0), Ok(None)));
1253 }
1254 }
1255
1256 #[test]
1257 fn unwind_from_cancun_to_pre_cancun_is_rejected() {
1258 let chain_spec = Arc::new(
1259 ChainSpecBuilder::mainnet()
1260 .berlin_activated()
1261 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(15))
1262 .build(),
1263 );
1264 let factory = create_test_provider_factory_with_chain_spec(chain_spec);
1265 let provider = factory.database_provider_rw().unwrap();
1266
1267 let mut rng = generators::rng();
1268 let mut genesis = generators::random_block(
1269 &mut rng,
1270 0,
1271 generators::BlockParams { tx_count: Some(0), ..Default::default() },
1272 )
1273 .unseal();
1274 genesis.header.timestamp = 0;
1275 let genesis = genesis.seal_slow();
1276
1277 let mut block_1 = generators::random_block(
1278 &mut rng,
1279 1,
1280 generators::BlockParams {
1281 parent: Some(genesis.hash()),
1282 tx_count: Some(0),
1283 ..Default::default()
1284 },
1285 )
1286 .unseal();
1287 block_1.header.timestamp = 10;
1288 let block_1 = block_1.seal_slow();
1289
1290 let mut block_2 = generators::random_block(
1291 &mut rng,
1292 2,
1293 generators::BlockParams {
1294 parent: Some(block_1.hash()),
1295 tx_count: Some(0),
1296 ..Default::default()
1297 },
1298 )
1299 .unseal();
1300 block_2.header.timestamp = 20;
1301 let block_2 = block_2.seal_slow();
1302
1303 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
1304 provider.insert_block(&block_1.try_recover().unwrap()).unwrap();
1305 provider.insert_block(&block_2.try_recover().unwrap()).unwrap();
1306 provider
1307 .static_file_provider()
1308 .latest_writer(StaticFileSegment::Headers)
1309 .unwrap()
1310 .commit()
1311 .unwrap();
1312
1313 let mut execution_stage = stage();
1314 let err = execution_stage
1315 .unwind(
1316 &provider,
1317 UnwindInput { checkpoint: StageCheckpoint::new(2), unwind_to: 1, bad_block: None },
1318 )
1319 .unwrap_err();
1320
1321 assert_matches!(err, StageError::Fatal(_));
1322 assert!(err.to_string().contains("across Cancun activation boundary"));
1323 }
1324
1325 #[tokio::test]
1326 #[allow(clippy::clone_on_copy)]
1327 async fn test_selfdestruct() {
1328 let test_db = TestStageDB::default();
1329 let provider = test_db.factory.database_provider_rw().unwrap();
1330 let input = ExecInput { target: Some(1), checkpoint: None };
1331 let mut genesis_rlp = hex!("f901f8f901f3a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0c9ceb8372c88cb461724d8d3d87e8b933f6fc5f679d4841800e662f4428ffd0da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080830f4240808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
1332 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
1333 let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice();
1334 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
1335 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
1336 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
1337 provider
1338 .static_file_provider()
1339 .latest_writer(StaticFileSegment::Headers)
1340 .unwrap()
1341 .commit()
1342 .unwrap();
1343 {
1344 let static_file_provider = provider.static_file_provider();
1345 let mut receipts_writer =
1346 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1347 receipts_writer.increment_block(0).unwrap();
1348 receipts_writer.commit().unwrap();
1349 }
1350 provider.commit().unwrap();
1351
1352 let caller_address = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1354 let destroyed_address = address!("0x095e7baea6a6c7c4c2dfeb977efac326af552d87");
1355 let beneficiary_address = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
1356
1357 let code = hex!("73095e7baea6a6c7c4c2dfeb977efac326af552d8731ff00");
1358 let balance = U256::from(0x0de0b6b3a7640000u64);
1359 let code_hash = keccak256(code);
1360
1361 let caller_info = Account { balance, ..Default::default() };
1363 let destroyed_info = Account { bytecode_hash: Some(code_hash), ..Default::default() };
1364
1365 let provider = test_db.factory.provider_rw().unwrap();
1367 provider
1368 .tx_ref()
1369 .put::<tables::PlainAccountState>(caller_address, caller_info.clone())
1370 .unwrap();
1371 provider
1372 .tx_ref()
1373 .put::<tables::PlainAccountState>(destroyed_address, destroyed_info.clone())
1374 .unwrap();
1375 provider
1376 .tx_ref()
1377 .put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.into()))
1378 .unwrap();
1379 provider
1381 .tx_ref()
1382 .put::<tables::PlainStorageState>(
1383 destroyed_address,
1384 StorageEntry { key: B256::ZERO, value: U256::ZERO },
1385 )
1386 .unwrap();
1387 provider
1388 .tx_ref()
1389 .put::<tables::PlainStorageState>(
1390 destroyed_address,
1391 StorageEntry { key: B256::with_last_byte(1), value: U256::ONE },
1392 )
1393 .unwrap();
1394
1395 provider.commit().unwrap();
1396
1397 let provider = test_db.factory.database_provider_rw().unwrap();
1399 let mut execution_stage = stage();
1400 let _ = execution_stage.execute(&provider, input).unwrap();
1401 provider.commit().unwrap();
1402
1403 let provider = test_db.factory.database_provider_rw().unwrap();
1405 assert!(matches!(provider.basic_account(&destroyed_address), Ok(None)));
1406
1407 assert!(matches!(
1408 provider.tx_ref().get::<tables::PlainStorageState>(destroyed_address),
1409 Ok(None)
1410 ));
1411 drop(provider);
1413 let plain_accounts = test_db.table::<tables::PlainAccountState>().unwrap();
1414 let plain_storage = test_db.table::<tables::PlainStorageState>().unwrap();
1415
1416 assert_eq!(
1417 plain_accounts,
1418 vec![
1419 (
1420 beneficiary_address,
1421 Account { balance: U256::from(0x1bc16d674eca30a0u64), ..Default::default() }
1422 ),
1423 (
1424 caller_address,
1425 Account {
1426 nonce: 1,
1427 balance: U256::from(0xde0b6b3a761cf60u64),
1428 ..Default::default()
1429 }
1430 )
1431 ]
1432 );
1433 assert!(plain_storage.is_empty());
1434
1435 let account_changesets = test_db.table::<tables::AccountChangeSets>().unwrap();
1436 let storage_changesets = test_db.table::<tables::StorageChangeSets>().unwrap();
1437
1438 assert_eq!(
1439 account_changesets,
1440 vec![
1441 (
1442 block.number,
1443 AccountBeforeTx { address: destroyed_address, info: Some(destroyed_info) },
1444 ),
1445 (block.number, AccountBeforeTx { address: beneficiary_address, info: None }),
1446 (
1447 block.number,
1448 AccountBeforeTx { address: caller_address, info: Some(caller_info) }
1449 ),
1450 ]
1451 );
1452
1453 assert_eq!(
1454 storage_changesets,
1455 vec![
1456 (
1457 (block.number, destroyed_address).into(),
1458 StorageEntry { key: B256::ZERO, value: U256::ZERO }
1459 ),
1460 (
1461 (block.number, destroyed_address).into(),
1462 StorageEntry { key: B256::with_last_byte(1), value: U256::ONE }
1463 )
1464 ]
1465 );
1466 }
1467
1468 #[test]
1469 fn test_ensure_consistency_with_skipped_receipts() {
1470 let factory = create_test_provider_factory();
1475 factory.set_storage_settings_cache(StorageSettings::v2());
1476
1477 let provider_rw = factory.database_provider_rw().unwrap();
1479 let mut rng = generators::rng();
1480 let genesis = generators::random_block(&mut rng, 0, Default::default());
1481 provider_rw
1482 .insert_block(&genesis.try_recover().unwrap())
1483 .expect("failed to insert genesis");
1484 let block = generators::random_block(
1485 &mut rng,
1486 1,
1487 generators::BlockParams { tx_count: Some(2), ..Default::default() },
1488 );
1489 provider_rw.insert_block(&block.try_recover().unwrap()).expect("failed to insert block");
1490
1491 let static_file_provider = provider_rw.static_file_provider();
1492 static_file_provider.latest_writer(StaticFileSegment::Headers).unwrap().commit().unwrap();
1493
1494 {
1497 let mut receipts_writer =
1498 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1499 receipts_writer.increment_block(0).unwrap();
1500 receipts_writer.increment_block(1).unwrap();
1501 receipts_writer.commit().unwrap();
1502 } provider_rw.commit().expect("failed to commit");
1505
1506 assert_eq!(
1508 factory
1509 .static_file_provider()
1510 .get_highest_static_file_block(StaticFileSegment::Receipts),
1511 Some(1)
1512 );
1513 assert_eq!(
1514 factory.static_file_provider().get_highest_static_file_tx(StaticFileSegment::Receipts),
1515 None
1516 );
1517
1518 let stage = stage();
1520
1521 let provider = factory.provider().unwrap();
1525 stage
1526 .ensure_consistency(&provider, 1, None)
1527 .expect("ensure_consistency should succeed when receipts are intentionally skipped");
1528 }
1529}