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