revm_precompile/
lib.rs

1//! # revm-precompile
2//!
3//! Implementations of EVM precompiled contracts.
4#![cfg_attr(not(test), warn(unused_crate_dependencies))]
5#![cfg_attr(not(feature = "std"), no_std)]
6
7#[macro_use]
8#[cfg(not(feature = "std"))]
9extern crate alloc as std;
10
11pub mod blake2;
12pub mod bls12_381;
13pub mod bls12_381_const;
14pub mod bls12_381_utils;
15pub mod bn128;
16pub mod hash;
17pub mod identity;
18pub mod interface;
19#[cfg(any(feature = "c-kzg", feature = "kzg-rs"))]
20pub mod kzg_point_evaluation;
21pub mod modexp;
22pub mod secp256k1;
23#[cfg(feature = "secp256r1")]
24pub mod secp256r1;
25pub mod utilities;
26
27pub use interface::*;
28
29// silence arkworks lint as bn impl will be used as default if both are enabled.
30cfg_if::cfg_if! {
31    if #[cfg(feature = "bn")]{
32        use ark_bn254 as _;
33        use ark_ff as _;
34        use ark_ec as _;
35        use ark_serialize as _;
36    }
37}
38
39#[cfg(all(feature = "c-kzg", feature = "kzg-rs"))]
40// silence kzg-rs lint as c-kzg will be used as default if both are enabled.
41use kzg_rs as _;
42
43// silence arkworks-bls12-381 lint as blst will be used as default if both are enabled.
44cfg_if::cfg_if! {
45    if #[cfg(feature = "blst")]{
46        use ark_bls12_381 as _;
47        use ark_ff as _;
48        use ark_ec as _;
49        use ark_serialize as _;
50    }
51}
52
53use cfg_if::cfg_if;
54use core::hash::Hash;
55use once_cell::race::OnceBox;
56use primitives::{hardfork::SpecId, Address, HashMap, HashSet};
57use std::{boxed::Box, vec::Vec};
58
59/// Calculate the linear cost of a precompile.
60pub fn calc_linear_cost_u32(len: usize, base: u64, word: u64) -> u64 {
61    (len as u64).div_ceil(32) * word + base
62}
63
64/// Precompiles contain map of precompile addresses to functions and HashSet of precompile addresses.
65#[derive(Clone, Default, Debug)]
66pub struct Precompiles {
67    /// Precompiles
68    inner: HashMap<Address, PrecompileFn>,
69    /// Addresses of precompile
70    addresses: HashSet<Address>,
71}
72
73impl Precompiles {
74    /// Returns the precompiles for the given spec.
75    pub fn new(spec: PrecompileSpecId) -> &'static Self {
76        match spec {
77            PrecompileSpecId::HOMESTEAD => Self::homestead(),
78            PrecompileSpecId::BYZANTIUM => Self::byzantium(),
79            PrecompileSpecId::ISTANBUL => Self::istanbul(),
80            PrecompileSpecId::BERLIN => Self::berlin(),
81            PrecompileSpecId::CANCUN => Self::cancun(),
82            PrecompileSpecId::PRAGUE => Self::prague(),
83            PrecompileSpecId::OSAKA => Self::osaka(),
84        }
85    }
86
87    /// Returns precompiles for Homestead spec.
88    pub fn homestead() -> &'static Self {
89        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
90        INSTANCE.get_or_init(|| {
91            let mut precompiles = Precompiles::default();
92            precompiles.extend([
93                secp256k1::ECRECOVER,
94                hash::SHA256,
95                hash::RIPEMD160,
96                identity::FUN,
97            ]);
98            Box::new(precompiles)
99        })
100    }
101
102    /// Returns inner HashMap of precompiles.
103    pub fn inner(&self) -> &HashMap<Address, PrecompileFn> {
104        &self.inner
105    }
106
107    /// Returns precompiles for Byzantium spec.
108    pub fn byzantium() -> &'static Self {
109        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
110        INSTANCE.get_or_init(|| {
111            let mut precompiles = Self::homestead().clone();
112            precompiles.extend([
113                // EIP-198: Big integer modular exponentiation.
114                modexp::BYZANTIUM,
115                // EIP-196: Precompiled contracts for addition and scalar multiplication on the elliptic curve alt_bn128.
116                // EIP-197: Precompiled contracts for optimal ate pairing check on the elliptic curve alt_bn128.
117                bn128::add::BYZANTIUM,
118                bn128::mul::BYZANTIUM,
119                bn128::pair::BYZANTIUM,
120            ]);
121            Box::new(precompiles)
122        })
123    }
124
125    /// Returns precompiles for Istanbul spec.
126    pub fn istanbul() -> &'static Self {
127        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
128        INSTANCE.get_or_init(|| {
129            let mut precompiles = Self::byzantium().clone();
130            precompiles.extend([
131                // EIP-1108: Reduce alt_bn128 precompile gas costs.
132                bn128::add::ISTANBUL,
133                bn128::mul::ISTANBUL,
134                bn128::pair::ISTANBUL,
135                // EIP-152: Add BLAKE2 compression function `F` precompile.
136                blake2::FUN,
137            ]);
138            Box::new(precompiles)
139        })
140    }
141
142    /// Returns precompiles for Berlin spec.
143    pub fn berlin() -> &'static Self {
144        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
145        INSTANCE.get_or_init(|| {
146            let mut precompiles = Self::istanbul().clone();
147            precompiles.extend([
148                // EIP-2565: ModExp Gas Cost.
149                modexp::BERLIN,
150            ]);
151            Box::new(precompiles)
152        })
153    }
154
155    /// Returns precompiles for Cancun spec.
156    ///
157    /// If the `c-kzg` feature is not enabled KZG Point Evaluation precompile will not be included,
158    /// effectively making this the same as Berlin.
159    pub fn cancun() -> &'static Self {
160        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
161        INSTANCE.get_or_init(|| {
162            let mut precompiles = Self::berlin().clone();
163
164            // EIP-4844: Shard Blob Transactions
165            cfg_if! {
166                if #[cfg(any(feature = "c-kzg", feature = "kzg-rs"))] {
167                    let precompile = kzg_point_evaluation::POINT_EVALUATION.clone();
168                } else {
169                    let precompile = PrecompileWithAddress(u64_to_address(0x0A), |_,_| Err(PrecompileError::Fatal("c-kzg feature is not enabled".into())));
170                }
171            }
172
173
174            precompiles.extend([
175                precompile,
176            ]);
177
178            Box::new(precompiles)
179        })
180    }
181
182    /// Returns precompiles for Prague spec.
183    pub fn prague() -> &'static Self {
184        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
185        INSTANCE.get_or_init(|| {
186            let mut precompiles = Self::cancun().clone();
187            precompiles.extend(bls12_381::precompiles());
188            Box::new(precompiles)
189        })
190    }
191
192    /// Returns precompiles for Osaka spec.
193    pub fn osaka() -> &'static Self {
194        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
195        INSTANCE.get_or_init(|| {
196            let mut precompiles = Self::prague().clone();
197            precompiles.extend([modexp::OSAKA]);
198            Box::new(precompiles)
199        })
200    }
201
202    /// Returns the precompiles for the latest spec.
203    pub fn latest() -> &'static Self {
204        Self::osaka()
205    }
206
207    /// Returns an iterator over the precompiles addresses.
208    #[inline]
209    pub fn addresses(&self) -> impl ExactSizeIterator<Item = &Address> {
210        self.inner.keys()
211    }
212
213    /// Consumes the type and returns all precompile addresses.
214    #[inline]
215    pub fn into_addresses(self) -> impl ExactSizeIterator<Item = Address> {
216        self.inner.into_keys()
217    }
218
219    /// Is the given address a precompile.
220    #[inline]
221    pub fn contains(&self, address: &Address) -> bool {
222        self.inner.contains_key(address)
223    }
224
225    /// Returns the precompile for the given address.
226    #[inline]
227    pub fn get(&self, address: &Address) -> Option<&PrecompileFn> {
228        self.inner.get(address)
229    }
230
231    /// Returns the precompile for the given address.
232    #[inline]
233    pub fn get_mut(&mut self, address: &Address) -> Option<&mut PrecompileFn> {
234        self.inner.get_mut(address)
235    }
236
237    /// Is the precompiles list empty.
238    pub fn is_empty(&self) -> bool {
239        self.inner.len() == 0
240    }
241
242    /// Returns the number of precompiles.
243    pub fn len(&self) -> usize {
244        self.inner.len()
245    }
246
247    /// Returns the precompiles addresses as a set.
248    pub fn addresses_set(&self) -> &HashSet<Address> {
249        &self.addresses
250    }
251
252    /// Extends the precompiles with the given precompiles.
253    ///
254    /// Other precompiles with overwrite existing precompiles.
255    #[inline]
256    pub fn extend(&mut self, other: impl IntoIterator<Item = PrecompileWithAddress>) {
257        let items: Vec<PrecompileWithAddress> = other.into_iter().collect::<Vec<_>>();
258        self.addresses.extend(items.iter().map(|p| *p.address()));
259        self.inner.extend(items.into_iter().map(|p| (p.0, p.1)));
260    }
261
262    /// Returns complement of `other` in `self`.
263    ///
264    /// Two entries are considered equal if the precompile addresses are equal.
265    pub fn difference(&self, other: &Self) -> Self {
266        let Self { inner, .. } = self;
267
268        let inner = inner
269            .iter()
270            .filter(|(a, _)| !other.inner.contains_key(*a))
271            .map(|(a, p)| (*a, *p))
272            .collect::<HashMap<_, _>>();
273
274        let addresses = inner.keys().cloned().collect::<HashSet<_>>();
275
276        Self { inner, addresses }
277    }
278
279    /// Returns intersection of `self` and `other`.
280    ///
281    /// Two entries are considered equal if the precompile addresses are equal.
282    pub fn intersection(&self, other: &Self) -> Self {
283        let Self { inner, .. } = self;
284
285        let inner = inner
286            .iter()
287            .filter(|(a, _)| other.inner.contains_key(*a))
288            .map(|(a, p)| (*a, *p))
289            .collect::<HashMap<_, _>>();
290
291        let addresses = inner.keys().cloned().collect::<HashSet<_>>();
292
293        Self { inner, addresses }
294    }
295}
296
297/// Precompile with address and function.
298#[derive(Clone, Debug)]
299pub struct PrecompileWithAddress(pub Address, pub PrecompileFn);
300
301impl From<(Address, PrecompileFn)> for PrecompileWithAddress {
302    fn from(value: (Address, PrecompileFn)) -> Self {
303        PrecompileWithAddress(value.0, value.1)
304    }
305}
306
307impl From<PrecompileWithAddress> for (Address, PrecompileFn) {
308    fn from(value: PrecompileWithAddress) -> Self {
309        (value.0, value.1)
310    }
311}
312
313impl PrecompileWithAddress {
314    /// Returns reference of address.
315    #[inline]
316    pub fn address(&self) -> &Address {
317        &self.0
318    }
319
320    /// Returns reference of precompile.
321    #[inline]
322    pub fn precompile(&self) -> &PrecompileFn {
323        &self.1
324    }
325}
326
327/// Ethereum hardfork spec ids. Represents the specs where precompiles had a change.
328#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
329pub enum PrecompileSpecId {
330    /// Frontier spec.
331    HOMESTEAD,
332    /// Byzantium spec introduced
333    /// * [EIP-198](https://eips.ethereum.org/EIPS/eip-198) a EIP-198: Big integer modular exponentiation (at 0x05 address).
334    /// * [EIP-196](https://eips.ethereum.org/EIPS/eip-196) a bn_add (at 0x06 address) and bn_mul (at 0x07 address) precompile
335    /// * [EIP-197](https://eips.ethereum.org/EIPS/eip-197) a bn_pair (at 0x08 address) precompile
336    BYZANTIUM,
337    /// Istanbul spec introduced
338    /// * [`EIP-152: Add BLAKE2 compression function`](https://eips.ethereum.org/EIPS/eip-152) `F` precompile (at 0x09 address).
339    /// * [`EIP-1108: Reduce alt_bn128 precompile gas costs`](https://eips.ethereum.org/EIPS/eip-1108). It reduced the
340    ///   gas cost of the bn_add, bn_mul, and bn_pair precompiles.
341    ISTANBUL,
342    /// Berlin spec made a change to:
343    /// * [`EIP-2565: ModExp Gas Cost`](https://eips.ethereum.org/EIPS/eip-2565). It changed the gas cost of the modexp precompile.
344    BERLIN,
345    /// Cancun spec added
346    /// * [`EIP-4844: Shard Blob Transactions`](https://eips.ethereum.org/EIPS/eip-4844). It added the KZG point evaluation precompile (at 0x0A address).
347    CANCUN,
348    /// Prague spec added bls precompiles [`EIP-2537: Precompile for BLS12-381 curve operations`](https://eips.ethereum.org/EIPS/eip-2537).
349    /// * `BLS12_G1ADD` at address 0x0b
350    /// * `BLS12_G1MSM` at address 0x0c
351    /// * `BLS12_G2ADD` at address 0x0d
352    /// * `BLS12_G2MSM` at address 0x0e
353    /// * `BLS12_PAIRING_CHECK` at address 0x0f
354    /// * `BLS12_MAP_FP_TO_G1` at address 0x10
355    /// * `BLS12_MAP_FP2_TO_G2` at address 0x11
356    PRAGUE,
357    /// Osaka spec added changes to modexp precompile:
358    /// * [`EIP-7823: Set upper bounds for MODEXP`](https://eips.ethereum.org/EIPS/eip-7823).
359    /// * [`EIP-7883: ModExp Gas Cost Increase`](https://eips.ethereum.org/EIPS/eip-7883)
360    OSAKA,
361}
362
363impl From<SpecId> for PrecompileSpecId {
364    fn from(spec_id: SpecId) -> Self {
365        Self::from_spec_id(spec_id)
366    }
367}
368
369impl PrecompileSpecId {
370    /// Returns the appropriate precompile Spec for the primitive [SpecId].
371    pub const fn from_spec_id(spec_id: primitives::hardfork::SpecId) -> Self {
372        use primitives::hardfork::SpecId::*;
373        match spec_id {
374            FRONTIER | FRONTIER_THAWING | HOMESTEAD | DAO_FORK | TANGERINE | SPURIOUS_DRAGON => {
375                Self::HOMESTEAD
376            }
377            BYZANTIUM | CONSTANTINOPLE | PETERSBURG => Self::BYZANTIUM,
378            ISTANBUL | MUIR_GLACIER => Self::ISTANBUL,
379            BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN,
380            CANCUN => Self::CANCUN,
381            PRAGUE => Self::PRAGUE,
382            OSAKA => Self::OSAKA,
383        }
384    }
385}
386
387/// Const function for making an address by concatenating the bytes from two given numbers.
388///
389/// Note that 32 + 128 = 160 = 20 bytes (the length of an address).
390///
391/// This function is used as a convenience for specifying the addresses of the various precompiles.
392#[inline]
393pub const fn u64_to_address(x: u64) -> Address {
394    let x = x.to_be_bytes();
395    Address::new([
396        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7],
397    ])
398}
399
400#[cfg(test)]
401mod test {
402    use crate::Precompiles;
403
404    #[test]
405    fn test_difference_precompile_sets() {
406        let difference = Precompiles::istanbul().difference(Precompiles::berlin());
407        assert!(difference.is_empty());
408    }
409
410    #[test]
411    fn test_intersection_precompile_sets() {
412        let intersection = Precompiles::homestead().intersection(Precompiles::byzantium());
413
414        assert_eq!(intersection.len(), 4)
415    }
416}