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