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