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