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 let cfg_env = arb_cfg_env(chain_id, spec, arbos_version);
92 let prevrandao = B256::from(U256::from(1));
94 let block_env = BlockEnv {
95 number: U256::from(l1_block_number),
96 beneficiary: header.beneficiary(),
97 timestamp: U256::from(header.timestamp()),
98 difficulty: header.difficulty(),
99 prevrandao: Some(prevrandao),
100 gas_limit: header.gas_limit(),
101 basefee: header.base_fee_per_gas().unwrap_or_default(),
102 blob_excess_gas_and_price: None,
104 };
105
106 Ok(EvmEnv { cfg_env, block_env })
107 }
108
109 fn next_evm_env(
110 &self,
111 parent: &Header,
112 attributes: &NextBlockEnvAttributes,
113 ) -> Result<EvmEnv<SpecId>, Self::Error> {
114 let chain_id = self.chain_spec.chain().id();
115 let arbos_version = arbos_version_from_mix_hash(&attributes.prev_randao);
116 let spec = self.chain_spec.spec_id_by_arbos_version(arbos_version);
117
118 let cfg_env = arb_cfg_env(chain_id, spec, arbos_version);
119 let l1_block_number = l1_block_number_from_mix_hash(&attributes.prev_randao);
121 let prevrandao = B256::from(U256::from(1));
123 let block_env = BlockEnv {
124 number: U256::from(l1_block_number),
125 beneficiary: attributes.suggested_fee_recipient,
126 timestamp: U256::from(attributes.timestamp),
127 difficulty: U256::from(1),
128 prevrandao: Some(prevrandao),
129 gas_limit: attributes.gas_limit,
130 basefee: parent.base_fee_per_gas().unwrap_or_default(),
131 blob_excess_gas_and_price: None,
133 };
134
135 Ok(EvmEnv { cfg_env, block_env })
136 }
137
138 fn context_for_block<'a>(
139 &self,
140 block: &'a SealedBlock<alloy_consensus::Block<arb_primitives::ArbTransactionSigned>>,
141 ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
142 let mut extra = block.header().extra_data.to_vec();
145 extra.extend_from_slice(&block.header().nonce.0);
146 extra.extend_from_slice(&block.header().number.to_be_bytes());
147 Ok(EthBlockExecutionCtx {
148 tx_count_hint: Some(block.transaction_count()),
149 parent_hash: block.header().parent_hash,
150 parent_beacon_block_root: block.header().parent_beacon_block_root,
151 ommers: &[],
152 withdrawals: None,
153 extra_data: extra.into(),
154 })
155 }
156
157 fn context_for_next_block(
158 &self,
159 parent: &SealedHeader<Header>,
160 attributes: NextBlockEnvAttributes,
161 ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
162 Ok(EthBlockExecutionCtx {
163 tx_count_hint: None,
164 parent_hash: parent.hash(),
165 parent_beacon_block_root: attributes.parent_beacon_block_root,
166 ommers: &[],
167 withdrawals: None,
168 extra_data: attributes.extra_data,
169 })
170 }
171}
172
173impl<ChainSpec> ConfigureEngineEvm<ExecutionData> for ArbEvmConfig<ChainSpec>
174where
175 ChainSpec:
176 EthExecutorSpec + EthChainSpec<Header = Header> + ArbitrumChainSpec + Hardforks + 'static,
177{
178 fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
179 let prev_randao = payload.payload.as_v1().prev_randao;
180 let arbos_version = arbos_version_from_mix_hash(&prev_randao);
181 let spec = self.chain_spec.spec_id_by_arbos_version(arbos_version);
182
183 let cfg_env = arb_cfg_env(self.chain_spec.chain().id(), spec, arbos_version);
184
185 let l1_block_number = l1_block_number_from_mix_hash(&prev_randao);
187 let prevrandao = B256::from(U256::from(1));
189 let block_env = BlockEnv {
190 number: U256::from(l1_block_number),
191 beneficiary: payload.payload.fee_recipient(),
192 timestamp: U256::from(payload.payload.timestamp()),
193 difficulty: U256::from(1),
194 prevrandao: Some(prevrandao),
195 gas_limit: payload.payload.gas_limit(),
196 basefee: payload.payload.saturated_base_fee_per_gas(),
197 blob_excess_gas_and_price: None,
199 };
200
201 Ok(EvmEnv { cfg_env, block_env })
202 }
203
204 fn context_for_payload<'a>(
205 &self,
206 payload: &'a ExecutionData,
207 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
208 Ok(EthBlockExecutionCtx {
209 tx_count_hint: Some(payload.payload.transactions().len()),
210 parent_hash: payload.parent_hash(),
211 parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
212 ommers: &[],
213 withdrawals: None,
214 extra_data: payload.payload.as_v1().extra_data.clone(),
215 })
216 }
217
218 fn tx_iterator_for_payload(
219 &self,
220 payload: &ExecutionData,
221 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
222 let txs = payload.payload.transactions().clone();
223 let convert = |tx: Bytes| {
224 let tx =
225 TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
226 let signer = tx.try_recover().map_err(AnyError::new)?;
227 Ok::<_, AnyError>(tx.with_signer(signer))
228 };
229 Ok((txs, convert))
230 }
231}
232
233impl<ChainSpec> ArbEvmConfig<ChainSpec>
234where
235 ChainSpec: EthChainSpec + 'static,
236{
237 pub fn arb_context_for_block(
239 &self,
240 header: &Header,
241 parent_hash: B256,
242 ) -> ArbBlockExecutionCtx {
243 let mix_hash = header.mix_hash;
244 ArbBlockExecutionCtx {
245 parent_hash,
246 parent_beacon_block_root: header.parent_beacon_block_root,
247 extra_data: header.extra_data.to_vec(),
248 delayed_messages_read: u64::from_be_bytes(header.nonce.0),
249 l1_block_number: l1_block_number_from_mix_hash(&mix_hash),
250 l2_block_number: header.number,
251 chain_id: self.chain_spec.chain().id(),
252 block_timestamp: header.timestamp,
253 basefee: U256::from(header.base_fee_per_gas.unwrap_or_default()),
254 time_passed: 0,
255 l1_base_fee: U256::ZERO,
256 arbos_version: arbos_version_from_mix_hash(&mix_hash),
257 coinbase: header.beneficiary,
258 l1_price_per_unit: U256::ZERO,
260 brotli_compression_level: 0,
261 network_fee_account: Address::ZERO,
262 infra_fee_account: Address::ZERO,
263 min_base_fee: U256::ZERO,
264 }
265 }
266
267 pub fn arb_context_for_next_block(
269 &self,
270 parent: &SealedHeader<Header>,
271 prev_randao: &B256,
272 extra_data: &[u8],
273 ) -> ArbBlockExecutionCtx {
274 let l1_block_number = l1_block_number_from_mix_hash(prev_randao);
275 ArbBlockExecutionCtx {
276 parent_hash: parent.hash(),
277 parent_beacon_block_root: parent.parent_beacon_block_root(),
278 extra_data: extra_data.to_vec(),
279 delayed_messages_read: 0,
280 l1_block_number,
281 l2_block_number: parent.number().saturating_add(1),
282 chain_id: self.chain_spec.chain().id(),
283 block_timestamp: parent.timestamp(),
284 basefee: U256::from(parent.base_fee_per_gas().unwrap_or_default()),
285 time_passed: 0,
286 l1_base_fee: U256::ZERO,
287 arbos_version: 0,
288 coinbase: Address::ZERO,
289 l1_price_per_unit: U256::ZERO,
290 brotli_compression_level: 0,
291 network_fee_account: Address::ZERO,
292 infra_fee_account: Address::ZERO,
293 min_base_fee: U256::ZERO,
294 }
295 }
296}
297
298fn arb_cfg_env(chain_id: u64, spec: SpecId, arbos_version: u64) -> CfgEnv {
305 let mut cfg = CfgEnv::new()
306 .with_chain_id(chain_id)
307 .with_spec_and_mainnet_gas_params(spec);
308 cfg.disable_priority_fee_check = true;
310 cfg.disable_eip7623 = true;
312 cfg.disable_eip3607 = true;
316 if arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS {
318 cfg.disable_eip3541 = true;
319 }
320 cfg.disable_balance_check = true;
326 cfg.disable_nonce_check = true;
327 cfg.disable_base_fee = true;
331 cfg
332}
333
334pub fn arbos_version_from_mix_hash(mix_hash: &B256) -> u64 {
336 let mut buf = [0u8; 8];
337 buf.copy_from_slice(&mix_hash.0[16..24]);
338 u64::from_be_bytes(buf)
339}
340
341pub fn l1_block_number_from_mix_hash(mix_hash: &B256) -> u64 {
343 let mut buf = [0u8; 8];
344 buf.copy_from_slice(&mix_hash.0[8..16]);
345 u64::from_be_bytes(buf)
346}