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
49pub fn fjord() -> &'static Precompiles {
51 static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
52 INSTANCE.get_or_init(|| {
53 let mut precompiles = Precompiles::cancun().clone();
54 precompiles.extend([secp256r1::P256VERIFY]);
56 Box::new(precompiles)
57 })
58}
59
60pub fn granite() -> &'static Precompiles {
62 static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
63 INSTANCE.get_or_init(|| {
64 let mut precompiles = fjord().clone();
65 precompiles.extend([bn128_pair::GRANITE]);
67 Box::new(precompiles)
68 })
69}
70
71pub fn isthmus() -> &'static Precompiles {
73 static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
74 INSTANCE.get_or_init(|| {
75 let mut precompiles = granite().clone();
76 precompiles.extend(precompile::bls12_381::precompiles());
78 Box::new(precompiles)
79 })
80}
81
82impl<CTX> PrecompileProvider<CTX> for OpPrecompiles
83where
84 CTX: ContextTr<Cfg: Cfg<Spec = OpSpecId>>,
85{
86 type Output = InterpreterResult;
87
88 #[inline]
89 fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool {
90 if spec == self.spec {
91 return false;
92 }
93 *self = Self::new_with_spec(spec);
94 true
95 }
96
97 #[inline]
98 fn run(
99 &mut self,
100 context: &mut CTX,
101 address: &Address,
102 inputs: &InputsImpl,
103 is_static: bool,
104 gas_limit: u64,
105 ) -> Result<Option<Self::Output>, String> {
106 self.inner
107 .run(context, address, inputs, is_static, gas_limit)
108 }
109
110 #[inline]
111 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
112 self.inner.warm_addresses()
113 }
114
115 #[inline]
116 fn contains(&self, address: &Address) -> bool {
117 self.inner.contains(address)
118 }
119}
120
121impl Default for OpPrecompiles {
122 fn default() -> Self {
123 Self::new_with_spec(OpSpecId::ISTHMUS)
124 }
125}
126
127pub mod bn128_pair {
128 use super::*;
129
130 pub const GRANITE_MAX_INPUT_SIZE: usize = 112687;
131 pub const GRANITE: PrecompileWithAddress =
132 PrecompileWithAddress(bn128::pair::ADDRESS, |input, gas_limit| {
133 run_pair(input, gas_limit)
134 });
135
136 pub fn run_pair(input: &[u8], gas_limit: u64) -> PrecompileResult {
137 if input.len() > GRANITE_MAX_INPUT_SIZE {
138 return Err(PrecompileError::Bn128PairLength);
139 }
140 bn128::run_pair(
141 input,
142 bn128::pair::ISTANBUL_PAIR_PER_POINT,
143 bn128::pair::ISTANBUL_PAIR_BASE,
144 gas_limit,
145 )
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use revm::{precompile::PrecompileError, primitives::hex};
153 use std::vec;
154
155 #[test]
156 fn test_bn128_pair() {
157 let input = hex::decode(
158 "\
159 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
160 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
161 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
162 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
163 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
164 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
165 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
166 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
167 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
168 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
169 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
170 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
171 )
172 .unwrap();
173 let expected =
174 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
175 .unwrap();
176 let outcome = bn128_pair::run_pair(&input, 260_000).unwrap();
177 assert_eq!(outcome.bytes, expected);
178
179 let input = hex::decode(
181 "\
182 1111111111111111111111111111111111111111111111111111111111111111\
183 1111111111111111111111111111111111111111111111111111111111111111\
184 111111111111111111111111111111\
185 ",
186 )
187 .unwrap();
188
189 let res = bn128_pair::run_pair(&input, 260_000);
190 assert!(matches!(res, Err(PrecompileError::Bn128PairLength)));
191
192 let input = vec![1u8; 586 * bn128::PAIR_ELEMENT_LEN];
194 let res = bn128_pair::run_pair(&input, 260_000);
195 assert!(matches!(res, Err(PrecompileError::OutOfGas)));
196
197 let input = vec![1u8; 587 * bn128::PAIR_ELEMENT_LEN];
199 let res = bn128_pair::run_pair(&input, 260_000);
200 assert!(matches!(res, Err(PrecompileError::Bn128PairLength)));
201 }
202
203 #[test]
204 fn test_cancun_precompiles_in_fjord() {
205 assert_eq!(fjord().difference(Precompiles::cancun()).len(), 1)
207 }
208
209 #[test]
210 fn test_cancun_precompiles_in_granite() {
211 assert_eq!(granite().difference(Precompiles::cancun()).len(), 1)
214 }
215
216 #[test]
217 fn test_prague_precompiles_in_isthmus() {
218 let new_prague_precompiles = Precompiles::prague().difference(Precompiles::cancun());
219
220 assert!(new_prague_precompiles.difference(isthmus()).is_empty())
222 }
223
224 #[test]
225 fn test_default_precompiles_is_latest() {
226 let latest = OpPrecompiles::new_with_spec(OpSpecId::default())
227 .inner
228 .precompiles;
229 let default = OpPrecompiles::default().inner.precompiles;
230 assert_eq!(latest.len(), default.len());
231
232 let intersection = default.intersection(latest);
233 assert_eq!(intersection.len(), latest.len())
234 }
235}