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