op_revm/
evm.rs

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