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}