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