alloy_evm/block/
mod.rs

1//! Block execution abstraction.
2
3use crate::{Database, Evm, EvmFactory, FromRecoveredTx, FromTxWithEncoded, RecoveredTx, ToTxEnv};
4use alloc::{boxed::Box, vec::Vec};
5use alloy_eips::eip7685::Requests;
6use revm::{
7    context::result::{ExecutionResult, ResultAndState},
8    database::State,
9    inspector::NoOpInspector,
10    Inspector,
11};
12
13mod error;
14pub use error::*;
15
16mod state_hook;
17pub use state_hook::*;
18
19pub mod system_calls;
20pub use system_calls::*;
21
22pub mod state_changes;
23
24pub mod calc;
25
26/// The result of executing a block.
27#[derive(Debug, Clone, Default, PartialEq, Eq)]
28pub struct BlockExecutionResult<T> {
29    /// All the receipts of the transactions in the block.
30    pub receipts: Vec<T>,
31    /// All the EIP-7685 requests in the block.
32    pub requests: Requests,
33    /// The total gas used by the block.
34    pub gas_used: u64,
35    /// Blob gas used by the block.
36    pub blob_gas_used: u64,
37}
38
39/// Helper trait to encapsulate requirements for a type to be used as input for [`BlockExecutor`].
40///
41/// This trait combines the requirements for a transaction to be executable by a block executor:
42/// - Must be convertible to the EVM's transaction environment via [`ToTxEnv`]
43/// - Must provide access to the transaction and signer via [`RecoveredTx`]
44/// - Must be [`Copy`] for efficient handling during block execution (the expectation here is that
45///   this always passed as & reference)
46///
47/// This trait is automatically implemented for any type that meets these requirements.
48/// Common implementations include:
49/// - [`Recovered<T>`](alloy_consensus::transaction::Recovered) where `T` is a transaction type
50/// - [`WithEncoded<Recovered<T>>`](alloy_eips::eip2718::WithEncoded) for transactions with encoded
51///   bytes
52///
53/// The trait ensures that the block executor can both execute the transaction in the EVM
54/// and access the original transaction data for receipt generation.
55pub trait ExecutableTx<E: BlockExecutor + ?Sized>:
56    ToTxEnv<<E::Evm as Evm>::Tx> + RecoveredTx<E::Transaction>
57{
58}
59impl<E: BlockExecutor + ?Sized, T> ExecutableTx<E> for T where
60    T: ToTxEnv<<E::Evm as Evm>::Tx> + RecoveredTx<E::Transaction>
61{
62}
63
64/// Marks whether transaction should be committed into block executor's state.
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66#[must_use]
67pub enum CommitChanges {
68    /// Transaction should be committed into block executor's state.
69    Yes,
70    /// Transaction should not be committed.
71    No,
72}
73
74impl CommitChanges {
75    /// Returns `true` if transaction should be committed into block executor's state.
76    pub const fn should_commit(self) -> bool {
77        matches!(self, Self::Yes)
78    }
79}
80
81/// A type that knows how to execute a single block.
82///
83/// The current abstraction assumes that block execution consists of the following steps:
84/// 1. Apply pre-execution changes. Those might include system calls, irregular state transitions
85///    (DAO fork), etc.
86/// 2. Apply block transactions to the state.
87/// 3. Apply post-execution changes and finalize the state. This might include other system calls,
88///    block rewards, etc.
89///
90/// The output of [`BlockExecutor::finish`] is a [`BlockExecutionResult`] which contains all
91/// relevant information about the block execution.
92pub trait BlockExecutor {
93    /// Input transaction type.
94    ///
95    /// This represents the consensus transaction type that the block executor operates on.
96    /// It's typically a type from the consensus layer (e.g.,
97    /// [`EthereumTxEnvelope`](alloy_consensus::EthereumTxEnvelope)) that contains
98    /// the raw transaction data, signature, and other consensus-level information.
99    ///
100    /// This type is used in several contexts:
101    /// - As the generic parameter for [`RecoveredTx<T>`](crate::RecoveredTx) in [`ExecutableTx`]
102    /// - As the generic parameter for [`FromRecoveredTx<T>`](crate::FromRecoveredTx) and
103    ///   [`FromTxWithEncoded<T>`](crate::FromTxWithEncoded) in the EVM constraint
104    /// - To generate receipts after transaction execution
105    ///
106    /// The transaction flow is:
107    /// 1. `Self::Transaction` (consensus tx) →
108    ///    [`Recovered<Self::Transaction>`](alloy_consensus::transaction::Recovered) (with sender)
109    /// 2. [`Recovered<Self::Transaction>`](alloy_consensus::transaction::Recovered) →
110    ///    [`TxEnv`](revm::context::TxEnv) (via [`FromRecoveredTx`])
111    /// 3. [`TxEnv`](revm::context::TxEnv) → EVM execution → [`ExecutionResult`]
112    /// 4. [`ExecutionResult`] + `Self::Transaction` → `Self::Receipt`
113    ///
114    /// Common examples:
115    /// - [`EthereumTxEnvelope`](alloy_consensus::EthereumTxEnvelope) for all Ethereum transaction
116    ///   variants
117    /// - `OpTxEnvelope` for opstack transaction variants
118    type Transaction;
119    /// Receipt type this executor produces.
120    type Receipt;
121    /// EVM used by the executor.
122    ///
123    /// The EVM's transaction type (`Evm::Tx`) must be able to be constructed from both:
124    /// - [`FromRecoveredTx<Self::Transaction>`](crate::FromRecoveredTx) - for transactions with
125    ///   recovered senders
126    /// - [`FromTxWithEncoded<Self::Transaction>`](crate::FromTxWithEncoded) - for transactions with
127    ///   encoded bytes
128    ///
129    /// This constraint ensures that the block executor can convert consensus transactions
130    /// into the EVM's transaction format for execution.
131    type Evm: Evm<Tx: FromRecoveredTx<Self::Transaction> + FromTxWithEncoded<Self::Transaction>>;
132
133    /// Applies any necessary changes before executing the block's transactions.
134    fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
135
136    /// Executes a single transaction and applies execution result to internal state.
137    ///
138    /// This method accepts any type implementing [`ExecutableTx`], which ensures the transaction:
139    /// - Can be converted to the EVM's transaction environment for execution
140    /// - Provides access to the original transaction and signer for receipt generation
141    ///
142    /// Common input types include:
143    /// - `&Recovered<Transaction>` - A transaction with its recovered sender
144    /// - `&WithEncoded<Recovered<Transaction>>` - A transaction with sender and encoded bytes
145    ///
146    /// The transaction is executed in the EVM, state changes are committed, and a receipt
147    /// is generated internally.
148    ///
149    /// Returns the gas used by the transaction.
150    fn execute_transaction(
151        &mut self,
152        tx: impl ExecutableTx<Self>,
153    ) -> Result<u64, BlockExecutionError> {
154        self.execute_transaction_with_result_closure(tx, |_| ())
155    }
156
157    /// Executes a single transaction and applies execution result to internal state. Invokes the
158    /// given closure with an internal [`ExecutionResult`] produced by the EVM.
159    ///
160    /// This method is similar to [`execute_transaction`](Self::execute_transaction) but provides
161    /// access to the raw execution result before it's converted to a receipt. This is useful for:
162    /// - Custom logging or metrics collection
163    /// - Debugging transaction execution
164    /// - Extracting additional information from the execution result
165    ///
166    /// The transaction is always committed after the closure is invoked.
167    fn execute_transaction_with_result_closure(
168        &mut self,
169        tx: impl ExecutableTx<Self>,
170        f: impl FnOnce(&ExecutionResult<<Self::Evm as Evm>::HaltReason>),
171    ) -> Result<u64, BlockExecutionError> {
172        self.execute_transaction_with_commit_condition(tx, |res| {
173            f(res);
174            CommitChanges::Yes
175        })
176        .map(Option::unwrap_or_default)
177    }
178
179    /// Executes a single transaction and applies execution result to internal state. Invokes the
180    /// given closure with an internal [`ExecutionResult`] produced by the EVM, and commits the
181    /// transaction to the state on [`CommitChanges::Yes`].
182    ///
183    /// This is the most flexible transaction execution method, allowing conditional commitment
184    /// based on the execution result. The closure receives the execution result and returns
185    /// whether to commit the changes to state.
186    ///
187    /// Use cases:
188    /// - Conditional execution based on transaction outcome
189    /// - Simulating transactions without committing
190    /// - Custom validation logic before committing
191    ///
192    /// The [`ExecutableTx`] constraint ensures that:
193    /// 1. The transaction can be converted to `TxEnv` via [`ToTxEnv`] for EVM execution
194    /// 2. The original transaction and signer can be accessed via [`RecoveredTx`] for receipt
195    ///    generation
196    ///
197    /// Returns [`None`] if committing changes from the transaction should be skipped via
198    /// [`CommitChanges::No`], otherwise returns the gas used by the transaction.
199    fn execute_transaction_with_commit_condition(
200        &mut self,
201        tx: impl ExecutableTx<Self>,
202        f: impl FnOnce(&ExecutionResult<<Self::Evm as Evm>::HaltReason>) -> CommitChanges,
203    ) -> Result<Option<u64>, BlockExecutionError> {
204        // Execute transaction without committing
205        let output = self.execute_transaction_without_commit(&tx)?;
206
207        if !f(&output.result).should_commit() {
208            return Ok(None);
209        }
210
211        let gas_used = self.commit_transaction(output, tx)?;
212        Ok(Some(gas_used))
213    }
214
215    /// Executes a single transaction without committing state changes.
216    ///
217    /// This method performs the transaction execution through the EVM but does not
218    /// commit the resulting state changes. The output can be inspected and potentially
219    /// committed later using [`commit_transaction`](Self::commit_transaction).
220    ///
221    /// Returns a [`revm::context_interface::result::ResultAndState`] containing the execution
222    /// result and state changes.
223    ///
224    /// # Use Cases
225    /// - Transaction simulation without affecting state
226    /// - Inspecting transaction effects before committing
227    /// - Building custom commit logic
228    fn execute_transaction_without_commit(
229        &mut self,
230        tx: impl ExecutableTx<Self>,
231    ) -> Result<ResultAndState<<Self::Evm as Evm>::HaltReason>, BlockExecutionError>;
232
233    /// Commits a previously executed transaction's state changes.
234    ///
235    /// Takes the output from
236    /// [`execute_transaction_without_commit`](Self::execute_transaction_without_commit)
237    /// and applies the state changes, updates gas accounting, and generates a receipt.
238    ///
239    /// Returns the gas used by the transaction.
240    ///
241    /// # Parameters
242    /// - `output`: The transaction output containing execution result and state changes
243    /// - `tx`: The original transaction (needed for receipt generation)
244    fn commit_transaction(
245        &mut self,
246        output: ResultAndState<<Self::Evm as Evm>::HaltReason>,
247        tx: impl ExecutableTx<Self>,
248    ) -> Result<u64, BlockExecutionError>;
249
250    /// Applies any necessary changes after executing the block's transactions, completes execution
251    /// and returns the underlying EVM along with execution result.
252    fn finish(
253        self,
254    ) -> Result<(Self::Evm, BlockExecutionResult<Self::Receipt>), BlockExecutionError>;
255
256    /// A helper to invoke [`BlockExecutor::finish`] returning only the [`BlockExecutionResult`].
257    fn apply_post_execution_changes(
258        self,
259    ) -> Result<BlockExecutionResult<Self::Receipt>, BlockExecutionError>
260    where
261        Self: Sized,
262    {
263        self.finish().map(|(_, result)| result)
264    }
265
266    /// Sets a hook to be called after each state change during execution.
267    fn set_state_hook(&mut self, hook: Option<Box<dyn OnStateHook>>);
268
269    /// A builder-style helper to invoke [`BlockExecutor::set_state_hook`].
270    #[must_use]
271    fn with_state_hook(mut self, hook: Option<Box<dyn OnStateHook>>) -> Self
272    where
273        Self: Sized,
274    {
275        self.set_state_hook(hook);
276        self
277    }
278
279    /// Exposes mutable reference to EVM.
280    fn evm_mut(&mut self) -> &mut Self::Evm;
281
282    /// Exposes immutable reference to EVM.
283    fn evm(&self) -> &Self::Evm;
284
285    /// Executes all transactions in a block, applying pre and post execution changes.
286    ///
287    /// This is a convenience method that orchestrates the complete block execution flow:
288    /// 1. Applies pre-execution changes (system calls, irregular state transitions)
289    /// 2. Executes all transactions in order
290    /// 3. Applies post-execution changes (block rewards, system calls)
291    ///
292    /// Each transaction in the iterator must implement [`ExecutableTx`], ensuring it can be:
293    /// - Converted to the EVM's transaction format for execution
294    /// - Used to generate receipts with access to the original transaction data
295    ///
296    /// # Example
297    ///
298    /// ```ignore
299    /// let recovered_txs: Vec<Recovered<Transaction>> = block.transactions
300    ///     .iter()
301    ///     .map(|tx| tx.recover_signer())
302    ///     .collect::<Result<_, _>>()?;
303    ///
304    /// let result = executor.execute_block(recovered_txs.iter())?;
305    /// ```
306    fn execute_block(
307        mut self,
308        transactions: impl IntoIterator<Item = impl ExecutableTx<Self>>,
309    ) -> Result<BlockExecutionResult<Self::Receipt>, BlockExecutionError>
310    where
311        Self: Sized,
312    {
313        self.apply_pre_execution_changes()?;
314
315        for tx in transactions {
316            self.execute_transaction(tx)?;
317        }
318
319        self.apply_post_execution_changes()
320    }
321}
322
323/// A helper trait encapsulating the constraints on [`BlockExecutor`] produced by the
324/// [`BlockExecutorFactory`] to avoid duplicating them in every implementation.
325pub trait BlockExecutorFor<'a, F: BlockExecutorFactory + ?Sized, DB, I = NoOpInspector>
326where
327    Self: BlockExecutor<
328        Evm = <F::EvmFactory as EvmFactory>::Evm<&'a mut State<DB>, I>,
329        Transaction = F::Transaction,
330        Receipt = F::Receipt,
331    >,
332    DB: Database + 'a,
333    I: Inspector<<F::EvmFactory as EvmFactory>::Context<&'a mut State<DB>>> + 'a,
334{
335}
336
337impl<'a, F, DB, I, T> BlockExecutorFor<'a, F, DB, I> for T
338where
339    F: BlockExecutorFactory,
340    DB: Database + 'a,
341    I: Inspector<<F::EvmFactory as EvmFactory>::Context<&'a mut State<DB>>> + 'a,
342    T: BlockExecutor<
343        Evm = <F::EvmFactory as EvmFactory>::Evm<&'a mut State<DB>, I>,
344        Transaction = F::Transaction,
345        Receipt = F::Receipt,
346    >,
347{
348}
349
350/// A factory that can create [`BlockExecutor`]s.
351///
352/// This trait serves as the main entry point for block execution, providing a way to construct
353/// [`BlockExecutor`] instances with the necessary context. It separates the concerns of:
354/// - EVM configuration (handled by [`EvmFactory`])
355/// - Block-specific execution context (provided via [`ExecutionCtx`])
356///
357/// It allows for:
358/// - Reusable EVM configuration across multiple block executions
359/// - Separation of EVM-related state from block execution state
360/// - Flexible instantiation of executors with different contexts
361///
362/// # Relationship with EvmFactory
363///
364/// Every block executor factory contains an [`EvmFactory`] instance which handles:
365/// - EVM configuration and instantiation
366/// - Transaction environment setup
367/// - State database management
368///
369/// The block executor factory extends this by adding block-level execution concerns.
370///
371/// For more context on the executor design, see the documentation for [`BlockExecutor`].
372///
373/// [`ExecutionCtx`]: BlockExecutorFactory::ExecutionCtx
374/// [`EvmFactory`]: crate::EvmFactory
375#[auto_impl::auto_impl(Arc)]
376pub trait BlockExecutorFactory: 'static {
377    /// The EVM factory used by the executor.
378    type EvmFactory: EvmFactory;
379
380    /// Context required for block execution beyond what the EVM provides (e.g.
381    /// [`EvmEnv`](crate::EvmEnv))
382    ///
383    /// While the EVM contains transaction-level context (gas limits, caller, value) and
384    /// block-level context (block number, timestamp, base fee), the `ExecutionCtx` provides
385    /// additional block execution context that is specific to your consensus implementation.
386    ///
387    /// # Purpose
388    ///
389    /// This type provides data needed for system calls that occur outside normal transaction
390    /// execution. Block execution requires additional context for:
391    /// - **Pre-execution system calls**: Setting up block hash history, beacon block roots
392    /// - **Post-execution system calls**: Applying block rewards, validator withdrawals
393    /// - **Consensus-specific data**: Uncle/ommer blocks, L2 data availability info
394    /// - **Protocol parameters**: Fork-specific rules, precompile configurations
395    /// - **Precompile metadata**: Context for precompiles that require block-level data (e.g.
396    ///   parameters stored in the block body)
397    ///
398    /// For example, in Ethereum: [`EthBlockExecutionCtx`](crate::eth::EthBlockExecutionCtx)
399    /// contains:
400    /// - Parent block hash for EIP-2935 block hash system call
401    /// - Parent beacon block root for EIP-4788 beacon root system call
402    /// - Uncle blocks for handling uncle rewards
403    /// - Withdrawals for EIP-4895 validator withdrawals
404    ///
405    /// # Design Considerations
406    ///
407    /// - Must be [`Clone`] to support creating multiple executors, can use `Cow` borrowed from the
408    ///   block.
409    /// - Should be lightweight (use references where possible)
410    /// - Contains only block-level data, not transaction-specific data
411    type ExecutionCtx<'a>: Clone;
412
413    /// Transaction type used by the executor, see [`BlockExecutor::Transaction`].
414    ///
415    /// This should be the same consensus transaction type that the block executor operates on.
416    /// It represents the transaction format from your consensus layer that needs to be
417    /// executed by the EVM.
418    type Transaction;
419
420    /// Receipt type produced by the executor, see [`BlockExecutor::Receipt`].
421    type Receipt;
422
423    /// Reference to EVM factory used by the executor.
424    fn evm_factory(&self) -> &Self::EvmFactory;
425
426    /// Creates an executor with given EVM and execution context.
427    ///
428    /// This method combines:
429    /// - An EVM instance (already configured with block environment and state)
430    /// - The execution context (containing additional data for system calls)
431    ///
432    /// To create a [`BlockExecutor`] that can:
433    /// 1. Apply pre-execution system calls (e.g., EIP-2935 blockhashes, EIP-4788 beacon roots)
434    /// 2. Execute transactions
435    /// 3. Apply post-execution system calls (e.g., withdrawals, rewards)
436    ///
437    /// # Parameters
438    ///
439    /// - `evm`: A configured EVM instance with block environment and state
440    /// - `ctx`: The execution context containing consensus-specific data needed for system calls
441    ///
442    /// # Example
443    ///
444    /// ```ignore
445    /// // Create EVM with block environment
446    /// let evm = factory.evm_factory().create_evm(block_env, state_db, inspector);
447    ///
448    /// // Create execution context with consensus-specific data required for block execution
449    /// let ctx = EthBlockExecutionCtx {
450    ///     parent_hash: parent_block.hash(),
451    ///     parent_beacon_block_root: parent_block.parent_beacon_block_root,
452    ///     ommers: &uncle_blocks,
453    ///     withdrawals: Some(Cow::Borrowed(&withdrawals)),
454    /// };
455    ///
456    /// // Create executor - it will use ctx for system calls
457    /// let mut executor = factory.create_executor(evm, ctx);
458    ///
459    /// // The executor will:
460    /// // 1. Apply pre-execution changes
461    /// // 2. Execute all transactions
462    /// // 3. Apply post-execution changes (e.g., process withdrawals, apply rewards)
463    /// let result = executor.execute_block(transactions)?;
464    /// ```
465    fn create_executor<'a, DB, I>(
466        &'a self,
467        evm: <Self::EvmFactory as EvmFactory>::Evm<&'a mut State<DB>, I>,
468        ctx: Self::ExecutionCtx<'a>,
469    ) -> impl BlockExecutorFor<'a, Self, DB, I>
470    where
471        DB: Database + 'a,
472        I: Inspector<<Self::EvmFactory as EvmFactory>::Context<&'a mut State<DB>>> + 'a;
473}