op_revm/
evm.rs

1use crate::precompiles::OpPrecompiles;
2use revm::{
3    context::{ContextSetters, Evm},
4    context_interface::ContextTr,
5    handler::{
6        instructions::{EthInstructions, InstructionProvider},
7        EvmTr, PrecompileProvider,
8    },
9    inspector::{InspectorEvmTr, JournalExt},
10    interpreter::{interpreter::EthInterpreter, Interpreter, InterpreterAction, InterpreterTypes},
11    Inspector,
12};
13
14pub struct OpEvm<CTX, INSP, I = EthInstructions<EthInterpreter, CTX>, P = OpPrecompiles>(
15    pub Evm<CTX, INSP, I, P>,
16);
17
18impl<CTX: ContextTr, INSP> OpEvm<CTX, INSP, EthInstructions<EthInterpreter, CTX>, OpPrecompiles> {
19    pub fn new(ctx: CTX, inspector: INSP) -> Self {
20        Self(Evm {
21            ctx,
22            inspector,
23            instruction: EthInstructions::new_mainnet(),
24            precompiles: OpPrecompiles::default(),
25        })
26    }
27}
28
29impl<CTX, INSP, I, P> OpEvm<CTX, INSP, I, P> {
30    /// Consumed self and returns a new Evm type with given Inspector.
31    pub fn with_inspector<OINSP>(self, inspector: OINSP) -> OpEvm<CTX, OINSP, I, P> {
32        OpEvm(self.0.with_inspector(inspector))
33    }
34
35    /// Consumes self and returns a new Evm type with given Precompiles.
36    pub fn with_precompiles<OP>(self, precompiles: OP) -> OpEvm<CTX, INSP, I, OP> {
37        OpEvm(self.0.with_precompiles(precompiles))
38    }
39
40    /// Consumes self and returns the inner Inspector.
41    pub fn into_inspector(self) -> INSP {
42        self.0.into_inspector()
43    }
44}
45
46impl<CTX, INSP, I, P> InspectorEvmTr for OpEvm<CTX, INSP, I, P>
47where
48    CTX: ContextTr<Journal: JournalExt> + ContextSetters,
49    I: InstructionProvider<
50        Context = CTX,
51        InterpreterTypes: InterpreterTypes<Output = InterpreterAction>,
52    >,
53    P: PrecompileProvider<CTX>,
54    INSP: Inspector<CTX, I::InterpreterTypes>,
55{
56    type Inspector = INSP;
57
58    fn inspector(&mut self) -> &mut Self::Inspector {
59        &mut self.0.inspector
60    }
61
62    fn ctx_inspector(&mut self) -> (&mut Self::Context, &mut Self::Inspector) {
63        (&mut self.0.ctx, &mut self.0.inspector)
64    }
65
66    fn run_inspect_interpreter(
67        &mut self,
68        interpreter: &mut Interpreter<
69            <Self::Instructions as InstructionProvider>::InterpreterTypes,
70        >,
71    ) -> <<Self::Instructions as InstructionProvider>::InterpreterTypes as InterpreterTypes>::Output
72    {
73        self.0.run_inspect_interpreter(interpreter)
74    }
75}
76
77impl<CTX, INSP, I, P> EvmTr for OpEvm<CTX, INSP, I, P>
78where
79    CTX: ContextTr,
80    I: InstructionProvider<
81        Context = CTX,
82        InterpreterTypes: InterpreterTypes<Output = InterpreterAction>,
83    >,
84    P: PrecompileProvider<CTX>,
85{
86    type Context = CTX;
87    type Instructions = I;
88    type Precompiles = P;
89
90    fn run_interpreter(
91        &mut self,
92        interpreter: &mut Interpreter<
93            <Self::Instructions as InstructionProvider>::InterpreterTypes,
94        >,
95    ) -> <<Self::Instructions as InstructionProvider>::InterpreterTypes as InterpreterTypes>::Output
96    {
97        let context = &mut self.0.ctx;
98        let instructions = &mut self.0.instruction;
99        interpreter.run_plain(instructions.instruction_table(), context)
100    }
101
102    fn ctx(&mut self) -> &mut Self::Context {
103        &mut self.0.ctx
104    }
105
106    fn ctx_ref(&self) -> &Self::Context {
107        &self.0.ctx
108    }
109
110    fn ctx_instructions(&mut self) -> (&mut Self::Context, &mut Self::Instructions) {
111        (&mut self.0.ctx, &mut self.0.instruction)
112    }
113
114    fn ctx_precompiles(&mut self) -> (&mut Self::Context, &mut Self::Precompiles) {
115        (&mut self.0.ctx, &mut self.0.precompiles)
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use crate::{
122        precompiles::bn128_pair::GRANITE_MAX_INPUT_SIZE,
123        transaction::deposit::DEPOSIT_TRANSACTION_TYPE, DefaultOp, L1BlockInfo, OpBuilder,
124        OpHaltReason, OpSpecId, OpTransaction,
125    };
126    use revm::{
127        bytecode::opcode,
128        context::{
129            result::{ExecutionResult, OutOfGasError},
130            BlockEnv, CfgEnv, TxEnv,
131        },
132        context_interface::result::HaltReason,
133        database::{BenchmarkDB, EmptyDB, BENCH_CALLER, BENCH_CALLER_BALANCE, BENCH_TARGET},
134        interpreter::{
135            gas::{calculate_initial_tx_gas, InitialAndFloorGas},
136            Interpreter, InterpreterTypes,
137        },
138        precompile::{bls12_381_const, bls12_381_utils, bn128, secp256r1, u64_to_address},
139        primitives::{Address, Bytes, Log, TxKind, U256},
140        state::Bytecode,
141        Context, ExecuteEvm, InspectEvm, Inspector, Journal,
142    };
143    use std::vec::Vec;
144
145    #[test]
146    fn test_deposit_tx() {
147        let ctx = Context::op()
148            .modify_tx_chained(|tx| {
149                tx.enveloped_tx = None;
150                tx.deposit.mint = Some(100);
151                tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE;
152            })
153            .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::HOLOCENE);
154
155        let mut evm = ctx.build_op();
156
157        let output = evm.replay().unwrap();
158
159        // balance should be 100
160        assert_eq!(
161            output
162                .state
163                .get(&Address::default())
164                .map(|a| a.info.balance),
165            Some(U256::from(100))
166        );
167    }
168
169    #[test]
170    fn test_halted_deposit_tx() {
171        let ctx = Context::op()
172            .modify_tx_chained(|tx| {
173                tx.enveloped_tx = None;
174                tx.deposit.mint = Some(100);
175                tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE;
176                tx.base.caller = BENCH_CALLER;
177                tx.base.kind = TxKind::Call(BENCH_TARGET);
178            })
179            .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::HOLOCENE)
180            .with_db(BenchmarkDB::new_bytecode(Bytecode::new_legacy(
181                [opcode::POP].into(),
182            )));
183
184        // POP would return a halt.
185        let mut evm = ctx.build_op();
186
187        let output = evm.replay().unwrap();
188
189        // balance should be 100 + previous balance
190        assert_eq!(
191            output.result,
192            ExecutionResult::Halt {
193                reason: OpHaltReason::FailedDeposit,
194                gas_used: 30_000_000
195            }
196        );
197        assert_eq!(
198            output.state.get(&BENCH_CALLER).map(|a| a.info.balance),
199            Some(U256::from(100) + BENCH_CALLER_BALANCE)
200        );
201    }
202
203    fn p256verify_test_tx() -> Context<
204        BlockEnv,
205        OpTransaction<TxEnv>,
206        CfgEnv<OpSpecId>,
207        EmptyDB,
208        Journal<EmptyDB>,
209        L1BlockInfo,
210    > {
211        const SPEC_ID: OpSpecId = OpSpecId::FJORD;
212
213        let InitialAndFloorGas { initial_gas, .. } =
214            calculate_initial_tx_gas(SPEC_ID.into(), &[], false, 0, 0, 0);
215
216        Context::op()
217            .modify_tx_chained(|tx| {
218                tx.base.kind = TxKind::Call(u64_to_address(secp256r1::P256VERIFY_ADDRESS));
219                tx.base.gas_limit = initial_gas + secp256r1::P256VERIFY_BASE_GAS_FEE;
220            })
221            .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID)
222    }
223
224    #[test]
225    fn test_tx_call_p256verify() {
226        let ctx = p256verify_test_tx();
227
228        let mut evm = ctx.build_op();
229        let output = evm.replay().unwrap();
230
231        // assert successful call to P256VERIFY
232        assert!(output.result.is_success());
233    }
234
235    #[test]
236    fn test_halted_tx_call_p256verify() {
237        let ctx = p256verify_test_tx().modify_tx_chained(|tx| tx.base.gas_limit -= 1);
238
239        let mut evm = ctx.build_op();
240        let output = evm.replay().unwrap();
241
242        // assert out of gas for P256VERIFY
243        assert!(matches!(
244            output.result,
245            ExecutionResult::Halt {
246                reason: OpHaltReason::Base(HaltReason::OutOfGas(OutOfGasError::Precompile)),
247                ..
248            }
249        ));
250    }
251
252    fn bn128_pair_test_tx(
253        spec: OpSpecId,
254    ) -> Context<
255        BlockEnv,
256        OpTransaction<TxEnv>,
257        CfgEnv<OpSpecId>,
258        EmptyDB,
259        Journal<EmptyDB>,
260        L1BlockInfo,
261    > {
262        let input = Bytes::from([1; GRANITE_MAX_INPUT_SIZE + 2]);
263        let InitialAndFloorGas { initial_gas, .. } =
264            calculate_initial_tx_gas(spec.into(), &input[..], false, 0, 0, 0);
265
266        Context::op()
267            .modify_tx_chained(|tx| {
268                tx.base.kind = TxKind::Call(bn128::pair::ADDRESS);
269                tx.base.data = input;
270                tx.base.gas_limit = initial_gas;
271            })
272            .modify_cfg_chained(|cfg| cfg.spec = spec)
273    }
274
275    #[test]
276    fn test_halted_tx_call_bn128_pair_fjord() {
277        let ctx = bn128_pair_test_tx(OpSpecId::FJORD);
278
279        let mut evm = ctx.build_op();
280        let output = evm.replay().unwrap();
281
282        // assert out of gas
283        assert!(matches!(
284            output.result,
285            ExecutionResult::Halt {
286                reason: OpHaltReason::Base(HaltReason::OutOfGas(OutOfGasError::Precompile)),
287                ..
288            }
289        ));
290    }
291
292    #[test]
293    fn test_halted_tx_call_bn128_pair_granite() {
294        let ctx = bn128_pair_test_tx(OpSpecId::GRANITE);
295
296        let mut evm = ctx.build_op();
297        let output = evm.replay().unwrap();
298
299        // assert bails early because input size too big
300        assert!(matches!(
301            output.result,
302            ExecutionResult::Halt {
303                reason: OpHaltReason::Base(HaltReason::PrecompileError),
304                ..
305            }
306        ));
307    }
308
309    #[test]
310    fn test_halted_tx_call_bls12_381_g1_add_out_of_gas() {
311        let ctx = Context::op()
312            .modify_tx_chained(|tx| {
313                tx.base.kind = TxKind::Call(bls12_381_const::G1_ADD_ADDRESS);
314                tx.base.gas_limit = 21_000 + bls12_381_const::G1_ADD_BASE_GAS_FEE - 1;
315            })
316            .modify_chain_chained(|l1_block| {
317                l1_block.operator_fee_constant = Some(U256::ZERO);
318                l1_block.operator_fee_scalar = Some(U256::ZERO)
319            })
320            .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS);
321
322        let mut evm = ctx.build_op();
323
324        let output = evm.replay().unwrap();
325
326        // assert out of gas
327        assert!(matches!(
328            output.result,
329            ExecutionResult::Halt {
330                reason: OpHaltReason::Base(HaltReason::OutOfGas(OutOfGasError::Precompile)),
331                ..
332            }
333        ));
334    }
335
336    #[test]
337    fn test_halted_tx_call_bls12_381_g1_add_input_wrong_size() {
338        let ctx = Context::op()
339            .modify_tx_chained(|tx| {
340                tx.base.kind = TxKind::Call(bls12_381_const::G1_ADD_ADDRESS);
341                tx.base.gas_limit = 21_000 + bls12_381_const::G1_ADD_BASE_GAS_FEE;
342            })
343            .modify_chain_chained(|l1_block| {
344                l1_block.operator_fee_constant = Some(U256::ZERO);
345                l1_block.operator_fee_scalar = Some(U256::ZERO)
346            })
347            .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS);
348
349        let mut evm = ctx.build_op();
350        let output = evm.replay().unwrap();
351
352        // assert fails post gas check, because input is wrong size
353        assert!(matches!(
354            output.result,
355            ExecutionResult::Halt {
356                reason: OpHaltReason::Base(HaltReason::PrecompileError),
357                ..
358            }
359        ));
360    }
361
362    fn g1_msm_test_tx() -> Context<
363        BlockEnv,
364        OpTransaction<TxEnv>,
365        CfgEnv<OpSpecId>,
366        EmptyDB,
367        Journal<EmptyDB>,
368        L1BlockInfo,
369    > {
370        const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS;
371
372        let input = Bytes::from([1; bls12_381_const::G1_MSM_INPUT_LENGTH]);
373        let InitialAndFloorGas { initial_gas, .. } =
374            calculate_initial_tx_gas(SPEC_ID.into(), &input[..], false, 0, 0, 0);
375        let gs1_msm_gas = bls12_381_utils::msm_required_gas(
376            1,
377            &bls12_381_const::DISCOUNT_TABLE_G1_MSM,
378            bls12_381_const::G1_MSM_BASE_GAS_FEE,
379        );
380
381        Context::op()
382            .modify_tx_chained(|tx| {
383                tx.base.kind = TxKind::Call(bls12_381_const::G1_MSM_ADDRESS);
384                tx.base.data = input;
385                tx.base.gas_limit = initial_gas + gs1_msm_gas;
386            })
387            .modify_chain_chained(|l1_block| {
388                l1_block.operator_fee_constant = Some(U256::ZERO);
389                l1_block.operator_fee_scalar = Some(U256::ZERO)
390            })
391            .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID)
392    }
393
394    #[test]
395    fn test_halted_tx_call_bls12_381_g1_msm_input_wrong_size() {
396        let ctx = g1_msm_test_tx().modify_tx_chained(|tx| tx.base.data = tx.base.data.slice(1..));
397
398        let mut evm = ctx.build_op();
399        let output = evm.replay().unwrap();
400
401        // assert fails pre gas check, because input is wrong size
402        assert!(matches!(
403            output.result,
404            ExecutionResult::Halt {
405                reason: OpHaltReason::Base(HaltReason::PrecompileError),
406                ..
407            }
408        ));
409    }
410
411    #[test]
412    fn test_halted_tx_call_bls12_381_g1_msm_out_of_gas() {
413        let ctx = g1_msm_test_tx().modify_tx_chained(|tx| tx.base.gas_limit -= 1);
414
415        let mut evm = ctx.build_op();
416        let output = evm.replay().unwrap();
417
418        // assert out of gas
419        assert!(matches!(
420            output.result,
421            ExecutionResult::Halt {
422                reason: OpHaltReason::Base(HaltReason::OutOfGas(OutOfGasError::Precompile)),
423                ..
424            }
425        ));
426    }
427
428    #[test]
429    fn test_halted_tx_call_bls12_381_g1_msm_wrong_input_layout() {
430        let ctx = g1_msm_test_tx();
431
432        let mut evm = ctx.build_op();
433        let output = evm.replay().unwrap();
434
435        // assert fails post gas check, because input is wrong layout
436        assert!(matches!(
437            output.result,
438            ExecutionResult::Halt {
439                reason: OpHaltReason::Base(HaltReason::PrecompileError),
440                ..
441            }
442        ));
443    }
444
445    #[test]
446    fn test_halted_tx_call_bls12_381_g2_add_out_of_gas() {
447        let ctx = Context::op()
448            .modify_tx_chained(|tx| {
449                tx.base.kind = TxKind::Call(bls12_381_const::G2_ADD_ADDRESS);
450                tx.base.gas_limit = 21_000 + bls12_381_const::G2_ADD_BASE_GAS_FEE - 1;
451            })
452            .modify_chain_chained(|l1_block| {
453                l1_block.operator_fee_constant = Some(U256::ZERO);
454                l1_block.operator_fee_scalar = Some(U256::ZERO)
455            })
456            .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS);
457
458        let mut evm = ctx.build_op();
459
460        let output = evm.replay().unwrap();
461
462        // assert out of gas
463        assert!(matches!(
464            output.result,
465            ExecutionResult::Halt {
466                reason: OpHaltReason::Base(HaltReason::OutOfGas(OutOfGasError::Precompile)),
467                ..
468            }
469        ));
470    }
471
472    #[test]
473    fn test_halted_tx_call_bls12_381_g2_add_input_wrong_size() {
474        let ctx = Context::op()
475            .modify_tx_chained(|tx| {
476                tx.base.kind = TxKind::Call(bls12_381_const::G2_ADD_ADDRESS);
477                tx.base.gas_limit = 21_000 + bls12_381_const::G2_ADD_BASE_GAS_FEE;
478            })
479            .modify_chain_chained(|l1_block| {
480                l1_block.operator_fee_constant = Some(U256::ZERO);
481                l1_block.operator_fee_scalar = Some(U256::ZERO)
482            })
483            .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS);
484
485        let mut evm = ctx.build_op();
486
487        let output = evm.replay().unwrap();
488
489        // assert fails post gas check, because input is wrong size
490        assert!(matches!(
491            output.result,
492            ExecutionResult::Halt {
493                reason: OpHaltReason::Base(HaltReason::PrecompileError),
494                ..
495            }
496        ));
497    }
498
499    fn g2_msm_test_tx() -> Context<
500        BlockEnv,
501        OpTransaction<TxEnv>,
502        CfgEnv<OpSpecId>,
503        EmptyDB,
504        Journal<EmptyDB>,
505        L1BlockInfo,
506    > {
507        const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS;
508
509        let input = Bytes::from([1; bls12_381_const::G2_MSM_INPUT_LENGTH]);
510        let InitialAndFloorGas { initial_gas, .. } =
511            calculate_initial_tx_gas(SPEC_ID.into(), &input[..], false, 0, 0, 0);
512        let gs2_msm_gas = bls12_381_utils::msm_required_gas(
513            1,
514            &bls12_381_const::DISCOUNT_TABLE_G2_MSM,
515            bls12_381_const::G2_MSM_BASE_GAS_FEE,
516        );
517
518        Context::op()
519            .modify_tx_chained(|tx| {
520                tx.base.kind = TxKind::Call(bls12_381_const::G2_MSM_ADDRESS);
521                tx.base.data = input;
522                tx.base.gas_limit = initial_gas + gs2_msm_gas;
523            })
524            .modify_chain_chained(|l1_block| {
525                l1_block.operator_fee_constant = Some(U256::ZERO);
526                l1_block.operator_fee_scalar = Some(U256::ZERO)
527            })
528            .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID)
529    }
530
531    #[test]
532    fn test_halted_tx_call_bls12_381_g2_msm_input_wrong_size() {
533        let ctx = g2_msm_test_tx().modify_tx_chained(|tx| tx.base.data = tx.base.data.slice(1..));
534
535        let mut evm = ctx.build_op();
536        let output = evm.replay().unwrap();
537
538        // assert fails pre gas check, because input is wrong size
539        assert!(matches!(
540            output.result,
541            ExecutionResult::Halt {
542                reason: OpHaltReason::Base(HaltReason::PrecompileError),
543                ..
544            }
545        ));
546    }
547
548    #[test]
549    fn test_halted_tx_call_bls12_381_g2_msm_out_of_gas() {
550        let ctx = g2_msm_test_tx().modify_tx_chained(|tx| tx.base.gas_limit -= 1);
551
552        let mut evm = ctx.build_op();
553        let output = evm.replay().unwrap();
554
555        // assert out of gas
556        assert!(matches!(
557            output.result,
558            ExecutionResult::Halt {
559                reason: OpHaltReason::Base(HaltReason::OutOfGas(OutOfGasError::Precompile)),
560                ..
561            }
562        ));
563    }
564
565    #[test]
566    fn test_halted_tx_call_bls12_381_g2_msm_wrong_input_layout() {
567        let ctx = g2_msm_test_tx();
568
569        let mut evm = ctx.build_op();
570        let output = evm.replay().unwrap();
571
572        // assert fails post gas check, because input is wrong layout
573        assert!(matches!(
574            output.result,
575            ExecutionResult::Halt {
576                reason: OpHaltReason::Base(HaltReason::PrecompileError),
577                ..
578            }
579        ));
580    }
581
582    fn bl12_381_pairing_test_tx() -> Context<
583        BlockEnv,
584        OpTransaction<TxEnv>,
585        CfgEnv<OpSpecId>,
586        EmptyDB,
587        Journal<EmptyDB>,
588        L1BlockInfo,
589    > {
590        const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS;
591
592        let input = Bytes::from([1; bls12_381_const::PAIRING_INPUT_LENGTH]);
593        let InitialAndFloorGas { initial_gas, .. } =
594            calculate_initial_tx_gas(SPEC_ID.into(), &input[..], false, 0, 0, 0);
595
596        let pairing_gas: u64 =
597            bls12_381_const::PAIRING_MULTIPLIER_BASE + bls12_381_const::PAIRING_OFFSET_BASE;
598
599        Context::op()
600            .modify_tx_chained(|tx| {
601                tx.base.kind = TxKind::Call(bls12_381_const::PAIRING_ADDRESS);
602                tx.base.data = input;
603                tx.base.gas_limit = initial_gas + pairing_gas;
604            })
605            .modify_chain_chained(|l1_block| {
606                l1_block.operator_fee_constant = Some(U256::ZERO);
607                l1_block.operator_fee_scalar = Some(U256::ZERO)
608            })
609            .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS)
610    }
611
612    #[test]
613    fn test_halted_tx_call_bls12_381_pairing_input_wrong_size() {
614        let ctx = bl12_381_pairing_test_tx()
615            .modify_tx_chained(|tx| tx.base.data = tx.base.data.slice(1..));
616
617        let mut evm = ctx.build_op();
618        let output = evm.replay().unwrap();
619
620        // assert fails pre gas check, because input is wrong size
621        assert!(matches!(
622            output.result,
623            ExecutionResult::Halt {
624                reason: OpHaltReason::Base(HaltReason::PrecompileError),
625                ..
626            }
627        ));
628    }
629
630    #[test]
631    fn test_halted_tx_call_bls12_381_pairing_out_of_gas() {
632        let ctx = bl12_381_pairing_test_tx().modify_tx_chained(|tx| tx.base.gas_limit -= 1);
633
634        let mut evm = ctx.build_op();
635        let output = evm.replay().unwrap();
636
637        // assert out of gas
638        assert!(matches!(
639            output.result,
640            ExecutionResult::Halt {
641                reason: OpHaltReason::Base(HaltReason::OutOfGas(OutOfGasError::Precompile)),
642                ..
643            }
644        ));
645    }
646
647    #[test]
648    fn test_tx_call_bls12_381_pairing_wrong_input_layout() {
649        let ctx = bl12_381_pairing_test_tx();
650
651        let mut evm = ctx.build_op();
652        let output = evm.replay().unwrap();
653
654        // assert fails post gas check, because input is wrong layout
655        assert!(matches!(
656            output.result,
657            ExecutionResult::Halt {
658                reason: OpHaltReason::Base(HaltReason::PrecompileError),
659                ..
660            }
661        ));
662    }
663
664    fn fp_to_g1_test_tx() -> Context<
665        BlockEnv,
666        OpTransaction<TxEnv>,
667        CfgEnv<OpSpecId>,
668        EmptyDB,
669        Journal<EmptyDB>,
670        L1BlockInfo,
671    > {
672        const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS;
673
674        let input = Bytes::from([1; bls12_381_const::PADDED_FP_LENGTH]);
675        let InitialAndFloorGas { initial_gas, .. } =
676            calculate_initial_tx_gas(SPEC_ID.into(), &input[..], false, 0, 0, 0);
677
678        Context::op()
679            .modify_tx_chained(|tx| {
680                tx.base.kind = TxKind::Call(bls12_381_const::MAP_FP_TO_G1_ADDRESS);
681                tx.base.data = input;
682                tx.base.gas_limit = initial_gas + bls12_381_const::MAP_FP_TO_G1_BASE_GAS_FEE;
683            })
684            .modify_chain_chained(|l1_block| {
685                l1_block.operator_fee_constant = Some(U256::ZERO);
686                l1_block.operator_fee_scalar = Some(U256::ZERO)
687            })
688            .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID)
689    }
690
691    #[test]
692    fn test_halted_tx_call_bls12_381_map_fp_to_g1_out_of_gas() {
693        let ctx = fp_to_g1_test_tx().modify_tx_chained(|tx| tx.base.gas_limit -= 1);
694
695        let mut evm = ctx.build_op();
696        let output = evm.replay().unwrap();
697
698        // assert out of gas
699        assert!(matches!(
700            output.result,
701            ExecutionResult::Halt {
702                reason: OpHaltReason::Base(HaltReason::OutOfGas(OutOfGasError::Precompile)),
703                ..
704            }
705        ));
706    }
707
708    #[test]
709    fn test_halted_tx_call_bls12_381_map_fp_to_g1_input_wrong_size() {
710        let ctx = fp_to_g1_test_tx().modify_tx_chained(|tx| tx.base.data = tx.base.data.slice(1..));
711
712        let mut evm = ctx.build_op();
713        let output = evm.replay().unwrap();
714
715        // assert fails post gas check, because input is wrong size
716        assert!(matches!(
717            output.result,
718            ExecutionResult::Halt {
719                reason: OpHaltReason::Base(HaltReason::PrecompileError),
720                ..
721            }
722        ));
723    }
724
725    fn fp2_to_g2_test_tx() -> Context<
726        BlockEnv,
727        OpTransaction<TxEnv>,
728        CfgEnv<OpSpecId>,
729        EmptyDB,
730        Journal<EmptyDB>,
731        L1BlockInfo,
732    > {
733        const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS;
734
735        let input = Bytes::from([1; bls12_381_const::PADDED_FP2_LENGTH]);
736        let InitialAndFloorGas { initial_gas, .. } =
737            calculate_initial_tx_gas(SPEC_ID.into(), &input[..], false, 0, 0, 0);
738
739        Context::op()
740            .modify_tx_chained(|tx| {
741                tx.base.kind = TxKind::Call(bls12_381_const::MAP_FP2_TO_G2_ADDRESS);
742                tx.base.data = input;
743                tx.base.gas_limit = initial_gas + bls12_381_const::MAP_FP2_TO_G2_BASE_GAS_FEE;
744            })
745            .modify_chain_chained(|l1_block| {
746                l1_block.operator_fee_constant = Some(U256::ZERO);
747                l1_block.operator_fee_scalar = Some(U256::ZERO)
748            })
749            .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID)
750    }
751
752    #[test]
753    fn test_halted_tx_call_bls12_381_map_fp2_to_g2_out_of_gas() {
754        let ctx = fp2_to_g2_test_tx().modify_tx_chained(|tx| tx.base.gas_limit -= 1);
755
756        let mut evm = ctx.build_op();
757        let output = evm.replay().unwrap();
758
759        // assert out of gas
760        assert!(matches!(
761            output.result,
762            ExecutionResult::Halt {
763                reason: OpHaltReason::Base(HaltReason::OutOfGas(OutOfGasError::Precompile)),
764                ..
765            }
766        ));
767    }
768
769    #[test]
770    fn test_halted_tx_call_bls12_381_map_fp2_to_g2_input_wrong_size() {
771        let ctx =
772            fp2_to_g2_test_tx().modify_tx_chained(|tx| tx.base.data = tx.base.data.slice(1..));
773
774        let mut evm = ctx.build_op();
775        let output = evm.replay().unwrap();
776
777        // assert fails post gas check, because input is wrong size
778        assert!(matches!(
779            output.result,
780            ExecutionResult::Halt {
781                reason: OpHaltReason::Base(HaltReason::PrecompileError),
782                ..
783            }
784        ));
785    }
786
787    #[derive(Default, Debug)]
788    struct LogInspector {
789        logs: Vec<Log>,
790    }
791
792    impl<CTX, INTR: InterpreterTypes> Inspector<CTX, INTR> for LogInspector {
793        fn log(&mut self, _interp: &mut Interpreter<INTR>, _context: &mut CTX, log: Log) {
794            self.logs.push(log)
795        }
796    }
797
798    #[test]
799    fn test_log_inspector() {
800        // simple yul contract emits a log in constructor
801
802        /*object "Contract" {
803            code {
804                log0(0, 0)
805            }
806        }*/
807
808        let contract_data: Bytes = Bytes::from([
809            opcode::PUSH1,
810            0x00,
811            opcode::DUP1,
812            opcode::LOG0,
813            opcode::STOP,
814        ]);
815        let bytecode = Bytecode::new_raw(contract_data);
816
817        let ctx = Context::op()
818            .with_db(BenchmarkDB::new_bytecode(bytecode.clone()))
819            .modify_tx_chained(|tx| {
820                tx.base.caller = BENCH_CALLER;
821                tx.base.kind = TxKind::Call(BENCH_TARGET);
822            });
823
824        let mut evm = ctx.build_op_with_inspector(LogInspector::default());
825
826        // Run evm.
827        let _ = evm.inspect_replay().unwrap();
828
829        let inspector = &evm.0.inspector;
830        assert!(!inspector.logs.is_empty());
831    }
832}