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