1pub(crate) use reth_engine_primitives::BigBlockData;
9use reth_engine_primitives::ExecutionPayload as _;
10use reth_storage_errors::any::AnyError;
11use revm_primitives::Bytes;
12
13use crate::evm::{BalIndexReader, BbBlockExecutorFactory, BbEvmPlan};
14use alloy_consensus::Header;
15use alloy_eips::Decodable2718;
16use alloy_evm::{
17 eth::{spec::EthExecutorSpec, EthBlockExecutionCtx},
18 EthEvmFactory,
19};
20use alloy_primitives::B256;
21use alloy_rpc_types::engine::ExecutionData;
22use core::convert::Infallible;
23use reth_chainspec::{ChainSpec, EthChainSpec};
24use reth_ethereum_forks::Hardforks;
25use reth_ethereum_primitives::{Block, EthPrimitives};
26use reth_evm::{
27 execute::BlockAssembler, ConfigureEngineEvm, ConfigureEvm, Database, EvmEnv, EvmEnvFor,
28 ExecutableTxIterator, ExecutionCtxFor, NextBlockEnvAttributes,
29};
30use reth_evm_ethereum::{EthEvmConfig, RethReceiptBuilder};
31use reth_primitives_traits::{SealedBlock, SealedHeader, SignedTransaction, TxTy};
32use revm::primitives::hardfork::SpecId;
33use std::sync::Arc;
34
35#[derive(Debug, Clone)]
41pub(crate) struct BigBlockSegment<'a> {
42 pub start_tx: usize,
44 pub evm_env: EvmEnv,
46 pub ctx: EthBlockExecutionCtx<'a>,
48}
49
50#[derive(Debug, Clone)]
63pub struct BbEvmConfig<C = ChainSpec> {
64 pub inner: EthEvmConfig<C>,
66 executor_factory: BbBlockExecutorFactory<Arc<C>>,
68 block_assembler: BbBlockAssembler,
70}
71
72impl<C> BbEvmConfig<C> {
73 pub fn new(inner: EthEvmConfig<C>) -> Self
75 where
76 C: Clone,
77 {
78 let chain_spec = inner.chain_spec().clone();
79 let executor_factory = BbBlockExecutorFactory::new(
80 RethReceiptBuilder::default(),
81 chain_spec,
82 EthEvmFactory::default(),
83 );
84
85 Self { inner, executor_factory, block_assembler: Default::default() }
86 }
87}
88
89fn seed_state_block_hashes<DB>(state: &mut &mut revm::database::State<DB>, hashes: &[(u64, B256)]) {
101 for &(number, hash) in hashes {
102 state.block_hashes.insert(number, hash);
103 }
104}
105
106fn read_bal_index<DB>(state: &&mut revm::database::State<DB>) -> u64 {
112 state.bal_state.bal_index()
113}
114
115fn bump_bal_index<DB: revm::Database>(state: &mut &mut revm::database::State<DB>) {
123 state.bump_bal_index();
124}
125
126fn set_bal_index<DB: revm::Database>(state: &mut &mut revm::database::State<DB>, index: u64) {
133 state.set_bal_index(index);
134}
135
136impl<C> ConfigureEvm for BbEvmConfig<C>
141where
142 C: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
143{
144 type Primitives = EthPrimitives;
145 type Error = Infallible;
146 type NextBlockEnvCtx = NextBlockEnvAttributes;
147 type BlockExecutorFactory = BbBlockExecutorFactory<Arc<C>>;
148 type BlockAssembler = BbBlockAssembler;
149
150 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
151 &self.executor_factory
152 }
153
154 fn block_assembler(&self) -> &Self::BlockAssembler {
155 &self.block_assembler
156 }
157
158 fn evm_env(&self, header: &Header) -> Result<EvmEnv<SpecId>, Self::Error> {
159 self.inner.evm_env(header)
160 }
161
162 fn next_evm_env(
163 &self,
164 parent: &Header,
165 attributes: &NextBlockEnvAttributes,
166 ) -> Result<EvmEnv, Self::Error> {
167 self.inner.next_evm_env(parent, attributes)
168 }
169
170 fn context_for_block<'a>(
171 &self,
172 _block: &'a SealedBlock<reth_ethereum_primitives::Block>,
173 ) -> Result<BbEvmPlan<'a>, Self::Error> {
174 unreachable!("big blocks EVM should only be used from within the engine pipeline")
175 }
176
177 fn context_for_next_block(
178 &self,
179 _parent: &SealedHeader,
180 _attributes: Self::NextBlockEnvCtx,
181 ) -> Result<BbEvmPlan<'_>, Self::Error> {
182 unreachable!("big blocks EVM should only be used from within the engine pipeline")
183 }
184
185 fn create_executor<'a, DB, I>(
186 &'a self,
187 evm: reth_evm::EvmFor<Self, &'a mut revm::database::State<DB>, I>,
188 ctx: BbEvmPlan<'a>,
189 ) -> alloy_evm::block::BlockExecutorFor<
190 'a,
191 Self::BlockExecutorFactory,
192 &'a mut revm::database::State<DB>,
193 I,
194 >
195 where
196 DB: Database,
197 I: reth_evm::InspectorFor<Self, &'a mut revm::database::State<DB>> + 'a,
198 {
199 let bal_index_reader: Option<BalIndexReader<&'a mut revm::database::State<DB>>> =
200 Some(read_bal_index::<DB>);
201
202 self.executor_factory.create_executor_with_seeder(
206 evm,
207 ctx,
208 Some(seed_state_block_hashes::<DB>),
209 bal_index_reader,
210 Some(bump_bal_index::<DB>),
211 Some(set_bal_index::<DB>),
212 )
213 }
214
215 fn create_executor_with_state<'ctx, 'db, DB, I>(
216 &'ctx self,
217 evm: reth_evm::EvmFor<Self, &'db mut revm::database::State<DB>, I>,
218 ctx: BbEvmPlan<'ctx>,
219 ) -> alloy_evm::block::BlockExecutorFor<
220 'ctx,
221 Self::BlockExecutorFactory,
222 &'db mut revm::database::State<DB>,
223 I,
224 >
225 where
226 DB: Database,
227 I: reth_evm::InspectorFor<Self, &'db mut revm::database::State<DB>>,
228 {
229 let bal_index_reader: Option<BalIndexReader<&'db mut revm::database::State<DB>>> =
230 Some(read_bal_index::<DB>);
231
232 self.executor_factory.create_executor_with_seeder(
233 evm,
234 ctx,
235 Some(seed_state_block_hashes::<DB>),
236 bal_index_reader,
237 Some(bump_bal_index::<DB>),
238 Some(set_bal_index::<DB>),
239 )
240 }
241}
242
243impl<C> ConfigureEngineEvm<BigBlockData<ExecutionData>> for BbEvmConfig<C>
248where
249 C: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
250{
251 fn evm_env_for_payload(
252 &self,
253 payload: &BigBlockData<ExecutionData>,
254 ) -> Result<EvmEnvFor<Self>, Self::Error> {
255 let first_exec_data = &payload.env_switches[0];
258 let mut env = self.inner.evm_env_for_payload(first_exec_data)?;
259
260 env.cfg_env.disable_base_fee = true;
264 env.block_env.gas_limit = payload.gas_limit();
265
266 Ok(env)
267 }
268
269 fn context_for_payload<'a>(
270 &self,
271 payload: &'a BigBlockData<ExecutionData>,
272 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
273 let mut current_tx = 0;
274 let segments: Vec<_> = payload
275 .env_switches
276 .iter()
277 .map(|exec_data| {
278 let start_tx = current_tx;
279 let mut evm_env = self.inner.evm_env_for_payload(exec_data)?;
280 evm_env.cfg_env.disable_base_fee = true;
281 let ctx = self.inner.context_for_payload(exec_data)?;
282 current_tx += exec_data.payload.transactions().len();
283 Ok(BigBlockSegment { start_tx, evm_env, ctx })
284 })
285 .collect::<Result<_, Self::Error>>()?;
286
287 let mut plan = BbEvmPlan::new(segments);
288
289 plan.block_hashes_to_seed.extend(payload.prior_block_hashes.clone());
291 plan.block_hashes_to_seed.sort_unstable_by_key(|(n, _)| *n);
292
293 Ok(plan)
294 }
295
296 fn tx_iterator_for_payload(
297 &self,
298 payload: &BigBlockData<ExecutionData>,
299 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
300 let transactions = payload
301 .env_switches
302 .iter()
303 .flat_map(|exec_data| exec_data.payload.transactions().clone())
304 .collect::<Vec<_>>();
305
306 let convert = |tx: Bytes| {
307 let tx =
308 TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
309 let signer = tx.try_recover().map_err(AnyError::new)?;
310 Ok::<_, AnyError>(tx.with_signer(signer))
311 };
312
313 Ok((transactions, convert))
314 }
315}
316
317#[derive(Debug, Default, Clone)]
318pub struct BbBlockAssembler;
319
320impl<Spec: EthExecutorSpec + 'static> BlockAssembler<BbBlockExecutorFactory<Spec>>
321 for BbBlockAssembler
322{
323 type Block = Block;
324
325 fn assemble_block(
326 &self,
327 _input: reth_evm::execute::BlockAssemblerInput<
328 '_,
329 '_,
330 BbBlockExecutorFactory<Spec>,
331 <Self::Block as reth_node_api::Block>::Header,
332 >,
333 ) -> Result<Self::Block, reth_errors::BlockExecutionError> {
334 unreachable!("block building is not supported for big blocks EVM")
335 }
336}