Skip to main content

reth_bb/
evm_config.rs

1//! Big-block EVM configuration.
2//!
3//! Wraps [`EthEvmConfig`] to create executors that handle multi-segment
4//! big-block execution internally. At transaction boundaries defined by
5//! [`BigBlockData`], the executor swaps the EVM environment (block env,
6//! cfg env) and applies pre/post execution changes for each segment.
7
8pub(crate) use reth_engine_primitives::BigBlockData;
9
10use crate::{
11    evm::{BbBlockExecutorFactory, BbEvmPlan},
12    BigBlockMap,
13};
14use alloy_consensus::Header;
15use alloy_evm::eth::EthBlockExecutionCtx;
16use alloy_primitives::B256;
17use alloy_rpc_types::engine::ExecutionData;
18use core::convert::Infallible;
19use reth_chainspec::{ChainSpec, EthChainSpec};
20use reth_ethereum_forks::Hardforks;
21use reth_ethereum_primitives::EthPrimitives;
22use reth_evm::{
23    ConfigureEngineEvm, ConfigureEvm, Database, EvmEnv, ExecutableTxIterator,
24    NextBlockEnvAttributes,
25};
26use reth_evm_ethereum::{EthBlockAssembler, EthEvmConfig, RethReceiptBuilder};
27use reth_primitives_traits::{SealedBlock, SealedHeader};
28use revm::primitives::hardfork::SpecId;
29use std::sync::Arc;
30use tracing::debug;
31
32use alloy_evm::{eth::spec::EthExecutorSpec, EthEvmFactory};
33use reth_evm::{EvmEnvFor, ExecutionCtxFor};
34
35// ---------------------------------------------------------------------------
36// Execution plan types
37// ---------------------------------------------------------------------------
38
39/// A single execution segment within a big block.
40#[derive(Debug, Clone)]
41pub(crate) struct BigBlockSegment {
42    /// Transaction index at which this segment starts.
43    pub start_tx: usize,
44    /// The EVM environment for this segment.
45    pub evm_env: EvmEnv,
46    /// The execution context for this segment.
47    pub ctx: EthBlockExecutionCtx<'static>,
48}
49
50// ---------------------------------------------------------------------------
51// BbEvmConfig
52// ---------------------------------------------------------------------------
53
54/// EVM configuration for big-block execution.
55///
56/// Wraps [`EthEvmConfig`] and a shared [`BigBlockMap`]. When a big-block
57/// payload is received, the plan is staged on the [`BbBlockExecutorFactory`]
58/// and consumed when the executor is created. Block hashes for inter-segment
59/// BLOCKHASH resolution are reseeded into `State::block_hashes` at each
60/// segment boundary via a [`BlockHashSeeder`](crate::evm::BlockHashSeeder)
61/// callback injected in [`ConfigureEvm::create_executor`].
62#[derive(Debug, Clone)]
63pub struct BbEvmConfig<C = ChainSpec> {
64    /// The inner Ethereum EVM configuration (used for env computation).
65    pub inner: EthEvmConfig<C>,
66    /// Shared map of pending big-block metadata.
67    pub pending: BigBlockMap,
68    /// Block executor factory for big-block execution.
69    executor_factory: BbBlockExecutorFactory<Arc<C>>,
70    /// Block assembler.
71    block_assembler: EthBlockAssembler<C>,
72}
73
74impl<C> BbEvmConfig<C> {
75    /// Creates a new big-block EVM configuration.
76    pub fn new(inner: EthEvmConfig<C>, pending: BigBlockMap) -> Self
77    where
78        C: Clone,
79    {
80        let chain_spec = inner.chain_spec().clone();
81        let executor_factory = BbBlockExecutorFactory::new(
82            RethReceiptBuilder::default(),
83            chain_spec,
84            EthEvmFactory::default(),
85        );
86        let block_assembler = inner.block_assembler.clone();
87
88        Self { inner, pending, executor_factory, block_assembler }
89    }
90}
91
92// ---------------------------------------------------------------------------
93// Block hash seeder for State<DB>
94// ---------------------------------------------------------------------------
95
96/// Reseeds `State::block_hashes` with the given hashes.
97///
98/// This is used as a [`BlockHashSeeder`](crate::evm::BlockHashSeeder) callback,
99/// injected into [`BbBlockExecutor`](crate::evm::BbBlockExecutor) from
100/// `ConfigureEvm::create_executor` where the concrete `State<DB>` type is known.
101/// At each segment boundary the executor calls this to populate the ring buffer
102/// with the 256 block hashes relevant to the new segment's block number window.
103fn seed_state_block_hashes<DB>(state: &mut &mut revm::database::State<DB>, hashes: &[(u64, B256)]) {
104    for &(number, hash) in hashes {
105        state.block_hashes.insert(number, hash);
106    }
107}
108
109// ---------------------------------------------------------------------------
110// ConfigureEvm
111// ---------------------------------------------------------------------------
112
113impl<C> ConfigureEvm for BbEvmConfig<C>
114where
115    C: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
116{
117    type Primitives = EthPrimitives;
118    type Error = Infallible;
119    type NextBlockEnvCtx = NextBlockEnvAttributes;
120    type BlockExecutorFactory = BbBlockExecutorFactory<Arc<C>>;
121    type BlockAssembler = EthBlockAssembler<C>;
122
123    fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
124        &self.executor_factory
125    }
126
127    fn block_assembler(&self) -> &Self::BlockAssembler {
128        &self.block_assembler
129    }
130
131    fn evm_env(&self, header: &Header) -> Result<EvmEnv<SpecId>, Self::Error> {
132        self.inner.evm_env(header)
133    }
134
135    fn next_evm_env(
136        &self,
137        parent: &Header,
138        attributes: &NextBlockEnvAttributes,
139    ) -> Result<EvmEnv, Self::Error> {
140        self.inner.next_evm_env(parent, attributes)
141    }
142
143    fn context_for_block<'a>(
144        &self,
145        block: &'a SealedBlock<reth_ethereum_primitives::Block>,
146    ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
147        self.inner.context_for_block(block)
148    }
149
150    fn context_for_next_block(
151        &self,
152        parent: &SealedHeader,
153        attributes: Self::NextBlockEnvCtx,
154    ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
155        self.inner.context_for_next_block(parent, attributes)
156    }
157
158    fn create_executor<'a, DB, I>(
159        &'a self,
160        evm: reth_evm::EvmFor<Self, &'a mut revm::database::State<DB>, I>,
161        ctx: EthBlockExecutionCtx<'a>,
162    ) -> impl alloy_evm::block::BlockExecutorFor<
163        'a,
164        Self::BlockExecutorFactory,
165        &'a mut revm::database::State<DB>,
166        I,
167    >
168    where
169        DB: Database,
170        I: reth_evm::InspectorFor<Self, &'a mut revm::database::State<DB>> + 'a,
171    {
172        // Use create_executor_with_seeder to inject a concrete seeder that
173        // can reseed State::block_hashes at segment boundaries. The seeder
174        // is a function pointer that knows the concrete State<DB> type,
175        // allowing the generic BbBlockExecutor to reseed without additional
176        // trait bounds on DB.
177        self.executor_factory.create_executor_with_seeder(
178            evm,
179            ctx,
180            Some(seed_state_block_hashes::<DB>),
181        )
182    }
183}
184
185// ---------------------------------------------------------------------------
186// ConfigureEngineEvm — intercepts payload methods for big blocks
187// ---------------------------------------------------------------------------
188
189impl<C> ConfigureEngineEvm<ExecutionData> for BbEvmConfig<C>
190where
191    C: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
192{
193    fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
194        let payload_hash = payload.block_hash();
195        let has_plan = self.pending.lock().unwrap().contains_key(&payload_hash);
196
197        if has_plan {
198            // Compute the env from the first segment BEFORE removing the
199            // entry (stage_plan_for_payload removes it).
200            let first_exec_data = {
201                let pending = self.pending.lock().unwrap();
202                let bb_data = pending.get(&payload_hash).unwrap();
203                bb_data.env_switches[0].1.clone()
204            };
205            let mut env = self.inner.evm_env_for_payload(&first_exec_data)?;
206
207            // Disable basefee validation: transactions from different
208            // original blocks may have gas prices below the big block's
209            // effective basefee.
210            env.cfg_env.disable_base_fee = true;
211
212            // Now stage the plan on the factory (removes the entry).
213            self.stage_plan_for_payload(&payload_hash);
214
215            Ok(env)
216        } else {
217            self.inner.evm_env_for_payload(payload)
218        }
219    }
220
221    fn context_for_payload<'a>(
222        &self,
223        payload: &'a ExecutionData,
224    ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
225        self.inner.context_for_payload(payload)
226    }
227
228    fn tx_iterator_for_payload(
229        &self,
230        payload: &ExecutionData,
231    ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
232        self.inner.tx_iterator_for_payload(payload)
233    }
234}
235
236// ---------------------------------------------------------------------------
237// Plan construction and staging
238// ---------------------------------------------------------------------------
239
240impl<C> BbEvmConfig<C>
241where
242    C: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
243{
244    /// Takes the big-block plan for a payload hash, builds a [`BbEvmPlan`],
245    /// and stages it on the factory.
246    ///
247    /// Must be called before `evm_with_env` is invoked for this payload.
248    /// In practice, this is called from `evm_env_for_payload` in the
249    /// engine pipeline.
250    pub fn stage_plan_for_payload(&self, payload_hash: &B256) {
251        let bb = match self.pending.lock().unwrap().remove(payload_hash) {
252            Some(bb) => bb,
253            None => return,
254        };
255
256        let segments: Vec<_> = bb
257            .env_switches
258            .into_iter()
259            .map(|(start_tx, exec_data)| {
260                let evm_env = self.inner.evm_env_for_payload(&exec_data).unwrap();
261                let ctx = self.inner.context_for_payload(&exec_data).unwrap();
262                let ctx = EthBlockExecutionCtx {
263                    tx_count_hint: ctx.tx_count_hint,
264                    parent_hash: ctx.parent_hash,
265                    parent_beacon_block_root: ctx.parent_beacon_block_root,
266                    ommers: &[],
267                    withdrawals: ctx.withdrawals.map(|w| std::borrow::Cow::Owned(w.into_owned())),
268                    extra_data: ctx.extra_data,
269                };
270                BigBlockSegment { start_tx, evm_env, ctx }
271            })
272            .collect();
273
274        debug!(
275            target: "engine::bb",
276            ?payload_hash,
277            segments = segments.len(),
278            seed_hashes = bb.prior_block_hashes.len(),
279            "Staging multi-segment plan"
280        );
281
282        let mut plan = BbEvmPlan::new(segments);
283
284        // Add prior block hashes to the seeding list.
285        plan.block_hashes_to_seed.extend(bb.prior_block_hashes);
286
287        plan.block_hashes_to_seed.sort_unstable_by_key(|(n, _)| *n);
288
289        self.executor_factory.stage_plan(plan);
290    }
291}