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