1use alloc::sync::Arc;
2use core::{convert::Infallible, fmt::Debug};
3
4use alloy_consensus::{BlockHeader, Header};
5use alloy_eips::Decodable2718;
6use alloy_evm::eth::{spec::EthExecutorSpec, EthBlockExecutionCtx};
7use alloy_primitives::{Address, Bytes, B256, 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};
16
17use crate::{assembler::ArbBlockAssembler, receipt::ArbReceiptBuilder};
18use reth_primitives_traits::{SealedBlock, SealedHeader, SignedTransaction, TxTy};
19use reth_storage_errors::any::AnyError;
20use revm::{
21 context::{BlockEnv, CfgEnv},
22 primitives::hardfork::SpecId,
23};
24
25use crate::{build::ArbBlockExecutorFactory, context::ArbBlockExecutionCtx, evm::ArbEvmFactory};
26
27#[derive(Debug, Clone)]
32pub struct ArbEvmConfig<ChainSpec = reth_chainspec::ChainSpec> {
33 pub executor_factory: ArbBlockExecutorFactory<ArbReceiptBuilder, Arc<ChainSpec>, ArbEvmFactory>,
34 pub block_assembler: ArbBlockAssembler<ChainSpec>,
35 chain_spec: Arc<ChainSpec>,
36}
37
38impl<ChainSpec> ArbEvmConfig<ChainSpec>
39where
40 ChainSpec: EthChainSpec + 'static,
41{
42 pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
44 let evm_factory = ArbEvmFactory::new();
45 Self {
46 executor_factory: ArbBlockExecutorFactory::new(
47 ArbReceiptBuilder,
48 chain_spec.clone(),
49 evm_factory,
50 ),
51 block_assembler: ArbBlockAssembler::new(chain_spec.clone()),
52 chain_spec,
53 }
54 }
55
56 pub fn chain_spec(&self) -> &Arc<ChainSpec> {
58 &self.chain_spec
59 }
60}
61
62impl<ChainSpec> ConfigureEvm for ArbEvmConfig<ChainSpec>
63where
64 ChainSpec:
65 EthExecutorSpec + EthChainSpec<Header = Header> + ArbitrumChainSpec + Hardforks + 'static,
66{
67 type Primitives = ArbPrimitives;
68 type Error = Infallible;
69 type NextBlockEnvCtx = NextBlockEnvAttributes;
70 type BlockExecutorFactory =
71 ArbBlockExecutorFactory<ArbReceiptBuilder, Arc<ChainSpec>, ArbEvmFactory>;
72 type BlockAssembler = ArbBlockAssembler<ChainSpec>;
73
74 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
75 &self.executor_factory
76 }
77
78 fn block_assembler(&self) -> &Self::BlockAssembler {
79 &self.block_assembler
80 }
81
82 fn evm_env(&self, header: &Header) -> Result<EvmEnv<SpecId>, Self::Error> {
83 let chain_id = self.chain_spec.chain().id();
84 let mix_hash = header.mix_hash().unwrap_or_default();
85 let arbos_version = arbos_version_from_mix_hash(&mix_hash);
86 let spec = self.chain_spec.spec_id_by_arbos_version(arbos_version);
87
88 let l1_block_number = l1_block_number_from_mix_hash(&mix_hash);
90
91 arb_precompiles::set_arbos_version(arbos_version);
94
95 let cfg_env = arb_cfg_env(chain_id, spec, arbos_version);
96 let prevrandao = B256::from(U256::from(1));
98 let block_env = BlockEnv {
99 number: U256::from(l1_block_number),
100 beneficiary: header.beneficiary(),
101 timestamp: U256::from(header.timestamp()),
102 difficulty: header.difficulty(),
103 prevrandao: Some(prevrandao),
104 gas_limit: header.gas_limit(),
105 basefee: header.base_fee_per_gas().unwrap_or_default(),
106 blob_excess_gas_and_price: if spec.is_enabled_in(SpecId::CANCUN) {
107 Some(revm::context_interface::block::BlobExcessGasAndPrice {
108 excess_blob_gas: 0,
109 blob_gasprice: 0,
110 })
111 } else {
112 None
113 },
114 };
115
116 Ok(EvmEnv { cfg_env, block_env })
117 }
118
119 fn next_evm_env(
120 &self,
121 parent: &Header,
122 attributes: &NextBlockEnvAttributes,
123 ) -> Result<EvmEnv<SpecId>, Self::Error> {
124 let chain_id = self.chain_spec.chain().id();
125 let arbos_version = arbos_version_from_mix_hash(&attributes.prev_randao);
126 let spec = self.chain_spec.spec_id_by_arbos_version(arbos_version);
127
128 arb_precompiles::set_arbos_version(arbos_version);
129
130 let cfg_env = arb_cfg_env(chain_id, spec, arbos_version);
131 let l1_block_number = l1_block_number_from_mix_hash(&attributes.prev_randao);
133 let prevrandao = B256::from(U256::from(1));
135 let block_env = BlockEnv {
136 number: U256::from(l1_block_number),
137 beneficiary: attributes.suggested_fee_recipient,
138 timestamp: U256::from(attributes.timestamp),
139 difficulty: U256::from(1),
140 prevrandao: Some(prevrandao),
141 gas_limit: attributes.gas_limit,
142 basefee: parent.base_fee_per_gas().unwrap_or_default(),
143 blob_excess_gas_and_price: if spec.is_enabled_in(SpecId::CANCUN) {
144 Some(revm::context_interface::block::BlobExcessGasAndPrice {
145 excess_blob_gas: 0,
146 blob_gasprice: 0,
147 })
148 } else {
149 None
150 },
151 };
152
153 Ok(EvmEnv { cfg_env, block_env })
154 }
155
156 fn context_for_block<'a>(
157 &self,
158 block: &'a SealedBlock<alloy_consensus::Block<arb_primitives::ArbTransactionSigned>>,
159 ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
160 let mut extra = block.header().extra_data.to_vec();
163 extra.extend_from_slice(&block.header().nonce.0);
164 extra.extend_from_slice(&block.header().number.to_be_bytes());
165 Ok(EthBlockExecutionCtx {
166 tx_count_hint: Some(block.transaction_count()),
167 parent_hash: block.header().parent_hash,
168 parent_beacon_block_root: block.header().parent_beacon_block_root,
169 ommers: &[],
170 withdrawals: None,
171 extra_data: extra.into(),
172 })
173 }
174
175 fn context_for_next_block(
176 &self,
177 parent: &SealedHeader<Header>,
178 attributes: NextBlockEnvAttributes,
179 ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
180 Ok(EthBlockExecutionCtx {
181 tx_count_hint: None,
182 parent_hash: parent.hash(),
183 parent_beacon_block_root: attributes.parent_beacon_block_root,
184 ommers: &[],
185 withdrawals: None,
186 extra_data: attributes.extra_data,
187 })
188 }
189}
190
191impl<ChainSpec> ConfigureEngineEvm<ExecutionData> for ArbEvmConfig<ChainSpec>
192where
193 ChainSpec:
194 EthExecutorSpec + EthChainSpec<Header = Header> + ArbitrumChainSpec + Hardforks + 'static,
195{
196 fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
197 let prev_randao = payload.payload.as_v1().prev_randao;
198 let arbos_version = arbos_version_from_mix_hash(&prev_randao);
199 let spec = self.chain_spec.spec_id_by_arbos_version(arbos_version);
200
201 arb_precompiles::set_arbos_version(arbos_version);
202
203 let cfg_env = arb_cfg_env(self.chain_spec.chain().id(), spec, arbos_version);
204
205 let l1_block_number = l1_block_number_from_mix_hash(&prev_randao);
207 let prevrandao = B256::from(U256::from(1));
209 let block_env = BlockEnv {
210 number: U256::from(l1_block_number),
211 beneficiary: payload.payload.fee_recipient(),
212 timestamp: U256::from(payload.payload.timestamp()),
213 difficulty: U256::from(1),
214 prevrandao: Some(prevrandao),
215 gas_limit: payload.payload.gas_limit(),
216 basefee: payload.payload.saturated_base_fee_per_gas(),
217 blob_excess_gas_and_price: if spec.is_enabled_in(SpecId::CANCUN) {
218 Some(revm::context_interface::block::BlobExcessGasAndPrice {
219 excess_blob_gas: 0,
220 blob_gasprice: 0,
221 })
222 } else {
223 None
224 },
225 };
226
227 Ok(EvmEnv { cfg_env, block_env })
228 }
229
230 fn context_for_payload<'a>(
231 &self,
232 payload: &'a ExecutionData,
233 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
234 Ok(EthBlockExecutionCtx {
235 tx_count_hint: Some(payload.payload.transactions().len()),
236 parent_hash: payload.parent_hash(),
237 parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
238 ommers: &[],
239 withdrawals: None,
240 extra_data: payload.payload.as_v1().extra_data.clone(),
241 })
242 }
243
244 fn tx_iterator_for_payload(
245 &self,
246 payload: &ExecutionData,
247 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
248 let txs = payload.payload.transactions().clone();
249 let convert = |tx: Bytes| {
250 let tx =
251 TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
252 let signer = tx.try_recover().map_err(AnyError::new)?;
253 Ok::<_, AnyError>(tx.with_signer(signer))
254 };
255 Ok((txs, convert))
256 }
257}
258
259impl<ChainSpec> ArbEvmConfig<ChainSpec>
260where
261 ChainSpec: EthChainSpec + 'static,
262{
263 pub fn arb_context_for_block(
265 &self,
266 header: &Header,
267 parent_hash: B256,
268 ) -> ArbBlockExecutionCtx {
269 let mix_hash = header.mix_hash;
270 ArbBlockExecutionCtx {
271 parent_hash,
272 parent_beacon_block_root: header.parent_beacon_block_root,
273 extra_data: header.extra_data.to_vec(),
274 delayed_messages_read: u64::from_be_bytes(header.nonce.0),
275 l1_block_number: l1_block_number_from_mix_hash(&mix_hash),
276 l2_block_number: header.number,
277 chain_id: self.chain_spec.chain().id(),
278 block_timestamp: header.timestamp,
279 basefee: U256::from(header.base_fee_per_gas.unwrap_or_default()),
280 time_passed: 0,
281 l1_base_fee: U256::ZERO,
282 arbos_version: arbos_version_from_mix_hash(&mix_hash),
283 coinbase: header.beneficiary,
284 l1_price_per_unit: U256::ZERO,
286 brotli_compression_level: 0,
287 network_fee_account: Address::ZERO,
288 infra_fee_account: Address::ZERO,
289 min_base_fee: U256::ZERO,
290 }
291 }
292
293 pub fn arb_context_for_next_block(
295 &self,
296 parent: &SealedHeader<Header>,
297 prev_randao: &B256,
298 extra_data: &[u8],
299 ) -> ArbBlockExecutionCtx {
300 let l1_block_number = l1_block_number_from_mix_hash(prev_randao);
301 ArbBlockExecutionCtx {
302 parent_hash: parent.hash(),
303 parent_beacon_block_root: parent.parent_beacon_block_root(),
304 extra_data: extra_data.to_vec(),
305 delayed_messages_read: 0,
306 l1_block_number,
307 l2_block_number: parent.number().saturating_add(1),
308 chain_id: self.chain_spec.chain().id(),
309 block_timestamp: parent.timestamp(),
310 basefee: U256::from(parent.base_fee_per_gas().unwrap_or_default()),
311 time_passed: 0,
312 l1_base_fee: U256::ZERO,
313 arbos_version: 0,
314 coinbase: Address::ZERO,
315 l1_price_per_unit: U256::ZERO,
316 brotli_compression_level: 0,
317 network_fee_account: Address::ZERO,
318 infra_fee_account: Address::ZERO,
319 min_base_fee: U256::ZERO,
320 }
321 }
322}
323
324fn arb_cfg_env(chain_id: u64, spec: SpecId, arbos_version: u64) -> CfgEnv {
331 let mut cfg = CfgEnv::new()
332 .with_chain_id(chain_id)
333 .with_spec_and_mainnet_gas_params(spec);
334 cfg.disable_priority_fee_check = true;
336 cfg.disable_eip7623 = true;
338 cfg.disable_eip3607 = true;
342 if arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS {
344 cfg.disable_eip3541 = true;
345 }
346 cfg.disable_balance_check = true;
352 cfg.disable_nonce_check = true;
353 cfg.disable_base_fee = true;
357 cfg.tx_gas_limit_cap = Some(u64::MAX);
360 cfg
361}
362
363pub fn arbos_version_from_mix_hash(mix_hash: &B256) -> u64 {
365 let mut buf = [0u8; 8];
366 buf.copy_from_slice(&mix_hash.0[16..24]);
367 u64::from_be_bytes(buf)
368}
369
370pub fn l1_block_number_from_mix_hash(mix_hash: &B256) -> u64 {
372 let mut buf = [0u8; 8];
373 buf.copy_from_slice(&mix_hash.0[8..16]);
374 u64::from_be_bytes(buf)
375}