rtvm_precompile/
lib.rs

1//! # rtvm-precompile
2//!
3//! Implementations of EVM precompiled contracts.
4#![warn(rustdoc::all)]
5#![cfg_attr(not(test), warn(unused_crate_dependencies))]
6#![deny(unused_must_use, rust_2018_idioms)]
7#![cfg_attr(not(feature = "std"), no_std)]
8
9#[macro_use]
10#[cfg(not(feature = "std"))]
11extern crate alloc as std;
12
13pub mod blake2;
14pub mod bn128;
15pub mod hash;
16pub mod identity;
17#[cfg(feature = "c-kzg")]
18pub mod kzg_point_evaluation;
19pub mod modexp;
20pub mod secp256k1;
21pub mod utilities;
22
23use core::hash::Hash;
24use once_cell::race::OnceBox;
25#[doc(hidden)]
26pub use rtvm_primitives as primitives;
27pub use rtvm_primitives::{
28    precompile::{PrecompileError as Error, *},
29    Address, Bytes, HashMap, Log, B256,
30};
31use std::{boxed::Box, vec::Vec};
32
33pub fn calc_linear_cost_u32(len: usize, base: u64, word: u64) -> u64 {
34    (len as u64 + 32 - 1) / 32 * word + base
35}
36
37#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
38pub struct PrecompileOutput {
39    pub cost: u64,
40    pub output: Vec<u8>,
41    pub logs: Vec<Log>,
42}
43
44impl PrecompileOutput {
45    pub fn without_logs(cost: u64, output: Vec<u8>) -> Self {
46        Self {
47            cost,
48            output,
49            logs: Vec::new(),
50        }
51    }
52}
53#[derive(Clone, Default, Debug)]
54pub struct Precompiles {
55    /// Precompiles.
56    pub inner: HashMap<Address, Precompile>,
57}
58
59impl Precompiles {
60    /// Returns the precompiles for the given spec.
61    pub fn new(spec: PrecompileSpecId) -> &'static Self {
62        match spec {
63            PrecompileSpecId::HOMESTEAD => Self::homestead(),
64            PrecompileSpecId::BYZANTIUM => Self::byzantium(),
65            PrecompileSpecId::ISTANBUL => Self::istanbul(),
66            PrecompileSpecId::BERLIN => Self::berlin(),
67            PrecompileSpecId::CANCUN => Self::cancun(),
68            PrecompileSpecId::LATEST => Self::latest(),
69        }
70    }
71
72    /// Returns precompiles for Homestead spec.
73    pub fn homestead() -> &'static Self {
74        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
75        INSTANCE.get_or_init(|| {
76            let mut precompiles = Precompiles::default();
77            precompiles.extend([
78                secp256k1::ECRECOVER,
79                hash::SHA256,
80                hash::RIPEMD160,
81                identity::FUN,
82            ]);
83            Box::new(precompiles)
84        })
85    }
86
87    /// Returns precompiles for Byzantium spec.
88    pub fn byzantium() -> &'static Self {
89        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
90        INSTANCE.get_or_init(|| {
91            let mut precompiles = Self::homestead().clone();
92            precompiles.extend([
93                // EIP-196: Precompiled contracts for addition and scalar multiplication on the elliptic curve alt_bn128.
94                // EIP-197: Precompiled contracts for optimal ate pairing check on the elliptic curve alt_bn128.
95                bn128::add::BYZANTIUM,
96                bn128::mul::BYZANTIUM,
97                bn128::pair::BYZANTIUM,
98                // EIP-198: Big integer modular exponentiation.
99                modexp::BYZANTIUM,
100            ]);
101            Box::new(precompiles)
102        })
103    }
104
105    /// Returns precompiles for Istanbul spec.
106    pub fn istanbul() -> &'static Self {
107        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
108        INSTANCE.get_or_init(|| {
109            let mut precompiles = Self::byzantium().clone();
110            precompiles.extend([
111                // EIP-152: Add BLAKE2 compression function `F` precompile.
112                blake2::FUN,
113                // EIP-1108: Reduce alt_bn128 precompile gas costs.
114                bn128::add::ISTANBUL,
115                bn128::mul::ISTANBUL,
116                bn128::pair::ISTANBUL,
117            ]);
118            Box::new(precompiles)
119        })
120    }
121
122    /// Returns precompiles for Berlin spec.
123    pub fn berlin() -> &'static Self {
124        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
125        INSTANCE.get_or_init(|| {
126            let mut precompiles = Self::istanbul().clone();
127            precompiles.extend([
128                // EIP-2565: ModExp Gas Cost.
129                modexp::BERLIN,
130            ]);
131            Box::new(precompiles)
132        })
133    }
134
135    /// Returns precompiles for Cancun spec.
136    ///
137    /// If the `c-kzg` feature is not enabled KZG Point Evaluation precompile will not be included,
138    /// effectively making this the same as Berlin.
139    pub fn cancun() -> &'static Self {
140        static INSTANCE: OnceBox<Precompiles> = OnceBox::new();
141        INSTANCE.get_or_init(|| {
142            let precompiles = Self::berlin().clone();
143
144            // Don't include KZG point evaluation precompile in no_std builds.
145            #[cfg(feature = "c-kzg")]
146            let precompiles = {
147                let mut precompiles = precompiles;
148                precompiles.extend([
149                    // EIP-4844: Shard Blob Transactions
150                    kzg_point_evaluation::POINT_EVALUATION,
151                ]);
152                precompiles
153            };
154
155            Box::new(precompiles)
156        })
157    }
158
159    /// Returns the precompiles for the latest spec.
160    pub fn latest() -> &'static Self {
161        Self::cancun()
162    }
163
164    /// Returns an iterator over the precompiles addresses.
165    #[inline]
166    pub fn addresses(&self) -> impl Iterator<Item = &Address> {
167        self.inner.keys()
168    }
169
170    /// Consumes the type and returns all precompile addresses.
171    #[inline]
172    pub fn into_addresses(self) -> impl Iterator<Item = Address> {
173        self.inner.into_keys()
174    }
175
176    /// Is the given address a precompile.
177    #[inline]
178    pub fn contains(&self, address: &Address) -> bool {
179        self.inner.contains_key(address)
180    }
181
182    /// Returns the precompile for the given address.
183    #[inline]
184    pub fn get(&self, address: &Address) -> Option<&Precompile> {
185        self.inner.get(address)
186    }
187
188    /// Returns the precompile for the given address.
189    #[inline]
190    pub fn get_mut(&mut self, address: &Address) -> Option<&mut Precompile> {
191        self.inner.get_mut(address)
192    }
193
194    /// Is the precompiles list empty.
195    pub fn is_empty(&self) -> bool {
196        self.inner.len() == 0
197    }
198
199    /// Returns the number of precompiles.
200    pub fn len(&self) -> usize {
201        self.inner.len()
202    }
203
204    /// Extends the precompiles with the given precompiles.
205    ///
206    /// Other precompiles with overwrite existing precompiles.
207    pub fn extend(&mut self, other: impl IntoIterator<Item = PrecompileWithAddress>) {
208        self.inner.extend(other.into_iter().map(Into::into));
209    }
210}
211
212#[derive(Clone, Debug)]
213pub struct PrecompileWithAddress(pub Address, pub Precompile);
214
215impl From<(Address, Precompile)> for PrecompileWithAddress {
216    fn from(value: (Address, Precompile)) -> Self {
217        PrecompileWithAddress(value.0, value.1)
218    }
219}
220
221impl From<PrecompileWithAddress> for (Address, Precompile) {
222    fn from(value: PrecompileWithAddress) -> Self {
223        (value.0, value.1)
224    }
225}
226
227#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
228pub enum PrecompileSpecId {
229    HOMESTEAD,
230    BYZANTIUM,
231    ISTANBUL,
232    BERLIN,
233    CANCUN,
234    LATEST,
235}
236
237impl PrecompileSpecId {
238    /// Returns the appropriate precompile Spec for the primitive [SpecId](rtvm_primitives::SpecId)
239    pub const fn from_spec_id(spec_id: rtvm_primitives::SpecId) -> Self {
240        use rtvm_primitives::SpecId::*;
241        match spec_id {
242            FRONTIER | FRONTIER_THAWING | HOMESTEAD | DAO_FORK | TANGERINE | SPURIOUS_DRAGON => {
243                Self::HOMESTEAD
244            }
245            BYZANTIUM | CONSTANTINOPLE | PETERSBURG => Self::BYZANTIUM,
246            ISTANBUL | MUIR_GLACIER => Self::ISTANBUL,
247            BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN,
248            CANCUN | PRAGUE => Self::CANCUN,
249            LATEST => Self::LATEST,
250            #[cfg(feature = "optimism")]
251            BEDROCK | REGOLITH | CANYON => Self::BERLIN,
252            #[cfg(feature = "optimism")]
253            ECOTONE => Self::CANCUN,
254        }
255    }
256}
257
258/// Const function for making an address by concatenating the bytes from two given numbers.
259///
260/// Note that 32 + 128 = 160 = 20 bytes (the length of an address). This function is used
261/// as a convenience for specifying the addresses of the various precompiles.
262#[inline]
263pub const fn u64_to_address(x: u64) -> Address {
264    let x = x.to_be_bytes();
265    Address::new([
266        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],
267    ])
268}