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