//! Traits for execution. // Re-export execution types pub use reth_execution_errors::{BlockExecutionError, BlockValidationError}; pub use reth_execution_types::{BlockExecutionInput, BlockExecutionOutput, ExecutionOutcome}; pub use reth_storage_errors::provider::ProviderError; use core::fmt::Display; use reth_primitives::{BlockNumber, BlockWithSenders, Receipt}; use reth_prune_types::PruneModes; use revm_primitives::db::Database; /// A general purpose executor trait that executes an input (e.g. block) and produces an output /// (e.g. state changes and receipts). /// /// This executor does not validate the output, see [`BatchExecutor`] for that. pub trait Executor { /// The input type for the executor. type Input<'a>; /// The output type for the executor. type Output; /// The error type returned by the executor. type Error; /// Consumes the type and executes the block. /// /// # Note /// Execution happens without any validation of the output. To validate the output, use the /// [`BatchExecutor`]. /// /// # Returns /// The output of the block execution. fn execute(self, input: Self::Input<'_>) -> Result; } /// A general purpose executor that can execute multiple inputs in sequence, validate the outputs, /// and keep track of the state over the entire batch. pub trait BatchExecutor { /// The input type for the executor. type Input<'a>; /// The output type for the executor. type Output; /// The error type returned by the executor. type Error; /// Executes the next block in the batch, verifies the output and updates the state internally. fn execute_and_verify_one(&mut self, input: Self::Input<'_>) -> Result<(), Self::Error>; /// Executes multiple inputs in the batch, verifies the output, and updates the state /// internally. /// /// This method is a convenience function for calling [`BatchExecutor::execute_and_verify_one`] /// for each input. fn execute_and_verify_many<'a, I>(&mut self, inputs: I) -> Result<(), Self::Error> where I: IntoIterator>, { for input in inputs { self.execute_and_verify_one(input)?; } Ok(()) } /// Executes the entire batch, verifies the output, and returns the final state. /// /// This method is a convenience function for calling [`BatchExecutor::execute_and_verify_many`] /// and [`BatchExecutor::finalize`]. fn execute_and_verify_batch<'a, I>(mut self, batch: I) -> Result where I: IntoIterator>, Self: Sized, { self.execute_and_verify_many(batch)?; Ok(self.finalize()) } /// Finishes the batch and return the final state. fn finalize(self) -> Self::Output; /// Set the expected tip of the batch. /// /// This can be used to optimize state pruning during execution. fn set_tip(&mut self, tip: BlockNumber); /// Set the prune modes. /// /// They are used to determine which parts of the state should be kept during execution. fn set_prune_modes(&mut self, prune_modes: PruneModes); /// The size hint of the batch's tracked state size. /// /// This is used to optimize DB commits depending on the size of the state. fn size_hint(&self) -> Option; } /// A type that can create a new executor for block execution. pub trait BlockExecutorProvider: Send + Sync + Clone + Unpin + 'static { /// An executor that can execute a single block given a database. /// /// # Verification /// /// The on [`Executor::execute`] the executor is expected to validate the execution output of /// the input, this includes: /// - Cumulative gas used must match the input's gas used. /// - Receipts must match the input's receipts root. /// /// It is not expected to validate the state trie root, this must be done by the caller using /// the returned state. type Executor + Display>>: for<'a> Executor< DB, Input<'a> = BlockExecutionInput<'a, BlockWithSenders>, Output = BlockExecutionOutput, Error = BlockExecutionError, >; /// An executor that can execute a batch of blocks given a database. type BatchExecutor + Display>>: for<'a> BatchExecutor< DB, Input<'a> = BlockExecutionInput<'a, BlockWithSenders>, Output = ExecutionOutcome, Error = BlockExecutionError, >; /// Creates a new executor for single block execution. /// /// This is used to execute a single block and get the changed state. fn executor(&self, db: DB) -> Self::Executor where DB: Database + Display>; /// Creates a new batch executor with the given database and pruning modes. /// /// Batch executor is used to execute multiple blocks in sequence and keep track of the state /// during historical sync which involves executing multiple blocks in sequence. fn batch_executor(&self, db: DB) -> Self::BatchExecutor where DB: Database + Display>; } #[cfg(test)] mod tests { use super::*; use reth_primitives::Block; use revm::db::{CacheDB, EmptyDBTyped}; use revm_primitives::U256; use std::marker::PhantomData; #[derive(Clone, Default)] struct TestExecutorProvider; impl BlockExecutorProvider for TestExecutorProvider { type Executor + Display>> = TestExecutor; type BatchExecutor + Display>> = TestExecutor; fn executor(&self, _db: DB) -> Self::Executor where DB: Database + Display>, { TestExecutor(PhantomData) } fn batch_executor(&self, _db: DB) -> Self::BatchExecutor where DB: Database + Display>, { TestExecutor(PhantomData) } } struct TestExecutor(PhantomData); impl Executor for TestExecutor { type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; type Output = BlockExecutionOutput; type Error = BlockExecutionError; fn execute(self, _input: Self::Input<'_>) -> Result { Err(BlockExecutionError::msg("execution unavailable for tests")) } } impl BatchExecutor for TestExecutor { type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; type Output = ExecutionOutcome; type Error = BlockExecutionError; fn execute_and_verify_one(&mut self, _input: Self::Input<'_>) -> Result<(), Self::Error> { Ok(()) } fn finalize(self) -> Self::Output { todo!() } fn set_tip(&mut self, _tip: BlockNumber) { todo!() } fn set_prune_modes(&mut self, _prune_modes: PruneModes) { todo!() } fn size_hint(&self) -> Option { None } } #[test] fn test_provider() { let provider = TestExecutorProvider; let db = CacheDB::>::default(); let executor = provider.executor(db); let block = Block { header: Default::default(), body: vec![], ommers: vec![], withdrawals: None, requests: None, }; let block = BlockWithSenders::new(block, Default::default()).unwrap(); let _ = executor.execute(BlockExecutionInput::new(&block, U256::ZERO)); } }