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