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;
17mod id;
18pub mod identity;
19pub mod interface;
20pub mod kzg_point_evaluation;
21pub mod modexp;
22pub mod secp256k1;
23pub mod secp256r1;
24pub mod utilities;
25
26pub use id::PrecompileId;
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
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 core::hash::Hash;
60use primitives::{
61    hardfork::SpecId, short_address, Address, HashMap, HashSet, OnceLock, SHORT_ADDRESS_CAP,
62};
63use std::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, Debug)]
72pub struct Precompiles {
73    /// Precompiles
74    inner: HashMap<Address, Precompile>,
75    /// Addresses of precompiles.
76    addresses: HashSet<Address>,
77    /// Optimized addresses filter.
78    optimized_access: Vec<Option<Precompile>>,
79    /// `true` if all precompiles are short addresses.
80    all_short_addresses: bool,
81}
82
83impl Default for Precompiles {
84    fn default() -> Self {
85        Self {
86            inner: HashMap::default(),
87            addresses: HashSet::default(),
88            optimized_access: vec![None; SHORT_ADDRESS_CAP],
89            all_short_addresses: true,
90        }
91    }
92}
93
94impl Precompiles {
95    /// Returns the precompiles for the given spec.
96    pub fn new(spec: PrecompileSpecId) -> &'static Self {
97        match spec {
98            PrecompileSpecId::HOMESTEAD => Self::homestead(),
99            PrecompileSpecId::BYZANTIUM => Self::byzantium(),
100            PrecompileSpecId::ISTANBUL => Self::istanbul(),
101            PrecompileSpecId::BERLIN => Self::berlin(),
102            PrecompileSpecId::CANCUN => Self::cancun(),
103            PrecompileSpecId::PRAGUE => Self::prague(),
104            PrecompileSpecId::OSAKA => Self::osaka(),
105        }
106    }
107
108    /// Returns precompiles for Homestead spec.
109    pub fn homestead() -> &'static Self {
110        static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
111        INSTANCE.get_or_init(|| {
112            let mut precompiles = Precompiles::default();
113            precompiles.extend([
114                secp256k1::ECRECOVER,
115                hash::SHA256,
116                hash::RIPEMD160,
117                identity::FUN,
118            ]);
119            precompiles
120        })
121    }
122
123    /// Returns inner HashMap of precompiles.
124    pub fn inner(&self) -> &HashMap<Address, Precompile> {
125        &self.inner
126    }
127
128    /// Returns precompiles for Byzantium spec.
129    pub fn byzantium() -> &'static Self {
130        static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
131        INSTANCE.get_or_init(|| {
132            let mut precompiles = Self::homestead().clone();
133            precompiles.extend([
134                // EIP-198: Big integer modular exponentiation.
135                modexp::BYZANTIUM,
136                // EIP-196: Precompiled contracts for addition and scalar multiplication on the elliptic curve alt_bn128.
137                // EIP-197: Precompiled contracts for optimal ate pairing check on the elliptic curve alt_bn128.
138                bn254::add::BYZANTIUM,
139                bn254::mul::BYZANTIUM,
140                bn254::pair::BYZANTIUM,
141            ]);
142            precompiles
143        })
144    }
145
146    /// Returns precompiles for Istanbul spec.
147    pub fn istanbul() -> &'static Self {
148        static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
149        INSTANCE.get_or_init(|| {
150            let mut precompiles = Self::byzantium().clone();
151            precompiles.extend([
152                // EIP-1108: Reduce alt_bn128 precompile gas costs.
153                bn254::add::ISTANBUL,
154                bn254::mul::ISTANBUL,
155                bn254::pair::ISTANBUL,
156                // EIP-152: Add BLAKE2 compression function `F` precompile.
157                blake2::FUN,
158            ]);
159            precompiles
160        })
161    }
162
163    /// Returns precompiles for Berlin spec.
164    pub fn berlin() -> &'static Self {
165        static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
166        INSTANCE.get_or_init(|| {
167            let mut precompiles = Self::istanbul().clone();
168            precompiles.extend([
169                // EIP-2565: ModExp Gas Cost.
170                modexp::BERLIN,
171            ]);
172            precompiles
173        })
174    }
175
176    /// Returns precompiles for Cancun spec.
177    ///
178    /// If the `c-kzg` feature is not enabled KZG Point Evaluation precompile will not be included,
179    /// effectively making this the same as Berlin.
180    pub fn cancun() -> &'static Self {
181        static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
182        INSTANCE.get_or_init(|| {
183            let mut precompiles = Self::berlin().clone();
184            precompiles.extend([
185                // EIP-4844: Shard Blob Transactions
186                kzg_point_evaluation::POINT_EVALUATION,
187            ]);
188            precompiles
189        })
190    }
191
192    /// Returns precompiles for Prague spec.
193    pub fn prague() -> &'static Self {
194        static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
195        INSTANCE.get_or_init(|| {
196            let mut precompiles = Self::cancun().clone();
197            precompiles.extend(bls12_381::precompiles());
198            precompiles
199        })
200    }
201
202    /// Returns precompiles for Osaka spec.
203    pub fn osaka() -> &'static Self {
204        static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
205        INSTANCE.get_or_init(|| {
206            let mut precompiles = Self::prague().clone();
207            precompiles.extend([modexp::OSAKA, secp256r1::P256VERIFY_OSAKA]);
208            precompiles
209        })
210    }
211
212    /// Returns the precompiles for the latest spec.
213    pub fn latest() -> &'static Self {
214        Self::osaka()
215    }
216
217    /// Returns an iterator over the precompiles addresses.
218    #[inline]
219    pub fn addresses(&self) -> impl ExactSizeIterator<Item = &Address> {
220        self.inner.keys()
221    }
222
223    /// Consumes the type and returns all precompile addresses.
224    #[inline]
225    pub fn into_addresses(self) -> impl ExactSizeIterator<Item = Address> {
226        self.inner.into_keys()
227    }
228
229    /// Is the given address a precompile.
230    #[inline]
231    pub fn contains(&self, address: &Address) -> bool {
232        self.inner.contains_key(address)
233    }
234
235    /// Returns the precompile for the given address.
236    #[inline]
237    pub fn get(&self, address: &Address) -> Option<&Precompile> {
238        if let Some(short_address) = short_address(address) {
239            return self.optimized_access[short_address].as_ref();
240        }
241        self.inner.get(address)
242    }
243
244    /// Returns the precompile for the given address.
245    #[inline]
246    pub fn get_mut(&mut self, address: &Address) -> Option<&mut Precompile> {
247        self.inner.get_mut(address)
248    }
249
250    /// Is the precompiles list empty.
251    pub fn is_empty(&self) -> bool {
252        self.inner.is_empty()
253    }
254
255    /// Returns the number of precompiles.
256    pub fn len(&self) -> usize {
257        self.inner.len()
258    }
259
260    /// Returns the precompiles addresses as a set.
261    pub fn addresses_set(&self) -> &HashSet<Address> {
262        &self.addresses
263    }
264
265    /// Extends the precompiles with the given precompiles.
266    ///
267    /// Other precompiles with overwrite existing precompiles.
268    #[inline]
269    pub fn extend(&mut self, other: impl IntoIterator<Item = Precompile>) {
270        let items: Vec<Precompile> = other.into_iter().collect::<Vec<_>>();
271        for item in items.iter() {
272            if let Some(short_address) = short_address(item.address()) {
273                self.optimized_access[short_address] = Some(item.clone());
274            } else {
275                self.all_short_addresses = false;
276            }
277        }
278
279        self.addresses.extend(items.iter().map(|p| *p.address()));
280        self.inner
281            .extend(items.into_iter().map(|p| (*p.address(), p.clone())));
282    }
283
284    /// Returns complement of `other` in `self`.
285    ///
286    /// Two entries are considered equal if the precompile addresses are equal.
287    pub fn difference(&self, other: &Self) -> Self {
288        let Self { inner, .. } = self;
289
290        let inner = inner
291            .iter()
292            .filter(|(a, _)| !other.inner.contains_key(*a))
293            .map(|(a, p)| (*a, p.clone()))
294            .collect::<HashMap<_, _>>();
295
296        let mut precompiles = Self::default();
297        precompiles.extend(inner.into_iter().map(|p| p.1));
298        precompiles
299    }
300
301    /// Returns intersection of `self` and `other`.
302    ///
303    /// Two entries are considered equal if the precompile addresses are equal.
304    pub fn intersection(&self, other: &Self) -> Self {
305        let Self { inner, .. } = self;
306
307        let inner = inner
308            .iter()
309            .filter(|(a, _)| other.inner.contains_key(*a))
310            .map(|(a, p)| (*a, p.clone()))
311            .collect::<HashMap<_, _>>();
312
313        let mut precompiles = Self::default();
314        precompiles.extend(inner.into_iter().map(|p| p.1));
315        precompiles
316    }
317}
318
319/// Precompile.
320#[derive(Clone, Debug)]
321pub struct Precompile {
322    /// Unique identifier.
323    id: PrecompileId,
324    /// Precompile address.
325    address: Address,
326    /// Precompile implementation.
327    fn_: PrecompileFn,
328}
329
330impl From<(PrecompileId, Address, PrecompileFn)> for Precompile {
331    fn from((id, address, fn_): (PrecompileId, Address, PrecompileFn)) -> Self {
332        Precompile { id, address, fn_ }
333    }
334}
335
336impl From<Precompile> for (PrecompileId, Address, PrecompileFn) {
337    fn from(value: Precompile) -> Self {
338        (value.id, value.address, value.fn_)
339    }
340}
341
342impl Precompile {
343    /// Create new precompile.
344    pub const fn new(id: PrecompileId, address: Address, fn_: PrecompileFn) -> Self {
345        Self { id, address, fn_ }
346    }
347
348    /// Returns reference to precompile identifier.
349    #[inline]
350    pub fn id(&self) -> &PrecompileId {
351        &self.id
352    }
353
354    /// Returns reference to address.
355    #[inline]
356    pub fn address(&self) -> &Address {
357        &self.address
358    }
359
360    /// Returns reference to precompile implementation.
361    #[inline]
362    pub fn precompile(&self) -> &PrecompileFn {
363        &self.fn_
364    }
365
366    /// Executes the precompile.
367    #[inline]
368    pub fn execute(&self, input: &[u8], gas_limit: u64) -> PrecompileResult {
369        (self.fn_)(input, gas_limit)
370    }
371}
372
373/// Ethereum hardfork spec ids. Represents the specs where precompiles had a change.
374#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
375pub enum PrecompileSpecId {
376    /// Frontier spec.
377    HOMESTEAD,
378    /// Byzantium spec introduced
379    /// * [EIP-198](https://eips.ethereum.org/EIPS/eip-198) a EIP-198: Big integer modular exponentiation (at 0x05 address).
380    /// * [EIP-196](https://eips.ethereum.org/EIPS/eip-196) a bn_add (at 0x06 address) and bn_mul (at 0x07 address) precompile
381    /// * [EIP-197](https://eips.ethereum.org/EIPS/eip-197) a bn_pair (at 0x08 address) precompile
382    BYZANTIUM,
383    /// Istanbul spec introduced
384    /// * [`EIP-152: Add BLAKE2 compression function`](https://eips.ethereum.org/EIPS/eip-152) `F` precompile (at 0x09 address).
385    /// * [`EIP-1108: Reduce alt_bn128 precompile gas costs`](https://eips.ethereum.org/EIPS/eip-1108). It reduced the
386    ///   gas cost of the bn_add, bn_mul, and bn_pair precompiles.
387    ISTANBUL,
388    /// Berlin spec made a change to:
389    /// * [`EIP-2565: ModExp Gas Cost`](https://eips.ethereum.org/EIPS/eip-2565). It changed the gas cost of the modexp precompile.
390    BERLIN,
391    /// Cancun spec added
392    /// * [`EIP-4844: Shard Blob Transactions`](https://eips.ethereum.org/EIPS/eip-4844). It added the KZG point evaluation precompile (at 0x0A address).
393    CANCUN,
394    /// Prague spec added bls precompiles [`EIP-2537: Precompile for BLS12-381 curve operations`](https://eips.ethereum.org/EIPS/eip-2537).
395    /// * `BLS12_G1ADD` at address 0x0b
396    /// * `BLS12_G1MSM` at address 0x0c
397    /// * `BLS12_G2ADD` at address 0x0d
398    /// * `BLS12_G2MSM` at address 0x0e
399    /// * `BLS12_PAIRING_CHECK` at address 0x0f
400    /// * `BLS12_MAP_FP_TO_G1` at address 0x10
401    /// * `BLS12_MAP_FP2_TO_G2` at address 0x11
402    PRAGUE,
403    /// Osaka spec added changes to modexp precompile:
404    /// * [`EIP-7823: Set upper bounds for MODEXP`](https://eips.ethereum.org/EIPS/eip-7823).
405    /// * [`EIP-7883: ModExp Gas Cost Increase`](https://eips.ethereum.org/EIPS/eip-7883)
406    OSAKA,
407}
408
409impl From<SpecId> for PrecompileSpecId {
410    fn from(spec_id: SpecId) -> Self {
411        Self::from_spec_id(spec_id)
412    }
413}
414
415impl PrecompileSpecId {
416    /// Returns the appropriate precompile Spec for the primitive [SpecId].
417    pub const fn from_spec_id(spec_id: primitives::hardfork::SpecId) -> Self {
418        use primitives::hardfork::SpecId::*;
419        match spec_id {
420            FRONTIER | FRONTIER_THAWING | HOMESTEAD | DAO_FORK | TANGERINE | SPURIOUS_DRAGON => {
421                Self::HOMESTEAD
422            }
423            BYZANTIUM | CONSTANTINOPLE | PETERSBURG => Self::BYZANTIUM,
424            ISTANBUL | MUIR_GLACIER => Self::ISTANBUL,
425            BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN,
426            CANCUN => Self::CANCUN,
427            PRAGUE => Self::PRAGUE,
428            OSAKA => Self::OSAKA,
429        }
430    }
431}
432
433/// Const function for making an address by concatenating the bytes from two given numbers.
434///
435/// Note that 32 + 128 = 160 = 20 bytes (the length of an address).
436///
437/// This function is used as a convenience for specifying the addresses of the various precompiles.
438#[inline]
439pub const fn u64_to_address(x: u64) -> Address {
440    let x = x.to_be_bytes();
441    Address::new([
442        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],
443    ])
444}
445
446#[cfg(test)]
447mod test {
448    use super::*;
449
450    fn temp_precompile(_input: &[u8], _gas_limit: u64) -> PrecompileResult {
451        PrecompileResult::Err(PrecompileError::OutOfGas)
452    }
453
454    #[test]
455    fn test_optimized_access() {
456        let mut precompiles = Precompiles::istanbul().clone();
457        assert!(precompiles.optimized_access[9].is_some());
458        assert!(precompiles.optimized_access[10].is_none());
459
460        precompiles.extend([Precompile::new(
461            PrecompileId::Custom("test".into()),
462            u64_to_address(100),
463            temp_precompile,
464        )]);
465        precompiles.extend([Precompile::new(
466            PrecompileId::Custom("test".into()),
467            u64_to_address(101),
468            temp_precompile,
469        )]);
470
471        assert_eq!(
472            precompiles.optimized_access[100]
473                .as_ref()
474                .unwrap()
475                .execute(&[], u64::MAX),
476            PrecompileResult::Err(PrecompileError::OutOfGas)
477        );
478
479        assert_eq!(
480            precompiles
481                .get(&Address::left_padding_from(&[101]))
482                .unwrap()
483                .execute(&[], u64::MAX),
484            PrecompileResult::Err(PrecompileError::OutOfGas)
485        );
486    }
487
488    #[test]
489    fn test_difference_precompile_sets() {
490        let difference = Precompiles::istanbul().difference(Precompiles::berlin());
491        assert!(difference.is_empty());
492    }
493
494    #[test]
495    fn test_intersection_precompile_sets() {
496        let intersection = Precompiles::homestead().intersection(Precompiles::byzantium());
497
498        assert_eq!(intersection.len(), 4)
499    }
500}