1use alloc::sync::Arc;
2use core::{convert::Infallible, fmt::Debug};
3
4use alloy_consensus::{BlockHeader, Header};
5use alloy_eips::Decodable2718;
6use alloy_evm::eth::{EthBlockExecutionCtx, spec::EthExecutorSpec};
7use alloy_primitives::{Address, B256, Bytes, U256};
8use alloy_rpc_types_engine::ExecutionData;
9use arb_chainspec::ArbitrumChainSpec;
10use arb_primitives::ArbPrimitives;
11use reth_chainspec::{EthChainSpec, Hardforks};
12use reth_evm::{
13 ConfigureEngineEvm, ConfigureEvm, Database, EvmEnv, EvmEnvFor, EvmFor, ExecutableTxIterator,
14 ExecutionCtxFor, InspectorFor, NextBlockEnvAttributes, block::BlockExecutorFor,
15};
16use reth_primitives_traits::{SealedBlock, SealedHeader, SignedTransaction, TxTy};
17use reth_storage_errors::any::AnyError;
18use revm::{
19 context::{BlockEnv, CfgEnv},
20 primitives::hardfork::SpecId,
21};
22
23use crate::{
24 assembler::ArbBlockAssembler, build::ArbBlockExecutorFactory, context::ArbBlockExecutionCtx,
25 evm::ArbEvmFactory, receipt::ArbReceiptBuilder,
26};
27
28#[derive(Debug, Clone)]
33pub struct ArbEvmConfig<ChainSpec = reth_chainspec::ChainSpec> {
34 pub executor_factory: ArbBlockExecutorFactory<ArbReceiptBuilder, Arc<ChainSpec>, ArbEvmFactory>,
35 pub block_assembler: ArbBlockAssembler<ChainSpec>,
36 chain_spec: Arc<ChainSpec>,
37}
38
39impl<ChainSpec> ArbEvmConfig<ChainSpec>
40where
41 ChainSpec: EthChainSpec + 'static,
42{
43 pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
45 Self::with_allow_debug_precompiles(chain_spec, false)
46 }
47
48 pub fn with_allow_debug_precompiles(chain_spec: Arc<ChainSpec>, allow_debug: bool) -> Self {
50 Self::build(chain_spec, allow_debug, ArbEvmFactory::new())
51 }
52
53 pub fn for_offline_execution(chain_spec: Arc<ChainSpec>, allow_debug: bool) -> Self {
56 Self::build(chain_spec, allow_debug, ArbEvmFactory::isolated())
57 }
58
59 fn build(chain_spec: Arc<ChainSpec>, allow_debug: bool, evm_factory: ArbEvmFactory) -> Self {
60 Self {
61 executor_factory: ArbBlockExecutorFactory::new(
62 ArbReceiptBuilder,
63 chain_spec.clone(),
64 evm_factory,
65 )
66 .with_allow_debug_precompiles(allow_debug),
67 block_assembler: ArbBlockAssembler::new(chain_spec.clone()),
68 chain_spec,
69 }
70 }
71
72 pub fn chain_spec(&self) -> &Arc<ChainSpec> {
74 &self.chain_spec
75 }
76}
77
78impl<ChainSpec> ConfigureEvm for ArbEvmConfig<ChainSpec>
79where
80 ChainSpec:
81 EthExecutorSpec + EthChainSpec<Header = Header> + ArbitrumChainSpec + Hardforks + 'static,
82{
83 type Primitives = ArbPrimitives;
84 type Error = Infallible;
85 type NextBlockEnvCtx = NextBlockEnvAttributes;
86 type BlockExecutorFactory =
87 ArbBlockExecutorFactory<ArbReceiptBuilder, Arc<ChainSpec>, ArbEvmFactory>;
88 type BlockAssembler = ArbBlockAssembler<ChainSpec>;
89
90 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
91 &self.executor_factory
92 }
93
94 fn block_assembler(&self) -> &Self::BlockAssembler {
95 &self.block_assembler
96 }
97
98 fn create_executor<'a, DB, I>(
99 &'a self,
100 evm: EvmFor<Self, &'a mut revm::database::State<DB>, I>,
101 ctx: ExecutionCtxFor<'a, Self>,
102 ) -> impl BlockExecutorFor<'a, Self::BlockExecutorFactory, &'a mut revm::database::State<DB>, I>
103 where
104 DB: Database,
105 I: InspectorFor<Self, &'a mut revm::database::State<DB>> + 'a,
106 {
107 self.executor_factory
108 .create_arb_executor(evm, ctx, self.chain_spec.chain().id())
109 }
110
111 fn evm_env(&self, header: &Header) -> Result<EvmEnv<SpecId>, Self::Error> {
112 let chain_id = self.chain_spec.chain().id();
113 let mix_hash = header.mix_hash().unwrap_or_default();
114 let arbos_version = arbos_version_from_mix_hash(&mix_hash);
115 let spec = self.chain_spec.spec_id_by_arbos_version(arbos_version);
116
117 let l1_block_number = l1_block_number_from_mix_hash(&mix_hash);
119
120 stage_rpc_block_ctx(
121 self.executor_factory.arb_evm_factory(),
122 arbos_version,
123 header.timestamp(),
124 l1_block_number,
125 header.number(),
126 self.executor_factory.allow_debug_precompiles(),
127 );
128
129 let cfg_env = arb_cfg_env(chain_id, spec, arbos_version);
130 let prevrandao = B256::from(U256::from(1));
132 let block_env = BlockEnv {
133 number: U256::from(l1_block_number),
134 beneficiary: header.beneficiary(),
135 timestamp: U256::from(header.timestamp()),
136 difficulty: header.difficulty(),
137 prevrandao: Some(prevrandao),
138 gas_limit: header.gas_limit(),
139 basefee: header.base_fee_per_gas().unwrap_or_default(),
140 slot_num: 0,
141 blob_excess_gas_and_price: if spec.is_enabled_in(SpecId::CANCUN) {
142 Some(revm::context_interface::block::BlobExcessGasAndPrice {
143 excess_blob_gas: 0,
144 blob_gasprice: 0,
145 })
146 } else {
147 None
148 },
149 };
150
151 Ok(EvmEnv { cfg_env, block_env })
152 }
153
154 fn next_evm_env(
155 &self,
156 parent: &Header,
157 attributes: &NextBlockEnvAttributes,
158 ) -> Result<EvmEnv<SpecId>, Self::Error> {
159 let chain_id = self.chain_spec.chain().id();
160 let arbos_version = arbos_version_from_mix_hash(&attributes.prev_randao);
161 let spec = self.chain_spec.spec_id_by_arbos_version(arbos_version);
162
163 let l1_block_number_initial = l1_block_number_from_mix_hash(&attributes.prev_randao);
164 stage_rpc_block_ctx(
165 self.executor_factory.arb_evm_factory(),
166 arbos_version,
167 attributes.timestamp,
168 l1_block_number_initial,
169 parent.number().saturating_add(1),
170 self.executor_factory.allow_debug_precompiles(),
171 );
172
173 let cfg_env = arb_cfg_env(chain_id, spec, arbos_version);
174 let prevrandao = B256::from(U256::from(1));
176 let block_env = BlockEnv {
177 number: U256::from(l1_block_number_initial),
178 beneficiary: attributes.suggested_fee_recipient,
179 timestamp: U256::from(attributes.timestamp),
180 difficulty: U256::from(1),
181 prevrandao: Some(prevrandao),
182 gas_limit: attributes.gas_limit,
183 basefee: parent.base_fee_per_gas().unwrap_or_default(),
184 slot_num: 0,
185 blob_excess_gas_and_price: if spec.is_enabled_in(SpecId::CANCUN) {
186 Some(revm::context_interface::block::BlobExcessGasAndPrice {
187 excess_blob_gas: 0,
188 blob_gasprice: 0,
189 })
190 } else {
191 None
192 },
193 };
194
195 Ok(EvmEnv { cfg_env, block_env })
196 }
197
198 fn context_for_block<'a>(
199 &self,
200 block: &'a SealedBlock<alloy_consensus::Block<arb_primitives::ArbTransactionSigned>>,
201 ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
202 let mut extra = block.header().extra_data.to_vec();
205 extra.extend_from_slice(&block.header().nonce.0);
206 extra.extend_from_slice(&block.header().number.to_be_bytes());
207 Ok(EthBlockExecutionCtx {
208 tx_count_hint: Some(block.transaction_count()),
209 parent_hash: block.header().parent_hash,
210 parent_beacon_block_root: block.header().parent_beacon_block_root,
211 ommers: &[],
212 withdrawals: None,
213 extra_data: extra.into(),
214 })
215 }
216
217 fn context_for_next_block(
218 &self,
219 parent: &SealedHeader<Header>,
220 attributes: NextBlockEnvAttributes,
221 ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
222 Ok(EthBlockExecutionCtx {
223 tx_count_hint: None,
224 parent_hash: parent.hash(),
225 parent_beacon_block_root: attributes.parent_beacon_block_root,
226 ommers: &[],
227 withdrawals: None,
228 extra_data: attributes.extra_data,
229 })
230 }
231}
232
233impl<ChainSpec> ConfigureEngineEvm<ExecutionData> for ArbEvmConfig<ChainSpec>
234where
235 ChainSpec:
236 EthExecutorSpec + EthChainSpec<Header = Header> + ArbitrumChainSpec + Hardforks + 'static,
237{
238 fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
239 let prev_randao = payload.payload.as_v1().prev_randao;
240 let arbos_version = arbos_version_from_mix_hash(&prev_randao);
241 let spec = self.chain_spec.spec_id_by_arbos_version(arbos_version);
242
243 let l1_block_number = l1_block_number_from_mix_hash(&prev_randao);
245 stage_rpc_block_ctx(
246 self.executor_factory.arb_evm_factory(),
247 arbos_version,
248 payload.payload.timestamp(),
249 l1_block_number,
250 payload.payload.block_number(),
251 self.executor_factory.allow_debug_precompiles(),
252 );
253
254 let cfg_env = arb_cfg_env(self.chain_spec.chain().id(), spec, arbos_version);
255
256 let prevrandao = B256::from(U256::from(1));
258 let block_env = BlockEnv {
259 number: U256::from(l1_block_number),
260 beneficiary: payload.payload.fee_recipient(),
261 timestamp: U256::from(payload.payload.timestamp()),
262 difficulty: U256::from(1),
263 prevrandao: Some(prevrandao),
264 gas_limit: payload.payload.gas_limit(),
265 basefee: payload.payload.saturated_base_fee_per_gas(),
266 slot_num: 0,
267 blob_excess_gas_and_price: if spec.is_enabled_in(SpecId::CANCUN) {
268 Some(revm::context_interface::block::BlobExcessGasAndPrice {
269 excess_blob_gas: 0,
270 blob_gasprice: 0,
271 })
272 } else {
273 None
274 },
275 };
276
277 Ok(EvmEnv { cfg_env, block_env })
278 }
279
280 fn context_for_payload<'a>(
281 &self,
282 payload: &'a ExecutionData,
283 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
284 Ok(EthBlockExecutionCtx {
285 tx_count_hint: Some(payload.payload.transactions().len()),
286 parent_hash: payload.parent_hash(),
287 parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
288 ommers: &[],
289 withdrawals: None,
290 extra_data: payload.payload.as_v1().extra_data.clone(),
291 })
292 }
293
294 fn tx_iterator_for_payload(
295 &self,
296 payload: &ExecutionData,
297 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
298 let txs = payload.payload.transactions().clone();
299 let convert = |tx: Bytes| {
300 let tx =
301 TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
302 let signer = tx.try_recover().map_err(AnyError::new)?;
303 Ok::<_, AnyError>(tx.with_signer(signer))
304 };
305 Ok((txs, convert))
306 }
307}
308
309impl<ChainSpec> ArbEvmConfig<ChainSpec>
310where
311 ChainSpec: EthChainSpec + 'static,
312{
313 pub fn arb_context_for_block(
315 &self,
316 header: &Header,
317 parent_hash: B256,
318 ) -> ArbBlockExecutionCtx {
319 let mix_hash = header.mix_hash;
320 ArbBlockExecutionCtx {
321 parent_hash,
322 parent_beacon_block_root: header.parent_beacon_block_root,
323 extra_data: header.extra_data.to_vec(),
324 delayed_messages_read: u64::from_be_bytes(header.nonce.0),
325 l1_block_number: l1_block_number_from_mix_hash(&mix_hash),
326 l2_block_number: header.number,
327 chain_id: self.chain_spec.chain().id(),
328 block_timestamp: header.timestamp,
329 basefee: U256::from(header.base_fee_per_gas.unwrap_or_default()),
330 time_passed: 0,
331 l1_base_fee: U256::ZERO,
332 arbos_version: arbos_version_from_mix_hash(&mix_hash),
333 coinbase: header.beneficiary,
334 l1_price_per_unit: U256::ZERO,
336 brotli_compression_level: 0,
337 network_fee_account: Address::ZERO,
338 infra_fee_account: Address::ZERO,
339 min_base_fee: U256::ZERO,
340 }
341 }
342
343 pub fn arb_context_for_next_block(
345 &self,
346 parent: &SealedHeader<Header>,
347 prev_randao: &B256,
348 extra_data: &[u8],
349 ) -> ArbBlockExecutionCtx {
350 let l1_block_number = l1_block_number_from_mix_hash(prev_randao);
351 ArbBlockExecutionCtx {
352 parent_hash: parent.hash(),
353 parent_beacon_block_root: parent.parent_beacon_block_root(),
354 extra_data: extra_data.to_vec(),
355 delayed_messages_read: 0,
356 l1_block_number,
357 l2_block_number: parent.number().saturating_add(1),
358 chain_id: self.chain_spec.chain().id(),
359 block_timestamp: parent.timestamp(),
360 basefee: U256::from(parent.base_fee_per_gas().unwrap_or_default()),
361 time_passed: 0,
362 l1_base_fee: U256::ZERO,
363 arbos_version: 0,
364 coinbase: Address::ZERO,
365 l1_price_per_unit: U256::ZERO,
366 brotli_compression_level: 0,
367 network_fee_account: Address::ZERO,
368 infra_fee_account: Address::ZERO,
369 min_base_fee: U256::ZERO,
370 }
371 }
372}
373
374fn stage_rpc_block_ctx(
380 factory: &ArbEvmFactory,
381 arbos_version: u64,
382 block_timestamp: u64,
383 l1_block_number: u64,
384 l2_block_number: u64,
385 allow_debug: bool,
386) {
387 let block_ctx = arb_context::BlockCtx::new_with_caches(
388 arbos_version,
389 block_timestamp,
390 l1_block_number,
391 l2_block_number,
392 allow_debug,
393 factory.chain_caches().clone(),
394 );
395 let ctx = std::sync::Arc::new(arb_context::ArbPrecompileCtx {
396 block: std::sync::Arc::new(block_ctx),
397 tx: std::sync::Arc::new(parking_lot::Mutex::new(arb_context::TxCtx::default())),
398 caller_stack: std::sync::Arc::new(parking_lot::Mutex::new(Vec::new())),
399 evm_depth: std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)),
400 stylus_frame_depth: std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)),
401 });
402 factory.stage_ctx(ctx);
403}
404
405fn arb_cfg_env(chain_id: u64, spec: SpecId, arbos_version: u64) -> CfgEnv {
412 let mut cfg = CfgEnv::new()
413 .with_chain_id(chain_id)
414 .with_spec_and_mainnet_gas_params(spec);
415 cfg.disable_priority_fee_check = true;
417 cfg.disable_eip7623 = true;
419 cfg.disable_eip3607 = true;
423 if arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS {
426 cfg.disable_eip3541 = true;
427 }
428 cfg.disable_balance_check = true;
434 cfg.disable_nonce_check = true;
435 cfg.disable_base_fee = true;
439 cfg.tx_gas_limit_cap = Some(u64::MAX);
442 cfg
443}
444
445pub fn arbos_version_from_mix_hash(mix_hash: &B256) -> u64 {
447 let mut buf = [0u8; 8];
448 buf.copy_from_slice(&mix_hash.0[16..24]);
449 u64::from_be_bytes(buf)
450}
451
452pub fn l1_block_number_from_mix_hash(mix_hash: &B256) -> u64 {
454 let mut buf = [0u8; 8];
455 buf.copy_from_slice(&mix_hash.0[8..16]);
456 u64::from_be_bytes(buf)
457}