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