Skip to main content

reth_evm/
lib.rs

1//! Traits for configuring an EVM specifics.
2//!
3//! # Revm features
4//!
5//! This crate does __not__ enforce specific revm features such as `blst` or `c-kzg`, which are
6//! critical for revm's evm internals, it is the responsibility of the implementer to ensure the
7//! proper features are selected.
8
9#![doc(
10    html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
11    html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
12    issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
13)]
14#![cfg_attr(not(test), warn(unused_crate_dependencies))]
15#![cfg_attr(docsrs, feature(doc_cfg))]
16#![cfg_attr(not(feature = "std"), no_std)]
17
18extern crate alloc;
19
20use crate::execute::{BasicBlockBuilder, Executor};
21use alloc::{string::String, vec::Vec};
22use alloy_eips::eip4895::Withdrawals;
23use alloy_evm::{
24    block::{BlockExecutorFactory, BlockExecutorFor},
25    precompiles::PrecompilesMap,
26};
27use alloy_primitives::{Address, Bytes, B256};
28use core::{error::Error, fmt::Debug};
29use execute::{BasicBlockExecutor, BlockAssembler, BlockBuilder};
30use reth_execution_errors::BlockExecutionError;
31use reth_primitives_traits::{
32    BlockTy, HeaderTy, NodePrimitives, ReceiptTy, SealedBlock, SealedHeader, TxTy,
33};
34use revm::{context::result::ResultAndState, database::State, primitives::hardfork::SpecId};
35
36pub mod either;
37/// EVM environment configuration.
38pub mod execute;
39
40mod aliases;
41pub use aliases::*;
42
43#[cfg(feature = "std")]
44mod engine;
45#[cfg(feature = "std")]
46pub use engine::{ConfigureEngineEvm, ConvertTx, ExecutableTxIterator, ExecutableTxTuple};
47mod sender_recovery;
48pub use sender_recovery::SenderRecoveryCache;
49
50#[cfg(feature = "metrics")]
51pub mod metrics;
52pub mod noop;
53#[cfg(any(test, feature = "test-utils"))]
54/// test helpers for mocking executor
55pub mod test_utils;
56
57pub use alloy_evm::{
58    block::{state_changes, system_calls, OnStateHook},
59    *,
60};
61
62/// A complete configuration of EVM for Reth.
63///
64/// This trait encapsulates complete configuration required for transaction execution and block
65/// execution/building, providing a unified interface for EVM operations.
66///
67/// # Architecture Overview
68///
69/// The EVM abstraction consists of the following layers:
70///
71/// 1. **[`Evm`] (produced by [`EvmFactory`])**: The core EVM implementation responsible for
72///    executing individual transactions and producing outputs including state changes, logs, gas
73///    usage, etc.
74///
75/// 2. **[`BlockExecutor`] (produced by [`BlockExecutorFactory`])**: A higher-level component that
76///    operates on top of [`Evm`] to execute entire blocks. This involves:
77///    - Executing all transactions in sequence
78///    - Building receipts from transaction outputs
79///    - Applying block rewards to the beneficiary
80///    - Executing system calls (e.g., EIP-4788 beacon root updates)
81///    - Managing state changes and bundle accumulation
82///
83/// 3. **[`BlockAssembler`]**: Responsible for assembling valid blocks from executed transactions.
84///    It takes the output from [`BlockExecutor`] along with execution context and produces a
85///    complete block ready for inclusion in the chain.
86///
87/// # Usage Patterns
88///
89/// The abstraction supports two primary use cases:
90///
91/// ## 1. Executing Externally Provided Blocks (e.g., during sync)
92///
93/// ```rust,ignore
94/// use reth_evm::ConfigureEvm;
95///
96/// // Execute a received block
97/// let mut executor = evm_config.executor(state_db);
98/// let output = executor.execute(&block)?;
99///
100/// // Access the execution results
101/// println!("Gas used: {}", output.result.gas_used);
102/// println!("Receipts: {:?}", output.result.receipts);
103/// ```
104///
105/// ## 2. Building New Blocks (e.g., payload building)
106///
107/// Payload building is slightly different as it doesn't have the block's header yet, but rather
108/// attributes for the block's environment, such as timestamp, fee recipient, and randomness value.
109/// The block's header will be the outcome of the block building process.
110///
111/// ```rust,ignore
112/// use reth_evm::{ConfigureEvm, NextBlockEnvAttributes};
113///
114/// // Create attributes for the next block
115/// let attributes = NextBlockEnvAttributes {
116///     timestamp: current_time + 12,
117///     suggested_fee_recipient: beneficiary_address,
118///     prev_randao: randomness_value,
119///     gas_limit: 30_000_000,
120///     withdrawals: Some(withdrawals),
121///     parent_beacon_block_root: Some(beacon_root),
122///     slot_number: None,
123/// };
124///
125/// // Build a new block on top of parent
126/// let mut builder = evm_config.builder_for_next_block(
127///     &mut state_db,
128///     &parent_header,
129///     attributes
130/// )?;
131///
132/// // Apply pre-execution changes (e.g., beacon root update)
133/// builder.apply_pre_execution_changes()?;
134///
135/// // Execute transactions
136/// for tx in pending_transactions {
137///     match builder.execute_transaction(tx) {
138///         Ok(gas_used) => {
139///             println!("Transaction executed, gas used: {}", gas_used);
140///         }
141///         Err(e) => {
142///             println!("Transaction failed: {:?}", e);
143///         }
144///     }
145/// }
146///
147/// // Finish block building and get the outcome (block)
148/// let outcome = builder.finish(state_provider, None)?;
149/// let block = outcome.block;
150/// ```
151///
152/// # Key Components
153///
154/// ## [`NextBlockEnvCtx`]
155///
156/// Contains attributes needed to configure the next block that cannot be derived from the
157/// parent block alone. This includes data typically provided by the consensus layer:
158/// - `timestamp`: Block timestamp
159/// - `suggested_fee_recipient`: Beneficiary address
160/// - `prev_randao`: Randomness value
161/// - `gas_limit`: Block gas limit
162/// - `withdrawals`: Consensus layer withdrawals
163/// - `parent_beacon_block_root`: EIP-4788 beacon root
164///
165/// ## [`BlockAssembler`]
166///
167/// Takes the execution output and produces a complete block. It receives:
168/// - Transaction execution results (receipts, gas used)
169/// - Final state root after all executions
170/// - Bundle state with all changes
171/// - Execution context and environment
172///
173/// The assembler is responsible for:
174/// - Setting the correct block header fields
175/// - Including executed transactions
176/// - Setting gas used and receipts root
177/// - Applying any chain-specific rules
178///
179/// [`ExecutionCtx`]: BlockExecutorFactory::ExecutionCtx
180/// [`NextBlockEnvCtx`]: ConfigureEvm::NextBlockEnvCtx
181/// [`BlockExecutor`]: alloy_evm::block::BlockExecutor
182#[auto_impl::auto_impl(&, Arc)]
183pub trait ConfigureEvm: Clone + Debug + Send + Sync + Unpin {
184    /// The primitives type used by the EVM.
185    type Primitives: NodePrimitives;
186
187    /// The error type that is returned by [`Self::next_evm_env`].
188    type Error: Error + Send + Sync + 'static;
189
190    /// Context required for configuring next block environment.
191    ///
192    /// Contains values that can't be derived from the parent block.
193    type NextBlockEnvCtx: Debug + Clone;
194
195    /// Configured [`BlockExecutorFactory`], contains [`EvmFactory`] internally.
196    type BlockExecutorFactory: for<'a> BlockExecutorFactory<
197        Transaction = TxTy<Self::Primitives>,
198        Receipt = ReceiptTy<Self::Primitives>,
199        ExecutionCtx<'a>: Debug + Send,
200        EvmFactory: EvmFactory<
201            Tx: TransactionEnvMut
202                    + FromRecoveredTx<TxTy<Self::Primitives>>
203                    + FromTxWithEncoded<TxTy<Self::Primitives>>,
204            Precompiles = PrecompilesMap,
205            Spec: Into<SpecId>,
206        >,
207    >;
208
209    /// A type that knows how to build a block.
210    type BlockAssembler: BlockAssembler<
211        Self::BlockExecutorFactory,
212        Block = BlockTy<Self::Primitives>,
213    >;
214
215    /// Returns reference to the configured [`BlockExecutorFactory`].
216    fn block_executor_factory(&self) -> &Self::BlockExecutorFactory;
217
218    /// Returns reference to the configured [`BlockAssembler`].
219    fn block_assembler(&self) -> &Self::BlockAssembler;
220
221    /// Creates a new [`EvmEnv`] for the given header.
222    fn evm_env(&self, header: &HeaderTy<Self::Primitives>) -> Result<EvmEnvFor<Self>, Self::Error>;
223
224    /// Returns the configured [`EvmEnv`] for `parent + 1` block.
225    ///
226    /// This is intended for usage in block building after the merge and requires additional
227    /// attributes that can't be derived from the parent block: attributes that are determined by
228    /// the CL, such as the timestamp, suggested fee recipient, and randomness value.
229    ///
230    /// # Example
231    ///
232    /// ```rust,ignore
233    /// let evm_env = evm_config.next_evm_env(&parent_header, &attributes)?;
234    /// // evm_env now contains:
235    /// // - Correct spec ID based on timestamp and block number
236    /// // - Block environment with next block's parameters
237    /// // - Configuration like chain ID and blob parameters
238    /// ```
239    fn next_evm_env(
240        &self,
241        parent: &HeaderTy<Self::Primitives>,
242        attributes: &Self::NextBlockEnvCtx,
243    ) -> Result<EvmEnvFor<Self>, Self::Error>;
244
245    /// Returns the configured [`BlockExecutorFactory::ExecutionCtx`] for a given block.
246    fn context_for_block<'a>(
247        &self,
248        block: &'a SealedBlock<BlockTy<Self::Primitives>>,
249    ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error>;
250
251    /// Returns the configured [`BlockExecutorFactory::ExecutionCtx`] for `parent + 1`
252    /// block.
253    fn context_for_next_block(
254        &self,
255        parent: &SealedHeader<HeaderTy<Self::Primitives>>,
256        attributes: Self::NextBlockEnvCtx,
257    ) -> Result<ExecutionCtxFor<'_, Self>, Self::Error>;
258
259    /// Returns a [`EvmFactory::Tx`] from a transaction.
260    fn tx_env(&self, transaction: impl IntoTxEnv<TxEnvFor<Self>>) -> TxEnvFor<Self> {
261        transaction.into_tx_env()
262    }
263
264    /// Provides a reference to [`EvmFactory`] implementation.
265    fn evm_factory(&self) -> &EvmFactoryFor<Self> {
266        self.block_executor_factory().evm_factory()
267    }
268
269    /// Returns a config with JIT support enabled for subsequently created EVMs, if supported.
270    ///
271    /// This is one of three gates required before an EVM can execute JIT-compiled code: the binary
272    /// must be built with the `jit` feature, runtime compilation must be enabled by `--jit` or the
273    /// `reth_jit` RPC method, and this local support flag must be enabled for the config that
274    /// creates the EVM.
275    #[auto_impl(keep_default_for(&, Arc))]
276    fn with_jit_support_enabled(self, _enabled: bool) -> Self
277    where
278        Self: Sized,
279    {
280        self
281    }
282
283    /// Returns a config with local JIT support enabled for subsequently created EVMs, if supported.
284    #[auto_impl(keep_default_for(&, Arc))]
285    fn with_jit_support(self) -> Self
286    where
287        Self: Sized,
288    {
289        self.with_jit_support_enabled(true)
290    }
291
292    /// Returns the JIT backend, if supported.
293    fn jit_backend(&self) -> Option<&dyn JitBackend> {
294        None
295    }
296
297    /// Returns a new EVM with the given database configured with the given environment settings,
298    /// including the spec id and transaction environment.
299    ///
300    /// This will preserve any handler modifications
301    fn evm_with_env<DB: Database>(&self, db: DB, evm_env: EvmEnvFor<Self>) -> EvmFor<Self, DB> {
302        self.evm_factory().create_evm(db, evm_env)
303    }
304
305    /// Returns a new EVM with the given database configured with `cfg` and `block_env`
306    /// configuration derived from the given header. Relies on
307    /// [`ConfigureEvm::evm_env`].
308    ///
309    /// # Caution
310    ///
311    /// This does not initialize the tx environment.
312    fn evm_for_block<DB: Database>(
313        &self,
314        db: DB,
315        header: &HeaderTy<Self::Primitives>,
316    ) -> Result<EvmFor<Self, DB>, Self::Error> {
317        let evm_env = self.evm_env(header)?;
318        Ok(self.evm_with_env(db, evm_env))
319    }
320
321    /// Returns a new EVM with the given database configured with the given environment settings,
322    /// including the spec id.
323    ///
324    /// This will use the given external inspector as the EVM external context.
325    ///
326    /// This will preserve any handler modifications
327    fn evm_with_env_and_inspector<DB, I>(
328        &self,
329        db: DB,
330        evm_env: EvmEnvFor<Self>,
331        inspector: I,
332    ) -> EvmFor<Self, DB, I>
333    where
334        DB: Database,
335        I: InspectorFor<Self, DB>,
336    {
337        self.evm_factory().create_evm_with_inspector(db, evm_env, inspector)
338    }
339
340    /// Creates a strategy with given EVM and execution context.
341    fn create_executor<'a, DB, I>(
342        &'a self,
343        evm: EvmFor<Self, &'a mut State<DB>, I>,
344        ctx: <Self::BlockExecutorFactory as BlockExecutorFactory>::ExecutionCtx<'a>,
345    ) -> BlockExecutorForEvm<'a, Self, DB, I>
346    where
347        DB: Database,
348        I: InspectorFor<Self, &'a mut State<DB>> + 'a,
349    {
350        self.block_executor_factory().create_executor(evm, ctx)
351    }
352
353    /// Creates a strategy with a DB state borrow that can be shorter than the execution context.
354    fn create_executor_with_state<'a, 'db, DB, I>(
355        &'a self,
356        evm: EvmFor<Self, &'db mut State<DB>, I>,
357        ctx: <Self::BlockExecutorFactory as BlockExecutorFactory>::ExecutionCtx<'a>,
358    ) -> BlockExecutorFor<'a, Self::BlockExecutorFactory, &'db mut State<DB>, I>
359    where
360        DB: Database,
361        I: InspectorFor<Self, &'db mut State<DB>>,
362    {
363        self.block_executor_factory().create_executor(evm, ctx)
364    }
365
366    /// Creates a strategy for execution of a given block.
367    fn executor_for_block<'a, DB: Database>(
368        &'a self,
369        db: &'a mut State<DB>,
370        block: &'a SealedBlock<<Self::Primitives as NodePrimitives>::Block>,
371    ) -> Result<BlockExecutorForEvm<'a, Self, DB>, Self::Error> {
372        let evm = self.evm_for_block(db, block.header())?;
373        let ctx = self.context_for_block(block)?;
374        Ok(self.create_executor(evm, ctx))
375    }
376
377    /// Creates a [`BlockBuilder`]. Should be used when building a new block.
378    ///
379    /// Block builder wraps an inner [`alloy_evm::block::BlockExecutor`] and has a similar
380    /// interface. Builder collects all of the executed transactions, and once
381    /// [`BlockBuilder::finish`] is called, it invokes the configured [`BlockAssembler`] to
382    /// create a block.
383    ///
384    /// # Example
385    ///
386    /// ```rust,ignore
387    /// // Create a builder with specific EVM configuration
388    /// let evm = evm_config.evm_with_env(&mut state_db, evm_env);
389    /// let ctx = evm_config.context_for_next_block(&parent, attributes);
390    /// let builder = evm_config.create_block_builder(evm, &parent, ctx);
391    /// ```
392    fn create_block_builder<'a, DB, I>(
393        &'a self,
394        evm: EvmFor<Self, &'a mut State<DB>, I>,
395        parent: &'a SealedHeader<HeaderTy<Self::Primitives>>,
396        ctx: <Self::BlockExecutorFactory as BlockExecutorFactory>::ExecutionCtx<'a>,
397    ) -> impl BlockBuilder<Primitives = Self::Primitives, Executor = BlockExecutorForEvm<'a, Self, DB, I>>
398    where
399        DB: Database,
400        I: InspectorFor<Self, &'a mut State<DB>> + 'a,
401    {
402        self.create_block_builder_with(evm, parent, ctx, |executor| executor)
403    }
404
405    /// Creates a [`BlockBuilder`] like [`ConfigureEvm::create_block_builder`], with a customized
406    /// executor.
407    fn create_block_builder_with<'a, DB, I, E>(
408        &'a self,
409        evm: EvmFor<Self, &'a mut State<DB>, I>,
410        parent: &'a SealedHeader<HeaderTy<Self::Primitives>>,
411        ctx: <Self::BlockExecutorFactory as BlockExecutorFactory>::ExecutionCtx<'a>,
412        f: impl FnOnce(BlockExecutorForEvm<'a, Self, DB, I>) -> E,
413    ) -> impl BlockBuilder<Primitives = Self::Primitives, Executor = E>
414    where
415        DB: Database,
416        I: InspectorFor<Self, &'a mut State<DB>> + 'a,
417        E: alloy_evm::block::BlockExecutor<
418            Evm = EvmFor<Self, &'a mut State<DB>, I>,
419            Transaction = TxTy<Self::Primitives>,
420            Receipt = ReceiptTy<Self::Primitives>,
421        >,
422    {
423        BasicBlockBuilder {
424            executor: f(self.create_executor(evm, ctx.clone())),
425            ctx,
426            assembler: self.block_assembler(),
427            parent,
428            transactions: Vec::new(),
429        }
430    }
431
432    /// Creates a [`BlockBuilder`] for building of a new block. This is a helper to invoke
433    /// [`ConfigureEvm::create_block_builder`].
434    ///
435    /// This is the primary method for building new blocks. It combines:
436    /// 1. Creating the EVM environment for the next block
437    /// 2. Setting up the execution context from attributes
438    /// 3. Initializing the block builder with proper configuration
439    ///
440    /// # Example
441    ///
442    /// ```rust,ignore
443    /// // Build a block with specific attributes
444    /// let mut builder = evm_config.builder_for_next_block(
445    ///     &mut state_db,
446    ///     &parent_header,
447    ///     attributes
448    /// )?;
449    ///
450    /// // Execute system calls (e.g., beacon root update)
451    /// builder.apply_pre_execution_changes()?;
452    ///
453    /// // Execute transactions
454    /// for tx in transactions {
455    ///     builder.execute_transaction(tx)?;
456    /// }
457    ///
458    /// // Complete block building
459    /// let outcome = builder.finish(state_provider, None)?;
460    /// ```
461    fn builder_for_next_block<'a, DB: Database + 'a>(
462        &'a self,
463        db: &'a mut State<DB>,
464        parent: &'a SealedHeader<<Self::Primitives as NodePrimitives>::BlockHeader>,
465        attributes: Self::NextBlockEnvCtx,
466    ) -> Result<
467        impl BlockBuilder<Primitives = Self::Primitives, Executor = BlockExecutorForEvm<'a, Self, DB>>,
468        Self::Error,
469    > {
470        let evm_env = self.next_evm_env(parent, &attributes)?;
471        let evm = self.evm_with_env(db, evm_env);
472        let ctx = self.context_for_next_block(parent, attributes)?;
473        Ok(self.create_block_builder(evm, parent, ctx))
474    }
475
476    /// Returns a new [`Executor`] for executing blocks.
477    ///
478    /// The executor processes complete blocks including:
479    /// - All transactions in order
480    /// - Block rewards and fees
481    /// - Block level system calls
482    /// - State transitions
483    ///
484    /// # Example
485    ///
486    /// ```rust,ignore
487    /// // Create an executor
488    /// let mut executor = evm_config.executor(state_db);
489    ///
490    /// // Execute a single block
491    /// let output = executor.execute(&block)?;
492    ///
493    /// // Execute multiple blocks
494    /// let batch_output = executor.execute_batch(&blocks)?;
495    /// ```
496    #[auto_impl(keep_default_for(&, Arc))]
497    fn executor<DB: Database>(
498        &self,
499        db: DB,
500    ) -> impl Executor<DB, Primitives = Self::Primitives, Error = BlockExecutionError> {
501        BasicBlockExecutor::new(self, db)
502    }
503
504    /// Returns a new [`BasicBlockExecutor`].
505    #[auto_impl(keep_default_for(&, Arc))]
506    fn batch_executor<DB: Database>(
507        &self,
508        db: DB,
509    ) -> impl Executor<DB, Primitives = Self::Primitives, Error = BlockExecutionError> {
510        BasicBlockExecutor::new(self, db)
511    }
512
513    /// Returns whether engine validation may commit transaction results from speculative prewarm
514    /// execution instead of executing the transactions again.
515    ///
516    /// Return `true` only if all of these hold, see also [`Self::prewarm_tx_result`]:
517    /// - the block executor executes a transaction by checking that its gas limit, capped by the
518    ///   transaction gas limit cap, fits into the block gas limit minus the last receipt's
519    ///   cumulative gas used, and then running [`Evm::transact`] with the transaction environment;
520    /// - [`Evm::transact`] performs the standard caller nonce and balance validation, and rejects
521    ///   transactions with the largest nonce even if nonce checks are disabled;
522    /// - EVMs created by this configuration with JIT support disabled call inspector hooks for
523    ///   every executed instruction;
524    /// - execution depends only on recorded database reads and the transaction and block
525    ///   environments reproduced during prewarming, with no unrecorded external state.
526    ///
527    /// [`Evm::transact`]: alloy_evm::Evm::transact
528    fn reuses_prewarm_results(&self) -> bool {
529        false
530    }
531
532    /// Wraps a speculative prewarm execution result of `tx` into the result consumed by
533    /// [`BlockExecutor::commit_transaction`](alloy_evm::block::BlockExecutor::commit_transaction).
534    ///
535    /// Only called if [`Self::reuses_prewarm_results`] returns `true`.
536    /// Returning `None` rejects and consumes this speculative result; the engine executes the
537    /// transaction normally instead.
538    fn prewarm_tx_result(
539        &self,
540        tx: &TxTy<Self::Primitives>,
541        result: ResultAndState<HaltReasonFor<Self>>,
542    ) -> Option<TxExecutionResultFor<Self>> {
543        let _ = (tx, result);
544        None
545    }
546
547    /// Returns the predicted environment of the block after `parent` with `beneficiary`, in which
548    /// engine validation speculatively executes pool transactions before the block arrives.
549    ///
550    /// Validation reuses such results only for blocks whose environment matches the predicted
551    /// one, apart from the beneficiary, timestamp, prevrandao and gas limit, which the
552    /// transactions must not have observed. Only called if [`Self::reuses_prewarm_results`]
553    /// returns `true`. Returns `None` if the environment cannot be predicted, which disables
554    /// recording pool transaction results.
555    fn speculative_next_evm_env(
556        &self,
557        parent: &SealedHeader<HeaderTy<Self::Primitives>>,
558        beneficiary: Address,
559    ) -> Option<EvmEnvFor<Self>> {
560        let _ = (parent, beneficiary);
561        None
562    }
563}
564
565/// JIT backend controls exposed by an EVM configuration.
566pub trait JitBackend: Send + Sync {
567    /// Enables or disables JIT compilation.
568    fn set_enabled(&self, enabled: bool) -> Result<(), String>;
569
570    /// Pauses JIT helper execution while keeping queueing and resident compiled code available.
571    fn pause(&self);
572
573    /// Resumes background JIT work.
574    fn resume(&self);
575
576    /// Clears JIT runtime state.
577    fn clear(&self);
578}
579
580/// Represents additional attributes required to configure the next block.
581///
582/// This struct contains all the information needed to build a new block that cannot be
583/// derived from the parent block header alone. These attributes are typically provided
584/// by the consensus layer (CL) through the Engine API during payload building.
585///
586/// # Relationship with [`ConfigureEvm`] and [`BlockAssembler`]
587///
588/// The flow for building a new block involves:
589///
590/// 1. **Receive attributes** from the consensus layer containing:
591///    - Timestamp for the new block
592///    - Fee recipient (coinbase/beneficiary)
593///    - Randomness value (prevRandao)
594///    - Withdrawals to process
595///    - Parent beacon block root for EIP-4788
596///
597/// 2. **Configure EVM environment** using these attributes: ```rust,ignore let evm_env =
598///    evm_config.next_evm_env(&parent, &attributes)?; ```
599///
600/// 3. **Build the block** with transactions: ```rust,ignore let mut builder =
601///    evm_config.builder_for_next_block( &mut state, &parent, attributes )?; ```
602///
603/// 4. **Assemble the final block** using [`BlockAssembler`] which takes:
604///    - Execution results from all transactions
605///    - The attributes used during execution
606///    - Final state root after all changes
607///
608/// This design cleanly separates:
609/// - **Configuration** (what parameters to use) - handled by `NextBlockEnvAttributes`
610/// - **Execution** (running transactions) - handled by `BlockExecutor`
611/// - **Assembly** (creating the final block) - handled by `BlockAssembler`
612#[derive(Debug, Clone, PartialEq, Eq)]
613pub struct NextBlockEnvAttributes {
614    /// The timestamp of the next block.
615    pub timestamp: u64,
616    /// The suggested fee recipient for the next block.
617    pub suggested_fee_recipient: Address,
618    /// The randomness value for the next block.
619    pub prev_randao: B256,
620    /// Block gas limit.
621    pub gas_limit: u64,
622    /// The parent beacon block root.
623    pub parent_beacon_block_root: Option<B256>,
624    /// Withdrawals
625    pub withdrawals: Option<Withdrawals>,
626    /// Optional extra data.
627    pub extra_data: Bytes,
628    /// Optional slot number for post-Amsterdam payloads.
629    pub slot_number: Option<u64>,
630}