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