//! Traits for configuring an EVM specifics. //! //! # Revm features //! //! This crate does __not__ enforce specific revm features such as `blst` or `c-kzg`, which are //! critical for revm's evm internals, it is the responsibility of the implementer to ensure the //! proper features are selected. #![doc( html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; use crate::execute::BasicBlockBuilder; use alloc::vec::Vec; use alloy_eips::{eip2930::AccessList, eip4895::Withdrawals}; pub use alloy_evm::evm::EvmFactory; use alloy_evm::{ block::{BlockExecutorFactory, BlockExecutorFor}, IntoTxEnv, }; use alloy_primitives::{Address, B256}; use core::{error::Error, fmt::Debug}; use execute::{BlockAssembler, BlockBuilder}; use reth_primitives_traits::{ BlockTy, HeaderTy, NodePrimitives, ReceiptTy, SealedBlock, SealedHeader, TxTy, }; use revm::context::TxEnv; use revm_database::State; pub mod either; /// EVM environment configuration. pub mod execute; mod aliases; pub use aliases::*; #[cfg(feature = "metrics")] pub mod metrics; pub mod noop; #[cfg(any(test, feature = "test-utils"))] /// test helpers for mocking executor pub mod test_utils; pub use alloy_evm::{ block::{state_changes, system_calls, OnStateHook}, env, Database, Evm, EvmEnv, EvmError, FromRecoveredTx, InvalidTxError, }; pub use alloy_evm::block::state_changes as state_change; /// A complete configuration of EVM for Reth. /// /// This trait encapsulates complete configuration required for transaction execution and block /// execution/building. /// /// The EVM abstraction consists of the following layers: /// - [`Evm`] produced by [`EvmFactory`]: The EVM implementation responsilble for executing /// individual transactions and producing output for them including state changes, logs, gas /// usage, etc. /// - [`BlockExecutor`] produced by [`BlockExecutorFactory`]: Executor operates on top of /// [`Evm`] and is responsible for executing entire blocks. This is different from simply /// aggregating outputs of transactions execution as it also involves higher level state /// changes such as receipt building, applying block rewards, system calls, etc. /// - [`BlockAssembler`]: Encapsulates logic for assembling blocks. It operates on context and /// output of [`BlockExecutor`], and is required to know how to assemble a next block to /// include in the chain. /// /// All of the above components need configuration environment which we are abstracting over to /// allow plugging EVM implementation into Reth SDK. /// /// The abstraction is designed to serve 2 codepaths: /// 1. Externally provided complete block (e.g received while syncing). /// 2. Block building when we know parent block and some additional context obtained from /// payload attributes or alike. /// /// First case is handled by [`ConfigureEvm::evm_env`] and [`ConfigureEvm::context_for_block`] /// which implement a conversion from [`NodePrimitives::Block`] to [`EvmEnv`] and [`ExecutionCtx`], /// and allow configuring EVM and block execution environment at a given block. /// /// Second case is handled by similar [`ConfigureEvm::next_evm_env`] and /// [`ConfigureEvm::context_for_next_block`] which take parent [`NodePrimitives::BlockHeader`] /// along with [`NextBlockEnvCtx`]. [`NextBlockEnvCtx`] is very similar to payload attributes and /// simply contains context for next block that is generally received from a CL node (timestamp, /// beneficiary, withdrawals, etc.). /// /// [`ExecutionCtx`]: BlockExecutorFactory::ExecutionCtx /// [`NextBlockEnvCtx`]: ConfigureEvm::NextBlockEnvCtx /// [`BlockExecutor`]: alloy_evm::block::BlockExecutor #[auto_impl::auto_impl(&, Arc)] pub trait ConfigureEvm: Send + Sync + Unpin + Clone { /// The primitives type used by the EVM. type Primitives: NodePrimitives; /// The error type that is returned by [`Self::next_evm_env`]. type Error: Error + Send + Sync + 'static; /// Context required for configuring next block environment. /// /// Contains values that can't be derived from the parent block. type NextBlockEnvCtx: Debug + Clone; /// Configured [`BlockExecutorFactory`], contains [`EvmFactory`] internally. type BlockExecutorFactory: BlockExecutorFactory< Transaction = TxTy, Receipt = ReceiptTy, EvmFactory: EvmFactory>>, >; /// A type that knows how to build a block. type BlockAssembler: BlockAssembler< Self::BlockExecutorFactory, Block = BlockTy, >; /// Returns reference to the configured [`BlockExecutorFactory`]. fn block_executor_factory(&self) -> &Self::BlockExecutorFactory; /// Returns reference to the configured [`BlockAssembler`]. fn block_assembler(&self) -> &Self::BlockAssembler; /// Creates a new [`EvmEnv`] for the given header. fn evm_env(&self, header: &HeaderTy) -> EvmEnvFor; /// Returns the configured [`EvmEnv`] for `parent + 1` block. /// /// This is intended for usage in block building after the merge and requires additional /// attributes that can't be derived from the parent block: attributes that are determined by /// the CL, such as the timestamp, suggested fee recipient, and randomness value. fn next_evm_env( &self, parent: &HeaderTy, attributes: &Self::NextBlockEnvCtx, ) -> Result, Self::Error>; /// Returns the configured [`BlockExecutorFactory::ExecutionCtx`] for a given block. fn context_for_block<'a>( &self, block: &'a SealedBlock>, ) -> ExecutionCtxFor<'a, Self>; /// Returns the configured [`BlockExecutorFactory::ExecutionCtx`] for `parent + 1` /// block. fn context_for_next_block( &self, parent: &SealedHeader>, attributes: Self::NextBlockEnvCtx, ) -> ExecutionCtxFor<'_, Self>; /// Returns a [`TxEnv`] from a transaction and [`Address`]. fn tx_env(&self, transaction: impl IntoTxEnv>) -> TxEnvFor { transaction.into_tx_env() } /// Provides a reference to [`EvmFactory`] implementation. fn evm_factory(&self) -> &EvmFactoryFor { self.block_executor_factory().evm_factory() } /// Returns a new EVM with the given database configured with the given environment settings, /// including the spec id and transaction environment. /// /// This will preserve any handler modifications fn evm_with_env(&self, db: DB, evm_env: EvmEnvFor) -> EvmFor { self.evm_factory().create_evm(db, evm_env) } /// Returns a new EVM with the given database configured with `cfg` and `block_env` /// configuration derived from the given header. Relies on /// [`ConfigureEvm::evm_env`]. /// /// # Caution /// /// This does not initialize the tx environment. fn evm_for_block( &self, db: DB, header: &HeaderTy, ) -> EvmFor { let evm_env = self.evm_env(header); self.evm_with_env(db, evm_env) } /// Returns a new EVM with the given database configured with the given environment settings, /// including the spec id. /// /// This will use the given external inspector as the EVM external context. /// /// This will preserve any handler modifications fn evm_with_env_and_inspector( &self, db: DB, evm_env: EvmEnvFor, inspector: I, ) -> EvmFor where DB: Database, I: InspectorFor, { self.evm_factory().create_evm_with_inspector(db, evm_env, inspector) } /// Creates a strategy with given EVM and execution context. fn create_executor<'a, DB, I>( &'a self, evm: EvmFor, I>, ctx: ::ExecutionCtx<'a>, ) -> impl BlockExecutorFor<'a, Self::BlockExecutorFactory, DB, I> where DB: Database, I: InspectorFor> + 'a, { self.block_executor_factory().create_executor(evm, ctx) } /// Creates a strategy for execution of a given block. fn executor_for_block<'a, DB: Database>( &'a self, db: &'a mut State, block: &'a SealedBlock<::Block>, ) -> impl BlockExecutorFor<'a, Self::BlockExecutorFactory, DB> { let evm = self.evm_for_block(db, block.header()); let ctx = self.context_for_block(block); self.create_executor(evm, ctx) } /// Creates a [`BlockBuilder`]. Should be used when building a new block. /// /// Block builder wraps an inner [`alloy_evm::block::BlockExecutor`] and has a similar /// interface. Builder collects all of the executed transactions, and once /// [`BlockBuilder::finish`] is called, it invokes the configured [`BlockAssembler`] to /// create a block. fn create_block_builder<'a, DB, I>( &'a self, evm: EvmFor, I>, parent: &'a SealedHeader>, ctx: ::ExecutionCtx<'a>, ) -> impl BlockBuilder< Primitives = Self::Primitives, Executor: BlockExecutorFor<'a, Self::BlockExecutorFactory, DB, I>, > where DB: Database, I: InspectorFor> + 'a, { BasicBlockBuilder { executor: self.create_executor(evm, ctx.clone()), ctx, assembler: self.block_assembler(), parent, transactions: Vec::new(), } } /// Creates a [`BlockBuilder`] for building of a new block. This is a helper to invoke /// [`ConfigureEvm::create_block_builder`]. fn builder_for_next_block<'a, DB: Database>( &'a self, db: &'a mut State, parent: &'a SealedHeader<::BlockHeader>, attributes: Self::NextBlockEnvCtx, ) -> Result, Self::Error> { let evm_env = self.next_evm_env(parent, &attributes)?; let evm = self.evm_with_env(db, evm_env); let ctx = self.context_for_next_block(parent, attributes); Ok(self.create_block_builder(evm, parent, ctx)) } } /// Represents additional attributes required to configure the next block. /// This is used to configure the next block's environment /// [`ConfigureEvm::next_evm_env`] and contains fields that can't be derived from the /// parent header alone (attributes that are determined by the CL.) #[derive(Debug, Clone, PartialEq, Eq)] pub struct NextBlockEnvAttributes { /// The timestamp of the next block. pub timestamp: u64, /// The suggested fee recipient for the next block. pub suggested_fee_recipient: Address, /// The randomness value for the next block. pub prev_randao: B256, /// Block gas limit. pub gas_limit: u64, /// The parent beacon block root. pub parent_beacon_block_root: Option, /// Withdrawals pub withdrawals: Option, } /// Abstraction over transaction environment. pub trait TransactionEnv: revm::context_interface::Transaction + Debug + Clone + Send + Sync + 'static { /// Set the gas limit. fn set_gas_limit(&mut self, gas_limit: u64); /// Set the gas limit. fn with_gas_limit(mut self, gas_limit: u64) -> Self { self.set_gas_limit(gas_limit); self } /// Returns the configured nonce. fn nonce(&self) -> u64; /// Sets the nonce. fn set_nonce(&mut self, nonce: u64); /// Sets the nonce. fn with_nonce(mut self, nonce: u64) -> Self { self.set_nonce(nonce); self } /// Set access list. fn set_access_list(&mut self, access_list: AccessList); /// Set access list. fn with_access_list(mut self, access_list: AccessList) -> Self { self.set_access_list(access_list); self } } impl TransactionEnv for TxEnv { fn set_gas_limit(&mut self, gas_limit: u64) { self.gas_limit = gas_limit; } fn nonce(&self) -> u64 { self.nonce } fn set_nonce(&mut self, nonce: u64) { self.nonce = nonce; } fn set_access_list(&mut self, access_list: AccessList) { self.access_list = access_list; } } #[cfg(feature = "op")] impl TransactionEnv for op_revm::OpTransaction { fn set_gas_limit(&mut self, gas_limit: u64) { self.base.set_gas_limit(gas_limit); } fn nonce(&self) -> u64 { TransactionEnv::nonce(&self.base) } fn set_nonce(&mut self, nonce: u64) { self.base.set_nonce(nonce); } fn set_access_list(&mut self, access_list: AccessList) { self.base.set_access_list(access_list); } }