1use crate::OpSpecId;
3use revm::{
4 context::Cfg,
5 context_interface::ContextTr,
6 handler::{EthPrecompiles, PrecompileProvider},
7 interpreter::{InputsImpl, 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 | OpSpecId::INTEROP | OpSpecId::OSAKA => isthmus(),
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
95impl<CTX> PrecompileProvider<CTX> for OpPrecompiles
96where
97 CTX: ContextTr<Cfg: Cfg<Spec = OpSpecId>>,
98{
99 type Output = InterpreterResult;
100
101 #[inline]
102 fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool {
103 if spec == self.spec {
104 return false;
105 }
106 *self = Self::new_with_spec(spec);
107 true
108 }
109
110 #[inline]
111 fn run(
112 &mut self,
113 context: &mut CTX,
114 address: &Address,
115 inputs: &InputsImpl,
116 is_static: bool,
117 gas_limit: u64,
118 ) -> Result<Option<Self::Output>, String> {
119 self.inner
120 .run(context, address, inputs, is_static, gas_limit)
121 }
122
123 #[inline]
124 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
125 self.inner.warm_addresses()
126 }
127
128 #[inline]
129 fn contains(&self, address: &Address) -> bool {
130 self.inner.contains(address)
131 }
132}
133
134impl Default for OpPrecompiles {
135 fn default() -> Self {
136 Self::new_with_spec(OpSpecId::ISTHMUS)
137 }
138}
139
140pub mod bn254_pair {
142 use super::*;
143
144 pub const GRANITE_MAX_INPUT_SIZE: usize = 112687;
146 pub const GRANITE: Precompile =
148 Precompile::new(PrecompileId::Bn254Pairing, bn254::pair::ADDRESS, run_pair);
149
150 pub fn run_pair(input: &[u8], gas_limit: u64) -> PrecompileResult {
152 if input.len() > GRANITE_MAX_INPUT_SIZE {
153 return Err(PrecompileError::Bn254PairLength);
154 }
155 bn254::run_pair(
156 input,
157 bn254::pair::ISTANBUL_PAIR_PER_POINT,
158 bn254::pair::ISTANBUL_PAIR_BASE,
159 gas_limit,
160 )
161 }
162}
163
164pub mod bls12_381 {
166 use super::*;
167 use revm::precompile::bls12_381_const::{G1_MSM_ADDRESS, G2_MSM_ADDRESS, PAIRING_ADDRESS};
168
169 #[cfg(not(feature = "std"))]
170 use crate::std::string::ToString;
171
172 pub const ISTHMUS_G1_MSM_MAX_INPUT_SIZE: usize = 513760;
174 pub const ISTHMUS_G2_MSM_MAX_INPUT_SIZE: usize = 488448;
176 pub const ISTHMUS_PAIRING_MAX_INPUT_SIZE: usize = 235008;
178
179 pub const ISTHMUS_G1_MSM: Precompile =
181 Precompile::new(PrecompileId::Bls12G1Msm, G1_MSM_ADDRESS, run_g1_msm);
182 pub const ISTHMUS_G2_MSM: Precompile =
184 Precompile::new(PrecompileId::Bls12G2Msm, G2_MSM_ADDRESS, run_g2_msm);
185 pub const ISTHMUS_PAIRING: Precompile =
187 Precompile::new(PrecompileId::Bls12Pairing, PAIRING_ADDRESS, run_pair);
188
189 pub fn run_g1_msm(input: &[u8], gas_limit: u64) -> PrecompileResult {
191 if input.len() > ISTHMUS_G1_MSM_MAX_INPUT_SIZE {
192 return Err(PrecompileError::Other(
193 "G1MSM input length too long for OP Stack input size limitation".to_string(),
194 ));
195 }
196 precompile::bls12_381::g1_msm::g1_msm(input, gas_limit)
197 }
198
199 pub fn run_g2_msm(input: &[u8], gas_limit: u64) -> PrecompileResult {
201 if input.len() > ISTHMUS_G2_MSM_MAX_INPUT_SIZE {
202 return Err(PrecompileError::Other(
203 "G2MSM input length too long for OP Stack input size limitation".to_string(),
204 ));
205 }
206 precompile::bls12_381::g2_msm::g2_msm(input, gas_limit)
207 }
208
209 pub fn run_pair(input: &[u8], gas_limit: u64) -> PrecompileResult {
211 if input.len() > ISTHMUS_PAIRING_MAX_INPUT_SIZE {
212 return Err(PrecompileError::Other(
213 "Pairing input length too long for OP Stack input size limitation".to_string(),
214 ));
215 }
216 precompile::bls12_381::pairing::pairing(input, gas_limit)
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use crate::precompiles::bls12_381::{
223 run_g1_msm, run_g2_msm, ISTHMUS_G1_MSM_MAX_INPUT_SIZE, ISTHMUS_G2_MSM_MAX_INPUT_SIZE,
224 ISTHMUS_PAIRING_MAX_INPUT_SIZE,
225 };
226
227 use super::*;
228 use revm::{
229 precompile::PrecompileError,
230 primitives::{hex, Bytes},
231 };
232 use std::vec;
233
234 #[test]
235 fn test_bn254_pair() {
236 let input = hex::decode(
237 "\
238 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
239 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
240 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
241 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
242 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
243 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
244 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
245 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
246 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
247 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
248 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
249 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
250 )
251 .unwrap();
252 let expected =
253 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
254 .unwrap();
255 let outcome = bn254_pair::run_pair(&input, 260_000).unwrap();
256 assert_eq!(outcome.bytes, expected);
257
258 let input = hex::decode(
260 "\
261 1111111111111111111111111111111111111111111111111111111111111111\
262 1111111111111111111111111111111111111111111111111111111111111111\
263 111111111111111111111111111111\
264 ",
265 )
266 .unwrap();
267
268 let res = bn254_pair::run_pair(&input, 260_000);
269 assert!(matches!(res, Err(PrecompileError::Bn254PairLength)));
270
271 let input = vec![1u8; 586 * bn254::PAIR_ELEMENT_LEN];
273 let res = bn254_pair::run_pair(&input, 260_000);
274 assert!(matches!(res, Err(PrecompileError::OutOfGas)));
275
276 let input = vec![1u8; 587 * bn254::PAIR_ELEMENT_LEN];
278 let res = bn254_pair::run_pair(&input, 260_000);
279 assert!(matches!(res, Err(PrecompileError::Bn254PairLength)));
280 }
281
282 #[test]
283 fn test_cancun_precompiles_in_fjord() {
284 assert_eq!(fjord().difference(Precompiles::cancun()).len(), 1)
286 }
287
288 #[test]
289 fn test_cancun_precompiles_in_granite() {
290 assert_eq!(granite().difference(Precompiles::cancun()).len(), 1)
293 }
294
295 #[test]
296 fn test_prague_precompiles_in_isthmus() {
297 let new_prague_precompiles = Precompiles::prague().difference(Precompiles::cancun());
298
299 assert!(new_prague_precompiles.difference(isthmus()).is_empty())
301 }
302
303 #[test]
304 fn test_default_precompiles_is_latest() {
305 let latest = OpPrecompiles::new_with_spec(OpSpecId::default())
306 .inner
307 .precompiles;
308 let default = OpPrecompiles::default().inner.precompiles;
309 assert_eq!(latest.len(), default.len());
310
311 let intersection = default.intersection(latest);
312 assert_eq!(intersection.len(), latest.len())
313 }
314
315 #[test]
316 fn test_g1_isthmus_max_size() {
317 let oversized_input = vec![0u8; ISTHMUS_G1_MSM_MAX_INPUT_SIZE + 1];
318 let input = Bytes::from(oversized_input);
319
320 let res = run_g1_msm(&input, 260_000);
321
322 assert!(
323 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
324 );
325 }
326 #[test]
327 fn test_g2_isthmus_max_size() {
328 let oversized_input = vec![0u8; ISTHMUS_G2_MSM_MAX_INPUT_SIZE + 1];
329 let input = Bytes::from(oversized_input);
330
331 let res = run_g2_msm(&input, 260_000);
332
333 assert!(
334 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
335 );
336 }
337 #[test]
338 fn test_pair_isthmus_max_size() {
339 let oversized_input = vec![0u8; ISTHMUS_PAIRING_MAX_INPUT_SIZE + 1];
340 let input = Bytes::from(oversized_input);
341
342 let res = bls12_381::run_pair(&input, 260_000);
343
344 assert!(
345 matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long"))
346 );
347 }
348}