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