reth_stages/stages/
execution.rs

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/// The execution stage executes all transactions and
40/// update history indexes.
41///
42/// Input tables:
43/// - [`tables::CanonicalHeaders`] get next block to execute.
44/// - [`tables::Headers`] get for revm environment variables.
45/// - [`tables::HeaderTerminalDifficulties`]
46/// - [`tables::BlockBodyIndices`] to get tx number
47/// - [`tables::Transactions`] to execute
48///
49/// For state access [`LatestStateProviderRef`] provides us latest state and history state
50/// For latest most recent state [`LatestStateProviderRef`] would need (Used for execution Stage):
51/// - [`tables::PlainAccountState`]
52/// - [`tables::Bytecodes`]
53/// - [`tables::PlainStorageState`]
54///
55/// Tables updated after state finishes execution:
56/// - [`tables::PlainAccountState`]
57/// - [`tables::PlainStorageState`]
58/// - [`tables::Bytecodes`]
59/// - [`tables::AccountChangeSets`]
60/// - [`tables::StorageChangeSets`]
61///
62/// For unwinds we are accessing:
63/// - [`tables::BlockBodyIndices`] get tx index to know what needs to be unwinded
64/// - [`tables::AccountsHistory`] to remove change set and apply old values to
65/// - [`tables::PlainAccountState`] [`tables::StoragesHistory`] to remove change set and apply old
66///   values to [`tables::PlainStorageState`]
67// false positive, we cannot derive it if !DB: Debug.
68#[derive(Debug)]
69pub struct ExecutionStage<E>
70where
71    E: BlockExecutorProvider,
72{
73    /// The stage's internal block executor
74    executor_provider: E,
75    /// The consensus instance for validating blocks.
76    consensus: Arc<dyn FullConsensus<E::Primitives, Error = ConsensusError>>,
77    /// The consensu
78    /// The commit thresholds of the execution stage.
79    thresholds: ExecutionStageThresholds,
80    /// The highest threshold (in number of blocks) for switching between incremental
81    /// and full calculations across [`super::MerkleStage`], [`super::AccountHashingStage`] and
82    /// [`super::StorageHashingStage`]. This is required to figure out if can prune or not
83    /// changesets on subsequent pipeline runs.
84    external_clean_threshold: u64,
85    /// Input for the post execute commit hook.
86    /// Set after every [`ExecutionStage::execute`] and cleared after
87    /// [`ExecutionStage::post_execute_commit`].
88    post_execute_commit_input: Option<Chain<E::Primitives>>,
89    /// Input for the post unwind commit hook.
90    /// Set after every [`ExecutionStage::unwind`] and cleared after
91    /// [`ExecutionStage::post_unwind_commit`].
92    post_unwind_commit_input: Option<Chain<E::Primitives>>,
93    /// Handle to communicate with `ExEx` manager.
94    exex_manager_handle: ExExManagerHandle<E::Primitives>,
95    /// Executor metrics.
96    metrics: ExecutorMetrics,
97}
98
99impl<E> ExecutionStage<E>
100where
101    E: BlockExecutorProvider,
102{
103    /// Create new execution stage with specified config.
104    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    /// Create an execution stage with the provided executor.
124    ///
125    /// The commit threshold will be set to [`MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD`].
126    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    /// Create new instance of [`ExecutionStage`] from configuration.
140    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    /// Returns whether we can perform pruning of [`tables::AccountChangeSets`] and
156    /// [`tables::StorageChangeSets`].
157    ///
158    /// This function verifies whether the [`super::MerkleStage`] or Hashing stages will run from
159    /// scratch. If at least one stage isn't starting anew, it implies that pruning of
160    /// changesets cannot occur. This is determined by checking the highest clean threshold
161    /// (`self.external_clean_threshold`) across the stages.
162    ///
163    /// Given that `start_block` changes with each checkpoint, it's necessary to inspect
164    /// [`tables::AccountsTrie`] to ensure that [`super::MerkleStage`] hasn't
165    /// been previously executed.
166    fn can_prune_changesets(
167        &self,
168        provider: impl StatsReader,
169        start_block: u64,
170        max_block: u64,
171    ) -> Result<bool, StageError> {
172        // We can only prune changesets if we're not executing MerkleStage from scratch (by
173        // threshold or first-sync)
174        Ok(max_block - start_block > self.external_clean_threshold ||
175            provider.count_entries::<tables::AccountsTrie>()?.is_zero())
176    }
177
178    /// Performs consistency check on static files.
179    ///
180    /// This function compares the highest receipt number recorded in the database with that in the
181    /// static file to detect any discrepancies due to unexpected shutdowns or database rollbacks.
182    /// **If the height in the static file is higher**, it rolls back (unwinds) the static file.
183    /// **Conversely, if the height in the database is lower**, it triggers a rollback in the
184    /// database (by returning [`StageError`]) until the heights in both the database and static
185    /// file match.
186    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 there's any receipts pruning configured, receipts are written directly to database and
196        // inconsistencies are expected.
197        if provider.prune_modes_ref().has_receipts_pruning() {
198            return Ok(())
199        }
200
201        // Get next expected receipt number
202        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        // Get next expected receipt number in static files
208        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        // Check if we had any unexpected shutdown after committing to static files, but
214        // NOT committing to database.
215        match next_static_file_receipt_num.cmp(&next_receipt_num) {
216            // It can be equal when it's a chain of empty blocks, but we still need to update the
217            // last block in the range.
218            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                // Since this is a database <-> static file inconsistency, we commit the change
224                // straight away.
225                static_file_producer.commit()?;
226            }
227            Ordering::Less => {
228                // If we are already in the process of unwind, this might be fine because we will
229                // fix the inconsistency right away.
230                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                        // This means we need a deeper unwind.
238                    } 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    /// Return the id of the stage
270    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    /// Execute the stage
285    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        // Progress tracking
300        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        // Execute block range
320        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            // Fetch the block
327            let fetch_block_start = Instant::now();
328
329            // we need the block's transactions but we don't need the transaction hashes
330            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            // Configure the executor to use the current state.
339            trace!(target: "sync::stages::execution", number = block_number, txs = block.body().transactions().len(), "Executing block");
340
341            // Execute the block
342            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            // Log execution throughput
362            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 we have ExExes we need to save the block in memory for later
381            if self.exex_manager_handle.has_exexs() {
382                blocks.push(block);
383            }
384
385            // Check if we should commit now
386            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        // prepare execution output for writing
397        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        // log the gas per second for the range we just executed
406        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        // Prepare the input for post execute commit hook, where an `ExExNotification` will be sent.
415        //
416        // Note: Since we only write to `blocks` if there are any ExExes, we don't need to perform
417        // the `has_exexs` check here as well
418        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                // Not processing the previous post execute commit input is a critical error, as it
424                // means that we didn't send the notification to ExExes
425                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            // Iterate over all reverts and clear them if pruning is configured.
437            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 both account history and storage history pruning is configured, clear reverts
445                // for this block.
446                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        // write output
459        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        // NOTE: We can ignore the error here, since an error means that the channel is closed,
483        // which means the manager has died, which then in turn means the node is shutting down.
484        let _ = self.exex_manager_handle.send(
485            ExExNotificationSource::Pipeline,
486            ExExNotification::ChainCommitted { new: Arc::new(chain) },
487        );
488
489        Ok(())
490    }
491
492    /// Unwind the stage.
493    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        // Unwind account and storage changesets, as well as receipts.
509        //
510        // This also updates `PlainStorageState` and `PlainAccountState`.
511        let bundle_state_with_receipts =
512            provider.take_state_above(unwind_to, StorageLocation::Both)?;
513
514        // Prepare the input for post unwind commit hook, where an `ExExNotification` will be sent.
515        if self.exex_manager_handle.has_exexs() {
516            // Get the blocks for the unwound range.
517            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        // Update the checkpoint.
534        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        // NOTE: We can ignore the error here, since an error means that the channel is closed,
557        // which means the manager has died, which then in turn means the node is shutting down.
558        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        // If checkpoint block range fully matches our range,
575        // we take the previously used stage checkpoint as-is.
576        Some(stage_checkpoint @ ExecutionCheckpoint { block_range, .. })
577            if block_range == CheckpointBlockRange::from(start_block..=max_block) =>
578        {
579            stage_checkpoint
580        }
581        // If checkpoint block range precedes our range seamlessly, we take the previously used
582        // stage checkpoint and add the amount of gas from our range to the checkpoint total.
583        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        // If checkpoint block range ends on the same block as our range, we take the previously
594        // used stage checkpoint.
595        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        // If there's any other non-empty checkpoint, we calculate the remaining amount of total gas
604        // to be processed not including the checkpoint range.
605        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        // Otherwise, we recalculate the whole stage checkpoint including the amount of gas
618        // already processed, if there's any.
619        _ => {
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        // insert pre state
873        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        // execute
897
898        // If there is a pruning configuration, then it's forced to use the database.
899        // This way we test both cases.
900        let modes = [None, Some(PruneModes::none())];
901        let random_filter = ReceiptsLogPruneConfig(BTreeMap::from([(
902            Address::random(),
903            PruneMode::Distance(100000),
904        )]));
905
906        // Tests node with database and node with static files
907        for mut mode in modes {
908            let mut provider = factory.database_provider_rw().unwrap();
909
910            if let Some(mode) = &mut mode {
911                // Simulating a full node where we write receipts to database
912                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            // check post state
941            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 accounts
958            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 storage
968            // Get on dupsort would return only first value. This is good enough for this test.
969            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        // variables
1015        let code = hex!("5a465a905090036002900360015500");
1016        let balance = U256::from(0x3635c9adc5dea00000u128);
1017        let code_hash = keccak256(code);
1018        // pre state
1019        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        // execute
1033        let mut provider = factory.database_provider_rw().unwrap();
1034
1035        // If there is a pruning configuration, then it's forced to use the database.
1036        // This way we test both cases.
1037        let modes = [None, Some(PruneModes::none())];
1038        let random_filter = ReceiptsLogPruneConfig(BTreeMap::from([(
1039            Address::random(),
1040            PruneMode::Before(100000),
1041        )]));
1042
1043        // Tests node with database and node with static files
1044        for mut mode in modes {
1045            if let Some(mode) = &mut mode {
1046                // Simulating a full node where we write receipts to database
1047                mode.receipts_log_filter = random_filter.clone();
1048            }
1049
1050            // Test Execution
1051            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            // Test Unwind
1058            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 unwind stage
1086            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        // variables
1123        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        // pre state
1132        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        // set account
1137        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        // set storage to check when account gets destroyed.
1148        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        // execute
1166        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        // assert unwind stage
1172        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        // drops tx so that it returns write privilege to test_tx
1180        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}