reth_stages/stages/
execution.rs

1use crate::stages::MERKLE_STAGE_DEFAULT_INCREMENTAL_THRESHOLD;
2use alloy_consensus::BlockHeader;
3use alloy_primitives::BlockNumber;
4use num_traits::Zero;
5use reth_config::config::ExecutionConfig;
6use reth_consensus::{ConsensusError, FullConsensus};
7use reth_db::{static_file::HeaderMask, tables};
8use reth_evm::{execute::Executor, metrics::ExecutorMetrics, ConfigureEvm};
9use reth_execution_types::Chain;
10use reth_exex::{ExExManagerHandle, ExExNotification, ExExNotificationSource};
11use reth_primitives_traits::{format_gas_throughput, BlockBody, NodePrimitives};
12use reth_provider::{
13    providers::{StaticFileProvider, StaticFileWriter},
14    BlockHashReader, BlockReader, DBProvider, ExecutionOutcome, HeaderProvider,
15    LatestStateProviderRef, OriginalValuesKnown, ProviderError, StateWriter,
16    StaticFileProviderFactory, StatsReader, TransactionVariant,
17};
18use reth_revm::database::StateProviderDatabase;
19use reth_stages_api::{
20    BlockErrorKind, CheckpointBlockRange, EntitiesCheckpoint, ExecInput, ExecOutput,
21    ExecutionCheckpoint, ExecutionStageThresholds, Stage, StageCheckpoint, StageError, StageId,
22    UnwindInput, UnwindOutput,
23};
24use reth_static_file_types::StaticFileSegment;
25use std::{
26    cmp::Ordering,
27    ops::RangeInclusive,
28    sync::Arc,
29    task::{ready, Context, Poll},
30    time::{Duration, Instant},
31};
32use tracing::*;
33
34use super::missing_static_data_error;
35
36/// The execution stage executes all transactions and
37/// update history indexes.
38///
39/// Input tables:
40/// - [`tables::CanonicalHeaders`] get next block to execute.
41/// - [`tables::Headers`] get for revm environment variables.
42/// - [`tables::BlockBodyIndices`] to get tx number
43/// - [`tables::Transactions`] to execute
44///
45/// For state access [`LatestStateProviderRef`] provides us latest state and history state
46/// For latest most recent state [`LatestStateProviderRef`] would need (Used for execution Stage):
47/// - [`tables::PlainAccountState`]
48/// - [`tables::Bytecodes`]
49/// - [`tables::PlainStorageState`]
50///
51/// Tables updated after state finishes execution:
52/// - [`tables::PlainAccountState`]
53/// - [`tables::PlainStorageState`]
54/// - [`tables::Bytecodes`]
55/// - [`tables::AccountChangeSets`]
56/// - [`tables::StorageChangeSets`]
57///
58/// For unwinds we are accessing:
59/// - [`tables::BlockBodyIndices`] get tx index to know what needs to be unwinded
60/// - [`tables::AccountsHistory`] to remove change set and apply old values to
61/// - [`tables::PlainAccountState`] [`tables::StoragesHistory`] to remove change set and apply old
62///   values to [`tables::PlainStorageState`]
63// false positive, we cannot derive it if !DB: Debug.
64#[derive(Debug)]
65pub struct ExecutionStage<E>
66where
67    E: ConfigureEvm,
68{
69    /// The stage's internal block executor
70    evm_config: E,
71    /// The consensus instance for validating blocks.
72    consensus: Arc<dyn FullConsensus<E::Primitives, Error = ConsensusError>>,
73    /// The commit thresholds of the execution stage.
74    thresholds: ExecutionStageThresholds,
75    /// The highest threshold (in number of blocks) for switching between incremental
76    /// and full calculations across [`super::MerkleStage`], [`super::AccountHashingStage`] and
77    /// [`super::StorageHashingStage`]. This is required to figure out if can prune or not
78    /// changesets on subsequent pipeline runs.
79    external_clean_threshold: u64,
80    /// Input for the post execute commit hook.
81    /// Set after every [`ExecutionStage::execute`] and cleared after
82    /// [`ExecutionStage::post_execute_commit`].
83    post_execute_commit_input: Option<Chain<E::Primitives>>,
84    /// Input for the post unwind commit hook.
85    /// Set after every [`ExecutionStage::unwind`] and cleared after
86    /// [`ExecutionStage::post_unwind_commit`].
87    post_unwind_commit_input: Option<Chain<E::Primitives>>,
88    /// Handle to communicate with `ExEx` manager.
89    exex_manager_handle: ExExManagerHandle<E::Primitives>,
90    /// Executor metrics.
91    metrics: ExecutorMetrics,
92}
93
94impl<E> ExecutionStage<E>
95where
96    E: ConfigureEvm,
97{
98    /// Create new execution stage with specified config.
99    pub fn new(
100        evm_config: E,
101        consensus: Arc<dyn FullConsensus<E::Primitives, Error = ConsensusError>>,
102        thresholds: ExecutionStageThresholds,
103        external_clean_threshold: u64,
104        exex_manager_handle: ExExManagerHandle<E::Primitives>,
105    ) -> Self {
106        Self {
107            external_clean_threshold,
108            evm_config,
109            consensus,
110            thresholds,
111            post_execute_commit_input: None,
112            post_unwind_commit_input: None,
113            exex_manager_handle,
114            metrics: ExecutorMetrics::default(),
115        }
116    }
117
118    /// Create an execution stage with the provided executor.
119    ///
120    /// The commit threshold will be set to [`MERKLE_STAGE_DEFAULT_INCREMENTAL_THRESHOLD`].
121    pub fn new_with_executor(
122        evm_config: E,
123        consensus: Arc<dyn FullConsensus<E::Primitives, Error = ConsensusError>>,
124    ) -> Self {
125        Self::new(
126            evm_config,
127            consensus,
128            ExecutionStageThresholds::default(),
129            MERKLE_STAGE_DEFAULT_INCREMENTAL_THRESHOLD,
130            ExExManagerHandle::empty(),
131        )
132    }
133
134    /// Create new instance of [`ExecutionStage`] from configuration.
135    pub fn from_config(
136        evm_config: E,
137        consensus: Arc<dyn FullConsensus<E::Primitives, Error = ConsensusError>>,
138        config: ExecutionConfig,
139        external_clean_threshold: u64,
140    ) -> Self {
141        Self::new(
142            evm_config,
143            consensus,
144            config.into(),
145            external_clean_threshold,
146            ExExManagerHandle::empty(),
147        )
148    }
149
150    /// Returns whether we can perform pruning of [`tables::AccountChangeSets`] and
151    /// [`tables::StorageChangeSets`].
152    ///
153    /// This function verifies whether the [`super::MerkleStage`] or Hashing stages will run from
154    /// scratch. If at least one stage isn't starting anew, it implies that pruning of
155    /// changesets cannot occur. This is determined by checking the highest clean threshold
156    /// (`self.external_clean_threshold`) across the stages.
157    ///
158    /// Given that `start_block` changes with each checkpoint, it's necessary to inspect
159    /// [`tables::AccountsTrie`] to ensure that [`super::MerkleStage`] hasn't
160    /// been previously executed.
161    fn can_prune_changesets(
162        &self,
163        provider: impl StatsReader,
164        start_block: u64,
165        max_block: u64,
166    ) -> Result<bool, StageError> {
167        // We can only prune changesets if we're not executing MerkleStage from scratch (by
168        // threshold or first-sync)
169        Ok(max_block - start_block > self.external_clean_threshold ||
170            provider.count_entries::<tables::AccountsTrie>()?.is_zero())
171    }
172
173    /// Performs consistency check on static files.
174    ///
175    /// This function compares the highest receipt number recorded in the database with that in the
176    /// static file to detect any discrepancies due to unexpected shutdowns or database rollbacks.
177    /// **If the height in the static file is higher**, it rolls back (unwinds) the static file.
178    /// **Conversely, if the height in the database is lower**, it triggers a rollback in the
179    /// database (by returning [`StageError`]) until the heights in both the database and static
180    /// file match.
181    fn ensure_consistency<Provider>(
182        &self,
183        provider: &Provider,
184        checkpoint: u64,
185        unwind_to: Option<u64>,
186    ) -> Result<(), StageError>
187    where
188        Provider: StaticFileProviderFactory + DBProvider + BlockReader + HeaderProvider,
189    {
190        // If there's any receipts pruning configured, receipts are written directly to database and
191        // inconsistencies are expected.
192        if provider.prune_modes_ref().has_receipts_pruning() {
193            return Ok(())
194        }
195
196        // Get next expected receipt number
197        let next_receipt_num =
198            provider.block_body_indices(checkpoint)?.map(|b| b.next_tx_num()).unwrap_or(0);
199
200        let static_file_provider = provider.static_file_provider();
201
202        // Get next expected receipt number in static files
203        let next_static_file_receipt_num = static_file_provider
204            .get_highest_static_file_tx(StaticFileSegment::Receipts)
205            .map(|num| num + 1)
206            .unwrap_or(0);
207
208        // Check if we had any unexpected shutdown after committing to static files, but
209        // NOT committing to database.
210        match next_static_file_receipt_num.cmp(&next_receipt_num) {
211            // It can be equal when it's a chain of empty blocks, but we still need to update the
212            // last block in the range.
213            Ordering::Greater | Ordering::Equal => {
214                let mut static_file_producer =
215                    static_file_provider.latest_writer(StaticFileSegment::Receipts)?;
216                static_file_producer
217                    .prune_receipts(next_static_file_receipt_num - next_receipt_num, checkpoint)?;
218                // Since this is a database <-> static file inconsistency, we commit the change
219                // straight away.
220                static_file_producer.commit()?;
221            }
222            Ordering::Less => {
223                // If we are already in the process of unwind, this might be fine because we will
224                // fix the inconsistency right away.
225                if let Some(unwind_to) = unwind_to {
226                    let next_receipt_num_after_unwind = provider
227                        .block_body_indices(unwind_to)?
228                        .map(|b| b.next_tx_num())
229                        .ok_or(ProviderError::BlockBodyIndicesNotFound(unwind_to))?;
230
231                    if next_receipt_num_after_unwind > next_static_file_receipt_num {
232                        // This means we need a deeper unwind.
233                    } else {
234                        return Ok(())
235                    }
236                }
237
238                return Err(missing_static_data_error(
239                    next_static_file_receipt_num.saturating_sub(1),
240                    &static_file_provider,
241                    provider,
242                    StaticFileSegment::Receipts,
243                )?)
244            }
245        }
246
247        Ok(())
248    }
249}
250
251impl<E, Provider> Stage<Provider> for ExecutionStage<E>
252where
253    E: ConfigureEvm,
254    Provider: DBProvider
255        + BlockReader<
256            Block = <E::Primitives as NodePrimitives>::Block,
257            Header = <E::Primitives as NodePrimitives>::BlockHeader,
258        > + StaticFileProviderFactory<
259            Primitives: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
260        > + StatsReader
261        + BlockHashReader
262        + StateWriter<Receipt = <E::Primitives as NodePrimitives>::Receipt>,
263{
264    /// Return the id of the stage
265    fn id(&self) -> StageId {
266        StageId::Execution
267    }
268
269    fn poll_execute_ready(
270        &mut self,
271        cx: &mut Context<'_>,
272        _: ExecInput,
273    ) -> Poll<Result<(), StageError>> {
274        ready!(self.exex_manager_handle.poll_ready(cx));
275
276        Poll::Ready(Ok(()))
277    }
278
279    /// Execute the stage
280    fn execute(&mut self, provider: &Provider, input: ExecInput) -> Result<ExecOutput, StageError> {
281        if input.target_reached() {
282            return Ok(ExecOutput::done(input.checkpoint()))
283        }
284
285        let start_block = input.next_block();
286        let max_block = input.target();
287        let static_file_provider = provider.static_file_provider();
288
289        self.ensure_consistency(provider, input.checkpoint().block_number, None)?;
290
291        let db = StateProviderDatabase(LatestStateProviderRef::new(provider));
292        let mut executor = self.evm_config.batch_executor(db);
293
294        // Progress tracking
295        let mut stage_progress = start_block;
296        let mut stage_checkpoint = execution_checkpoint(
297            &static_file_provider,
298            start_block,
299            max_block,
300            input.checkpoint(),
301        )?;
302
303        let mut fetch_block_duration = Duration::default();
304        let mut execution_duration = Duration::default();
305
306        let mut last_block = start_block;
307        let mut last_execution_duration = Duration::default();
308        let mut last_cumulative_gas = 0;
309        let mut last_log_instant = Instant::now();
310        let log_duration = Duration::from_secs(10);
311
312        debug!(target: "sync::stages::execution", start = start_block, end = max_block, "Executing range");
313
314        // Execute block range
315        let mut cumulative_gas = 0;
316        let batch_start = Instant::now();
317
318        let mut blocks = Vec::new();
319        let mut results = Vec::new();
320        for block_number in start_block..=max_block {
321            // Fetch the block
322            let fetch_block_start = Instant::now();
323
324            // we need the block's transactions but we don't need the transaction hashes
325            let block = provider
326                .recovered_block(block_number.into(), TransactionVariant::NoHash)?
327                .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?;
328
329            fetch_block_duration += fetch_block_start.elapsed();
330
331            cumulative_gas += block.header().gas_used();
332
333            // Configure the executor to use the current state.
334            trace!(target: "sync::stages::execution", number = block_number, txs = block.body().transactions().len(), "Executing block");
335
336            // Execute the block
337            let execute_start = Instant::now();
338
339            let result = self.metrics.metered_one(&block, |input| {
340                executor.execute_one(input).map_err(|error| StageError::Block {
341                    block: Box::new(block.block_with_parent()),
342                    error: BlockErrorKind::Execution(error),
343                })
344            })?;
345
346            if let Err(err) = self.consensus.validate_block_post_execution(&block, &result) {
347                return Err(StageError::Block {
348                    block: Box::new(block.block_with_parent()),
349                    error: BlockErrorKind::Validation(err),
350                })
351            }
352            results.push(result);
353
354            execution_duration += execute_start.elapsed();
355
356            // Log execution throughput
357            if last_log_instant.elapsed() >= log_duration {
358                info!(
359                    target: "sync::stages::execution",
360                    start = last_block,
361                    end = block_number,
362                    throughput = format_gas_throughput(cumulative_gas - last_cumulative_gas, execution_duration - last_execution_duration),
363                    "Executed block range"
364                );
365
366                last_block = block_number + 1;
367                last_execution_duration = execution_duration;
368                last_cumulative_gas = cumulative_gas;
369                last_log_instant = Instant::now();
370            }
371
372            stage_progress = block_number;
373            stage_checkpoint.progress.processed += block.header().gas_used();
374
375            // If we have ExExes we need to save the block in memory for later
376            if self.exex_manager_handle.has_exexs() {
377                blocks.push(block);
378            }
379
380            // Check if we should commit now
381            if self.thresholds.is_end_of_batch(
382                block_number - start_block,
383                executor.size_hint() as u64,
384                cumulative_gas,
385                batch_start.elapsed(),
386            ) {
387                break
388            }
389        }
390
391        // prepare execution output for writing
392        let time = Instant::now();
393        let mut state = ExecutionOutcome::from_blocks(
394            start_block,
395            executor.into_state().take_bundle(),
396            results,
397        );
398        let write_preparation_duration = time.elapsed();
399
400        // log the gas per second for the range we just executed
401        debug!(
402            target: "sync::stages::execution",
403            start = start_block,
404            end = stage_progress,
405            throughput = format_gas_throughput(cumulative_gas, execution_duration),
406            "Finished executing block range"
407        );
408
409        // Prepare the input for post execute commit hook, where an `ExExNotification` will be sent.
410        //
411        // Note: Since we only write to `blocks` if there are any ExExes, we don't need to perform
412        // the `has_exexs` check here as well
413        if !blocks.is_empty() {
414            let previous_input =
415                self.post_execute_commit_input.replace(Chain::new(blocks, state.clone(), None));
416
417            if previous_input.is_some() {
418                // Not processing the previous post execute commit input is a critical error, as it
419                // means that we didn't send the notification to ExExes
420                return Err(StageError::PostExecuteCommit(
421                    "Previous post execute commit input wasn't processed",
422                ))
423            }
424        }
425
426        let time = Instant::now();
427
428        if self.can_prune_changesets(provider, start_block, max_block)? {
429            let prune_modes = provider.prune_modes_ref();
430
431            // Iterate over all reverts and clear them if pruning is configured.
432            for block_number in start_block..=max_block {
433                let Some(reverts) =
434                    state.bundle.reverts.get_mut((block_number - start_block) as usize)
435                else {
436                    break
437                };
438
439                // If both account history and storage history pruning is configured, clear reverts
440                // for this block.
441                if prune_modes
442                    .account_history
443                    .is_some_and(|m| m.should_prune(block_number, max_block)) &&
444                    prune_modes
445                        .storage_history
446                        .is_some_and(|m| m.should_prune(block_number, max_block))
447                {
448                    reverts.clear();
449                }
450            }
451        }
452
453        // write output
454        provider.write_state(&state, OriginalValuesKnown::Yes)?;
455
456        let db_write_duration = time.elapsed();
457        debug!(
458            target: "sync::stages::execution",
459            block_fetch = ?fetch_block_duration,
460            execution = ?execution_duration,
461            write_preparation = ?write_preparation_duration,
462            write = ?db_write_duration,
463            "Execution time"
464        );
465
466        let done = stage_progress == max_block;
467        Ok(ExecOutput {
468            checkpoint: StageCheckpoint::new(stage_progress)
469                .with_execution_stage_checkpoint(stage_checkpoint),
470            done,
471        })
472    }
473
474    fn post_execute_commit(&mut self) -> Result<(), StageError> {
475        let Some(chain) = self.post_execute_commit_input.take() else { return Ok(()) };
476
477        // NOTE: We can ignore the error here, since an error means that the channel is closed,
478        // which means the manager has died, which then in turn means the node is shutting down.
479        let _ = self.exex_manager_handle.send(
480            ExExNotificationSource::Pipeline,
481            ExExNotification::ChainCommitted { new: Arc::new(chain) },
482        );
483
484        Ok(())
485    }
486
487    /// Unwind the stage.
488    fn unwind(
489        &mut self,
490        provider: &Provider,
491        input: UnwindInput,
492    ) -> Result<UnwindOutput, StageError> {
493        let (range, unwind_to, _) =
494            input.unwind_block_range_with_threshold(self.thresholds.max_blocks.unwrap_or(u64::MAX));
495        if range.is_empty() {
496            return Ok(UnwindOutput {
497                checkpoint: input.checkpoint.with_block_number(input.unwind_to),
498            })
499        }
500
501        self.ensure_consistency(provider, input.checkpoint.block_number, Some(unwind_to))?;
502
503        // Unwind account and storage changesets, as well as receipts.
504        //
505        // This also updates `PlainStorageState` and `PlainAccountState`.
506        let bundle_state_with_receipts = provider.take_state_above(unwind_to)?;
507
508        // Prepare the input for post unwind commit hook, where an `ExExNotification` will be sent.
509        if self.exex_manager_handle.has_exexs() {
510            // Get the blocks for the unwound range.
511            let blocks = provider.recovered_block_range(range.clone())?;
512            let previous_input = self.post_unwind_commit_input.replace(Chain::new(
513                blocks,
514                bundle_state_with_receipts,
515                None,
516            ));
517
518            debug_assert!(
519                previous_input.is_none(),
520                "Previous post unwind commit input wasn't processed"
521            );
522            if let Some(previous_input) = previous_input {
523                tracing::debug!(target: "sync::stages::execution", ?previous_input, "Previous post unwind commit input wasn't processed");
524            }
525        }
526
527        // Update the checkpoint.
528        let mut stage_checkpoint = input.checkpoint.execution_stage_checkpoint();
529        if let Some(stage_checkpoint) = stage_checkpoint.as_mut() {
530            for block_number in range {
531                stage_checkpoint.progress.processed -= provider
532                    .header_by_number(block_number)?
533                    .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?
534                    .gas_used();
535            }
536        }
537        let checkpoint = if let Some(stage_checkpoint) = stage_checkpoint {
538            StageCheckpoint::new(unwind_to).with_execution_stage_checkpoint(stage_checkpoint)
539        } else {
540            StageCheckpoint::new(unwind_to)
541        };
542
543        Ok(UnwindOutput { checkpoint })
544    }
545
546    fn post_unwind_commit(&mut self) -> Result<(), StageError> {
547        let Some(chain) = self.post_unwind_commit_input.take() else { return Ok(()) };
548
549        // NOTE: We can ignore the error here, since an error means that the channel is closed,
550        // which means the manager has died, which then in turn means the node is shutting down.
551        let _ = self.exex_manager_handle.send(
552            ExExNotificationSource::Pipeline,
553            ExExNotification::ChainReverted { old: Arc::new(chain) },
554        );
555
556        Ok(())
557    }
558}
559
560fn execution_checkpoint<N>(
561    provider: &StaticFileProvider<N>,
562    start_block: BlockNumber,
563    max_block: BlockNumber,
564    checkpoint: StageCheckpoint,
565) -> Result<ExecutionCheckpoint, ProviderError>
566where
567    N: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
568{
569    Ok(match checkpoint.execution_stage_checkpoint() {
570        // If checkpoint block range fully matches our range,
571        // we take the previously used stage checkpoint as-is.
572        Some(stage_checkpoint @ ExecutionCheckpoint { block_range, .. })
573            if block_range == CheckpointBlockRange::from(start_block..=max_block) =>
574        {
575            stage_checkpoint
576        }
577        // If checkpoint block range precedes our range seamlessly, we take the previously used
578        // stage checkpoint and add the amount of gas from our range to the checkpoint total.
579        Some(ExecutionCheckpoint {
580            block_range: CheckpointBlockRange { to, .. },
581            progress: EntitiesCheckpoint { processed, total },
582        }) if to == start_block - 1 => ExecutionCheckpoint {
583            block_range: CheckpointBlockRange { from: start_block, to: max_block },
584            progress: EntitiesCheckpoint {
585                processed,
586                total: total + calculate_gas_used_from_headers(provider, start_block..=max_block)?,
587            },
588        },
589        // If checkpoint block range ends on the same block as our range, we take the previously
590        // used stage checkpoint.
591        Some(ExecutionCheckpoint { block_range: CheckpointBlockRange { to, .. }, progress })
592            if to == max_block =>
593        {
594            ExecutionCheckpoint {
595                block_range: CheckpointBlockRange { from: start_block, to: max_block },
596                progress,
597            }
598        }
599        // If there's any other non-empty checkpoint, we calculate the remaining amount of total gas
600        // to be processed not including the checkpoint range.
601        Some(ExecutionCheckpoint { progress: EntitiesCheckpoint { processed, .. }, .. }) => {
602            let after_checkpoint_block_number =
603                calculate_gas_used_from_headers(provider, checkpoint.block_number + 1..=max_block)?;
604
605            ExecutionCheckpoint {
606                block_range: CheckpointBlockRange { from: start_block, to: max_block },
607                progress: EntitiesCheckpoint {
608                    processed,
609                    total: processed + after_checkpoint_block_number,
610                },
611            }
612        }
613        // Otherwise, we recalculate the whole stage checkpoint including the amount of gas
614        // already processed, if there's any.
615        _ => {
616            let processed = calculate_gas_used_from_headers(provider, 0..=start_block - 1)?;
617
618            ExecutionCheckpoint {
619                block_range: CheckpointBlockRange { from: start_block, to: max_block },
620                progress: EntitiesCheckpoint {
621                    processed,
622                    total: processed +
623                        calculate_gas_used_from_headers(provider, start_block..=max_block)?,
624                },
625            }
626        }
627    })
628}
629
630/// Calculates the total amount of gas used from the headers in the given range.
631pub fn calculate_gas_used_from_headers<N>(
632    provider: &StaticFileProvider<N>,
633    range: RangeInclusive<BlockNumber>,
634) -> Result<u64, ProviderError>
635where
636    N: NodePrimitives<BlockHeader: reth_db_api::table::Value>,
637{
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<N::BlockHeader>>(number.into()),
648    )? {
649        let entry = entry?;
650        gas_total += entry.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::{stages::MERKLE_STAGE_DEFAULT_REBUILD_THRESHOLD, test_utils::TestStageDB};
663    use alloy_primitives::{address, hex_literal::hex, keccak256, 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_ethereum::EthEvmConfig;
674    use reth_primitives_traits::{Account, Bytecode, SealedBlock, StorageEntry};
675    use reth_provider::{
676        test_utils::create_test_provider_factory, AccountReader, BlockWriter,
677        DatabaseProviderFactory, ReceiptProvider, StaticFileProviderFactory,
678    };
679    use reth_prune::PruneModes;
680    use reth_stages_api::StageUnitCheckpoint;
681
682    fn stage() -> ExecutionStage<EthEvmConfig> {
683        let evm_config =
684            EthEvmConfig::new(Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build()));
685        let consensus = Arc::new(EthBeaconConsensus::new(Arc::new(
686            ChainSpecBuilder::mainnet().berlin_activated().build(),
687        )));
688        ExecutionStage::new(
689            evm_config,
690            consensus,
691            ExecutionStageThresholds {
692                max_blocks: Some(100),
693                max_changes: None,
694                max_cumulative_gas: None,
695                max_duration: None,
696            },
697            MERKLE_STAGE_DEFAULT_REBUILD_THRESHOLD,
698            ExExManagerHandle::empty(),
699        )
700    }
701
702    #[test]
703    fn execution_checkpoint_matches() {
704        let factory = create_test_provider_factory();
705
706        let previous_stage_checkpoint = ExecutionCheckpoint {
707            block_range: CheckpointBlockRange { from: 0, to: 0 },
708            progress: EntitiesCheckpoint { processed: 1, total: 2 },
709        };
710        let previous_checkpoint = StageCheckpoint {
711            block_number: 0,
712            stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
713        };
714
715        let stage_checkpoint = execution_checkpoint(
716            &factory.static_file_provider(),
717            previous_stage_checkpoint.block_range.from,
718            previous_stage_checkpoint.block_range.to,
719            previous_checkpoint,
720        );
721
722        assert!(
723            matches!(stage_checkpoint, Ok(checkpoint) if checkpoint == previous_stage_checkpoint)
724        );
725    }
726
727    #[test]
728    fn execution_checkpoint_precedes() {
729        let factory = create_test_provider_factory();
730        let provider = factory.provider_rw().unwrap();
731
732        let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
733        let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
734        let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
735        let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
736        provider.insert_block(genesis.try_recover().unwrap()).unwrap();
737        provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
738        provider
739            .static_file_provider()
740            .latest_writer(StaticFileSegment::Headers)
741            .unwrap()
742            .commit()
743            .unwrap();
744        provider.commit().unwrap();
745
746        let previous_stage_checkpoint = ExecutionCheckpoint {
747            block_range: CheckpointBlockRange { from: 0, to: 0 },
748            progress: EntitiesCheckpoint { processed: 1, total: 1 },
749        };
750        let previous_checkpoint = StageCheckpoint {
751            block_number: 1,
752            stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
753        };
754
755        let stage_checkpoint =
756            execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
757
758        assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
759            block_range: CheckpointBlockRange { from: 1, to: 1 },
760            progress: EntitiesCheckpoint {
761                processed,
762                total
763            }
764        }) if processed == previous_stage_checkpoint.progress.processed &&
765            total == previous_stage_checkpoint.progress.total + block.gas_used);
766    }
767
768    #[test]
769    fn execution_checkpoint_recalculate_full_previous_some() {
770        let factory = create_test_provider_factory();
771        let provider = factory.provider_rw().unwrap();
772
773        let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
774        let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
775        let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
776        let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
777        provider.insert_block(genesis.try_recover().unwrap()).unwrap();
778        provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
779        provider
780            .static_file_provider()
781            .latest_writer(StaticFileSegment::Headers)
782            .unwrap()
783            .commit()
784            .unwrap();
785        provider.commit().unwrap();
786
787        let previous_stage_checkpoint = ExecutionCheckpoint {
788            block_range: CheckpointBlockRange { from: 0, to: 0 },
789            progress: EntitiesCheckpoint { processed: 1, total: 1 },
790        };
791        let previous_checkpoint = StageCheckpoint {
792            block_number: 1,
793            stage_checkpoint: Some(StageUnitCheckpoint::Execution(previous_stage_checkpoint)),
794        };
795
796        let stage_checkpoint =
797            execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
798
799        assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
800            block_range: CheckpointBlockRange { from: 1, to: 1 },
801            progress: EntitiesCheckpoint {
802                processed,
803                total
804            }
805        }) if processed == previous_stage_checkpoint.progress.processed &&
806            total == previous_stage_checkpoint.progress.total + block.gas_used());
807    }
808
809    #[test]
810    fn execution_checkpoint_recalculate_full_previous_none() {
811        let factory = create_test_provider_factory();
812        let provider = factory.provider_rw().unwrap();
813
814        let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
815        let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
816        let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
817        let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
818        provider.insert_block(genesis.try_recover().unwrap()).unwrap();
819        provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
820        provider
821            .static_file_provider()
822            .latest_writer(StaticFileSegment::Headers)
823            .unwrap()
824            .commit()
825            .unwrap();
826        provider.commit().unwrap();
827
828        let previous_checkpoint = StageCheckpoint { block_number: 1, stage_checkpoint: None };
829
830        let stage_checkpoint =
831            execution_checkpoint(&factory.static_file_provider(), 1, 1, previous_checkpoint);
832
833        assert_matches!(stage_checkpoint, Ok(ExecutionCheckpoint {
834            block_range: CheckpointBlockRange { from: 1, to: 1 },
835            progress: EntitiesCheckpoint {
836                processed: 0,
837                total
838            }
839        }) if total == block.gas_used);
840    }
841
842    #[tokio::test]
843    async fn sanity_execution_of_block() {
844        let factory = create_test_provider_factory();
845        let provider = factory.provider_rw().unwrap();
846        let input = ExecInput { target: Some(1), checkpoint: None };
847        let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
848        let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
849        let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
850        let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
851        provider.insert_block(genesis.try_recover().unwrap()).unwrap();
852        provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
853        provider
854            .static_file_provider()
855            .latest_writer(StaticFileSegment::Headers)
856            .unwrap()
857            .commit()
858            .unwrap();
859        {
860            let static_file_provider = provider.static_file_provider();
861            let mut receipts_writer =
862                static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
863            receipts_writer.increment_block(0).unwrap();
864            receipts_writer.commit().unwrap();
865        }
866        provider.commit().unwrap();
867
868        // insert pre state
869        let provider = factory.provider_rw().unwrap();
870
871        let db_tx = provider.tx_ref();
872        let acc1 = address!("0x1000000000000000000000000000000000000000");
873        let acc2 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
874        let code = hex!("5a465a905090036002900360015500");
875        let balance = U256::from(0x3635c9adc5dea00000u128);
876        let code_hash = keccak256(code);
877        db_tx
878            .put::<tables::PlainAccountState>(
879                acc1,
880                Account { nonce: 0, balance: U256::ZERO, bytecode_hash: Some(code_hash) },
881            )
882            .unwrap();
883        db_tx
884            .put::<tables::PlainAccountState>(
885                acc2,
886                Account { nonce: 0, balance, bytecode_hash: None },
887            )
888            .unwrap();
889        db_tx.put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.to_vec().into())).unwrap();
890        provider.commit().unwrap();
891
892        // execute
893
894        // If there is a pruning configuration, then it's forced to use the database.
895        // This way we test both cases.
896        let modes = [None, Some(PruneModes::default())];
897
898        // Tests node with database and node with static files
899        for mode in modes {
900            let mut provider = factory.database_provider_rw().unwrap();
901
902            let mut execution_stage = stage();
903            provider.set_prune_modes(mode.clone().unwrap_or_default());
904
905            let output = execution_stage.execute(&provider, input).unwrap();
906            provider.commit().unwrap();
907
908            assert_matches!(output, ExecOutput {
909                checkpoint: StageCheckpoint {
910                    block_number: 1,
911                    stage_checkpoint: Some(StageUnitCheckpoint::Execution(ExecutionCheckpoint {
912                        block_range: CheckpointBlockRange {
913                            from: 1,
914                            to: 1,
915                        },
916                        progress: EntitiesCheckpoint {
917                            processed,
918                            total
919                        }
920                    }))
921                },
922                done: true
923            } if processed == total && total == block.gas_used);
924
925            let provider = factory.provider().unwrap();
926
927            // check post state
928            let account1 = address!("0x1000000000000000000000000000000000000000");
929            let account1_info =
930                Account { balance: U256::ZERO, nonce: 0x00, bytecode_hash: Some(code_hash) };
931            let account2 = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
932            let account2_info = Account {
933                balance: U256::from(0x1bc16d674ece94bau128),
934                nonce: 0x00,
935                bytecode_hash: None,
936            };
937            let account3 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
938            let account3_info = Account {
939                balance: U256::from(0x3635c9adc5de996b46u128),
940                nonce: 0x01,
941                bytecode_hash: None,
942            };
943
944            // assert accounts
945            assert!(
946                matches!(provider.basic_account(&account1), Ok(Some(acc)) if acc == account1_info)
947            );
948            assert!(
949                matches!(provider.basic_account(&account2), Ok(Some(acc)) if acc == account2_info)
950            );
951            assert!(
952                matches!(provider.basic_account(&account3), Ok(Some(acc)) if acc == account3_info)
953            );
954            // assert storage
955            // Get on dupsort would return only first value. This is good enough for this test.
956            assert!(matches!(
957                provider.tx_ref().get::<tables::PlainStorageState>(account1),
958                Ok(Some(entry)) if entry.key == B256::with_last_byte(1) && entry.value == U256::from(2)
959            ));
960
961            let mut provider = factory.database_provider_rw().unwrap();
962            let mut stage = stage();
963            provider.set_prune_modes(mode.unwrap_or_default());
964
965            let _result = stage
966                .unwind(
967                    &provider,
968                    UnwindInput { checkpoint: output.checkpoint, unwind_to: 0, bad_block: None },
969                )
970                .unwrap();
971            provider.commit().unwrap();
972        }
973    }
974
975    #[tokio::test]
976    async fn sanity_execute_unwind() {
977        let factory = create_test_provider_factory();
978        let provider = factory.provider_rw().unwrap();
979        let input = ExecInput { target: Some(1), checkpoint: None };
980        let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
981        let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
982        let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice();
983        let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
984        provider.insert_block(genesis.try_recover().unwrap()).unwrap();
985        provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
986        provider
987            .static_file_provider()
988            .latest_writer(StaticFileSegment::Headers)
989            .unwrap()
990            .commit()
991            .unwrap();
992        {
993            let static_file_provider = provider.static_file_provider();
994            let mut receipts_writer =
995                static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
996            receipts_writer.increment_block(0).unwrap();
997            receipts_writer.commit().unwrap();
998        }
999        provider.commit().unwrap();
1000
1001        // variables
1002        let code = hex!("5a465a905090036002900360015500");
1003        let balance = U256::from(0x3635c9adc5dea00000u128);
1004        let code_hash = keccak256(code);
1005        // pre state
1006        let provider = factory.provider_rw().unwrap();
1007
1008        let db_tx = provider.tx_ref();
1009        let acc1 = address!("0x1000000000000000000000000000000000000000");
1010        let acc1_info = Account { nonce: 0, balance: U256::ZERO, bytecode_hash: Some(code_hash) };
1011        let acc2 = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1012        let acc2_info = Account { nonce: 0, balance, bytecode_hash: None };
1013
1014        db_tx.put::<tables::PlainAccountState>(acc1, acc1_info).unwrap();
1015        db_tx.put::<tables::PlainAccountState>(acc2, acc2_info).unwrap();
1016        db_tx.put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.to_vec().into())).unwrap();
1017        provider.commit().unwrap();
1018
1019        // execute
1020        let mut provider = factory.database_provider_rw().unwrap();
1021
1022        // If there is a pruning configuration, then it's forced to use the database.
1023        // This way we test both cases.
1024        let modes = [None, Some(PruneModes::default())];
1025
1026        // Tests node with database and node with static files
1027        for mode in modes {
1028            // Test Execution
1029            let mut execution_stage = stage();
1030            provider.set_prune_modes(mode.clone().unwrap_or_default());
1031
1032            let result = execution_stage.execute(&provider, input).unwrap();
1033            provider.commit().unwrap();
1034
1035            // Test Unwind
1036            provider = factory.database_provider_rw().unwrap();
1037            let mut stage = stage();
1038            provider.set_prune_modes(mode.clone().unwrap_or_default());
1039
1040            let result = stage
1041                .unwind(
1042                    &provider,
1043                    UnwindInput { checkpoint: result.checkpoint, unwind_to: 0, bad_block: None },
1044                )
1045                .unwrap();
1046
1047            provider.static_file_provider().commit().unwrap();
1048
1049            assert_matches!(result, UnwindOutput {
1050                checkpoint: StageCheckpoint {
1051                    block_number: 0,
1052                    stage_checkpoint: Some(StageUnitCheckpoint::Execution(ExecutionCheckpoint {
1053                        block_range: CheckpointBlockRange {
1054                            from: 1,
1055                            to: 1,
1056                        },
1057                        progress: EntitiesCheckpoint {
1058                            processed: 0,
1059                            total
1060                        }
1061                    }))
1062                }
1063            } if total == block.gas_used);
1064
1065            // assert unwind stage
1066            assert!(matches!(provider.basic_account(&acc1), Ok(Some(acc)) if acc == acc1_info));
1067            assert!(matches!(provider.basic_account(&acc2), Ok(Some(acc)) if acc == acc2_info));
1068
1069            let miner_acc = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
1070            assert!(matches!(provider.basic_account(&miner_acc), Ok(None)));
1071
1072            assert!(matches!(provider.receipt(0), Ok(None)));
1073        }
1074    }
1075
1076    #[tokio::test]
1077    async fn test_selfdestruct() {
1078        let test_db = TestStageDB::default();
1079        let provider = test_db.factory.database_provider_rw().unwrap();
1080        let input = ExecInput { target: Some(1), checkpoint: None };
1081        let mut genesis_rlp = hex!("f901f8f901f3a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0c9ceb8372c88cb461724d8d3d87e8b933f6fc5f679d4841800e662f4428ffd0da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080830f4240808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice();
1082        let genesis = SealedBlock::<Block>::decode(&mut genesis_rlp).unwrap();
1083        let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice();
1084        let block = SealedBlock::<Block>::decode(&mut block_rlp).unwrap();
1085        provider.insert_block(genesis.try_recover().unwrap()).unwrap();
1086        provider.insert_block(block.clone().try_recover().unwrap()).unwrap();
1087        provider
1088            .static_file_provider()
1089            .latest_writer(StaticFileSegment::Headers)
1090            .unwrap()
1091            .commit()
1092            .unwrap();
1093        {
1094            let static_file_provider = provider.static_file_provider();
1095            let mut receipts_writer =
1096                static_file_provider.latest_writer(StaticFileSegment::Receipts).unwrap();
1097            receipts_writer.increment_block(0).unwrap();
1098            receipts_writer.commit().unwrap();
1099        }
1100        provider.commit().unwrap();
1101
1102        // variables
1103        let caller_address = address!("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
1104        let destroyed_address = address!("0x095e7baea6a6c7c4c2dfeb977efac326af552d87");
1105        let beneficiary_address = address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
1106
1107        let code = hex!("73095e7baea6a6c7c4c2dfeb977efac326af552d8731ff00");
1108        let balance = U256::from(0x0de0b6b3a7640000u64);
1109        let code_hash = keccak256(code);
1110
1111        // pre state
1112        let caller_info = Account { nonce: 0, balance, bytecode_hash: None };
1113        let destroyed_info =
1114            Account { nonce: 0, balance: U256::ZERO, bytecode_hash: Some(code_hash) };
1115
1116        // set account
1117        let provider = test_db.factory.provider_rw().unwrap();
1118        provider.tx_ref().put::<tables::PlainAccountState>(caller_address, caller_info).unwrap();
1119        provider
1120            .tx_ref()
1121            .put::<tables::PlainAccountState>(destroyed_address, destroyed_info)
1122            .unwrap();
1123        provider
1124            .tx_ref()
1125            .put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(code.to_vec().into()))
1126            .unwrap();
1127        // set storage to check when account gets destroyed.
1128        provider
1129            .tx_ref()
1130            .put::<tables::PlainStorageState>(
1131                destroyed_address,
1132                StorageEntry { key: B256::ZERO, value: U256::ZERO },
1133            )
1134            .unwrap();
1135        provider
1136            .tx_ref()
1137            .put::<tables::PlainStorageState>(
1138                destroyed_address,
1139                StorageEntry { key: B256::with_last_byte(1), value: U256::from(1u64) },
1140            )
1141            .unwrap();
1142
1143        provider.commit().unwrap();
1144
1145        // execute
1146        let provider = test_db.factory.database_provider_rw().unwrap();
1147        let mut execution_stage = stage();
1148        let _ = execution_stage.execute(&provider, input).unwrap();
1149        provider.commit().unwrap();
1150
1151        // assert unwind stage
1152        let provider = test_db.factory.database_provider_rw().unwrap();
1153        assert!(matches!(provider.basic_account(&destroyed_address), Ok(None)));
1154
1155        assert!(matches!(
1156            provider.tx_ref().get::<tables::PlainStorageState>(destroyed_address),
1157            Ok(None)
1158        ));
1159        // drops tx so that it returns write privilege to test_tx
1160        drop(provider);
1161        let plain_accounts = test_db.table::<tables::PlainAccountState>().unwrap();
1162        let plain_storage = test_db.table::<tables::PlainStorageState>().unwrap();
1163
1164        assert_eq!(
1165            plain_accounts,
1166            vec![
1167                (
1168                    beneficiary_address,
1169                    Account {
1170                        nonce: 0,
1171                        balance: U256::from(0x1bc16d674eca30a0u64),
1172                        bytecode_hash: None
1173                    }
1174                ),
1175                (
1176                    caller_address,
1177                    Account {
1178                        nonce: 1,
1179                        balance: U256::from(0xde0b6b3a761cf60u64),
1180                        bytecode_hash: None
1181                    }
1182                )
1183            ]
1184        );
1185        assert!(plain_storage.is_empty());
1186
1187        let account_changesets = test_db.table::<tables::AccountChangeSets>().unwrap();
1188        let storage_changesets = test_db.table::<tables::StorageChangeSets>().unwrap();
1189
1190        assert_eq!(
1191            account_changesets,
1192            vec![
1193                (
1194                    block.number,
1195                    AccountBeforeTx { address: destroyed_address, info: Some(destroyed_info) },
1196                ),
1197                (block.number, AccountBeforeTx { address: beneficiary_address, info: None }),
1198                (
1199                    block.number,
1200                    AccountBeforeTx { address: caller_address, info: Some(caller_info) }
1201                ),
1202            ]
1203        );
1204
1205        assert_eq!(
1206            storage_changesets,
1207            vec![
1208                (
1209                    (block.number, destroyed_address).into(),
1210                    StorageEntry { key: B256::ZERO, value: U256::ZERO }
1211                ),
1212                (
1213                    (block.number, destroyed_address).into(),
1214                    StorageEntry { key: B256::with_last_byte(1), value: U256::from(1u64) }
1215                )
1216            ]
1217        );
1218    }
1219}