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 pub fn with_inspector<OINSP>(self, inspector: OINSP) -> OpEvm<CTX, OINSP, I, P> {
32 OpEvm(self.0.with_inspector(inspector))
33 }
34
35 pub fn with_precompiles<OP>(self, precompiles: OP) -> OpEvm<CTX, INSP, I, OP> {
37 OpEvm(self.0.with_precompiles(precompiles))
38 }
39
40 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 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 let mut evm = ctx.build_op();
186
187 let output = evm.replay().unwrap();
188
189 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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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!(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 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 let _ = evm.inspect_replay().unwrap();
828
829 let inspector = &evm.0.inspector;
830 assert!(!inspector.logs.is_empty());
831 }
832}