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#[allow(missing_debug_implementations)] pub 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 pub const fn ctx(&self) -> &OpContext<DB> {
50 &self.inner.0.ctx
51 }
52
53 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 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#[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}