1use crate::OpSpecId;
3use once_cell::race::OnceBox;
4use revm::{
5 context::Cfg,
6 context_interface::ContextTr,
7 handler::{EthPrecompiles, PrecompileProvider},
8 interpreter::{InputsImpl, InterpreterResult},
9 precompile::{
10 self, bn128, secp256r1, PrecompileError, PrecompileResult, PrecompileWithAddress,
11 Precompiles,
12 },
13 primitives::{hardfork::SpecId, Address},
14};
15use std::boxed::Box;
16use std::string::String;
17
18#[derive(Debug, Clone)]
20pub struct OpPrecompiles {
21 inner: EthPrecompiles,
23 spec: OpSpecId,
25}
26
27impl OpPrecompiles {
28 #[inline]
30 pub fn new_with_spec(spec: OpSpecId) -> Self {
31 let precompiles = match spec {
32 spec @ (OpSpecId::BEDROCK
33 | OpSpecId::REGOLITH
34 | OpSpecId::CANYON
35 | OpSpecId::ECOTONE) => Precompiles::new(spec.into_eth_spec().into()),
36 OpSpecId::FJORD => fjord(),
37 OpSpecId::GRANITE | OpSpecId::HOLOCENE => granite(),
38 OpSpecId::ISTHMUS | OpSpecId::INTEROP | OpSpecId::OSAKA => isthmus(),
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: OnceBox<Precompiles> = OnceBox::new();
60 INSTANCE.get_or_init(|| {
61 let mut precompiles = Precompiles::cancun().clone();
62 precompiles.extend([secp256r1::P256VERIFY]);
64 Box::new(precompiles)
65 })
66}
67
68pub fn granite() -> &'static Precompiles {
70 static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
71 INSTANCE.get_or_init(|| {
72 let mut precompiles = fjord().clone();
73 precompiles.extend([bn128_pair::GRANITE]);
75 Box::new(precompiles)
76 })
77}
78
79pub fn isthmus() -> &'static Precompiles {
81 static INSTANCE: OnceBox<Precompiles> = OnceBox::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 Box::new(precompiles)
93 })
94}
95
96impl<CTX> PrecompileProvider<CTX> for OpPrecompiles
97where
98 CTX: ContextTr<Cfg: Cfg<Spec = OpSpecId>>,
99{
100 type Output = InterpreterResult;
101
102 #[inline]
103 fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool {
104 if spec == self.spec {
105 return false;
106 }
107 *self = Self::new_with_spec(spec);
108 true
109 }
110
111 #[inline]
112 fn run(
113 &mut self,
114 context: &mut CTX,
115 address: &Address,
116 inputs: &InputsImpl,
117 is_static: bool,
118 gas_limit: u64,
119 ) -> Result<Option<Self::Output>, String> {
120 self.inner
121 .run(context, address, inputs, is_static, gas_limit)
122 }
123
124 #[inline]
125 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
126 self.inner.warm_addresses()
127 }
128
129 #[inline]
130 fn contains(&self, address: &Address) -> bool {
131 self.inner.contains(address)
132 }
133}
134
135impl Default for OpPrecompiles {
136 fn default() -> Self {
137 Self::new_with_spec(OpSpecId::ISTHMUS)
138 }
139}
140
141pub mod bn128_pair {
143 use super::*;
144
145 pub const GRANITE_MAX_INPUT_SIZE: usize = 112687;
147 pub const GRANITE: PrecompileWithAddress =
149 PrecompileWithAddress(bn128::pair::ADDRESS, |input, gas_limit| {
150 run_pair(input, gas_limit)
151 });
152
153 pub fn run_pair(input: &[u8], gas_limit: u64) -> PrecompileResult {
155 if input.len() > GRANITE_MAX_INPUT_SIZE {
156 return Err(PrecompileError::Bn128PairLength);
157 }
158 bn128::run_pair(
159 input,
160 bn128::pair::ISTANBUL_PAIR_PER_POINT,
161 bn128::pair::ISTANBUL_PAIR_BASE,
162 gas_limit,
163 )
164 }
165}
166
167pub mod bls12_381 {
169 use super::*;
170 use revm::precompile::bls12_381_const::{G1_MSM_ADDRESS, G2_MSM_ADDRESS, PAIRING_ADDRESS};
171
172 #[cfg(not(feature = "std"))]
173 use crate::std::string::ToString;
174
175 pub const ISTHMUS_G1_MSM_MAX_INPUT_SIZE: usize = 513760;
177 pub const ISTHMUS_G2_MSM_MAX_INPUT_SIZE: usize = 488448;
179 pub const ISTHMUS_PAIRING_MAX_INPUT_SIZE: usize = 235008;
181
182 pub const ISTHMUS_G1_MSM: PrecompileWithAddress =
184 PrecompileWithAddress(G1_MSM_ADDRESS, run_g1_msm);
185 pub const ISTHMUS_G2_MSM: PrecompileWithAddress =
187 PrecompileWithAddress(G2_MSM_ADDRESS, run_g2_msm);
188 pub const ISTHMUS_PAIRING: PrecompileWithAddress =
190 PrecompileWithAddress(PAIRING_ADDRESS, run_pair);
191
192 pub fn run_g1_msm(input: &[u8], gas_limit: u64) -> PrecompileResult {
194 if input.len() > ISTHMUS_G1_MSM_MAX_INPUT_SIZE {
195 return Err(PrecompileError::Other(
196 "G1MSM input length too long for OP Stack input size limitation".to_string(),
197 ));
198 }
199 precompile::bls12_381::g1_msm::g1_msm(input, gas_limit)
200 }
201
202 pub fn run_g2_msm(input: &[u8], gas_limit: u64) -> PrecompileResult {
204 if input.len() > ISTHMUS_G2_MSM_MAX_INPUT_SIZE {
205 return Err(PrecompileError::Other(
206 "G2MSM input length too long for OP Stack input size limitation".to_string(),
207 ));
208 }
209 precompile::bls12_381::g2_msm::g2_msm(input, gas_limit)
210 }
211
212 pub fn run_pair(input: &[u8], gas_limit: u64) -> PrecompileResult {
214 if input.len() > ISTHMUS_PAIRING_MAX_INPUT_SIZE {
215 return Err(PrecompileError::Other(
216 "Pairing input length too long for OP Stack input size limitation".to_string(),
217 ));
218 }
219 precompile::bls12_381::pairing::pairing(input, gas_limit)
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use crate::precompiles::bls12_381::{
226 run_g1_msm, run_g2_msm, ISTHMUS_G1_MSM_MAX_INPUT_SIZE, ISTHMUS_G2_MSM_MAX_INPUT_SIZE,
227 ISTHMUS_PAIRING_MAX_INPUT_SIZE,
228 };
229
230 use super::*;
231 use revm::{
232 precompile::PrecompileError,
233 primitives::{hex, Bytes},
234 };
235 use std::vec;
236
237 #[test]
238 fn test_bn128_pair() {
239 let input = hex::decode(
240 "\
241 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
242 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
243 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
244 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
245 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
246 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
247 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
248 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
249 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
250 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
251 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
252 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
253 )
254 .unwrap();
255 let expected =
256 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
257 .unwrap();
258 let outcome = bn128_pair::run_pair(&input, 260_000).unwrap();
259 assert_eq!(outcome.bytes, expected);
260
261 let input = hex::decode(
263 "\
264 1111111111111111111111111111111111111111111111111111111111111111\
265 1111111111111111111111111111111111111111111111111111111111111111\
266 111111111111111111111111111111\
267 ",
268 )
269 .unwrap();
270
271 let res = bn128_pair::run_pair(&input, 260_000);
272 assert!(matches!(res, Err(PrecompileError::Bn128PairLength)));
273
274 let input = vec![1u8; 586 * bn128::PAIR_ELEMENT_LEN];
276 let res = bn128_pair::run_pair(&input, 260_000);
277 assert!(matches!(res, Err(PrecompileError::OutOfGas)));
278
279 let input = vec![1u8; 587 * bn128::PAIR_ELEMENT_LEN];
281 let res = bn128_pair::run_pair(&input, 260_000);
282 assert!(matches!(res, Err(PrecompileError::Bn128PairLength)));
283 }
284
285 #[test]
286 fn test_cancun_precompiles_in_fjord() {
287 assert_eq!(fjord().difference(Precompiles::cancun()).len(), 1)
289 }
290
291 #[test]
292 fn test_cancun_precompiles_in_granite() {
293 assert_eq!(granite().difference(Precompiles::cancun()).len(), 1)
296 }
297
298 #[test]
299 fn test_prague_precompiles_in_isthmus() {
300 let new_prague_precompiles = Precompiles::prague().difference(Precompiles::cancun());
301
302 assert!(new_prague_precompiles.difference(isthmus()).is_empty())
304 }
305
306 #[test]
307 fn test_default_precompiles_is_latest() {
308 let latest = OpPrecompiles::new_with_spec(OpSpecId::default())
309 .inner
310 .precompiles;
311 let default = OpPrecompiles::default().inner.precompiles;
312 assert_eq!(latest.len(), default.len());
313
314 let intersection = default.intersection(latest);
315 assert_eq!(intersection.len(), latest.len())
316 }
317
318 #[test]
319 fn test_g1_isthmus_max_size() {
320 let oversized_input = vec![0u8; ISTHMUS_G1_MSM_MAX_INPUT_SIZE + 1];
321 let input = Bytes::from(oversized_input);
322
323 let res = run_g1_msm(&input, 260_000);
324
325 assert!(
326 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
327 );
328 }
329 #[test]
330 fn test_g2_isthmus_max_size() {
331 let oversized_input = vec![0u8; ISTHMUS_G2_MSM_MAX_INPUT_SIZE + 1];
332 let input = Bytes::from(oversized_input);
333
334 let res = run_g2_msm(&input, 260_000);
335
336 assert!(
337 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
338 );
339 }
340 #[test]
341 fn test_pair_isthmus_max_size() {
342 let oversized_input = vec![0u8; ISTHMUS_PAIRING_MAX_INPUT_SIZE + 1];
343 let input = Bytes::from(oversized_input);
344
345 let res = bls12_381::run_pair(&input, 260_000);
346
347 assert!(
348 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
349 );
350 }
351}