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
41cfg_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#[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
61pub fn calc_linear_cost_u32(len: usize, base: u64, word: u64) -> u64 {
63 (len as u64).div_ceil(32) * word + base
64}
65
66#[derive(Clone, Debug)]
68pub struct Precompiles {
69 inner: HashMap<Address, Precompile>,
71 addresses: HashSet<Address>,
73 optimized_access: Vec<Option<Precompile>>,
75 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 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 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 pub fn inner(&self) -> &HashMap<Address, Precompile> {
121 &self.inner
122 }
123
124 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 modexp::BYZANTIUM,
132 bn254::add::BYZANTIUM,
135 bn254::mul::BYZANTIUM,
136 bn254::pair::BYZANTIUM,
137 ]);
138 precompiles
139 })
140 }
141
142 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 bn254::add::ISTANBUL,
150 bn254::mul::ISTANBUL,
151 bn254::pair::ISTANBUL,
152 blake2::FUN,
154 ]);
155 precompiles
156 })
157 }
158
159 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 modexp::BERLIN,
167 ]);
168 precompiles
169 })
170 }
171
172 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 kzg_point_evaluation::POINT_EVALUATION,
183 ]);
184 precompiles
185 })
186 }
187
188 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 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 pub fn latest() -> &'static Self {
210 Self::osaka()
211 }
212
213 #[inline]
215 pub fn addresses(&self) -> impl ExactSizeIterator<Item = &Address> {
216 self.inner.keys()
217 }
218
219 #[inline]
221 pub fn into_addresses(self) -> impl ExactSizeIterator<Item = Address> {
222 self.inner.into_keys()
223 }
224
225 #[inline]
227 pub fn contains(&self, address: &Address) -> bool {
228 self.inner.contains_key(address)
229 }
230
231 #[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 #[inline]
242 pub fn get_mut(&mut self, address: &Address) -> Option<&mut Precompile> {
243 self.inner.get_mut(address)
244 }
245
246 pub fn is_empty(&self) -> bool {
248 self.inner.is_empty()
249 }
250
251 pub fn len(&self) -> usize {
253 self.inner.len()
254 }
255
256 pub fn addresses_set(&self) -> &HashSet<Address> {
258 &self.addresses
259 }
260
261 #[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 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 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#[derive(Clone, Debug)]
317pub struct Precompile {
318 id: PrecompileId,
320 address: Address,
322 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 pub const fn new(id: PrecompileId, address: Address, fn_: PrecompileFn) -> Self {
341 Self { id, address, fn_ }
342 }
343
344 #[inline]
346 pub fn id(&self) -> &PrecompileId {
347 &self.id
348 }
349
350 #[inline]
352 pub fn address(&self) -> &Address {
353 &self.address
354 }
355
356 #[inline]
358 pub fn precompile(&self) -> &PrecompileFn {
359 &self.fn_
360 }
361
362 #[inline]
364 pub fn into_precompile(self) -> PrecompileFn {
365 self.fn_
366 }
367
368 #[inline]
370 pub fn execute(&self, input: &[u8], gas_limit: u64) -> PrecompileResult {
371 (self.fn_)(input, gas_limit)
372 }
373}
374
375#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
377pub enum PrecompileSpecId {
378 HOMESTEAD,
380 BYZANTIUM,
385 ISTANBUL,
390 BERLIN,
393 CANCUN,
396 PRAGUE,
405 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 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#[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}