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