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