1#![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
29cfg_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#[cfg(all(feature = "c-kzg", feature = "kzg-rs"))]
42use kzg_rs as _;
44
45cfg_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#[cfg(feature = "gmp")]
57use aurora_engine_modexp as _;
58
59use core::hash::Hash;
60use primitives::{
61 hardfork::SpecId, short_address, Address, HashMap, HashSet, OnceLock, SHORT_ADDRESS_CAP,
62};
63use std::vec::Vec;
64
65pub fn calc_linear_cost_u32(len: usize, base: u64, word: u64) -> u64 {
67 (len as u64).div_ceil(32) * word + base
68}
69
70#[derive(Clone, Debug)]
72pub struct Precompiles {
73 inner: HashMap<Address, Precompile>,
75 addresses: HashSet<Address>,
77 optimized_access: Vec<Option<Precompile>>,
79 all_short_addresses: bool,
81}
82
83impl Default for Precompiles {
84 fn default() -> Self {
85 Self {
86 inner: HashMap::default(),
87 addresses: HashSet::default(),
88 optimized_access: vec![None; SHORT_ADDRESS_CAP],
89 all_short_addresses: true,
90 }
91 }
92}
93
94impl Precompiles {
95 pub fn new(spec: PrecompileSpecId) -> &'static Self {
97 match spec {
98 PrecompileSpecId::HOMESTEAD => Self::homestead(),
99 PrecompileSpecId::BYZANTIUM => Self::byzantium(),
100 PrecompileSpecId::ISTANBUL => Self::istanbul(),
101 PrecompileSpecId::BERLIN => Self::berlin(),
102 PrecompileSpecId::CANCUN => Self::cancun(),
103 PrecompileSpecId::PRAGUE => Self::prague(),
104 PrecompileSpecId::OSAKA => Self::osaka(),
105 }
106 }
107
108 pub fn homestead() -> &'static Self {
110 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
111 INSTANCE.get_or_init(|| {
112 let mut precompiles = Precompiles::default();
113 precompiles.extend([
114 secp256k1::ECRECOVER,
115 hash::SHA256,
116 hash::RIPEMD160,
117 identity::FUN,
118 ]);
119 precompiles
120 })
121 }
122
123 pub fn inner(&self) -> &HashMap<Address, Precompile> {
125 &self.inner
126 }
127
128 pub fn byzantium() -> &'static Self {
130 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
131 INSTANCE.get_or_init(|| {
132 let mut precompiles = Self::homestead().clone();
133 precompiles.extend([
134 modexp::BYZANTIUM,
136 bn254::add::BYZANTIUM,
139 bn254::mul::BYZANTIUM,
140 bn254::pair::BYZANTIUM,
141 ]);
142 precompiles
143 })
144 }
145
146 pub fn istanbul() -> &'static Self {
148 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
149 INSTANCE.get_or_init(|| {
150 let mut precompiles = Self::byzantium().clone();
151 precompiles.extend([
152 bn254::add::ISTANBUL,
154 bn254::mul::ISTANBUL,
155 bn254::pair::ISTANBUL,
156 blake2::FUN,
158 ]);
159 precompiles
160 })
161 }
162
163 pub fn berlin() -> &'static Self {
165 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
166 INSTANCE.get_or_init(|| {
167 let mut precompiles = Self::istanbul().clone();
168 precompiles.extend([
169 modexp::BERLIN,
171 ]);
172 precompiles
173 })
174 }
175
176 pub fn cancun() -> &'static Self {
181 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
182 INSTANCE.get_or_init(|| {
183 let mut precompiles = Self::berlin().clone();
184 precompiles.extend([
185 kzg_point_evaluation::POINT_EVALUATION,
187 ]);
188 precompiles
189 })
190 }
191
192 pub fn prague() -> &'static Self {
194 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
195 INSTANCE.get_or_init(|| {
196 let mut precompiles = Self::cancun().clone();
197 precompiles.extend(bls12_381::precompiles());
198 precompiles
199 })
200 }
201
202 pub fn osaka() -> &'static Self {
204 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
205 INSTANCE.get_or_init(|| {
206 let mut precompiles = Self::prague().clone();
207 precompiles.extend([modexp::OSAKA, secp256r1::P256VERIFY_OSAKA]);
208 precompiles
209 })
210 }
211
212 pub fn latest() -> &'static Self {
214 Self::osaka()
215 }
216
217 #[inline]
219 pub fn addresses(&self) -> impl ExactSizeIterator<Item = &Address> {
220 self.inner.keys()
221 }
222
223 #[inline]
225 pub fn into_addresses(self) -> impl ExactSizeIterator<Item = Address> {
226 self.inner.into_keys()
227 }
228
229 #[inline]
231 pub fn contains(&self, address: &Address) -> bool {
232 self.inner.contains_key(address)
233 }
234
235 #[inline]
237 pub fn get(&self, address: &Address) -> Option<&Precompile> {
238 if let Some(short_address) = short_address(address) {
239 return self.optimized_access[short_address].as_ref();
240 }
241 self.inner.get(address)
242 }
243
244 #[inline]
246 pub fn get_mut(&mut self, address: &Address) -> Option<&mut Precompile> {
247 self.inner.get_mut(address)
248 }
249
250 pub fn is_empty(&self) -> bool {
252 self.inner.is_empty()
253 }
254
255 pub fn len(&self) -> usize {
257 self.inner.len()
258 }
259
260 pub fn addresses_set(&self) -> &HashSet<Address> {
262 &self.addresses
263 }
264
265 #[inline]
269 pub fn extend(&mut self, other: impl IntoIterator<Item = Precompile>) {
270 let items: Vec<Precompile> = other.into_iter().collect::<Vec<_>>();
271 for item in items.iter() {
272 if let Some(short_address) = short_address(item.address()) {
273 self.optimized_access[short_address] = Some(item.clone());
274 } else {
275 self.all_short_addresses = false;
276 }
277 }
278
279 self.addresses.extend(items.iter().map(|p| *p.address()));
280 self.inner
281 .extend(items.into_iter().map(|p| (*p.address(), p.clone())));
282 }
283
284 pub fn difference(&self, other: &Self) -> Self {
288 let Self { inner, .. } = self;
289
290 let inner = inner
291 .iter()
292 .filter(|(a, _)| !other.inner.contains_key(*a))
293 .map(|(a, p)| (*a, p.clone()))
294 .collect::<HashMap<_, _>>();
295
296 let mut precompiles = Self::default();
297 precompiles.extend(inner.into_iter().map(|p| p.1));
298 precompiles
299 }
300
301 pub fn intersection(&self, other: &Self) -> Self {
305 let Self { inner, .. } = self;
306
307 let inner = inner
308 .iter()
309 .filter(|(a, _)| other.inner.contains_key(*a))
310 .map(|(a, p)| (*a, p.clone()))
311 .collect::<HashMap<_, _>>();
312
313 let mut precompiles = Self::default();
314 precompiles.extend(inner.into_iter().map(|p| p.1));
315 precompiles
316 }
317}
318
319#[derive(Clone, Debug)]
321pub struct Precompile {
322 id: PrecompileId,
324 address: Address,
326 fn_: PrecompileFn,
328}
329
330impl From<(PrecompileId, Address, PrecompileFn)> for Precompile {
331 fn from((id, address, fn_): (PrecompileId, Address, PrecompileFn)) -> Self {
332 Precompile { id, address, fn_ }
333 }
334}
335
336impl From<Precompile> for (PrecompileId, Address, PrecompileFn) {
337 fn from(value: Precompile) -> Self {
338 (value.id, value.address, value.fn_)
339 }
340}
341
342impl Precompile {
343 pub const fn new(id: PrecompileId, address: Address, fn_: PrecompileFn) -> Self {
345 Self { id, address, fn_ }
346 }
347
348 #[inline]
350 pub fn id(&self) -> &PrecompileId {
351 &self.id
352 }
353
354 #[inline]
356 pub fn address(&self) -> &Address {
357 &self.address
358 }
359
360 #[inline]
362 pub fn precompile(&self) -> &PrecompileFn {
363 &self.fn_
364 }
365
366 #[inline]
368 pub fn execute(&self, input: &[u8], gas_limit: u64) -> PrecompileResult {
369 (self.fn_)(input, gas_limit)
370 }
371}
372
373#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
375pub enum PrecompileSpecId {
376 HOMESTEAD,
378 BYZANTIUM,
383 ISTANBUL,
388 BERLIN,
391 CANCUN,
394 PRAGUE,
403 OSAKA,
407}
408
409impl From<SpecId> for PrecompileSpecId {
410 fn from(spec_id: SpecId) -> Self {
411 Self::from_spec_id(spec_id)
412 }
413}
414
415impl PrecompileSpecId {
416 pub const fn from_spec_id(spec_id: primitives::hardfork::SpecId) -> Self {
418 use primitives::hardfork::SpecId::*;
419 match spec_id {
420 FRONTIER | FRONTIER_THAWING | HOMESTEAD | DAO_FORK | TANGERINE | SPURIOUS_DRAGON => {
421 Self::HOMESTEAD
422 }
423 BYZANTIUM | CONSTANTINOPLE | PETERSBURG => Self::BYZANTIUM,
424 ISTANBUL | MUIR_GLACIER => Self::ISTANBUL,
425 BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN,
426 CANCUN => Self::CANCUN,
427 PRAGUE => Self::PRAGUE,
428 OSAKA => Self::OSAKA,
429 }
430 }
431}
432
433#[inline]
439pub const fn u64_to_address(x: u64) -> Address {
440 let x = x.to_be_bytes();
441 Address::new([
442 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],
443 ])
444}
445
446#[cfg(test)]
447mod test {
448 use super::*;
449
450 fn temp_precompile(_input: &[u8], _gas_limit: u64) -> PrecompileResult {
451 PrecompileResult::Err(PrecompileError::OutOfGas)
452 }
453
454 #[test]
455 fn test_optimized_access() {
456 let mut precompiles = Precompiles::istanbul().clone();
457 assert!(precompiles.optimized_access[9].is_some());
458 assert!(precompiles.optimized_access[10].is_none());
459
460 precompiles.extend([Precompile::new(
461 PrecompileId::Custom("test".into()),
462 u64_to_address(100),
463 temp_precompile,
464 )]);
465 precompiles.extend([Precompile::new(
466 PrecompileId::Custom("test".into()),
467 u64_to_address(101),
468 temp_precompile,
469 )]);
470
471 assert_eq!(
472 precompiles.optimized_access[100]
473 .as_ref()
474 .unwrap()
475 .execute(&[], u64::MAX),
476 PrecompileResult::Err(PrecompileError::OutOfGas)
477 );
478
479 assert_eq!(
480 precompiles
481 .get(&Address::left_padding_from(&[101]))
482 .unwrap()
483 .execute(&[], u64::MAX),
484 PrecompileResult::Err(PrecompileError::OutOfGas)
485 );
486 }
487
488 #[test]
489 fn test_difference_precompile_sets() {
490 let difference = Precompiles::istanbul().difference(Precompiles::berlin());
491 assert!(difference.is_empty());
492 }
493
494 #[test]
495 fn test_intersection_precompile_sets() {
496 let intersection = Precompiles::homestead().intersection(Precompiles::byzantium());
497
498 assert_eq!(intersection.len(), 4)
499 }
500}