alloy_op_evm/
lib.rs

1#![doc = include_str!("../README.md")]
2#![doc(
3    html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg",
4    html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico"
5)]
6#![cfg_attr(not(test), warn(unused_crate_dependencies))]
7#![cfg_attr(docsrs, feature(doc_cfg))]
8#![cfg_attr(not(feature = "std"), no_std)]
9
10extern crate alloc;
11
12pub use alloy_evm::op::{spec, spec_by_timestamp_after_bedrock};
13
14use alloy_evm::{precompiles::PrecompilesMap, Database, Evm, EvmEnv, EvmFactory};
15use alloy_primitives::{Address, Bytes};
16use core::{
17    fmt::Debug,
18    ops::{Deref, DerefMut},
19};
20use op_revm::{
21    precompiles::OpPrecompiles, DefaultOp, OpBuilder, OpContext, OpHaltReason, OpSpecId,
22    OpTransaction, OpTransactionError,
23};
24use revm::{
25    context::{BlockEnv, TxEnv},
26    context_interface::result::{EVMError, ResultAndState},
27    handler::{instructions::EthInstructions, PrecompileProvider},
28    inspector::NoOpInspector,
29    interpreter::{interpreter::EthInterpreter, InterpreterResult},
30    Context, ExecuteEvm, InspectEvm, Inspector, SystemCallEvm,
31};
32
33pub mod block;
34pub use block::{OpBlockExecutionCtx, OpBlockExecutor, OpBlockExecutorFactory};
35
36/// OP EVM implementation.
37///
38/// This is a wrapper type around the `revm` evm with optional [`Inspector`] (tracing)
39/// support. [`Inspector`] support is configurable at runtime because it's part of the underlying
40/// [`OpEvm`](op_revm::OpEvm) type.
41#[allow(missing_debug_implementations)] // missing revm::OpContext Debug impl
42pub struct OpEvm<DB: Database, I, P = OpPrecompiles> {
43    inner: op_revm::OpEvm<OpContext<DB>, I, EthInstructions<EthInterpreter, OpContext<DB>>, P>,
44    inspect: bool,
45}
46
47impl<DB: Database, I, P> OpEvm<DB, I, P> {
48    /// Provides a reference to the EVM context.
49    pub const fn ctx(&self) -> &OpContext<DB> {
50        &self.inner.0.ctx
51    }
52
53    /// Provides a mutable reference to the EVM context.
54    pub const fn ctx_mut(&mut self) -> &mut OpContext<DB> {
55        &mut self.inner.0.ctx
56    }
57}
58
59impl<DB: Database, I, P> OpEvm<DB, I, P> {
60    /// Creates a new OP EVM instance.
61    ///
62    /// The `inspect` argument determines whether the configured [`Inspector`] of the given
63    /// [`OpEvm`](op_revm::OpEvm) should be invoked on [`Evm::transact`].
64    pub const fn new(
65        evm: op_revm::OpEvm<OpContext<DB>, I, EthInstructions<EthInterpreter, OpContext<DB>>, P>,
66        inspect: bool,
67    ) -> Self {
68        Self { inner: evm, inspect }
69    }
70}
71
72impl<DB: Database, I, P> Deref for OpEvm<DB, I, P> {
73    type Target = OpContext<DB>;
74
75    #[inline]
76    fn deref(&self) -> &Self::Target {
77        self.ctx()
78    }
79}
80
81impl<DB: Database, I, P> DerefMut for OpEvm<DB, I, P> {
82    #[inline]
83    fn deref_mut(&mut self) -> &mut Self::Target {
84        self.ctx_mut()
85    }
86}
87
88impl<DB, I, P> Evm for OpEvm<DB, I, P>
89where
90    DB: Database,
91    I: Inspector<OpContext<DB>>,
92    P: PrecompileProvider<OpContext<DB>, Output = InterpreterResult>,
93{
94    type DB = DB;
95    type Tx = OpTransaction<TxEnv>;
96    type Error = EVMError<DB::Error, OpTransactionError>;
97    type HaltReason = OpHaltReason;
98    type Spec = OpSpecId;
99    type BlockEnv = BlockEnv;
100    type Precompiles = P;
101    type Inspector = I;
102
103    fn block(&self) -> &BlockEnv {
104        &self.block
105    }
106
107    fn chain_id(&self) -> u64 {
108        self.cfg.chain_id
109    }
110
111    fn transact_raw(
112        &mut self,
113        tx: Self::Tx,
114    ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
115        if self.inspect {
116            self.inner.inspect_tx(tx)
117        } else {
118            self.inner.transact(tx)
119        }
120    }
121
122    fn transact_system_call(
123        &mut self,
124        caller: Address,
125        contract: Address,
126        data: Bytes,
127    ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
128        self.inner.system_call_with_caller(caller, contract, data)
129    }
130
131    fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>) {
132        let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.0.ctx;
133
134        (journaled_state.database, EvmEnv { block_env, cfg_env })
135    }
136
137    fn set_inspector_enabled(&mut self, enabled: bool) {
138        self.inspect = enabled;
139    }
140
141    fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) {
142        (
143            &self.inner.0.ctx.journaled_state.database,
144            &self.inner.0.inspector,
145            &self.inner.0.precompiles,
146        )
147    }
148
149    fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) {
150        (
151            &mut self.inner.0.ctx.journaled_state.database,
152            &mut self.inner.0.inspector,
153            &mut self.inner.0.precompiles,
154        )
155    }
156}
157
158/// Factory producing [`OpEvm`]s.
159#[derive(Debug, Default, Clone, Copy)]
160#[non_exhaustive]
161pub struct OpEvmFactory;
162
163impl EvmFactory for OpEvmFactory {
164    type Evm<DB: Database, I: Inspector<OpContext<DB>>> = OpEvm<DB, I, Self::Precompiles>;
165    type Context<DB: Database> = OpContext<DB>;
166    type Tx = OpTransaction<TxEnv>;
167    type Error<DBError: core::error::Error + Send + Sync + 'static> =
168        EVMError<DBError, OpTransactionError>;
169    type HaltReason = OpHaltReason;
170    type Spec = OpSpecId;
171    type BlockEnv = BlockEnv;
172    type Precompiles = PrecompilesMap;
173
174    fn create_evm<DB: Database>(
175        &self,
176        db: DB,
177        input: EvmEnv<OpSpecId>,
178    ) -> Self::Evm<DB, NoOpInspector> {
179        let spec_id = input.cfg_env.spec;
180        OpEvm {
181            inner: Context::op()
182                .with_db(db)
183                .with_block(input.block_env)
184                .with_cfg(input.cfg_env)
185                .build_op_with_inspector(NoOpInspector {})
186                .with_precompiles(PrecompilesMap::from_static(
187                    OpPrecompiles::new_with_spec(spec_id).precompiles(),
188                )),
189            inspect: false,
190        }
191    }
192
193    fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>(
194        &self,
195        db: DB,
196        input: EvmEnv<OpSpecId>,
197        inspector: I,
198    ) -> Self::Evm<DB, I> {
199        let spec_id = input.cfg_env.spec;
200        OpEvm {
201            inner: Context::op()
202                .with_db(db)
203                .with_block(input.block_env)
204                .with_cfg(input.cfg_env)
205                .build_op_with_inspector(inspector)
206                .with_precompiles(PrecompilesMap::from_static(
207                    OpPrecompiles::new_with_spec(spec_id).precompiles(),
208                )),
209            inspect: true,
210        }
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use alloc::{string::ToString, vec};
217    use alloy_evm::{
218        precompiles::{Precompile, PrecompileInput},
219        EvmInternals,
220    };
221    use alloy_primitives::U256;
222    use op_revm::precompiles::{bls12_381, bn254_pair};
223    use revm::{
224        context::{CfgEnv, JournalTr},
225        database::EmptyDB,
226        precompile::PrecompileError,
227        Journal, JournalEntry,
228    };
229
230    use super::*;
231
232    #[test]
233    fn test_precompiles_jovian_fail() {
234        let evm = OpEvmFactory::default().create_evm(
235            EmptyDB::default(),
236            EvmEnv::new(CfgEnv::new_with_spec(OpSpecId::JOVIAN), BlockEnv::default()),
237        );
238
239        let jovian_precompile = evm.precompiles().get(bn254_pair::JOVIAN.address()).unwrap();
240        let result = jovian_precompile.call(PrecompileInput {
241            data: &vec![0; bn254_pair::JOVIAN_MAX_INPUT_SIZE + 1],
242            gas: u64::MAX,
243            caller: Address::ZERO,
244            value: U256::ZERO,
245            target_address: Address::ZERO,
246            bytecode_address: Address::ZERO,
247            internals: EvmInternals::new(
248                &mut Journal::<EmptyDB, JournalEntry>::new(EmptyDB::default()),
249                &BlockEnv::default(),
250            ),
251        });
252
253        assert!(result.is_err());
254        assert!(matches!(result.unwrap_err(), PrecompileError::Bn254PairLength));
255
256        let jovian_precompile = evm.precompiles().get(bls12_381::JOVIAN_G1_MSM.address()).unwrap();
257        let result = jovian_precompile.call(PrecompileInput {
258            data: &vec![0; bls12_381::JOVIAN_G1_MSM_MAX_INPUT_SIZE + 1],
259            gas: u64::MAX,
260            caller: Address::ZERO,
261            value: U256::ZERO,
262            target_address: Address::ZERO,
263            bytecode_address: Address::ZERO,
264            internals: EvmInternals::new(
265                &mut Journal::<EmptyDB, JournalEntry>::new(EmptyDB::default()),
266                &BlockEnv::default(),
267            ),
268        });
269
270        assert!(result.is_err());
271        assert!(result.unwrap_err().to_string().contains("G1MSM input length too long"));
272
273        let jovian_precompile = evm.precompiles().get(bls12_381::JOVIAN_G2_MSM.address()).unwrap();
274        let result = jovian_precompile.call(PrecompileInput {
275            data: &vec![0; bls12_381::JOVIAN_G2_MSM_MAX_INPUT_SIZE + 1],
276            gas: u64::MAX,
277            caller: Address::ZERO,
278            value: U256::ZERO,
279            target_address: Address::ZERO,
280            bytecode_address: Address::ZERO,
281            internals: EvmInternals::new(
282                &mut Journal::<EmptyDB, JournalEntry>::new(EmptyDB::default()),
283                &BlockEnv::default(),
284            ),
285        });
286
287        assert!(result.is_err());
288        assert!(result.unwrap_err().to_string().contains("G2MSM input length too long"));
289
290        let jovian_precompile = evm.precompiles().get(bls12_381::JOVIAN_PAIRING.address()).unwrap();
291        let result = jovian_precompile.call(PrecompileInput {
292            data: &vec![0; bls12_381::JOVIAN_PAIRING_MAX_INPUT_SIZE + 1],
293            gas: u64::MAX,
294            caller: Address::ZERO,
295            value: U256::ZERO,
296            target_address: Address::ZERO,
297            bytecode_address: Address::ZERO,
298            internals: EvmInternals::new(
299                &mut Journal::<EmptyDB, JournalEntry>::new(EmptyDB::default()),
300                &BlockEnv::default(),
301            ),
302        });
303
304        assert!(result.is_err());
305        assert!(result.unwrap_err().to_string().contains("Pairing input length too long"));
306    }
307
308    #[test]
309    fn test_precompiles_jovian() {
310        let evm = OpEvmFactory::default().create_evm(
311            EmptyDB::default(),
312            EvmEnv::new(CfgEnv::new_with_spec(OpSpecId::JOVIAN), BlockEnv::default()),
313        );
314        let jovian_precompile = evm.precompiles().get(bn254_pair::JOVIAN.address()).unwrap();
315        let result = jovian_precompile.call(PrecompileInput {
316            data: &vec![0; bn254_pair::JOVIAN_MAX_INPUT_SIZE],
317            gas: u64::MAX,
318            caller: Address::ZERO,
319            value: U256::ZERO,
320            target_address: Address::ZERO,
321            bytecode_address: Address::ZERO,
322            internals: EvmInternals::new(
323                &mut Journal::<EmptyDB, JournalEntry>::new(EmptyDB::default()),
324                &BlockEnv::default(),
325            ),
326        });
327
328        assert!(result.is_ok());
329
330        let jovian_precompile = evm.precompiles().get(bls12_381::JOVIAN_G1_MSM.address()).unwrap();
331        let result = jovian_precompile.call(PrecompileInput {
332            data: &vec![0; bls12_381::JOVIAN_G1_MSM_MAX_INPUT_SIZE],
333            gas: u64::MAX,
334            caller: Address::ZERO,
335            value: U256::ZERO,
336            target_address: Address::ZERO,
337            bytecode_address: Address::ZERO,
338            internals: EvmInternals::new(
339                &mut Journal::<EmptyDB, JournalEntry>::new(EmptyDB::default()),
340                &BlockEnv::default(),
341            ),
342        });
343
344        assert!(result.is_ok());
345
346        let jovian_precompile = evm.precompiles().get(bls12_381::JOVIAN_G2_MSM.address()).unwrap();
347        let result = jovian_precompile.call(PrecompileInput {
348            data: &vec![0; bls12_381::JOVIAN_G2_MSM_MAX_INPUT_SIZE],
349            gas: u64::MAX,
350            caller: Address::ZERO,
351            value: U256::ZERO,
352            target_address: Address::ZERO,
353            bytecode_address: Address::ZERO,
354            internals: EvmInternals::new(
355                &mut Journal::<EmptyDB, JournalEntry>::new(EmptyDB::default()),
356                &BlockEnv::default(),
357            ),
358        });
359
360        assert!(result.is_ok());
361
362        let jovian_precompile = evm.precompiles().get(bls12_381::JOVIAN_PAIRING.address()).unwrap();
363        let result = jovian_precompile.call(PrecompileInput {
364            data: &vec![0; bls12_381::JOVIAN_PAIRING_MAX_INPUT_SIZE],
365            gas: u64::MAX,
366            caller: Address::ZERO,
367            value: U256::ZERO,
368            target_address: Address::ZERO,
369            bytecode_address: Address::ZERO,
370            internals: EvmInternals::new(
371                &mut Journal::<EmptyDB, JournalEntry>::new(EmptyDB::default()),
372                &BlockEnv::default(),
373            ),
374        });
375
376        assert!(result.is_ok());
377    }
378}