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