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::{hashed_cursor::zero_destroyed_account_storage, KeccakKeyHasher};
27use reth_trie_db::DatabaseHashedCursorFactory;
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
197 + DBProvider
198 + BlockReader
199 + HeaderProvider
200 + StorageSettingsCache,
201 {
202 if EitherWriter::receipts_destination(provider).is_database() {
205 return Ok(())
206 }
207
208 let next_receipt_num =
210 provider.block_body_indices(checkpoint)?.map(|b| b.next_tx_num()).unwrap_or(0);
211
212 let static_file_provider = provider.static_file_provider();
213
214 let next_static_file_receipt_num = static_file_provider
216 .get_highest_static_file_tx(StaticFileSegment::Receipts)
217 .map(|num| num + 1)
218 .unwrap_or(0);
219
220 let static_file_block_num = static_file_provider
222 .get_highest_static_file_block(StaticFileSegment::Receipts)
223 .unwrap_or(0);
224
225 match static_file_block_num.cmp(&checkpoint) {
228 Ordering::Greater | Ordering::Equal => {
231 let mut static_file_producer =
232 static_file_provider.latest_writer(StaticFileSegment::Receipts)?;
233 static_file_producer.prune_receipts(
234 next_static_file_receipt_num.saturating_sub(next_receipt_num),
235 checkpoint,
236 )?;
237 static_file_producer.commit()?;
240 }
241 Ordering::Less => {
242 if let Some(unwind_to) = unwind_to &&
245 unwind_to <= static_file_block_num
246 {
247 return Ok(())
248 }
249
250 return Err(missing_static_data_error(
253 next_static_file_receipt_num.saturating_sub(1),
254 &static_file_provider,
255 provider,
256 StaticFileSegment::Receipts,
257 )?)
258 }
259 }
260
261 Ok(())
262 }
263}
264
265impl<E, Provider> Stage<Provider> for ExecutionStage<E>
266where
267 E: ConfigureEvm,
268 Provider: DBProvider
269 + BlockReader<
270 Block = <E::Primitives as NodePrimitives>::Block,
271 Header = <E::Primitives as NodePrimitives>::BlockHeader,
272 > + StaticFileProviderFactory<
273 Primitives: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
274 > + StatsReader
275 + BlockHashReader
276 + StateWriter<Receipt = <E::Primitives as NodePrimitives>::Receipt>
277 + StorageSettingsCache
278 + StoragePath
279 + ChainSpecProvider<ChainSpec: EthereumHardforks>,
280{
281 fn id(&self) -> StageId {
283 StageId::Execution
284 }
285
286 fn poll_execute_ready(
287 &mut self,
288 cx: &mut Context<'_>,
289 _: ExecInput,
290 ) -> Poll<Result<(), StageError>> {
291 ready!(self.exex_manager_handle.poll_ready(cx));
292
293 Poll::Ready(Ok(()))
294 }
295
296 fn execute(&mut self, provider: &Provider, input: ExecInput) -> Result<ExecOutput, StageError> {
298 if input.target_reached() {
299 return Ok(ExecOutput::done(input.checkpoint()))
300 }
301
302 let start_block = input.next_block();
303 let max_block = input.target();
304 let static_file_provider = provider.static_file_provider();
305
306 self.ensure_consistency(provider, input.checkpoint().block_number, None)?;
307
308 let db = StateProviderDatabase(LatestStateProviderRef::new(provider));
309 let mut executor = self.evm_config.batch_executor(db);
310
311 let mut stage_progress = start_block;
313 let mut stage_checkpoint = execution_checkpoint(
314 &static_file_provider,
315 start_block,
316 max_block,
317 input.checkpoint(),
318 )?;
319
320 let mut fetch_block_duration = Duration::default();
321 let mut execution_duration = Duration::default();
322
323 let mut last_block = start_block;
324 let mut last_execution_duration = Duration::default();
325 let mut last_cumulative_gas = 0;
326 let mut last_log_instant = Instant::now();
327 let log_duration = Duration::from_secs(10);
328
329 debug!(target: "sync::stages::execution", start = start_block, end = max_block, "Executing range");
330
331 let mut cumulative_gas = 0;
333 let batch_start = Instant::now();
334
335 let mut blocks = Vec::new();
336 let mut results = Vec::new();
337 for block_number in start_block..=max_block {
338 let fetch_block_start = Instant::now();
340
341 let block = provider
343 .recovered_block(block_number.into(), TransactionVariant::NoHash)?
344 .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?;
345
346 fetch_block_duration += fetch_block_start.elapsed();
347
348 cumulative_gas += block.header().gas_used();
349
350 trace!(target: "sync::stages::execution", number = block_number, txs = block.body().transactions().len(), "Executing block");
352
353 let execute_start = Instant::now();
355
356 let result = self.metrics.metered_one(&block, |input| {
357 executor.execute_one(input).map_err(|error| StageError::Block {
358 block: Box::new(block.block_with_parent()),
359 error: BlockErrorKind::Execution(error),
360 })
361 })?;
362
363 if let Err(err) =
364 self.consensus.validate_block_post_execution(&block, &result, None, None)
365 {
366 return Err(StageError::Block {
367 block: Box::new(block.block_with_parent()),
368 error: BlockErrorKind::Validation(err),
369 })
370 }
371 results.push(result);
372
373 execution_duration += execute_start.elapsed();
374
375 if last_log_instant.elapsed() >= log_duration {
377 info!(
378 target: "sync::stages::execution",
379 start = last_block,
380 end = block_number,
381 throughput = format_gas_throughput(cumulative_gas - last_cumulative_gas, execution_duration - last_execution_duration),
382 "Executed block range"
383 );
384
385 last_block = block_number + 1;
386 last_execution_duration = execution_duration;
387 last_cumulative_gas = cumulative_gas;
388 last_log_instant = Instant::now();
389 }
390
391 stage_progress = block_number;
392 stage_checkpoint.progress.processed += block.header().gas_used();
393
394 if self.exex_manager_handle.has_exexs() {
396 blocks.push(block);
397 }
398
399 if self.thresholds.is_end_of_batch(
401 block_number - start_block,
402 executor.size_hint() as u64,
403 cumulative_gas,
404 batch_start.elapsed(),
405 ) {
406 break
407 }
408 }
409
410 let time = Instant::now();
412 let mut state = ExecutionOutcome::from_blocks(
413 start_block,
414 executor.into_state().take_bundle(),
415 results,
416 );
417 let write_preparation_duration = time.elapsed();
418
419 debug!(
421 target: "sync::stages::execution",
422 start = start_block,
423 end = stage_progress,
424 throughput = format_gas_throughput(cumulative_gas, execution_duration),
425 "Finished executing block range"
426 );
427
428 if !blocks.is_empty() {
433 let previous_input = self.post_execute_commit_input.replace(Chain::new(
434 blocks,
435 state.clone(),
436 BTreeMap::new(),
437 ));
438
439 if previous_input.is_some() {
440 return Err(StageError::PostExecuteCommit(
443 "Previous post execute commit input wasn't processed",
444 ))
445 }
446 }
447
448 let time = Instant::now();
449
450 if self.can_prune_changesets(provider, start_block, max_block)? {
451 let prune_modes = provider.prune_modes_ref();
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 slot_preimages::inject_plain_wipe_slots(&path, provider, &mut state)?;
489 } else if path.exists() {
490 let _ = std::fs::remove_dir_all(&path);
492 }
493 }
494
495 provider.write_state(&state, OriginalValuesKnown::Yes, StateWriteConfig::default())?;
499
500 if provider.cached_storage_settings().use_hashed_state() {
501 let mut hashed_state = state.hash_state_slow::<KeccakKeyHasher>();
502 zero_destroyed_account_storage(
503 &DatabaseHashedCursorFactory::new(provider.tx_ref()),
504 state.bundle.state(),
505 &mut hashed_state,
506 )?;
507 provider.write_hashed_state(&hashed_state.into_sorted())?;
508 }
509
510 let db_write_duration = time.elapsed();
511 debug!(
512 target: "sync::stages::execution",
513 block_fetch = ?fetch_block_duration,
514 execution = ?execution_duration,
515 write_preparation = ?write_preparation_duration,
516 write = ?db_write_duration,
517 "Execution time"
518 );
519
520 let done = stage_progress == max_block;
521 Ok(ExecOutput {
522 checkpoint: StageCheckpoint::new(stage_progress)
523 .with_execution_stage_checkpoint(stage_checkpoint),
524 done,
525 })
526 }
527
528 fn post_execute_commit(&mut self) -> Result<(), StageError> {
529 let Some(chain) = self.post_execute_commit_input.take() else { return Ok(()) };
530
531 let _ = self.exex_manager_handle.send(
534 ExExNotificationSource::Pipeline,
535 ExExNotification::ChainCommitted { new: Arc::new(chain) },
536 );
537
538 Ok(())
539 }
540
541 fn unwind(
543 &mut self,
544 provider: &Provider,
545 input: UnwindInput,
546 ) -> Result<UnwindOutput, StageError> {
547 let (range, unwind_to, _) =
548 input.unwind_block_range_with_threshold(self.thresholds.max_blocks.unwrap_or(u64::MAX));
549 if range.is_empty() {
550 return Ok(UnwindOutput {
551 checkpoint: input.checkpoint.with_block_number(input.unwind_to),
552 })
553 }
554
555 reject_cancun_boundary_unwind(provider, input.checkpoint.block_number, unwind_to)?;
556
557 self.ensure_consistency(provider, input.checkpoint.block_number, Some(unwind_to))?;
558
559 let bundle_state_with_receipts = provider.take_state_above(unwind_to)?;
563
564 if self.exex_manager_handle.has_exexs() {
566 let blocks = provider.recovered_block_range(range.clone())?;
568 let previous_input = self.post_unwind_commit_input.replace(Chain::new(
569 blocks,
570 bundle_state_with_receipts,
571 BTreeMap::new(),
572 ));
573
574 debug_assert!(
575 previous_input.is_none(),
576 "Previous post unwind commit input wasn't processed"
577 );
578 if let Some(previous_input) = previous_input {
579 tracing::debug!(target: "sync::stages::execution", ?previous_input, "Previous post unwind commit input wasn't processed");
580 }
581 }
582
583 let mut stage_checkpoint = input.checkpoint.execution_stage_checkpoint();
585 if let Some(stage_checkpoint) = stage_checkpoint.as_mut() {
586 for block_number in range {
587 stage_checkpoint.progress.processed -= provider
588 .header_by_number(block_number)?
589 .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?
590 .gas_used();
591 }
592 }
593 let checkpoint = if let Some(stage_checkpoint) = stage_checkpoint {
594 StageCheckpoint::new(unwind_to).with_execution_stage_checkpoint(stage_checkpoint)
595 } else {
596 StageCheckpoint::new(unwind_to)
597 };
598
599 Ok(UnwindOutput { checkpoint })
600 }
601
602 fn post_unwind_commit(&mut self) -> Result<(), StageError> {
603 let Some(chain) = self.post_unwind_commit_input.take() else { return Ok(()) };
604
605 let _ = self.exex_manager_handle.send(
608 ExExNotificationSource::Pipeline,
609 ExExNotification::ChainReverted { old: Arc::new(chain) },
610 );
611
612 Ok(())
613 }
614}
615
616fn reject_cancun_boundary_unwind<Provider>(
617 provider: &Provider,
618 checkpoint_block: u64,
619 unwind_to: u64,
620) -> Result<(), StageError>
621where
622 Provider: HeaderProvider + ChainSpecProvider<ChainSpec: EthereumHardforks>,
623{
624 let checkpoint_header = provider
625 .header_by_number(checkpoint_block)?
626 .ok_or_else(|| ProviderError::HeaderNotFound(checkpoint_block.into()))?;
627 let unwind_to_header = provider
628 .header_by_number(unwind_to)?
629 .ok_or_else(|| ProviderError::HeaderNotFound(unwind_to.into()))?;
630 let checkpoint_is_cancun =
631 provider.chain_spec().is_cancun_active_at_timestamp(checkpoint_header.timestamp());
632 let unwind_to_is_cancun =
633 provider.chain_spec().is_cancun_active_at_timestamp(unwind_to_header.timestamp());
634 if checkpoint_is_cancun && !unwind_to_is_cancun {
635 return Err(StageError::Fatal(
636 std::io::Error::other(format!(
637 "execution unwind across Cancun activation boundary is not allowed: checkpoint \
638 block #{checkpoint_block} (ts={}) is Cancun-active but unwind target \
639 #{unwind_to} (ts={}) is pre-Cancun",
640 checkpoint_header.timestamp(),
641 unwind_to_header.timestamp()
642 ))
643 .into(),
644 ))
645 }
646
647 Ok(())
648}
649
650fn execution_checkpoint<N>(
651 provider: &StaticFileProvider<N>,
652 start_block: BlockNumber,
653 max_block: BlockNumber,
654 checkpoint: StageCheckpoint,
655) -> Result<ExecutionCheckpoint, ProviderError>
656where
657 N: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
658{
659 Ok(match checkpoint.execution_stage_checkpoint() {
660 Some(stage_checkpoint @ ExecutionCheckpoint { block_range, .. })
663 if block_range == CheckpointBlockRange::from(start_block..=max_block) =>
664 {
665 stage_checkpoint
666 }
667 Some(ExecutionCheckpoint {
670 block_range: CheckpointBlockRange { to, .. },
671 progress: EntitiesCheckpoint { processed, total },
672 }) if to == start_block - 1 => ExecutionCheckpoint {
673 block_range: CheckpointBlockRange { from: start_block, to: max_block },
674 progress: EntitiesCheckpoint {
675 processed,
676 total: total + calculate_gas_used_from_headers(provider, start_block..=max_block)?,
677 },
678 },
679 Some(ExecutionCheckpoint { block_range: CheckpointBlockRange { to, .. }, progress })
682 if to == max_block =>
683 {
684 ExecutionCheckpoint {
685 block_range: CheckpointBlockRange { from: start_block, to: max_block },
686 progress,
687 }
688 }
689 Some(ExecutionCheckpoint { progress: EntitiesCheckpoint { processed, .. }, .. }) => {
692 let after_checkpoint_block_number =
693 calculate_gas_used_from_headers(provider, checkpoint.block_number + 1..=max_block)?;
694
695 ExecutionCheckpoint {
696 block_range: CheckpointBlockRange { from: start_block, to: max_block },
697 progress: EntitiesCheckpoint {
698 processed,
699 total: processed + after_checkpoint_block_number,
700 },
701 }
702 }
703 _ => {
706 let genesis_block_number = provider.genesis_block_number();
707 let processed = calculate_gas_used_from_headers(
708 provider,
709 genesis_block_number..=max(start_block - 1, genesis_block_number),
710 )?;
711
712 ExecutionCheckpoint {
713 block_range: CheckpointBlockRange { from: start_block, to: max_block },
714 progress: EntitiesCheckpoint {
715 processed,
716 total: processed +
717 calculate_gas_used_from_headers(provider, start_block..=max_block)?,
718 },
719 }
720 }
721 })
722}
723
724pub fn calculate_gas_used_from_headers<N>(
726 provider: &StaticFileProvider<N>,
727 range: RangeInclusive<BlockNumber>,
728) -> Result<u64, ProviderError>
729where
730 N: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
731{
732 debug!(target: "sync::stages::execution", ?range, "Calculating gas used from headers");
733
734 let mut gas_total = 0;
735
736 let start = Instant::now();
737
738 for entry in provider.fetch_range_iter(
739 StaticFileSegment::Headers,
740 *range.start()..*range.end() + 1,
741 |cursor, number| cursor.get_one::<HeaderMask<N::BlockHeader>>(number.into()),
742 )? {
743 if let Some(entry) = entry? {
744 gas_total += entry.gas_used();
745 }
746 }
747
748 let duration = start.elapsed();
749 debug!(target: "sync::stages::execution", ?range, ?duration, "Finished calculating gas used from headers");
750
751 Ok(gas_total)
752}
753
754#[cfg(test)]
755mod tests {
756 use super::*;
757 use crate::{stages::MERKLE_STAGE_DEFAULT_REBUILD_THRESHOLD, test_utils::TestStageDB};
758 use alloy_primitives::{address, hex_literal::hex, keccak256, Address, B256, U256};
759 use alloy_rlp::Decodable;
760 use assert_matches::assert_matches;
761 use reth_chainspec::{ChainSpecBuilder, EthereumHardfork, ForkCondition};
762 use reth_db_api::{
763 models::{metadata::StorageSettings, AccountBeforeTx},
764 transaction::{DbTx, DbTxMut},
765 };
766 use reth_ethereum_consensus::EthBeaconConsensus;
767 use reth_ethereum_primitives::Block;
768 use reth_evm_ethereum::EthEvmConfig;
769 use reth_primitives_traits::{Account, Block as _, Bytecode, SealedBlock, StorageEntry};
770 use reth_provider::{
771 test_utils::{create_test_provider_factory, create_test_provider_factory_with_chain_spec},
772 AccountReader, BlockWriter, DatabaseProviderFactory, ReceiptProvider,
773 StaticFileProviderFactory,
774 };
775 use reth_prune::PruneModes;
776 use reth_prune_types::{PruneMode, ReceiptsLogPruneConfig};
777 use reth_revm::revm::database::{AccountStatus, BundleAccount};
778 use reth_stages_api::StageUnitCheckpoint;
779 use reth_testing_utils::generators;
780 use reth_trie::HashedPostState;
781 use std::collections::BTreeMap;
782
783 fn stage() -> ExecutionStage<EthEvmConfig> {
784 let evm_config =
785 EthEvmConfig::new(Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build()));
786 let consensus = Arc::new(EthBeaconConsensus::new(Arc::new(
787 ChainSpecBuilder::mainnet().berlin_activated().build(),
788 )));
789 ExecutionStage::new(
790 evm_config,
791 consensus,
792 ExecutionStageThresholds {
793 max_blocks: Some(100),
794 max_changes: None,
795 max_cumulative_gas: None,
796 max_duration: None,
797 },
798 MERKLE_STAGE_DEFAULT_REBUILD_THRESHOLD,
799 ExExManagerHandle::empty(),
800 )
801 }
802
803 #[test]
804 fn destroyed_storage_is_materialized_without_reverts() {
805 let factory = create_test_provider_factory();
806 let provider = factory.database_provider_rw().unwrap();
807 let address = Address::repeat_byte(0x11);
808 let hashed_address = keccak256(address);
809 let retained_slot = B256::repeat_byte(0x22);
810 let deleted_slot = B256::repeat_byte(0x33);
811 let retained_value = U256::from(1);
812
813 provider
814 .tx_ref()
815 .put::<tables::HashedStorages>(
816 hashed_address,
817 StorageEntry { key: retained_slot, value: U256::from(2) },
818 )
819 .unwrap();
820 provider
821 .tx_ref()
822 .put::<tables::HashedStorages>(
823 hashed_address,
824 StorageEntry { key: deleted_slot, value: U256::from(3) },
825 )
826 .unwrap();
827
828 let mut state = ExecutionOutcome::<()>::default();
829 state.bundle.state.insert(
830 address,
831 BundleAccount::new(
832 Some(Default::default()),
833 Some(Default::default()),
834 Default::default(),
835 AccountStatus::DestroyedChanged,
836 ),
837 );
838
839 let mut hashed_state = HashedPostState::default();
840 hashed_state
841 .storages
842 .entry(hashed_address)
843 .or_default()
844 .storage
845 .insert(retained_slot, retained_value);
846
847 zero_destroyed_account_storage(
848 &DatabaseHashedCursorFactory::new(provider.tx_ref()),
849 state.bundle.state(),
850 &mut hashed_state,
851 )
852 .unwrap();
853
854 let storage = &hashed_state.storages[&hashed_address];
855 assert!(!storage.wiped);
856 assert_eq!(storage.storage[&retained_slot], retained_value);
857 assert_eq!(storage.storage[&deleted_slot], U256::ZERO);
858 assert!(state.bundle.reverts.is_empty());
859 }
860
861 #[test]
862 fn execution_checkpoint_matches() {
863 let factory = create_test_provider_factory();
864
865 let previous_stage_checkpoint = ExecutionCheckpoint {
866 block_range: CheckpointBlockRange { from: 0, to: 0 },
867 progress: EntitiesCheckpoint { processed: 1, total: 2 },
868 };
869 let previous_checkpoint = StageCheckpoint {
870 block_number: 0,
871 stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
872 };
873
874 let stage_checkpoint = execution_checkpoint(
875 &factory.static_file_provider(),
876 previous_stage_checkpoint.block_range.from,
877 previous_stage_checkpoint.block_range.to,
878 previous_checkpoint,
879 );
880
881 assert!(
882 matches!(stage_checkpoint, Ok(checkpoint) if checkpoint == previous_stage_checkpoint)
883 );
884 }
885
886 #[test]
887 fn execution_checkpoint_precedes() {
888 let factory = create_test_provider_factory();
889 let provider = factory.provider_rw().unwrap();
890
891 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
892 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
893 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
894 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
895 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
896 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
897 provider
898 .static_file_provider()
899 .latest_writer(StaticFileSegment::Headers)
900 .unwrap()
901 .commit()
902 .unwrap();
903 provider.commit().unwrap();
904
905 let previous_stage_checkpoint = ExecutionCheckpoint {
906 block_range: CheckpointBlockRange { from: 0, to: 0 },
907 progress: EntitiesCheckpoint { processed: 1, total: 1 },
908 };
909 let previous_checkpoint = StageCheckpoint {
910 block_number: 1,
911 stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
912 };
913
914 let stage_checkpoint =
915 execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
916
917 assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
918 block_range: CheckpointBlockRange { from: 1, to: 1 },
919 progress: EntitiesCheckpoint {
920 processed,
921 total
922 }
923 }) if processed == previous_stage_checkpoint.progress.processed &&
924 total == previous_stage_checkpoint.progress.total + block.gas_used);
925 }
926
927 #[test]
928 fn execution_checkpoint_recalculate_full_previous_some() {
929 let factory = create_test_provider_factory();
930 let provider = factory.provider_rw().unwrap();
931
932 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
933 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
934 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
935 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
936 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
937 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
938 provider
939 .static_file_provider()
940 .latest_writer(StaticFileSegment::Headers)
941 .unwrap()
942 .commit()
943 .unwrap();
944 provider.commit().unwrap();
945
946 let previous_stage_checkpoint = ExecutionCheckpoint {
947 block_range: CheckpointBlockRange { from: 0, to: 0 },
948 progress: EntitiesCheckpoint { processed: 1, total: 1 },
949 };
950 let previous_checkpoint = StageCheckpoint {
951 block_number: 1,
952 stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
953 };
954
955 let stage_checkpoint =
956 execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
957
958 assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
959 block_range: CheckpointBlockRange { from: 1, to: 1 },
960 progress: EntitiesCheckpoint {
961 processed,
962 total
963 }
964 }) if processed == previous_stage_checkpoint.progress.processed &&
965 total == previous_stage_checkpoint.progress.total + block.gas_used());
966 }
967
968 #[test]
969 fn execution_checkpoint_recalculate_full_previous_none() {
970 let factory = create_test_provider_factory();
971 let provider = factory.provider_rw().unwrap();
972
973 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
974 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
975 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
976 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
977 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
978 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
979 provider
980 .static_file_provider()
981 .latest_writer(StaticFileSegment::Headers)
982 .unwrap()
983 .commit()
984 .unwrap();
985 provider.commit().unwrap();
986
987 let previous_checkpoint = StageCheckpoint { block_number: 1, stage_checkpoint: None };
988
989 let stage_checkpoint =
990 execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
991
992 assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
993 block_range: CheckpointBlockRange { from: 1, to: 1 },
994 progress: EntitiesCheckpoint {
995 processed: 0,
996 total
997 }
998 }) if total == block.gas_used);
999 }
1000
1001 #[tokio::test]
1002 async fn sanity_execution_of_block() {
1003 let factory = create_test_provider_factory();
1004 let provider = factory.provider_rw().unwrap();
1005 let input = ExecInput { target: Some(1), checkpoint: None };
1006 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
1007 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
1008 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
1009 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
1010 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
1011 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
1012 provider
1013 .static_file_provider()
1014 .latest_writer(StaticFileSegment::Headers)
1015 .unwrap()
1016 .commit()
1017 .unwrap();
1018 {
1019 let static_file_provider = provider.static_file_provider();
1020 let mut receipts_writer =
1021 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1022 receipts_writer.increment_block(0).unwrap();
1023 receipts_writer.commit().unwrap();
1024 }
1025 provider.commit().unwrap();
1026
1027 let provider = factory.provider_rw().unwrap();
1029
1030 let db_tx = provider.tx_ref();
1031 let acc1 = address!("0x1000000000000000000000000000000000000000");
1032 let acc2 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1033 let code = hex!("5a465a905090036002900360015500");
1034 let balance = U256::from(0x3635c9adc5dea00000u128);
1035 let code_hash = keccak256(code);
1036 db_tx
1037 .put::<tables::PlainAccountState>(
1038 acc1,
1039 Account { nonce: 0, balance: U256::ZERO, bytecode_hash: Some(code_hash) },
1040 )
1041 .unwrap();
1042 db_tx
1043 .put::<tables::PlainAccountState>(
1044 acc2,
1045 Account { nonce: 0, balance, bytecode_hash: None },
1046 )
1047 .unwrap();
1048 db_tx.put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.to_vec().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 { balance: U256::ZERO, nonce: 0x00, bytecode_hash: Some(code_hash) };
1100 let account2 = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
1101 let account2_info = Account {
1102 balance: U256::from(0x1bc16d674ece94bau128),
1103 nonce: 0x00,
1104 bytecode_hash: None,
1105 };
1106 let account3 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1107 let account3_info = Account {
1108 balance: U256::from(0x3635c9adc5de996b46u128),
1109 nonce: 0x01,
1110 bytecode_hash: None,
1111 };
1112
1113 assert!(matches!(
1115 provider.basic_account(&account1),
1116 Ok(Some(acc)) if acc == account1_info
1117 ));
1118 assert!(matches!(
1119 provider.basic_account(&account2),
1120 Ok(Some(acc)) if acc == account2_info
1121 ));
1122 assert!(matches!(
1123 provider.basic_account(&account3),
1124 Ok(Some(acc)) if acc == account3_info
1125 ));
1126 assert!(matches!(
1129 provider.tx_ref().get::<tables::PlainStorageState>(account1),
1130 Ok(Some(entry)) if entry.key == B256::with_last_byte(1) && entry.value == U256::from(2)
1131 ));
1132 }
1133
1134 let mut provider = factory.database_provider_rw().unwrap();
1135 let mut stage = stage();
1136 provider.set_prune_modes(mode.unwrap_or_default());
1137
1138 let _result = stage
1139 .unwind(
1140 &provider,
1141 UnwindInput { checkpoint: output.checkpoint, unwind_to: 0, bad_block: None },
1142 )
1143 .unwrap();
1144 provider.commit().unwrap();
1145 }
1146 }
1147
1148 #[tokio::test]
1149 async fn sanity_execute_unwind() {
1150 let factory = create_test_provider_factory();
1151 let provider = factory.provider_rw().unwrap();
1152 let input = ExecInput { target: Some(1), checkpoint: None };
1153 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
1154 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
1155 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
1156 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
1157 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
1158 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
1159 provider
1160 .static_file_provider()
1161 .latest_writer(StaticFileSegment::Headers)
1162 .unwrap()
1163 .commit()
1164 .unwrap();
1165 {
1166 let static_file_provider = provider.static_file_provider();
1167 let mut receipts_writer =
1168 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1169 receipts_writer.increment_block(0).unwrap();
1170 receipts_writer.commit().unwrap();
1171 }
1172 provider.commit().unwrap();
1173
1174 let code = hex!("5a465a905090036002900360015500");
1176 let balance = U256::from(0x3635c9adc5dea00000u128);
1177 let code_hash = keccak256(code);
1178 let provider = factory.provider_rw().unwrap();
1180
1181 let db_tx = provider.tx_ref();
1182 let acc1 = address!("0x1000000000000000000000000000000000000000");
1183 let acc1_info = Account { nonce: 0, balance: U256::ZERO, bytecode_hash: Some(code_hash) };
1184 let acc2 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1185 let acc2_info = Account { nonce: 0, balance, bytecode_hash: None };
1186
1187 db_tx.put::<tables::PlainAccountState>(acc1, acc1_info).unwrap();
1188 db_tx.put::<tables::PlainAccountState>(acc2, acc2_info).unwrap();
1189 db_tx.put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.to_vec().into())).unwrap();
1190 provider.commit().unwrap();
1191
1192 let mut provider = factory.database_provider_rw().unwrap();
1194
1195 let modes = [None, Some(PruneModes::default())];
1198 let random_filter = ReceiptsLogPruneConfig(BTreeMap::from([(
1199 Address::random(),
1200 PruneMode::Before(100000),
1201 )]));
1202
1203 for mut mode in modes {
1205 if let Some(mode) = &mut mode {
1206 mode.receipts_log_filter = random_filter.clone();
1208 }
1209
1210 let mut execution_stage = stage();
1212 provider.set_prune_modes(mode.clone().unwrap_or_default());
1213
1214 let result = execution_stage.execute(&provider, input).unwrap();
1215 provider.commit().unwrap();
1216
1217 provider = factory.database_provider_rw().unwrap();
1219 let mut stage = stage();
1220 provider.set_prune_modes(mode.clone().unwrap_or_default());
1221
1222 let result = stage
1223 .unwind(
1224 &provider,
1225 UnwindInput { checkpoint: result.checkpoint, unwind_to: 0, bad_block: None },
1226 )
1227 .unwrap();
1228
1229 provider.static_file_provider().commit().unwrap();
1230
1231 assert_matches!(result, UnwindOutput {
1232 checkpoint: StageCheckpoint {
1233 block_number: 0,
1234 stage_checkpoint: Some(StageUnitCheckpoint::Execution(ExecutionCheckpoint {
1235 block_range: CheckpointBlockRange {
1236 from: 1,
1237 to: 1,
1238 },
1239 progress: EntitiesCheckpoint {
1240 processed: 0,
1241 total
1242 }
1243 }))
1244 }
1245 } if total == block.gas_used);
1246
1247 assert!(matches!(provider.basic_account(&acc1), Ok(Some(acc)) if acc == acc1_info));
1249 assert!(matches!(provider.basic_account(&acc2), Ok(Some(acc)) if acc == acc2_info));
1250
1251 let miner_acc = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
1252 assert!(matches!(provider.basic_account(&miner_acc), Ok(None)));
1253
1254 assert!(matches!(provider.receipt(0), Ok(None)));
1255 }
1256 }
1257
1258 #[test]
1259 fn unwind_from_cancun_to_pre_cancun_is_rejected() {
1260 let chain_spec = Arc::new(
1261 ChainSpecBuilder::mainnet()
1262 .berlin_activated()
1263 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(15))
1264 .build(),
1265 );
1266 let factory = create_test_provider_factory_with_chain_spec(chain_spec);
1267 let provider = factory.database_provider_rw().unwrap();
1268
1269 let mut rng = generators::rng();
1270 let mut genesis = generators::random_block(
1271 &mut rng,
1272 0,
1273 generators::BlockParams { tx_count: Some(0), ..Default::default() },
1274 )
1275 .unseal();
1276 genesis.header.timestamp = 0;
1277 let genesis = genesis.seal_slow();
1278
1279 let mut block_1 = generators::random_block(
1280 &mut rng,
1281 1,
1282 generators::BlockParams {
1283 parent: Some(genesis.hash()),
1284 tx_count: Some(0),
1285 ..Default::default()
1286 },
1287 )
1288 .unseal();
1289 block_1.header.timestamp = 10;
1290 let block_1 = block_1.seal_slow();
1291
1292 let mut block_2 = generators::random_block(
1293 &mut rng,
1294 2,
1295 generators::BlockParams {
1296 parent: Some(block_1.hash()),
1297 tx_count: Some(0),
1298 ..Default::default()
1299 },
1300 )
1301 .unseal();
1302 block_2.header.timestamp = 20;
1303 let block_2 = block_2.seal_slow();
1304
1305 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
1306 provider.insert_block(&block_1.try_recover().unwrap()).unwrap();
1307 provider.insert_block(&block_2.try_recover().unwrap()).unwrap();
1308 provider
1309 .static_file_provider()
1310 .latest_writer(StaticFileSegment::Headers)
1311 .unwrap()
1312 .commit()
1313 .unwrap();
1314
1315 let mut execution_stage = stage();
1316 let err = execution_stage
1317 .unwind(
1318 &provider,
1319 UnwindInput { checkpoint: StageCheckpoint::new(2), unwind_to: 1, bad_block: None },
1320 )
1321 .unwrap_err();
1322
1323 assert_matches!(err, StageError::Fatal(_));
1324 assert!(err.to_string().contains("across Cancun activation boundary"));
1325 }
1326
1327 #[tokio::test]
1328 async fn test_selfdestruct() {
1329 let test_db = TestStageDB::default();
1330 let provider = test_db.factory.database_provider_rw().unwrap();
1331 let input = ExecInput { target: Some(1), checkpoint: None };
1332 let mut genesis_rlp = hex!("f901f8f901f3a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0c9ceb8372c88cb461724d8d3d87e8b933f6fc5f679d4841800e662f4428ffd0da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080830f4240808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
1333 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
1334 let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice();
1335 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
1336 provider.insert_block(&genesis.try_recover().unwrap()).unwrap();
1337 provider.insert_block(&block.clone().try_recover().unwrap()).unwrap();
1338 provider
1339 .static_file_provider()
1340 .latest_writer(StaticFileSegment::Headers)
1341 .unwrap()
1342 .commit()
1343 .unwrap();
1344 {
1345 let static_file_provider = provider.static_file_provider();
1346 let mut receipts_writer =
1347 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1348 receipts_writer.increment_block(0).unwrap();
1349 receipts_writer.commit().unwrap();
1350 }
1351 provider.commit().unwrap();
1352
1353 let caller_address = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1355 let destroyed_address = address!("0x095e7baea6a6c7c4c2dfeb977efac326af552d87");
1356 let beneficiary_address = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
1357
1358 let code = hex!("73095e7baea6a6c7c4c2dfeb977efac326af552d8731ff00");
1359 let balance = U256::from(0x0de0b6b3a7640000u64);
1360 let code_hash = keccak256(code);
1361
1362 let caller_info = Account { nonce: 0, balance, bytecode_hash: None };
1364 let destroyed_info =
1365 Account { nonce: 0, balance: U256::ZERO, bytecode_hash: Some(code_hash) };
1366
1367 let provider = test_db.factory.provider_rw().unwrap();
1369 provider.tx_ref().put::<tables::PlainAccountState>(caller_address, caller_info).unwrap();
1370 provider
1371 .tx_ref()
1372 .put::<tables::PlainAccountState>(destroyed_address, destroyed_info)
1373 .unwrap();
1374 provider
1375 .tx_ref()
1376 .put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.to_vec().into()))
1377 .unwrap();
1378 provider
1380 .tx_ref()
1381 .put::<tables::PlainStorageState>(
1382 destroyed_address,
1383 StorageEntry { key: B256::ZERO, value: U256::ZERO },
1384 )
1385 .unwrap();
1386 provider
1387 .tx_ref()
1388 .put::<tables::PlainStorageState>(
1389 destroyed_address,
1390 StorageEntry { key: B256::with_last_byte(1), value: U256::from(1u64) },
1391 )
1392 .unwrap();
1393
1394 provider.commit().unwrap();
1395
1396 let provider = test_db.factory.database_provider_rw().unwrap();
1398 let mut execution_stage = stage();
1399 let _ = execution_stage.execute(&provider, input).unwrap();
1400 provider.commit().unwrap();
1401
1402 let provider = test_db.factory.database_provider_rw().unwrap();
1404 assert!(matches!(provider.basic_account(&destroyed_address), Ok(None)));
1405
1406 assert!(matches!(
1407 provider.tx_ref().get::<tables::PlainStorageState>(destroyed_address),
1408 Ok(None)
1409 ));
1410 drop(provider);
1412 let plain_accounts = test_db.table::<tables::PlainAccountState>().unwrap();
1413 let plain_storage = test_db.table::<tables::PlainStorageState>().unwrap();
1414
1415 assert_eq!(
1416 plain_accounts,
1417 vec![
1418 (
1419 beneficiary_address,
1420 Account {
1421 nonce: 0,
1422 balance: U256::from(0x1bc16d674eca30a0u64),
1423 bytecode_hash: None
1424 }
1425 ),
1426 (
1427 caller_address,
1428 Account {
1429 nonce: 1,
1430 balance: U256::from(0xde0b6b3a761cf60u64),
1431 bytecode_hash: None
1432 }
1433 )
1434 ]
1435 );
1436 assert!(plain_storage.is_empty());
1437
1438 let account_changesets = test_db.table::<tables::AccountChangeSets>().unwrap();
1439 let storage_changesets = test_db.table::<tables::StorageChangeSets>().unwrap();
1440
1441 assert_eq!(
1442 account_changesets,
1443 vec![
1444 (
1445 block.number,
1446 AccountBeforeTx { address: destroyed_address, info: Some(destroyed_info) },
1447 ),
1448 (block.number, AccountBeforeTx { address: beneficiary_address, info: None }),
1449 (
1450 block.number,
1451 AccountBeforeTx { address: caller_address, info: Some(caller_info) }
1452 ),
1453 ]
1454 );
1455
1456 assert_eq!(
1457 storage_changesets,
1458 vec![
1459 (
1460 (block.number, destroyed_address).into(),
1461 StorageEntry { key: B256::ZERO, value: U256::ZERO }
1462 ),
1463 (
1464 (block.number, destroyed_address).into(),
1465 StorageEntry { key: B256::with_last_byte(1), value: U256::from(1u64) }
1466 )
1467 ]
1468 );
1469 }
1470
1471 #[test]
1472 fn test_ensure_consistency_with_skipped_receipts() {
1473 let factory = create_test_provider_factory();
1478 factory.set_storage_settings_cache(StorageSettings::v2());
1479
1480 let provider_rw = factory.database_provider_rw().unwrap();
1482 let mut rng = generators::rng();
1483 let genesis = generators::random_block(&mut rng, 0, Default::default());
1484 provider_rw
1485 .insert_block(&genesis.try_recover().unwrap())
1486 .expect("failed to insert genesis");
1487 let block = generators::random_block(
1488 &mut rng,
1489 1,
1490 generators::BlockParams { tx_count: Some(2), ..Default::default() },
1491 );
1492 provider_rw.insert_block(&block.try_recover().unwrap()).expect("failed to insert block");
1493
1494 let static_file_provider = provider_rw.static_file_provider();
1495 static_file_provider.latest_writer(StaticFileSegment::Headers).unwrap().commit().unwrap();
1496
1497 {
1500 let mut receipts_writer =
1501 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1502 receipts_writer.increment_block(0).unwrap();
1503 receipts_writer.increment_block(1).unwrap();
1504 receipts_writer.commit().unwrap();
1505 } provider_rw.commit().expect("failed to commit");
1508
1509 assert_eq!(
1511 factory
1512 .static_file_provider()
1513 .get_highest_static_file_block(StaticFileSegment::Receipts),
1514 Some(1)
1515 );
1516 assert_eq!(
1517 factory.static_file_provider().get_highest_static_file_tx(StaticFileSegment::Receipts),
1518 None
1519 );
1520
1521 let stage = stage();
1523
1524 let provider = factory.provider().unwrap();
1528 stage
1529 .ensure_consistency(&provider, 1, None)
1530 .expect("ensure_consistency should succeed when receipts are intentionally skipped");
1531 }
1532}