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, EvmEnv, EvmEnvFor, ExecutableTxIterator, ExecutionCtxFor,
14 NextBlockEnvAttributes,
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 evm_env(&self, header: &Header) -> Result<EvmEnv<SpecId>, Self::Error> {
99 let chain_id = self.chain_spec.chain().id();
100 let mix_hash = header.mix_hash().unwrap_or_default();
101 let arbos_version = arbos_version_from_mix_hash(&mix_hash);
102 let spec = self.chain_spec.spec_id_by_arbos_version(arbos_version);
103
104 let l1_block_number = l1_block_number_from_mix_hash(&mix_hash);
106
107 stage_rpc_block_ctx(
108 self.executor_factory.arb_evm_factory(),
109 arbos_version,
110 header.timestamp(),
111 l1_block_number,
112 header.number(),
113 self.executor_factory.allow_debug_precompiles(),
114 );
115
116 let cfg_env = arb_cfg_env(chain_id, spec, arbos_version);
117 let prevrandao = B256::from(U256::from(1));
119 let block_env = BlockEnv {
120 number: U256::from(l1_block_number),
121 beneficiary: header.beneficiary(),
122 timestamp: U256::from(header.timestamp()),
123 difficulty: header.difficulty(),
124 prevrandao: Some(prevrandao),
125 gas_limit: header.gas_limit(),
126 basefee: header.base_fee_per_gas().unwrap_or_default(),
127 blob_excess_gas_and_price: if spec.is_enabled_in(SpecId::CANCUN) {
128 Some(revm::context_interface::block::BlobExcessGasAndPrice {
129 excess_blob_gas: 0,
130 blob_gasprice: 0,
131 })
132 } else {
133 None
134 },
135 };
136
137 Ok(EvmEnv { cfg_env, block_env })
138 }
139
140 fn next_evm_env(
141 &self,
142 parent: &Header,
143 attributes: &NextBlockEnvAttributes,
144 ) -> Result<EvmEnv<SpecId>, Self::Error> {
145 let chain_id = self.chain_spec.chain().id();
146 let arbos_version = arbos_version_from_mix_hash(&attributes.prev_randao);
147 let spec = self.chain_spec.spec_id_by_arbos_version(arbos_version);
148
149 let l1_block_number_initial = l1_block_number_from_mix_hash(&attributes.prev_randao);
150 stage_rpc_block_ctx(
151 self.executor_factory.arb_evm_factory(),
152 arbos_version,
153 attributes.timestamp,
154 l1_block_number_initial,
155 parent.number().saturating_add(1),
156 self.executor_factory.allow_debug_precompiles(),
157 );
158
159 let cfg_env = arb_cfg_env(chain_id, spec, arbos_version);
160 let prevrandao = B256::from(U256::from(1));
162 let block_env = BlockEnv {
163 number: U256::from(l1_block_number_initial),
164 beneficiary: attributes.suggested_fee_recipient,
165 timestamp: U256::from(attributes.timestamp),
166 difficulty: U256::from(1),
167 prevrandao: Some(prevrandao),
168 gas_limit: attributes.gas_limit,
169 basefee: parent.base_fee_per_gas().unwrap_or_default(),
170 blob_excess_gas_and_price: if spec.is_enabled_in(SpecId::CANCUN) {
171 Some(revm::context_interface::block::BlobExcessGasAndPrice {
172 excess_blob_gas: 0,
173 blob_gasprice: 0,
174 })
175 } else {
176 None
177 },
178 };
179
180 Ok(EvmEnv { cfg_env, block_env })
181 }
182
183 fn context_for_block<'a>(
184 &self,
185 block: &'a SealedBlock<alloy_consensus::Block<arb_primitives::ArbTransactionSigned>>,
186 ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
187 let mut extra = block.header().extra_data.to_vec();
190 extra.extend_from_slice(&block.header().nonce.0);
191 extra.extend_from_slice(&block.header().number.to_be_bytes());
192 Ok(EthBlockExecutionCtx {
193 tx_count_hint: Some(block.transaction_count()),
194 parent_hash: block.header().parent_hash,
195 parent_beacon_block_root: block.header().parent_beacon_block_root,
196 ommers: &[],
197 withdrawals: None,
198 extra_data: extra.into(),
199 })
200 }
201
202 fn context_for_next_block(
203 &self,
204 parent: &SealedHeader<Header>,
205 attributes: NextBlockEnvAttributes,
206 ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
207 Ok(EthBlockExecutionCtx {
208 tx_count_hint: None,
209 parent_hash: parent.hash(),
210 parent_beacon_block_root: attributes.parent_beacon_block_root,
211 ommers: &[],
212 withdrawals: None,
213 extra_data: attributes.extra_data,
214 })
215 }
216}
217
218impl<ChainSpec> ConfigureEngineEvm<ExecutionData> for ArbEvmConfig<ChainSpec>
219where
220 ChainSpec:
221 EthExecutorSpec + EthChainSpec<Header = Header> + ArbitrumChainSpec + Hardforks + 'static,
222{
223 fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
224 let prev_randao = payload.payload.as_v1().prev_randao;
225 let arbos_version = arbos_version_from_mix_hash(&prev_randao);
226 let spec = self.chain_spec.spec_id_by_arbos_version(arbos_version);
227
228 let l1_block_number = l1_block_number_from_mix_hash(&prev_randao);
230 stage_rpc_block_ctx(
231 self.executor_factory.arb_evm_factory(),
232 arbos_version,
233 payload.payload.timestamp(),
234 l1_block_number,
235 payload.payload.block_number(),
236 self.executor_factory.allow_debug_precompiles(),
237 );
238
239 let cfg_env = arb_cfg_env(self.chain_spec.chain().id(), spec, arbos_version);
240
241 let prevrandao = B256::from(U256::from(1));
243 let block_env = BlockEnv {
244 number: U256::from(l1_block_number),
245 beneficiary: payload.payload.fee_recipient(),
246 timestamp: U256::from(payload.payload.timestamp()),
247 difficulty: U256::from(1),
248 prevrandao: Some(prevrandao),
249 gas_limit: payload.payload.gas_limit(),
250 basefee: payload.payload.saturated_base_fee_per_gas(),
251 blob_excess_gas_and_price: if spec.is_enabled_in(SpecId::CANCUN) {
252 Some(revm::context_interface::block::BlobExcessGasAndPrice {
253 excess_blob_gas: 0,
254 blob_gasprice: 0,
255 })
256 } else {
257 None
258 },
259 };
260
261 Ok(EvmEnv { cfg_env, block_env })
262 }
263
264 fn context_for_payload<'a>(
265 &self,
266 payload: &'a ExecutionData,
267 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
268 Ok(EthBlockExecutionCtx {
269 tx_count_hint: Some(payload.payload.transactions().len()),
270 parent_hash: payload.parent_hash(),
271 parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
272 ommers: &[],
273 withdrawals: None,
274 extra_data: payload.payload.as_v1().extra_data.clone(),
275 })
276 }
277
278 fn tx_iterator_for_payload(
279 &self,
280 payload: &ExecutionData,
281 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
282 let txs = payload.payload.transactions().clone();
283 let convert = |tx: Bytes| {
284 let tx =
285 TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
286 let signer = tx.try_recover().map_err(AnyError::new)?;
287 Ok::<_, AnyError>(tx.with_signer(signer))
288 };
289 Ok((txs, convert))
290 }
291}
292
293impl<ChainSpec> ArbEvmConfig<ChainSpec>
294where
295 ChainSpec: EthChainSpec + 'static,
296{
297 pub fn arb_context_for_block(
299 &self,
300 header: &Header,
301 parent_hash: B256,
302 ) -> ArbBlockExecutionCtx {
303 let mix_hash = header.mix_hash;
304 ArbBlockExecutionCtx {
305 parent_hash,
306 parent_beacon_block_root: header.parent_beacon_block_root,
307 extra_data: header.extra_data.to_vec(),
308 delayed_messages_read: u64::from_be_bytes(header.nonce.0),
309 l1_block_number: l1_block_number_from_mix_hash(&mix_hash),
310 l2_block_number: header.number,
311 chain_id: self.chain_spec.chain().id(),
312 block_timestamp: header.timestamp,
313 basefee: U256::from(header.base_fee_per_gas.unwrap_or_default()),
314 time_passed: 0,
315 l1_base_fee: U256::ZERO,
316 arbos_version: arbos_version_from_mix_hash(&mix_hash),
317 coinbase: header.beneficiary,
318 l1_price_per_unit: U256::ZERO,
320 brotli_compression_level: 0,
321 network_fee_account: Address::ZERO,
322 infra_fee_account: Address::ZERO,
323 min_base_fee: U256::ZERO,
324 }
325 }
326
327 pub fn arb_context_for_next_block(
329 &self,
330 parent: &SealedHeader<Header>,
331 prev_randao: &B256,
332 extra_data: &[u8],
333 ) -> ArbBlockExecutionCtx {
334 let l1_block_number = l1_block_number_from_mix_hash(prev_randao);
335 ArbBlockExecutionCtx {
336 parent_hash: parent.hash(),
337 parent_beacon_block_root: parent.parent_beacon_block_root(),
338 extra_data: extra_data.to_vec(),
339 delayed_messages_read: 0,
340 l1_block_number,
341 l2_block_number: parent.number().saturating_add(1),
342 chain_id: self.chain_spec.chain().id(),
343 block_timestamp: parent.timestamp(),
344 basefee: U256::from(parent.base_fee_per_gas().unwrap_or_default()),
345 time_passed: 0,
346 l1_base_fee: U256::ZERO,
347 arbos_version: 0,
348 coinbase: Address::ZERO,
349 l1_price_per_unit: U256::ZERO,
350 brotli_compression_level: 0,
351 network_fee_account: Address::ZERO,
352 infra_fee_account: Address::ZERO,
353 min_base_fee: U256::ZERO,
354 }
355 }
356}
357
358fn stage_rpc_block_ctx(
364 factory: &ArbEvmFactory,
365 arbos_version: u64,
366 block_timestamp: u64,
367 l1_block_number: u64,
368 l2_block_number: u64,
369 allow_debug: bool,
370) {
371 let block_ctx = arb_context::BlockCtx::new_with_caches(
372 arbos_version,
373 block_timestamp,
374 l1_block_number,
375 l2_block_number,
376 allow_debug,
377 factory.chain_caches().clone(),
378 );
379 let ctx = std::sync::Arc::new(arb_context::ArbPrecompileCtx {
380 block: std::sync::Arc::new(block_ctx),
381 tx: std::sync::Arc::new(parking_lot::Mutex::new(arb_context::TxCtx::default())),
382 caller_stack: std::sync::Arc::new(parking_lot::Mutex::new(Vec::new())),
383 evm_depth: std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)),
384 stylus_frame_depth: std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)),
385 });
386 factory.stage_ctx(ctx);
387}
388
389fn arb_cfg_env(chain_id: u64, spec: SpecId, arbos_version: u64) -> CfgEnv {
396 let mut cfg = CfgEnv::new()
397 .with_chain_id(chain_id)
398 .with_spec_and_mainnet_gas_params(spec);
399 cfg.disable_priority_fee_check = true;
401 cfg.disable_eip7623 = true;
403 cfg.disable_eip3607 = true;
407 if arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS {
410 cfg.disable_eip3541 = true;
411 }
412 cfg.disable_balance_check = true;
418 cfg.disable_nonce_check = true;
419 cfg.disable_base_fee = true;
423 cfg.tx_gas_limit_cap = Some(u64::MAX);
426 cfg
427}
428
429pub fn arbos_version_from_mix_hash(mix_hash: &B256) -> u64 {
431 let mut buf = [0u8; 8];
432 buf.copy_from_slice(&mix_hash.0[16..24]);
433 u64::from_be_bytes(buf)
434}
435
436pub fn l1_block_number_from_mix_hash(mix_hash: &B256) -> u64 {
438 let mut buf = [0u8; 8];
439 buf.copy_from_slice(&mix_hash.0[8..16]);
440 u64::from_be_bytes(buf)
441}