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