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