op_revm/
precompiles.rs

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// Optimism precompile provider
18#[derive(Debug, Clone)]
19pub struct OpPrecompiles {
20    /// Inner precompile provider is same as Ethereums.
21    inner: EthPrecompiles,
22    spec: OpSpecId,
23}
24
25impl OpPrecompiles {
26    /// Create a new precompile provider with the given OpSpec.
27    #[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
49/// Returns precompiles for Fjord spec.
50pub fn fjord() -> &'static Precompiles {
51    static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
52    INSTANCE.get_or_init(|| {
53        let mut precompiles = Precompiles::cancun().clone();
54        // EIP-7212: secp256r1 P256verify
55        precompiles.extend([secp256r1::P256VERIFY]);
56        Box::new(precompiles)
57    })
58}
59
60/// Returns precompiles for Granite spec.
61pub fn granite() -> &'static Precompiles {
62    static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
63    INSTANCE.get_or_init(|| {
64        let mut precompiles = fjord().clone();
65        // Restrict bn256Pairing input size
66        precompiles.extend([bn128_pair::GRANITE]);
67        Box::new(precompiles)
68    })
69}
70
71/// Returns precompiles for isthumus spec.
72pub fn isthmus() -> &'static Precompiles {
73    static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
74    INSTANCE.get_or_init(|| {
75        let mut precompiles = granite().clone();
76        // Prague bls12 precompiles
77        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        // Invalid input length
180        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        // Valid input length shorter than 112687
193        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        // Input length longer than 112687
198        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        // additional to cancun, fjord has p256verify
206        assert_eq!(fjord().difference(Precompiles::cancun()).len(), 1)
207    }
208
209    #[test]
210    fn test_cancun_precompiles_in_granite() {
211        // granite has p256verify (fjord)
212        // granite has modification of cancun's bn128 pair (doesn't count as new precompile)
213        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        // isthmus contains all precompiles that were new in prague, without modifications
221        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}