1use crate::OpSpecId;
3use revm::{
4 context::Cfg,
5 context_interface::ContextTr,
6 handler::{EthPrecompiles, PrecompileProvider},
7 interpreter::{CallInputs, InterpreterResult},
8 precompile::{
9 self, bn254, secp256r1, Precompile, PrecompileError, PrecompileId, PrecompileResult,
10 Precompiles,
11 },
12 primitives::{hardfork::SpecId, Address, OnceLock},
13};
14use std::boxed::Box;
15use std::string::String;
16
17#[derive(Debug, Clone)]
19pub struct OpPrecompiles {
20 inner: EthPrecompiles,
22 spec: OpSpecId,
24}
25
26impl OpPrecompiles {
27 #[inline]
29 pub fn new_with_spec(spec: OpSpecId) -> Self {
30 let precompiles = match spec {
31 spec @ (OpSpecId::BEDROCK
32 | OpSpecId::REGOLITH
33 | OpSpecId::CANYON
34 | OpSpecId::ECOTONE) => Precompiles::new(spec.into_eth_spec().into()),
35 OpSpecId::FJORD => fjord(),
36 OpSpecId::GRANITE | OpSpecId::HOLOCENE => granite(),
37 OpSpecId::ISTHMUS => isthmus(),
38 OpSpecId::INTEROP | OpSpecId::OSAKA | OpSpecId::JOVIAN => jovian(),
39 };
40
41 Self {
42 inner: EthPrecompiles {
43 precompiles,
44 spec: SpecId::default(),
45 },
46 spec,
47 }
48 }
49
50 #[inline]
52 pub fn precompiles(&self) -> &'static Precompiles {
53 self.inner.precompiles
54 }
55}
56
57pub fn fjord() -> &'static Precompiles {
59 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
60 INSTANCE.get_or_init(|| {
61 let mut precompiles = Precompiles::cancun().clone();
62 precompiles.extend([secp256r1::P256VERIFY]);
64 precompiles
65 })
66}
67
68pub fn granite() -> &'static Precompiles {
70 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
71 INSTANCE.get_or_init(|| {
72 let mut precompiles = fjord().clone();
73 precompiles.extend([bn254_pair::GRANITE]);
75 precompiles
76 })
77}
78
79pub fn isthmus() -> &'static Precompiles {
81 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
82 INSTANCE.get_or_init(|| {
83 let mut precompiles = granite().clone();
84 precompiles.extend(precompile::bls12_381::precompiles());
86 precompiles.extend([
88 bls12_381::ISTHMUS_G1_MSM,
89 bls12_381::ISTHMUS_G2_MSM,
90 bls12_381::ISTHMUS_PAIRING,
91 ]);
92 precompiles
93 })
94}
95
96pub fn jovian() -> &'static Precompiles {
98 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
99 INSTANCE.get_or_init(|| {
100 let mut precompiles = isthmus().clone();
101
102 let mut to_remove = Precompiles::default();
103 to_remove.extend([
104 bn254::pair::ISTANBUL,
105 bls12_381::ISTHMUS_G1_MSM,
106 bls12_381::ISTHMUS_G2_MSM,
107 bls12_381::ISTHMUS_PAIRING,
108 ]);
109
110 precompiles.difference(&to_remove);
112
113 precompiles.extend([
114 bn254_pair::JOVIAN,
115 bls12_381::JOVIAN_G1_MSM,
116 bls12_381::JOVIAN_G2_MSM,
117 bls12_381::JOVIAN_PAIRING,
118 ]);
119
120 precompiles
121 })
122}
123
124impl<CTX> PrecompileProvider<CTX> for OpPrecompiles
125where
126 CTX: ContextTr<Cfg: Cfg<Spec = OpSpecId>>,
127{
128 type Output = InterpreterResult;
129
130 #[inline]
131 fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool {
132 if spec == self.spec {
133 return false;
134 }
135 *self = Self::new_with_spec(spec);
136 true
137 }
138
139 #[inline]
140 fn run(
141 &mut self,
142 context: &mut CTX,
143 inputs: &CallInputs,
144 ) -> Result<Option<Self::Output>, String> {
145 self.inner.run(context, inputs)
146 }
147
148 #[inline]
149 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
150 self.inner.warm_addresses()
151 }
152
153 #[inline]
154 fn contains(&self, address: &Address) -> bool {
155 self.inner.contains(address)
156 }
157}
158
159impl Default for OpPrecompiles {
160 fn default() -> Self {
161 Self::new_with_spec(OpSpecId::JOVIAN)
162 }
163}
164
165pub mod bn254_pair {
167 use super::*;
168
169 pub const GRANITE_MAX_INPUT_SIZE: usize = 112687;
171 pub const GRANITE: Precompile = Precompile::new(
173 PrecompileId::Bn254Pairing,
174 bn254::pair::ADDRESS,
175 run_pair_granite,
176 );
177
178 pub fn run_pair_granite(input: &[u8], gas_limit: u64) -> PrecompileResult {
180 if input.len() > GRANITE_MAX_INPUT_SIZE {
181 return Err(PrecompileError::Bn254PairLength);
182 }
183 bn254::run_pair(
184 input,
185 bn254::pair::ISTANBUL_PAIR_PER_POINT,
186 bn254::pair::ISTANBUL_PAIR_BASE,
187 gas_limit,
188 )
189 }
190
191 pub const JOVIAN_MAX_INPUT_SIZE: usize = 81_984;
193 pub const JOVIAN: Precompile = Precompile::new(
195 PrecompileId::Bn254Pairing,
196 bn254::pair::ADDRESS,
197 run_pair_jovian,
198 );
199
200 pub fn run_pair_jovian(input: &[u8], gas_limit: u64) -> PrecompileResult {
202 if input.len() > JOVIAN_MAX_INPUT_SIZE {
203 return Err(PrecompileError::Bn254PairLength);
204 }
205 bn254::run_pair(
206 input,
207 bn254::pair::ISTANBUL_PAIR_PER_POINT,
208 bn254::pair::ISTANBUL_PAIR_BASE,
209 gas_limit,
210 )
211 }
212}
213
214pub mod bls12_381 {
216 use super::*;
217 use revm::precompile::bls12_381_const::{G1_MSM_ADDRESS, G2_MSM_ADDRESS, PAIRING_ADDRESS};
218
219 pub const ISTHMUS_G1_MSM_MAX_INPUT_SIZE: usize = 513760;
221
222 pub const JOVIAN_G1_MSM_MAX_INPUT_SIZE: usize = 288_960;
224
225 pub const ISTHMUS_G2_MSM_MAX_INPUT_SIZE: usize = 488448;
227
228 pub const JOVIAN_G2_MSM_MAX_INPUT_SIZE: usize = 278_784;
230
231 pub const ISTHMUS_PAIRING_MAX_INPUT_SIZE: usize = 235008;
233
234 pub const JOVIAN_PAIRING_MAX_INPUT_SIZE: usize = 156_672;
236
237 pub const ISTHMUS_G1_MSM: Precompile =
239 Precompile::new(PrecompileId::Bls12G1Msm, G1_MSM_ADDRESS, run_g1_msm_isthmus);
240 pub const ISTHMUS_G2_MSM: Precompile =
242 Precompile::new(PrecompileId::Bls12G2Msm, G2_MSM_ADDRESS, run_g2_msm_isthmus);
243 pub const ISTHMUS_PAIRING: Precompile = Precompile::new(
245 PrecompileId::Bls12Pairing,
246 PAIRING_ADDRESS,
247 run_pair_isthmus,
248 );
249
250 pub const JOVIAN_G1_MSM: Precompile =
252 Precompile::new(PrecompileId::Bls12G1Msm, G1_MSM_ADDRESS, run_g1_msm_jovian);
253 pub const JOVIAN_G2_MSM: Precompile =
255 Precompile::new(PrecompileId::Bls12G2Msm, G2_MSM_ADDRESS, run_g2_msm_jovian);
256 pub const JOVIAN_PAIRING: Precompile =
258 Precompile::new(PrecompileId::Bls12Pairing, PAIRING_ADDRESS, run_pair_jovian);
259
260 pub fn run_g1_msm_isthmus(input: &[u8], gas_limit: u64) -> PrecompileResult {
262 if input.len() > ISTHMUS_G1_MSM_MAX_INPUT_SIZE {
263 return Err(PrecompileError::Other(
264 "G1MSM input length too long for OP Stack input size limitation after the Isthmus Hardfork".into(),
265 ));
266 }
267 precompile::bls12_381::g1_msm::g1_msm(input, gas_limit)
268 }
269
270 pub fn run_g1_msm_jovian(input: &[u8], gas_limit: u64) -> PrecompileResult {
272 if input.len() > JOVIAN_G1_MSM_MAX_INPUT_SIZE {
273 return Err(PrecompileError::Other(
274 "G1MSM input length too long for OP Stack input size limitation after the Jovian Hardfork".into(),
275 ));
276 }
277 precompile::bls12_381::g1_msm::g1_msm(input, gas_limit)
278 }
279
280 pub fn run_g2_msm_isthmus(input: &[u8], gas_limit: u64) -> PrecompileResult {
282 if input.len() > ISTHMUS_G2_MSM_MAX_INPUT_SIZE {
283 return Err(PrecompileError::Other(
284 "G2MSM input length too long for OP Stack input size limitation".into(),
285 ));
286 }
287 precompile::bls12_381::g2_msm::g2_msm(input, gas_limit)
288 }
289
290 pub fn run_g2_msm_jovian(input: &[u8], gas_limit: u64) -> PrecompileResult {
292 if input.len() > JOVIAN_G2_MSM_MAX_INPUT_SIZE {
293 return Err(PrecompileError::Other(
294 "G2MSM input length too long for OP Stack input size limitation after the Jovian Hardfork".into(),
295 ));
296 }
297 precompile::bls12_381::g2_msm::g2_msm(input, gas_limit)
298 }
299
300 pub fn run_pair_isthmus(input: &[u8], gas_limit: u64) -> PrecompileResult {
302 if input.len() > ISTHMUS_PAIRING_MAX_INPUT_SIZE {
303 return Err(PrecompileError::Other(
304 "Pairing input length too long for OP Stack input size limitation".into(),
305 ));
306 }
307 precompile::bls12_381::pairing::pairing(input, gas_limit)
308 }
309
310 pub fn run_pair_jovian(input: &[u8], gas_limit: u64) -> PrecompileResult {
312 if input.len() > JOVIAN_PAIRING_MAX_INPUT_SIZE {
313 return Err(PrecompileError::Other(
314 "Pairing input length too long for OP Stack input size limitation after the Jovian Hardfork".into(),
315 ));
316 }
317 precompile::bls12_381::pairing::pairing(input, gas_limit)
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use crate::precompiles::bls12_381::{
324 run_g1_msm_isthmus, run_g1_msm_jovian, run_g2_msm_isthmus, run_g2_msm_jovian,
325 ISTHMUS_G1_MSM_MAX_INPUT_SIZE, ISTHMUS_G2_MSM_MAX_INPUT_SIZE,
326 ISTHMUS_PAIRING_MAX_INPUT_SIZE, JOVIAN_G1_MSM_MAX_INPUT_SIZE, JOVIAN_G2_MSM_MAX_INPUT_SIZE,
327 JOVIAN_PAIRING_MAX_INPUT_SIZE,
328 };
329
330 use super::*;
331 use revm::{
332 precompile::{bls12_381_const, PrecompileError},
333 primitives::{hex, Bytes},
334 };
335 use std::vec;
336
337 #[test]
338 fn test_bn254_pair() {
339 let input = hex::decode(
340 "\
341 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
342 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
343 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
344 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
345 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
346 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
347 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
348 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
349 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
350 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
351 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
352 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
353 )
354 .unwrap();
355 let expected =
356 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
357 .unwrap();
358 let outcome = bn254_pair::run_pair_granite(&input, 260_000).unwrap();
359 assert_eq!(outcome.bytes, expected);
360
361 let input = hex::decode(
363 "\
364 1111111111111111111111111111111111111111111111111111111111111111\
365 1111111111111111111111111111111111111111111111111111111111111111\
366 111111111111111111111111111111\
367 ",
368 )
369 .unwrap();
370
371 let res = bn254_pair::run_pair_granite(&input, 260_000);
372 assert!(matches!(res, Err(PrecompileError::Bn254PairLength)));
373
374 let input = vec![1u8; 586 * bn254::PAIR_ELEMENT_LEN];
376 let res = bn254_pair::run_pair_granite(&input, 260_000);
377 assert!(matches!(res, Err(PrecompileError::OutOfGas)));
378
379 let input = vec![1u8; 587 * bn254::PAIR_ELEMENT_LEN];
381 let res = bn254_pair::run_pair_granite(&input, 260_000);
382 assert!(matches!(res, Err(PrecompileError::Bn254PairLength)));
383 }
384
385 #[test]
386 fn test_accelerated_bn254_pairing_jovian() {
387 const TEST_INPUT: [u8; 384] = hex!(
388 "2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f61fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d92bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f902fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd451971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc72a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea223a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc"
389 );
390 const EXPECTED_OUTPUT: [u8; 32] =
391 hex!("0000000000000000000000000000000000000000000000000000000000000001");
392
393 let res = bn254_pair::run_pair_jovian(TEST_INPUT.as_ref(), u64::MAX);
394 assert!(matches!(res, Ok(outcome) if **outcome.bytes == EXPECTED_OUTPUT));
395 }
396
397 #[test]
398 fn test_accelerated_bn254_pairing_bad_input_len_jovian() {
399 let input = [0u8; bn254_pair::JOVIAN_MAX_INPUT_SIZE + 1];
400 let res = bn254_pair::run_pair_jovian(&input, u64::MAX);
401 assert!(matches!(res, Err(PrecompileError::Bn254PairLength)));
402 }
403
404 #[test]
405 fn test_get_jovian_precompile_with_bad_input_len() {
406 let precompiles = OpPrecompiles::new_with_spec(OpSpecId::JOVIAN);
407 let bn254_pair_precompile = precompiles
408 .precompiles()
409 .get(&bn254::pair::ADDRESS)
410 .unwrap();
411
412 let mut bad_input_len = bn254_pair::JOVIAN_MAX_INPUT_SIZE + 1;
413 assert!(bad_input_len < bn254_pair::GRANITE_MAX_INPUT_SIZE);
414 let input = vec![0u8; bad_input_len];
415
416 let res = bn254_pair_precompile.execute(&input, u64::MAX);
417 assert!(matches!(res, Err(PrecompileError::Bn254PairLength)));
418
419 let bls12_381_g1_msm_precompile = precompiles
420 .precompiles()
421 .get(&bls12_381_const::G1_MSM_ADDRESS)
422 .unwrap();
423 bad_input_len = bls12_381::JOVIAN_G1_MSM_MAX_INPUT_SIZE + 1;
424 assert!(bad_input_len < bls12_381::ISTHMUS_G1_MSM_MAX_INPUT_SIZE);
425 let input = vec![0u8; bad_input_len];
426 let res = bls12_381_g1_msm_precompile.execute(&input, u64::MAX);
427 assert!(
428 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
429 );
430
431 let bls12_381_g2_msm_precompile = precompiles
432 .precompiles()
433 .get(&bls12_381_const::G2_MSM_ADDRESS)
434 .unwrap();
435 bad_input_len = bls12_381::JOVIAN_G2_MSM_MAX_INPUT_SIZE + 1;
436 assert!(bad_input_len < bls12_381::ISTHMUS_G2_MSM_MAX_INPUT_SIZE);
437 let input = vec![0u8; bad_input_len];
438 let res = bls12_381_g2_msm_precompile.execute(&input, u64::MAX);
439 assert!(
440 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
441 );
442
443 let bls12_381_pairing_precompile = precompiles
444 .precompiles()
445 .get(&bls12_381_const::PAIRING_ADDRESS)
446 .unwrap();
447 bad_input_len = bls12_381::JOVIAN_PAIRING_MAX_INPUT_SIZE + 1;
448 assert!(bad_input_len < bls12_381::ISTHMUS_PAIRING_MAX_INPUT_SIZE);
449 let input = vec![0u8; bad_input_len];
450 let res = bls12_381_pairing_precompile.execute(&input, u64::MAX);
451 assert!(
452 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
453 );
454 }
455
456 #[test]
457 fn test_cancun_precompiles_in_fjord() {
458 assert_eq!(fjord().difference(Precompiles::cancun()).len(), 1)
460 }
461
462 #[test]
463 fn test_cancun_precompiles_in_granite() {
464 assert_eq!(granite().difference(Precompiles::cancun()).len(), 1)
467 }
468
469 #[test]
470 fn test_prague_precompiles_in_isthmus() {
471 let new_prague_precompiles = Precompiles::prague().difference(Precompiles::cancun());
472
473 assert!(new_prague_precompiles.difference(isthmus()).is_empty())
475 }
476
477 #[test]
478 fn test_prague_precompiles_in_jovian() {
479 let new_prague_precompiles = Precompiles::prague().difference(Precompiles::cancun());
480
481 assert!(new_prague_precompiles.difference(jovian()).is_empty())
483 }
484
485 #[test]
487 fn test_isthmus_precompiles_in_jovian() {
488 let new_isthmus_precompiles = isthmus().difference(Precompiles::cancun());
489
490 assert!(new_isthmus_precompiles.difference(jovian()).is_empty())
492 }
493
494 #[test]
495 fn test_default_precompiles_is_latest() {
496 let latest = OpPrecompiles::new_with_spec(OpSpecId::default())
497 .inner
498 .precompiles;
499 let default = OpPrecompiles::default().inner.precompiles;
500 assert_eq!(latest.len(), default.len());
501
502 let intersection = default.intersection(latest);
503 assert_eq!(intersection.len(), latest.len())
504 }
505
506 #[test]
507 fn test_g1_isthmus_max_size() {
508 let oversized_input = vec![0u8; ISTHMUS_G1_MSM_MAX_INPUT_SIZE + 1];
509 let input = Bytes::from(oversized_input);
510
511 let res = run_g1_msm_isthmus(&input, 260_000);
512
513 assert!(
514 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
515 );
516 }
517
518 #[test]
519 fn test_g1_jovian_max_size() {
520 let oversized_input = vec![0u8; JOVIAN_G1_MSM_MAX_INPUT_SIZE + 1];
521 let input = Bytes::from(oversized_input);
522
523 let res = run_g1_msm_jovian(&input, u64::MAX);
524
525 assert!(
526 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
527 );
528 }
529 #[test]
530 fn test_g2_isthmus_max_size() {
531 let oversized_input = vec![0u8; ISTHMUS_G2_MSM_MAX_INPUT_SIZE + 1];
532 let input = Bytes::from(oversized_input);
533
534 let res = run_g2_msm_isthmus(&input, 260_000);
535
536 assert!(
537 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
538 );
539 }
540 #[test]
541 fn test_g2_jovian_max_size() {
542 let oversized_input = vec![0u8; JOVIAN_G2_MSM_MAX_INPUT_SIZE + 1];
543 let input = Bytes::from(oversized_input);
544
545 let res = run_g2_msm_jovian(&input, u64::MAX);
546
547 assert!(
548 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
549 );
550 }
551 #[test]
552 fn test_pair_isthmus_max_size() {
553 let oversized_input = vec![0u8; ISTHMUS_PAIRING_MAX_INPUT_SIZE + 1];
554 let input = Bytes::from(oversized_input);
555
556 let res = bls12_381::run_pair_isthmus(&input, 260_000);
557
558 assert!(
559 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
560 );
561 }
562 #[test]
563 fn test_pair_jovian_max_size() {
564 let oversized_input = vec![0u8; JOVIAN_PAIRING_MAX_INPUT_SIZE + 1];
565 let input = Bytes::from(oversized_input);
566
567 let res = bls12_381::run_pair_jovian(&input, u64::MAX);
568
569 assert!(
570 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
571 );
572 }
573}