Skip to main content

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