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 #[cfg(not(feature = "std"))]
220 use crate::std::string::ToString;
221
222 pub const ISTHMUS_G1_MSM_MAX_INPUT_SIZE: usize = 513760;
224
225 pub const JOVIAN_G1_MSM_MAX_INPUT_SIZE: usize = 288_960;
227
228 pub const ISTHMUS_G2_MSM_MAX_INPUT_SIZE: usize = 488448;
230
231 pub const JOVIAN_G2_MSM_MAX_INPUT_SIZE: usize = 278_784;
233
234 pub const ISTHMUS_PAIRING_MAX_INPUT_SIZE: usize = 235008;
236
237 pub const JOVIAN_PAIRING_MAX_INPUT_SIZE: usize = 156_672;
239
240 pub const ISTHMUS_G1_MSM: Precompile =
242 Precompile::new(PrecompileId::Bls12G1Msm, G1_MSM_ADDRESS, run_g1_msm_isthmus);
243 pub const ISTHMUS_G2_MSM: Precompile =
245 Precompile::new(PrecompileId::Bls12G2Msm, G2_MSM_ADDRESS, run_g2_msm_isthmus);
246 pub const ISTHMUS_PAIRING: Precompile = Precompile::new(
248 PrecompileId::Bls12Pairing,
249 PAIRING_ADDRESS,
250 run_pair_isthmus,
251 );
252
253 pub const JOVIAN_G1_MSM: Precompile =
255 Precompile::new(PrecompileId::Bls12G1Msm, G1_MSM_ADDRESS, run_g1_msm_jovian);
256 pub const JOVIAN_G2_MSM: Precompile =
258 Precompile::new(PrecompileId::Bls12G2Msm, G2_MSM_ADDRESS, run_g2_msm_jovian);
259 pub const JOVIAN_PAIRING: Precompile =
261 Precompile::new(PrecompileId::Bls12Pairing, PAIRING_ADDRESS, run_pair_jovian);
262
263 pub fn run_g1_msm_isthmus(input: &[u8], gas_limit: u64) -> PrecompileResult {
265 if input.len() > ISTHMUS_G1_MSM_MAX_INPUT_SIZE {
266 return Err(PrecompileError::Other(
267 "G1MSM input length too long for OP Stack input size limitation after the Isthmus Hardfork".to_string(),
268 ));
269 }
270 precompile::bls12_381::g1_msm::g1_msm(input, gas_limit)
271 }
272
273 pub fn run_g1_msm_jovian(input: &[u8], gas_limit: u64) -> PrecompileResult {
275 if input.len() > JOVIAN_G1_MSM_MAX_INPUT_SIZE {
276 return Err(PrecompileError::Other(
277 "G1MSM input length too long for OP Stack input size limitation after the Jovian Hardfork".to_string(),
278 ));
279 }
280 precompile::bls12_381::g1_msm::g1_msm(input, gas_limit)
281 }
282
283 pub fn run_g2_msm_isthmus(input: &[u8], gas_limit: u64) -> PrecompileResult {
285 if input.len() > ISTHMUS_G2_MSM_MAX_INPUT_SIZE {
286 return Err(PrecompileError::Other(
287 "G2MSM input length too long for OP Stack input size limitation".to_string(),
288 ));
289 }
290 precompile::bls12_381::g2_msm::g2_msm(input, gas_limit)
291 }
292
293 pub fn run_g2_msm_jovian(input: &[u8], gas_limit: u64) -> PrecompileResult {
295 if input.len() > JOVIAN_G2_MSM_MAX_INPUT_SIZE {
296 return Err(PrecompileError::Other(
297 "G2MSM input length too long for OP Stack input size limitation after the Jovian Hardfork".to_string(),
298 ));
299 }
300 precompile::bls12_381::g2_msm::g2_msm(input, gas_limit)
301 }
302
303 pub fn run_pair_isthmus(input: &[u8], gas_limit: u64) -> PrecompileResult {
305 if input.len() > ISTHMUS_PAIRING_MAX_INPUT_SIZE {
306 return Err(PrecompileError::Other(
307 "Pairing input length too long for OP Stack input size limitation".to_string(),
308 ));
309 }
310 precompile::bls12_381::pairing::pairing(input, gas_limit)
311 }
312
313 pub fn run_pair_jovian(input: &[u8], gas_limit: u64) -> PrecompileResult {
315 if input.len() > JOVIAN_PAIRING_MAX_INPUT_SIZE {
316 return Err(PrecompileError::Other(
317 "Pairing input length too long for OP Stack input size limitation after the Jovian Hardfork".to_string(),
318 ));
319 }
320 precompile::bls12_381::pairing::pairing(input, gas_limit)
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use crate::precompiles::bls12_381::{
327 run_g1_msm_isthmus, run_g1_msm_jovian, run_g2_msm_isthmus, run_g2_msm_jovian,
328 ISTHMUS_G1_MSM_MAX_INPUT_SIZE, ISTHMUS_G2_MSM_MAX_INPUT_SIZE,
329 ISTHMUS_PAIRING_MAX_INPUT_SIZE, JOVIAN_G1_MSM_MAX_INPUT_SIZE, JOVIAN_G2_MSM_MAX_INPUT_SIZE,
330 JOVIAN_PAIRING_MAX_INPUT_SIZE,
331 };
332
333 use super::*;
334 use revm::{
335 precompile::{bls12_381_const, PrecompileError},
336 primitives::{hex, Bytes},
337 };
338 use std::vec;
339
340 #[test]
341 fn test_bn254_pair() {
342 let input = hex::decode(
343 "\
344 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
345 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
346 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
347 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
348 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
349 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
350 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
351 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
352 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
353 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
354 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
355 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
356 )
357 .unwrap();
358 let expected =
359 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
360 .unwrap();
361 let outcome = bn254_pair::run_pair_granite(&input, 260_000).unwrap();
362 assert_eq!(outcome.bytes, expected);
363
364 let input = hex::decode(
366 "\
367 1111111111111111111111111111111111111111111111111111111111111111\
368 1111111111111111111111111111111111111111111111111111111111111111\
369 111111111111111111111111111111\
370 ",
371 )
372 .unwrap();
373
374 let res = bn254_pair::run_pair_granite(&input, 260_000);
375 assert!(matches!(res, Err(PrecompileError::Bn254PairLength)));
376
377 let input = vec![1u8; 586 * bn254::PAIR_ELEMENT_LEN];
379 let res = bn254_pair::run_pair_granite(&input, 260_000);
380 assert!(matches!(res, Err(PrecompileError::OutOfGas)));
381
382 let input = vec![1u8; 587 * bn254::PAIR_ELEMENT_LEN];
384 let res = bn254_pair::run_pair_granite(&input, 260_000);
385 assert!(matches!(res, Err(PrecompileError::Bn254PairLength)));
386 }
387
388 #[test]
389 fn test_accelerated_bn254_pairing_jovian() {
390 const TEST_INPUT: [u8; 384] = hex!(
391 "2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f61fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d92bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f902fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd451971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc72a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea223a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc"
392 );
393 const EXPECTED_OUTPUT: [u8; 32] =
394 hex!("0000000000000000000000000000000000000000000000000000000000000001");
395
396 let res = bn254_pair::run_pair_jovian(TEST_INPUT.as_ref(), u64::MAX);
397 assert!(matches!(res, Ok(outcome) if **outcome.bytes == EXPECTED_OUTPUT));
398 }
399
400 #[test]
401 fn test_accelerated_bn254_pairing_bad_input_len_jovian() {
402 let input = [0u8; bn254_pair::JOVIAN_MAX_INPUT_SIZE + 1];
403 let res = bn254_pair::run_pair_jovian(&input, u64::MAX);
404 assert!(matches!(res, Err(PrecompileError::Bn254PairLength)));
405 }
406
407 #[test]
408 fn test_get_jovian_precompile_with_bad_input_len() {
409 let precompiles = OpPrecompiles::new_with_spec(OpSpecId::JOVIAN);
410 let bn254_pair_precompile = precompiles
411 .precompiles()
412 .get(&bn254::pair::ADDRESS)
413 .unwrap();
414
415 let mut bad_input_len = bn254_pair::JOVIAN_MAX_INPUT_SIZE + 1;
416 assert!(bad_input_len < bn254_pair::GRANITE_MAX_INPUT_SIZE);
417 let input = vec![0u8; bad_input_len];
418
419 let res = bn254_pair_precompile.execute(&input, u64::MAX);
420 assert!(matches!(res, Err(PrecompileError::Bn254PairLength)));
421
422 let bls12_381_g1_msm_precompile = precompiles
423 .precompiles()
424 .get(&bls12_381_const::G1_MSM_ADDRESS)
425 .unwrap();
426 bad_input_len = bls12_381::JOVIAN_G1_MSM_MAX_INPUT_SIZE + 1;
427 assert!(bad_input_len < bls12_381::ISTHMUS_G1_MSM_MAX_INPUT_SIZE);
428 let input = vec![0u8; bad_input_len];
429 let res = bls12_381_g1_msm_precompile.execute(&input, u64::MAX);
430 assert!(
431 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
432 );
433
434 let bls12_381_g2_msm_precompile = precompiles
435 .precompiles()
436 .get(&bls12_381_const::G2_MSM_ADDRESS)
437 .unwrap();
438 bad_input_len = bls12_381::JOVIAN_G2_MSM_MAX_INPUT_SIZE + 1;
439 assert!(bad_input_len < bls12_381::ISTHMUS_G2_MSM_MAX_INPUT_SIZE);
440 let input = vec![0u8; bad_input_len];
441 let res = bls12_381_g2_msm_precompile.execute(&input, u64::MAX);
442 assert!(
443 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
444 );
445
446 let bls12_381_pairing_precompile = precompiles
447 .precompiles()
448 .get(&bls12_381_const::PAIRING_ADDRESS)
449 .unwrap();
450 bad_input_len = bls12_381::JOVIAN_PAIRING_MAX_INPUT_SIZE + 1;
451 assert!(bad_input_len < bls12_381::ISTHMUS_PAIRING_MAX_INPUT_SIZE);
452 let input = vec![0u8; bad_input_len];
453 let res = bls12_381_pairing_precompile.execute(&input, u64::MAX);
454 assert!(
455 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
456 );
457 }
458
459 #[test]
460 fn test_cancun_precompiles_in_fjord() {
461 assert_eq!(fjord().difference(Precompiles::cancun()).len(), 1)
463 }
464
465 #[test]
466 fn test_cancun_precompiles_in_granite() {
467 assert_eq!(granite().difference(Precompiles::cancun()).len(), 1)
470 }
471
472 #[test]
473 fn test_prague_precompiles_in_isthmus() {
474 let new_prague_precompiles = Precompiles::prague().difference(Precompiles::cancun());
475
476 assert!(new_prague_precompiles.difference(isthmus()).is_empty())
478 }
479
480 #[test]
481 fn test_prague_precompiles_in_jovian() {
482 let new_prague_precompiles = Precompiles::prague().difference(Precompiles::cancun());
483
484 assert!(new_prague_precompiles.difference(jovian()).is_empty())
486 }
487
488 #[test]
490 fn test_isthmus_precompiles_in_jovian() {
491 let new_isthmus_precompiles = isthmus().difference(Precompiles::cancun());
492
493 assert!(new_isthmus_precompiles.difference(jovian()).is_empty())
495 }
496
497 #[test]
498 fn test_default_precompiles_is_latest() {
499 let latest = OpPrecompiles::new_with_spec(OpSpecId::default())
500 .inner
501 .precompiles;
502 let default = OpPrecompiles::default().inner.precompiles;
503 assert_eq!(latest.len(), default.len());
504
505 let intersection = default.intersection(latest);
506 assert_eq!(intersection.len(), latest.len())
507 }
508
509 #[test]
510 fn test_g1_isthmus_max_size() {
511 let oversized_input = vec![0u8; ISTHMUS_G1_MSM_MAX_INPUT_SIZE + 1];
512 let input = Bytes::from(oversized_input);
513
514 let res = run_g1_msm_isthmus(&input, 260_000);
515
516 assert!(
517 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
518 );
519 }
520
521 #[test]
522 fn test_g1_jovian_max_size() {
523 let oversized_input = vec![0u8; JOVIAN_G1_MSM_MAX_INPUT_SIZE + 1];
524 let input = Bytes::from(oversized_input);
525
526 let res = run_g1_msm_jovian(&input, u64::MAX);
527
528 assert!(
529 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
530 );
531 }
532 #[test]
533 fn test_g2_isthmus_max_size() {
534 let oversized_input = vec![0u8; ISTHMUS_G2_MSM_MAX_INPUT_SIZE + 1];
535 let input = Bytes::from(oversized_input);
536
537 let res = run_g2_msm_isthmus(&input, 260_000);
538
539 assert!(
540 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
541 );
542 }
543 #[test]
544 fn test_g2_jovian_max_size() {
545 let oversized_input = vec![0u8; JOVIAN_G2_MSM_MAX_INPUT_SIZE + 1];
546 let input = Bytes::from(oversized_input);
547
548 let res = run_g2_msm_jovian(&input, u64::MAX);
549
550 assert!(
551 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
552 );
553 }
554 #[test]
555 fn test_pair_isthmus_max_size() {
556 let oversized_input = vec![0u8; ISTHMUS_PAIRING_MAX_INPUT_SIZE + 1];
557 let input = Bytes::from(oversized_input);
558
559 let res = bls12_381::run_pair_isthmus(&input, 260_000);
560
561 assert!(
562 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
563 );
564 }
565 #[test]
566 fn test_pair_jovian_max_size() {
567 let oversized_input = vec![0u8; JOVIAN_PAIRING_MAX_INPUT_SIZE + 1];
568 let input = Bytes::from(oversized_input);
569
570 let res = bls12_381::run_pair_jovian(&input, u64::MAX);
571
572 assert!(
573 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
574 );
575 }
576}