1use crate::stages::MERKLE_STAGE_DEFAULT_INCREMENTAL_THRESHOLD;
2use alloy_consensus::BlockHeader;
3use alloy_primitives::BlockNumber;
4use num_traits::Zero;
5use reth_config::config::ExecutionConfig;
6use reth_consensus::{ConsensusError, FullConsensus};
7use reth_db::{static_file::HeaderMask, tables};
8use reth_evm::{execute::Executor, metrics::ExecutorMetrics, ConfigureEvm};
9use reth_execution_types::Chain;
10use reth_exex::{ExExManagerHandle, ExExNotification, ExExNotificationSource};
11use reth_primitives_traits::{format_gas_throughput, BlockBody, NodePrimitives};
12use reth_provider::{
13 providers::{StaticFileProvider, StaticFileWriter},
14 BlockHashReader, BlockReader, DBProvider, EitherWriter, ExecutionOutcome, HeaderProvider,
15 LatestStateProviderRef, OriginalValuesKnown, ProviderError, StateWriter,
16 StaticFileProviderFactory, StatsReader, StorageSettingsCache, TransactionVariant,
17};
18use reth_revm::database::StateProviderDatabase;
19use reth_stages_api::{
20 BlockErrorKind, CheckpointBlockRange, EntitiesCheckpoint, ExecInput, ExecOutput,
21 ExecutionCheckpoint, ExecutionStageThresholds, Stage, StageCheckpoint, StageError, StageId,
22 UnwindInput, UnwindOutput,
23};
24use reth_static_file_types::StaticFileSegment;
25use std::{
26 cmp::{max, Ordering},
27 ops::RangeInclusive,
28 sync::Arc,
29 task::{ready, Context, Poll},
30 time::{Duration, Instant},
31};
32use tracing::*;
33
34use super::missing_static_data_error;
35
36#[derive(Debug)]
65pub struct ExecutionStage<E>
66where
67 E: ConfigureEvm,
68{
69 evm_config: E,
71 consensus: Arc<dyn FullConsensus<E::Primitives, Error = ConsensusError>>,
73 thresholds: ExecutionStageThresholds,
75 external_clean_threshold: u64,
80 post_execute_commit_input: Option<Chain<E::Primitives>>,
84 post_unwind_commit_input: Option<Chain<E::Primitives>>,
88 exex_manager_handle: ExExManagerHandle<E::Primitives>,
90 metrics: ExecutorMetrics,
92}
93
94impl<E> ExecutionStage<E>
95where
96 E: ConfigureEvm,
97{
98 pub fn new(
100 evm_config: E,
101 consensus: Arc<dyn FullConsensus<E::Primitives, Error = ConsensusError>>,
102 thresholds: ExecutionStageThresholds,
103 external_clean_threshold: u64,
104 exex_manager_handle: ExExManagerHandle<E::Primitives>,
105 ) -> Self {
106 Self {
107 external_clean_threshold,
108 evm_config,
109 consensus,
110 thresholds,
111 post_execute_commit_input: None,
112 post_unwind_commit_input: None,
113 exex_manager_handle,
114 metrics: ExecutorMetrics::default(),
115 }
116 }
117
118 pub fn new_with_executor(
122 evm_config: E,
123 consensus: Arc<dyn FullConsensus<E::Primitives, Error = ConsensusError>>,
124 ) -> Self {
125 Self::new(
126 evm_config,
127 consensus,
128 ExecutionStageThresholds::default(),
129 MERKLE_STAGE_DEFAULT_INCREMENTAL_THRESHOLD,
130 ExExManagerHandle::empty(),
131 )
132 }
133
134 pub fn from_config(
136 evm_config: E,
137 consensus: Arc<dyn FullConsensus<E::Primitives, Error = ConsensusError>>,
138 config: ExecutionConfig,
139 external_clean_threshold: u64,
140 ) -> Self {
141 Self::new(
142 evm_config,
143 consensus,
144 config.into(),
145 external_clean_threshold,
146 ExExManagerHandle::empty(),
147 )
148 }
149
150 fn can_prune_changesets(
162 &self,
163 provider: impl StatsReader,
164 start_block: u64,
165 max_block: u64,
166 ) -> Result<bool, StageError> {
167 Ok(max_block - start_block > self.external_clean_threshold ||
170 provider.count_entries::<tables::AccountsTrie>()?.is_zero())
171 }
172
173 fn ensure_consistency<Provider>(
182 &self,
183 provider: &Provider,
184 checkpoint: u64,
185 unwind_to: Option<u64>,
186 ) -> Result<(), StageError>
187 where
188 Provider: StaticFileProviderFactory
189 + DBProvider
190 + BlockReader
191 + HeaderProvider
192 + StorageSettingsCache,
193 {
194 if EitherWriter::receipts_destination(provider).is_database() {
197 return Ok(())
198 }
199
200 let next_receipt_num =
202 provider.block_body_indices(checkpoint)?.map(|b| b.next_tx_num()).unwrap_or(0);
203
204 let static_file_provider = provider.static_file_provider();
205
206 let next_static_file_receipt_num = static_file_provider
208 .get_highest_static_file_tx(StaticFileSegment::Receipts)
209 .map(|num| num + 1)
210 .unwrap_or(0);
211
212 let static_file_block_num = static_file_provider
214 .get_highest_static_file_block(StaticFileSegment::Receipts)
215 .unwrap_or(0);
216
217 match static_file_block_num.cmp(&checkpoint) {
220 Ordering::Greater | Ordering::Equal => {
223 let mut static_file_producer =
224 static_file_provider.latest_writer(StaticFileSegment::Receipts)?;
225 static_file_producer.prune_receipts(
226 next_static_file_receipt_num.saturating_sub(next_receipt_num),
227 checkpoint,
228 )?;
229 static_file_producer.commit()?;
232 }
233 Ordering::Less => {
234 if let Some(unwind_to) = unwind_to &&
237 unwind_to <= static_file_block_num
238 {
239 return Ok(())
240 }
241
242 return Err(missing_static_data_error(
245 next_static_file_receipt_num.saturating_sub(1),
246 &static_file_provider,
247 provider,
248 StaticFileSegment::Receipts,
249 )?)
250 }
251 }
252
253 Ok(())
254 }
255}
256
257impl<E, Provider> Stage<Provider> for ExecutionStage<E>
258where
259 E: ConfigureEvm,
260 Provider: DBProvider
261 + BlockReader<
262 Block = <E::Primitives as NodePrimitives>::Block,
263 Header = <E::Primitives as NodePrimitives>::BlockHeader,
264 > + StaticFileProviderFactory<
265 Primitives: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
266 > + StatsReader
267 + BlockHashReader
268 + StateWriter<Receipt = <E::Primitives as NodePrimitives>::Receipt>
269 + StorageSettingsCache,
270{
271 fn id(&self) -> StageId {
273 StageId::Execution
274 }
275
276 fn poll_execute_ready(
277 &mut self,
278 cx: &mut Context<'_>,
279 _: ExecInput,
280 ) -> Poll<Result<(), StageError>> {
281 ready!(self.exex_manager_handle.poll_ready(cx));
282
283 Poll::Ready(Ok(()))
284 }
285
286 fn execute(&mut self, provider: &Provider, input: ExecInput) -> Result<ExecOutput, StageError> {
288 if input.target_reached() {
289 return Ok(ExecOutput::done(input.checkpoint()))
290 }
291
292 let start_block = input.next_block();
293 let max_block = input.target();
294 let static_file_provider = provider.static_file_provider();
295
296 self.ensure_consistency(provider, input.checkpoint().block_number, None)?;
297
298 let db = StateProviderDatabase(LatestStateProviderRef::new(provider));
299 let mut executor = self.evm_config.batch_executor(db);
300
301 let mut stage_progress = start_block;
303 let mut stage_checkpoint = execution_checkpoint(
304 &static_file_provider,
305 start_block,
306 max_block,
307 input.checkpoint(),
308 )?;
309
310 let mut fetch_block_duration = Duration::default();
311 let mut execution_duration = Duration::default();
312
313 let mut last_block = start_block;
314 let mut last_execution_duration = Duration::default();
315 let mut last_cumulative_gas = 0;
316 let mut last_log_instant = Instant::now();
317 let log_duration = Duration::from_secs(10);
318
319 debug!(target: "sync::stages::execution", start = start_block, end = max_block, "Executing range");
320
321 let mut cumulative_gas = 0;
323 let batch_start = Instant::now();
324
325 let mut blocks = Vec::new();
326 let mut results = Vec::new();
327 for block_number in start_block..=max_block {
328 let fetch_block_start = Instant::now();
330
331 let block = provider
333 .recovered_block(block_number.into(), TransactionVariant::NoHash)?
334 .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?;
335
336 fetch_block_duration += fetch_block_start.elapsed();
337
338 cumulative_gas += block.header().gas_used();
339
340 trace!(target: "sync::stages::execution", number = block_number, txs = block.body().transactions().len(), "Executing block");
342
343 let execute_start = Instant::now();
345
346 let result = self.metrics.metered_one(&block, |input| {
347 executor.execute_one(input).map_err(|error| StageError::Block {
348 block: Box::new(block.block_with_parent()),
349 error: BlockErrorKind::Execution(error),
350 })
351 })?;
352
353 if let Err(err) = self.consensus.validate_block_post_execution(&block, &result) {
354 return Err(StageError::Block {
355 block: Box::new(block.block_with_parent()),
356 error: BlockErrorKind::Validation(err),
357 })
358 }
359 results.push(result);
360
361 execution_duration += execute_start.elapsed();
362
363 if last_log_instant.elapsed() >= log_duration {
365 info!(
366 target: "sync::stages::execution",
367 start = last_block,
368 end = block_number,
369 throughput = format_gas_throughput(cumulative_gas - last_cumulative_gas, execution_duration - last_execution_duration),
370 "Executed block range"
371 );
372
373 last_block = block_number + 1;
374 last_execution_duration = execution_duration;
375 last_cumulative_gas = cumulative_gas;
376 last_log_instant = Instant::now();
377 }
378
379 stage_progress = block_number;
380 stage_checkpoint.progress.processed += block.header().gas_used();
381
382 if self.exex_manager_handle.has_exexs() {
384 blocks.push(block);
385 }
386
387 if self.thresholds.is_end_of_batch(
389 block_number - start_block,
390 executor.size_hint() as u64,
391 cumulative_gas,
392 batch_start.elapsed(),
393 ) {
394 break
395 }
396 }
397
398 let time = Instant::now();
400 let mut state = ExecutionOutcome::from_blocks(
401 start_block,
402 executor.into_state().take_bundle(),
403 results,
404 );
405 let write_preparation_duration = time.elapsed();
406
407 debug!(
409 target: "sync::stages::execution",
410 start = start_block,
411 end = stage_progress,
412 throughput = format_gas_throughput(cumulative_gas, execution_duration),
413 "Finished executing block range"
414 );
415
416 if !blocks.is_empty() {
421 let previous_input =
422 self.post_execute_commit_input.replace(Chain::new(blocks, state.clone(), None));
423
424 if previous_input.is_some() {
425 return Err(StageError::PostExecuteCommit(
428 "Previous post execute commit input wasn't processed",
429 ))
430 }
431 }
432
433 let time = Instant::now();
434
435 if self.can_prune_changesets(provider, start_block, max_block)? {
436 let prune_modes = provider.prune_modes_ref();
437
438 for block_number in start_block..=max_block {
440 let Some(reverts) =
441 state.bundle.reverts.get_mut((block_number - start_block) as usize)
442 else {
443 break
444 };
445
446 if prune_modes
449 .account_history
450 .is_some_and(|m| m.should_prune(block_number, max_block)) &&
451 prune_modes
452 .storage_history
453 .is_some_and(|m| m.should_prune(block_number, max_block))
454 {
455 reverts.clear();
456 }
457 }
458 }
459
460 provider.write_state(&state, OriginalValuesKnown::Yes)?;
462
463 let db_write_duration = time.elapsed();
464 debug!(
465 target: "sync::stages::execution",
466 block_fetch = ?fetch_block_duration,
467 execution = ?execution_duration,
468 write_preparation = ?write_preparation_duration,
469 write = ?db_write_duration,
470 "Execution time"
471 );
472
473 let done = stage_progress == max_block;
474 Ok(ExecOutput {
475 checkpoint: StageCheckpoint::new(stage_progress)
476 .with_execution_stage_checkpoint(stage_checkpoint),
477 done,
478 })
479 }
480
481 fn post_execute_commit(&mut self) -> Result<(), StageError> {
482 let Some(chain) = self.post_execute_commit_input.take() else { return Ok(()) };
483
484 let _ = self.exex_manager_handle.send(
487 ExExNotificationSource::Pipeline,
488 ExExNotification::ChainCommitted { new: Arc::new(chain) },
489 );
490
491 Ok(())
492 }
493
494 fn unwind(
496 &mut self,
497 provider: &Provider,
498 input: UnwindInput,
499 ) -> Result<UnwindOutput, StageError> {
500 let (range, unwind_to, _) =
501 input.unwind_block_range_with_threshold(self.thresholds.max_blocks.unwrap_or(u64::MAX));
502 if range.is_empty() {
503 return Ok(UnwindOutput {
504 checkpoint: input.checkpoint.with_block_number(input.unwind_to),
505 })
506 }
507
508 self.ensure_consistency(provider, input.checkpoint.block_number, Some(unwind_to))?;
509
510 let bundle_state_with_receipts = provider.take_state_above(unwind_to)?;
514
515 if self.exex_manager_handle.has_exexs() {
517 let blocks = provider.recovered_block_range(range.clone())?;
519 let previous_input = self.post_unwind_commit_input.replace(Chain::new(
520 blocks,
521 bundle_state_with_receipts,
522 None,
523 ));
524
525 debug_assert!(
526 previous_input.is_none(),
527 "Previous post unwind commit input wasn't processed"
528 );
529 if let Some(previous_input) = previous_input {
530 tracing::debug!(target: "sync::stages::execution", ?previous_input, "Previous post unwind commit input wasn't processed");
531 }
532 }
533
534 let mut stage_checkpoint = input.checkpoint.execution_stage_checkpoint();
536 if let Some(stage_checkpoint) = stage_checkpoint.as_mut() {
537 for block_number in range {
538 stage_checkpoint.progress.processed -= provider
539 .header_by_number(block_number)?
540 .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?
541 .gas_used();
542 }
543 }
544 let checkpoint = if let Some(stage_checkpoint) = stage_checkpoint {
545 StageCheckpoint::new(unwind_to).with_execution_stage_checkpoint(stage_checkpoint)
546 } else {
547 StageCheckpoint::new(unwind_to)
548 };
549
550 Ok(UnwindOutput { checkpoint })
551 }
552
553 fn post_unwind_commit(&mut self) -> Result<(), StageError> {
554 let Some(chain) = self.post_unwind_commit_input.take() else { return Ok(()) };
555
556 let _ = self.exex_manager_handle.send(
559 ExExNotificationSource::Pipeline,
560 ExExNotification::ChainReverted { old: Arc::new(chain) },
561 );
562
563 Ok(())
564 }
565}
566
567fn execution_checkpoint<N>(
568 provider: &StaticFileProvider<N>,
569 start_block: BlockNumber,
570 max_block: BlockNumber,
571 checkpoint: StageCheckpoint,
572) -> Result<ExecutionCheckpoint, ProviderError>
573where
574 N: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
575{
576 Ok(match checkpoint.execution_stage_checkpoint() {
577 Some(stage_checkpoint @ ExecutionCheckpoint { block_range, .. })
580 if block_range == CheckpointBlockRange::from(start_block..=max_block) =>
581 {
582 stage_checkpoint
583 }
584 Some(ExecutionCheckpoint {
587 block_range: CheckpointBlockRange { to, .. },
588 progress: EntitiesCheckpoint { processed, total },
589 }) if to == start_block - 1 => ExecutionCheckpoint {
590 block_range: CheckpointBlockRange { from: start_block, to: max_block },
591 progress: EntitiesCheckpoint {
592 processed,
593 total: total + calculate_gas_used_from_headers(provider, start_block..=max_block)?,
594 },
595 },
596 Some(ExecutionCheckpoint { block_range: CheckpointBlockRange { to, .. }, progress })
599 if to == max_block =>
600 {
601 ExecutionCheckpoint {
602 block_range: CheckpointBlockRange { from: start_block, to: max_block },
603 progress,
604 }
605 }
606 Some(ExecutionCheckpoint { progress: EntitiesCheckpoint { processed, .. }, .. }) => {
609 let after_checkpoint_block_number =
610 calculate_gas_used_from_headers(provider, checkpoint.block_number + 1..=max_block)?;
611
612 ExecutionCheckpoint {
613 block_range: CheckpointBlockRange { from: start_block, to: max_block },
614 progress: EntitiesCheckpoint {
615 processed,
616 total: processed + after_checkpoint_block_number,
617 },
618 }
619 }
620 _ => {
623 let genesis_block_number = provider.genesis_block_number();
624 let processed = calculate_gas_used_from_headers(
625 provider,
626 genesis_block_number..=max(start_block - 1, genesis_block_number),
627 )?;
628
629 ExecutionCheckpoint {
630 block_range: CheckpointBlockRange { from: start_block, to: max_block },
631 progress: EntitiesCheckpoint {
632 processed,
633 total: processed +
634 calculate_gas_used_from_headers(provider, start_block..=max_block)?,
635 },
636 }
637 }
638 })
639}
640
641pub fn calculate_gas_used_from_headers<N>(
643 provider: &StaticFileProvider<N>,
644 range: RangeInclusive<BlockNumber>,
645) -> Result<u64, ProviderError>
646where
647 N: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
648{
649 debug!(target: "sync::stages::execution", ?range, "Calculating gas used from headers");
650
651 let mut gas_total = 0;
652
653 let start = Instant::now();
654
655 for entry in provider.fetch_range_iter(
656 StaticFileSegment::Headers,
657 *range.start()..*range.end() + 1,
658 |cursor, number| cursor.get_one::<HeaderMask<N::BlockHeader>>(number.into()),
659 )? {
660 if let Some(entry) = entry? {
661 gas_total += entry.gas_used();
662 }
663 }
664
665 let duration = start.elapsed();
666 debug!(target: "sync::stages::execution", ?range, ?duration, "Finished calculating gas used from headers");
667
668 Ok(gas_total)
669}
670
671#[cfg(test)]
672mod tests {
673 use super::*;
674 use crate::{stages::MERKLE_STAGE_DEFAULT_REBUILD_THRESHOLD, test_utils::TestStageDB};
675 use alloy_primitives::{address, hex_literal::hex, keccak256, Address, B256, U256};
676 use alloy_rlp::Decodable;
677 use assert_matches::assert_matches;
678 use reth_chainspec::ChainSpecBuilder;
679 use reth_db_api::{
680 models::{metadata::StorageSettings, AccountBeforeTx},
681 transaction::{DbTx, DbTxMut},
682 };
683 use reth_ethereum_consensus::EthBeaconConsensus;
684 use reth_ethereum_primitives::Block;
685 use reth_evm_ethereum::EthEvmConfig;
686 use reth_primitives_traits::{Account, Bytecode, SealedBlock, StorageEntry};
687 use reth_provider::{
688 test_utils::create_test_provider_factory, AccountReader, BlockWriter,
689 DatabaseProviderFactory, ReceiptProvider, StaticFileProviderFactory,
690 };
691 use reth_prune::PruneModes;
692 use reth_prune_types::{PruneMode, ReceiptsLogPruneConfig};
693 use reth_stages_api::StageUnitCheckpoint;
694 use reth_testing_utils::generators;
695 use std::collections::BTreeMap;
696
697 fn stage() -> ExecutionStage<EthEvmConfig> {
698 let evm_config =
699 EthEvmConfig::new(Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build()));
700 let consensus = Arc::new(EthBeaconConsensus::new(Arc::new(
701 ChainSpecBuilder::mainnet().berlin_activated().build(),
702 )));
703 ExecutionStage::new(
704 evm_config,
705 consensus,
706 ExecutionStageThresholds {
707 max_blocks: Some(100),
708 max_changes: None,
709 max_cumulative_gas: None,
710 max_duration: None,
711 },
712 MERKLE_STAGE_DEFAULT_REBUILD_THRESHOLD,
713 ExExManagerHandle::empty(),
714 )
715 }
716
717 #[test]
718 fn execution_checkpoint_matches() {
719 let factory = create_test_provider_factory();
720
721 let previous_stage_checkpoint = ExecutionCheckpoint {
722 block_range: CheckpointBlockRange { from: 0, to: 0 },
723 progress: EntitiesCheckpoint { processed: 1, total: 2 },
724 };
725 let previous_checkpoint = StageCheckpoint {
726 block_number: 0,
727 stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
728 };
729
730 let stage_checkpoint = execution_checkpoint(
731 &factory.static_file_provider(),
732 previous_stage_checkpoint.block_range.from,
733 previous_stage_checkpoint.block_range.to,
734 previous_checkpoint,
735 );
736
737 assert!(
738 matches!(stage_checkpoint, Ok(checkpoint) if checkpoint == previous_stage_checkpoint)
739 );
740 }
741
742 #[test]
743 fn execution_checkpoint_precedes() {
744 let factory = create_test_provider_factory();
745 let provider = factory.provider_rw().unwrap();
746
747 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
748 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
749 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
750 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
751 provider.insert_block(genesis.try_recover().unwrap()).unwrap();
752 provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
753 provider
754 .static_file_provider()
755 .latest_writer(StaticFileSegment::Headers)
756 .unwrap()
757 .commit()
758 .unwrap();
759 provider.commit().unwrap();
760
761 let previous_stage_checkpoint = ExecutionCheckpoint {
762 block_range: CheckpointBlockRange { from: 0, to: 0 },
763 progress: EntitiesCheckpoint { processed: 1, total: 1 },
764 };
765 let previous_checkpoint = StageCheckpoint {
766 block_number: 1,
767 stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
768 };
769
770 let stage_checkpoint =
771 execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
772
773 assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
774 block_range: CheckpointBlockRange { from: 1, to: 1 },
775 progress: EntitiesCheckpoint {
776 processed,
777 total
778 }
779 }) if processed == previous_stage_checkpoint.progress.processed &&
780 total == previous_stage_checkpoint.progress.total + block.gas_used);
781 }
782
783 #[test]
784 fn execution_checkpoint_recalculate_full_previous_some() {
785 let factory = create_test_provider_factory();
786 let provider = factory.provider_rw().unwrap();
787
788 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
789 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
790 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
791 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
792 provider.insert_block(genesis.try_recover().unwrap()).unwrap();
793 provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
794 provider
795 .static_file_provider()
796 .latest_writer(StaticFileSegment::Headers)
797 .unwrap()
798 .commit()
799 .unwrap();
800 provider.commit().unwrap();
801
802 let previous_stage_checkpoint = ExecutionCheckpoint {
803 block_range: CheckpointBlockRange { from: 0, to: 0 },
804 progress: EntitiesCheckpoint { processed: 1, total: 1 },
805 };
806 let previous_checkpoint = StageCheckpoint {
807 block_number: 1,
808 stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
809 };
810
811 let stage_checkpoint =
812 execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
813
814 assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
815 block_range: CheckpointBlockRange { from: 1, to: 1 },
816 progress: EntitiesCheckpoint {
817 processed,
818 total
819 }
820 }) if processed == previous_stage_checkpoint.progress.processed &&
821 total == previous_stage_checkpoint.progress.total + block.gas_used());
822 }
823
824 #[test]
825 fn execution_checkpoint_recalculate_full_previous_none() {
826 let factory = create_test_provider_factory();
827 let provider = factory.provider_rw().unwrap();
828
829 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
830 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
831 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
832 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
833 provider.insert_block(genesis.try_recover().unwrap()).unwrap();
834 provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
835 provider
836 .static_file_provider()
837 .latest_writer(StaticFileSegment::Headers)
838 .unwrap()
839 .commit()
840 .unwrap();
841 provider.commit().unwrap();
842
843 let previous_checkpoint = StageCheckpoint { block_number: 1, stage_checkpoint: None };
844
845 let stage_checkpoint =
846 execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
847
848 assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
849 block_range: CheckpointBlockRange { from: 1, to: 1 },
850 progress: EntitiesCheckpoint {
851 processed: 0,
852 total
853 }
854 }) if total == block.gas_used);
855 }
856
857 #[tokio::test]
858 async fn sanity_execution_of_block() {
859 let factory = create_test_provider_factory();
860 let provider = factory.provider_rw().unwrap();
861 let input = ExecInput { target: Some(1), checkpoint: None };
862 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
863 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
864 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
865 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
866 provider.insert_block(genesis.try_recover().unwrap()).unwrap();
867 provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
868 provider
869 .static_file_provider()
870 .latest_writer(StaticFileSegment::Headers)
871 .unwrap()
872 .commit()
873 .unwrap();
874 {
875 let static_file_provider = provider.static_file_provider();
876 let mut receipts_writer =
877 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
878 receipts_writer.increment_block(0).unwrap();
879 receipts_writer.commit().unwrap();
880 }
881 provider.commit().unwrap();
882
883 let provider = factory.provider_rw().unwrap();
885
886 let db_tx = provider.tx_ref();
887 let acc1 = address!("0x1000000000000000000000000000000000000000");
888 let acc2 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
889 let code = hex!("5a465a905090036002900360015500");
890 let balance = U256::from(0x3635c9adc5dea00000u128);
891 let code_hash = keccak256(code);
892 db_tx
893 .put::<tables::PlainAccountState>(
894 acc1,
895 Account { nonce: 0, balance: U256::ZERO, bytecode_hash: Some(code_hash) },
896 )
897 .unwrap();
898 db_tx
899 .put::<tables::PlainAccountState>(
900 acc2,
901 Account { nonce: 0, balance, bytecode_hash: None },
902 )
903 .unwrap();
904 db_tx.put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.to_vec().into())).unwrap();
905 provider.commit().unwrap();
906
907 let modes = [None, Some(PruneModes::default())];
912 let random_filter = ReceiptsLogPruneConfig(BTreeMap::from([(
913 Address::random(),
914 PruneMode::Distance(100000),
915 )]));
916
917 for mut mode in modes {
919 let mut provider = factory.database_provider_rw().unwrap();
920
921 if let Some(mode) = &mut mode {
922 mode.receipts_log_filter = random_filter.clone();
924 }
925
926 let mut execution_stage = stage();
927 provider.set_prune_modes(mode.clone().unwrap_or_default());
928
929 let output = execution_stage.execute(&provider, input).unwrap();
930 provider.commit().unwrap();
931
932 assert_matches!(output, ExecOutput {
933 checkpoint: StageCheckpoint {
934 block_number: 1,
935 stage_checkpoint: Some(StageUnitCheckpoint::Execution(ExecutionCheckpoint {
936 block_range: CheckpointBlockRange {
937 from: 1,
938 to: 1,
939 },
940 progress: EntitiesCheckpoint {
941 processed,
942 total
943 }
944 }))
945 },
946 done: true
947 } if processed == total && total == block.gas_used);
948
949 let provider = factory.provider().unwrap();
950
951 let account1 = address!("0x1000000000000000000000000000000000000000");
953 let account1_info =
954 Account { balance: U256::ZERO, nonce: 0x00, bytecode_hash: Some(code_hash) };
955 let account2 = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
956 let account2_info = Account {
957 balance: U256::from(0x1bc16d674ece94bau128),
958 nonce: 0x00,
959 bytecode_hash: None,
960 };
961 let account3 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
962 let account3_info = Account {
963 balance: U256::from(0x3635c9adc5de996b46u128),
964 nonce: 0x01,
965 bytecode_hash: None,
966 };
967
968 assert!(
970 matches!(provider.basic_account(&account1), Ok(Some(acc)) if acc == account1_info)
971 );
972 assert!(
973 matches!(provider.basic_account(&account2), Ok(Some(acc)) if acc == account2_info)
974 );
975 assert!(
976 matches!(provider.basic_account(&account3), Ok(Some(acc)) if acc == account3_info)
977 );
978 assert!(matches!(
981 provider.tx_ref().get::<tables::PlainStorageState>(account1),
982 Ok(Some(entry)) if entry.key == B256::with_last_byte(1) && entry.value == U256::from(2)
983 ));
984
985 let mut provider = factory.database_provider_rw().unwrap();
986 let mut stage = stage();
987 provider.set_prune_modes(mode.unwrap_or_default());
988
989 let _result = stage
990 .unwind(
991 &provider,
992 UnwindInput { checkpoint: output.checkpoint, unwind_to: 0, bad_block: None },
993 )
994 .unwrap();
995 provider.commit().unwrap();
996 }
997 }
998
999 #[tokio::test]
1000 async fn sanity_execute_unwind() {
1001 let factory = create_test_provider_factory();
1002 let provider = factory.provider_rw().unwrap();
1003 let input = ExecInput { target: Some(1), checkpoint: None };
1004 let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
1005 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
1006 let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
1007 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
1008 provider.insert_block(genesis.try_recover().unwrap()).unwrap();
1009 provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
1010 provider
1011 .static_file_provider()
1012 .latest_writer(StaticFileSegment::Headers)
1013 .unwrap()
1014 .commit()
1015 .unwrap();
1016 {
1017 let static_file_provider = provider.static_file_provider();
1018 let mut receipts_writer =
1019 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1020 receipts_writer.increment_block(0).unwrap();
1021 receipts_writer.commit().unwrap();
1022 }
1023 provider.commit().unwrap();
1024
1025 let code = hex!("5a465a905090036002900360015500");
1027 let balance = U256::from(0x3635c9adc5dea00000u128);
1028 let code_hash = keccak256(code);
1029 let provider = factory.provider_rw().unwrap();
1031
1032 let db_tx = provider.tx_ref();
1033 let acc1 = address!("0x1000000000000000000000000000000000000000");
1034 let acc1_info = Account { nonce: 0, balance: U256::ZERO, bytecode_hash: Some(code_hash) };
1035 let acc2 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1036 let acc2_info = Account { nonce: 0, balance, bytecode_hash: None };
1037
1038 db_tx.put::<tables::PlainAccountState>(acc1, acc1_info).unwrap();
1039 db_tx.put::<tables::PlainAccountState>(acc2, acc2_info).unwrap();
1040 db_tx.put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.to_vec().into())).unwrap();
1041 provider.commit().unwrap();
1042
1043 let mut provider = factory.database_provider_rw().unwrap();
1045
1046 let modes = [None, Some(PruneModes::default())];
1049 let random_filter = ReceiptsLogPruneConfig(BTreeMap::from([(
1050 Address::random(),
1051 PruneMode::Before(100000),
1052 )]));
1053
1054 for mut mode in modes {
1056 if let Some(mode) = &mut mode {
1057 mode.receipts_log_filter = random_filter.clone();
1059 }
1060
1061 let mut execution_stage = stage();
1063 provider.set_prune_modes(mode.clone().unwrap_or_default());
1064
1065 let result = execution_stage.execute(&provider, input).unwrap();
1066 provider.commit().unwrap();
1067
1068 provider = factory.database_provider_rw().unwrap();
1070 let mut stage = stage();
1071 provider.set_prune_modes(mode.clone().unwrap_or_default());
1072
1073 let result = stage
1074 .unwind(
1075 &provider,
1076 UnwindInput { checkpoint: result.checkpoint, unwind_to: 0, bad_block: None },
1077 )
1078 .unwrap();
1079
1080 provider.static_file_provider().commit().unwrap();
1081
1082 assert_matches!(result, UnwindOutput {
1083 checkpoint: StageCheckpoint {
1084 block_number: 0,
1085 stage_checkpoint: Some(StageUnitCheckpoint::Execution(ExecutionCheckpoint {
1086 block_range: CheckpointBlockRange {
1087 from: 1,
1088 to: 1,
1089 },
1090 progress: EntitiesCheckpoint {
1091 processed: 0,
1092 total
1093 }
1094 }))
1095 }
1096 } if total == block.gas_used);
1097
1098 assert!(matches!(provider.basic_account(&acc1), Ok(Some(acc)) if acc == acc1_info));
1100 assert!(matches!(provider.basic_account(&acc2), Ok(Some(acc)) if acc == acc2_info));
1101
1102 let miner_acc = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
1103 assert!(matches!(provider.basic_account(&miner_acc), Ok(None)));
1104
1105 assert!(matches!(provider.receipt(0), Ok(None)));
1106 }
1107 }
1108
1109 #[tokio::test]
1110 async fn test_selfdestruct() {
1111 let test_db = TestStageDB::default();
1112 let provider = test_db.factory.database_provider_rw().unwrap();
1113 let input = ExecInput { target: Some(1), checkpoint: None };
1114 let mut genesis_rlp = hex!("f901f8f901f3a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0c9ceb8372c88cb461724d8d3d87e8b933f6fc5f679d4841800e662f4428ffd0da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080830f4240808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
1115 let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
1116 let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice();
1117 let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
1118 provider.insert_block(genesis.try_recover().unwrap()).unwrap();
1119 provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
1120 provider
1121 .static_file_provider()
1122 .latest_writer(StaticFileSegment::Headers)
1123 .unwrap()
1124 .commit()
1125 .unwrap();
1126 {
1127 let static_file_provider = provider.static_file_provider();
1128 let mut receipts_writer =
1129 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1130 receipts_writer.increment_block(0).unwrap();
1131 receipts_writer.commit().unwrap();
1132 }
1133 provider.commit().unwrap();
1134
1135 let caller_address = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1137 let destroyed_address = address!("0x095e7baea6a6c7c4c2dfeb977efac326af552d87");
1138 let beneficiary_address = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
1139
1140 let code = hex!("73095e7baea6a6c7c4c2dfeb977efac326af552d8731ff00");
1141 let balance = U256::from(0x0de0b6b3a7640000u64);
1142 let code_hash = keccak256(code);
1143
1144 let caller_info = Account { nonce: 0, balance, bytecode_hash: None };
1146 let destroyed_info =
1147 Account { nonce: 0, balance: U256::ZERO, bytecode_hash: Some(code_hash) };
1148
1149 let provider = test_db.factory.provider_rw().unwrap();
1151 provider.tx_ref().put::<tables::PlainAccountState>(caller_address, caller_info).unwrap();
1152 provider
1153 .tx_ref()
1154 .put::<tables::PlainAccountState>(destroyed_address, destroyed_info)
1155 .unwrap();
1156 provider
1157 .tx_ref()
1158 .put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.to_vec().into()))
1159 .unwrap();
1160 provider
1162 .tx_ref()
1163 .put::<tables::PlainStorageState>(
1164 destroyed_address,
1165 StorageEntry { key: B256::ZERO, value: U256::ZERO },
1166 )
1167 .unwrap();
1168 provider
1169 .tx_ref()
1170 .put::<tables::PlainStorageState>(
1171 destroyed_address,
1172 StorageEntry { key: B256::with_last_byte(1), value: U256::from(1u64) },
1173 )
1174 .unwrap();
1175
1176 provider.commit().unwrap();
1177
1178 let provider = test_db.factory.database_provider_rw().unwrap();
1180 let mut execution_stage = stage();
1181 let _ = execution_stage.execute(&provider, input).unwrap();
1182 provider.commit().unwrap();
1183
1184 let provider = test_db.factory.database_provider_rw().unwrap();
1186 assert!(matches!(provider.basic_account(&destroyed_address), Ok(None)));
1187
1188 assert!(matches!(
1189 provider.tx_ref().get::<tables::PlainStorageState>(destroyed_address),
1190 Ok(None)
1191 ));
1192 drop(provider);
1194 let plain_accounts = test_db.table::<tables::PlainAccountState>().unwrap();
1195 let plain_storage = test_db.table::<tables::PlainStorageState>().unwrap();
1196
1197 assert_eq!(
1198 plain_accounts,
1199 vec![
1200 (
1201 beneficiary_address,
1202 Account {
1203 nonce: 0,
1204 balance: U256::from(0x1bc16d674eca30a0u64),
1205 bytecode_hash: None
1206 }
1207 ),
1208 (
1209 caller_address,
1210 Account {
1211 nonce: 1,
1212 balance: U256::from(0xde0b6b3a761cf60u64),
1213 bytecode_hash: None
1214 }
1215 )
1216 ]
1217 );
1218 assert!(plain_storage.is_empty());
1219
1220 let account_changesets = test_db.table::<tables::AccountChangeSets>().unwrap();
1221 let storage_changesets = test_db.table::<tables::StorageChangeSets>().unwrap();
1222
1223 assert_eq!(
1224 account_changesets,
1225 vec![
1226 (
1227 block.number,
1228 AccountBeforeTx { address: destroyed_address, info: Some(destroyed_info) },
1229 ),
1230 (block.number, AccountBeforeTx { address: beneficiary_address, info: None }),
1231 (
1232 block.number,
1233 AccountBeforeTx { address: caller_address, info: Some(caller_info) }
1234 ),
1235 ]
1236 );
1237
1238 assert_eq!(
1239 storage_changesets,
1240 vec![
1241 (
1242 (block.number, destroyed_address).into(),
1243 StorageEntry { key: B256::ZERO, value: U256::ZERO }
1244 ),
1245 (
1246 (block.number, destroyed_address).into(),
1247 StorageEntry { key: B256::with_last_byte(1), value: U256::from(1u64) }
1248 )
1249 ]
1250 );
1251 }
1252
1253 #[test]
1254 fn test_ensure_consistency_with_skipped_receipts() {
1255 let factory = create_test_provider_factory();
1260 factory.set_storage_settings_cache(
1261 StorageSettings::legacy().with_receipts_in_static_files(true),
1262 );
1263
1264 let provider_rw = factory.database_provider_rw().unwrap();
1266 let mut rng = generators::rng();
1267 let genesis = generators::random_block(&mut rng, 0, Default::default());
1268 provider_rw.insert_block(genesis.try_recover().unwrap()).expect("failed to insert genesis");
1269 let block = generators::random_block(
1270 &mut rng,
1271 1,
1272 generators::BlockParams { tx_count: Some(2), ..Default::default() },
1273 );
1274 provider_rw.insert_block(block.try_recover().unwrap()).expect("failed to insert block");
1275
1276 let static_file_provider = provider_rw.static_file_provider();
1277 static_file_provider.latest_writer(StaticFileSegment::Headers).unwrap().commit().unwrap();
1278
1279 {
1282 let mut receipts_writer =
1283 static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1284 receipts_writer.increment_block(0).unwrap();
1285 receipts_writer.increment_block(1).unwrap();
1286 receipts_writer.commit().unwrap();
1287 } provider_rw.commit().expect("failed to commit");
1290
1291 assert_eq!(
1293 factory
1294 .static_file_provider()
1295 .get_highest_static_file_block(StaticFileSegment::Receipts),
1296 Some(1)
1297 );
1298 assert_eq!(
1299 factory.static_file_provider().get_highest_static_file_tx(StaticFileSegment::Receipts),
1300 None
1301 );
1302
1303 let stage = stage();
1305
1306 let provider = factory.provider().unwrap();
1310 stage
1311 .ensure_consistency(&provider, 1, None)
1312 .expect("ensure_consistency should succeed when receipts are intentionally skipped");
1313 }
1314}