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 iter = other.into_iter();
267 let (lower, _) = iter.size_hint();
268 self.addresses.reserve(lower);
269 self.inner.reserve(lower);
270 for item in iter {
271 let address = *item.address();
272 if let Some(short_idx) = short_address(&address) {
273 self.optimized_access[short_idx] = Some(item.clone());
274 } else {
275 self.all_short_addresses = false;
276 }
277 self.addresses.insert(address);
278 self.inner.insert(address, item);
279 }
280 }
281
282 pub fn difference(&self, other: &Self) -> Self {
286 let Self { inner, .. } = self;
287
288 let inner = inner
289 .iter()
290 .filter(|(a, _)| !other.inner.contains_key(*a))
291 .map(|(a, p)| (*a, p.clone()))
292 .collect::<HashMap<_, _>>();
293
294 let mut precompiles = Self::default();
295 precompiles.extend(inner.into_iter().map(|p| p.1));
296 precompiles
297 }
298
299 pub fn intersection(&self, other: &Self) -> Self {
303 let Self { inner, .. } = self;
304
305 let inner = inner
306 .iter()
307 .filter(|(a, _)| other.inner.contains_key(*a))
308 .map(|(a, p)| (*a, p.clone()))
309 .collect::<HashMap<_, _>>();
310
311 let mut precompiles = Self::default();
312 precompiles.extend(inner.into_iter().map(|p| p.1));
313 precompiles
314 }
315}
316
317#[derive(Clone, Debug)]
319pub struct Precompile {
320 id: PrecompileId,
322 address: Address,
324 fn_: PrecompileFn,
326}
327
328impl From<(PrecompileId, Address, PrecompileFn)> for Precompile {
329 fn from((id, address, fn_): (PrecompileId, Address, PrecompileFn)) -> Self {
330 Precompile { id, address, fn_ }
331 }
332}
333
334impl From<Precompile> for (PrecompileId, Address, PrecompileFn) {
335 fn from(value: Precompile) -> Self {
336 (value.id, value.address, value.fn_)
337 }
338}
339
340impl Precompile {
341 pub const fn new(id: PrecompileId, address: Address, fn_: PrecompileFn) -> Self {
343 Self { id, address, fn_ }
344 }
345
346 #[inline]
348 pub fn id(&self) -> &PrecompileId {
349 &self.id
350 }
351
352 #[inline]
354 pub fn address(&self) -> &Address {
355 &self.address
356 }
357
358 #[inline]
360 pub fn precompile(&self) -> &PrecompileFn {
361 &self.fn_
362 }
363
364 #[inline]
366 pub fn into_precompile(self) -> PrecompileFn {
367 self.fn_
368 }
369
370 #[inline]
372 pub fn execute(&self, input: &[u8], gas_limit: u64) -> PrecompileResult {
373 (self.fn_)(input, gas_limit)
374 }
375}
376
377#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
379pub enum PrecompileSpecId {
380 HOMESTEAD,
382 BYZANTIUM,
387 ISTANBUL,
392 BERLIN,
395 CANCUN,
398 PRAGUE,
407 OSAKA,
411}
412
413impl From<SpecId> for PrecompileSpecId {
414 fn from(spec_id: SpecId) -> Self {
415 Self::from_spec_id(spec_id)
416 }
417}
418
419impl PrecompileSpecId {
420 pub const fn from_spec_id(spec_id: primitives::hardfork::SpecId) -> Self {
422 use primitives::hardfork::SpecId::*;
423 match spec_id {
424 FRONTIER | FRONTIER_THAWING | HOMESTEAD | DAO_FORK | TANGERINE | SPURIOUS_DRAGON => {
425 Self::HOMESTEAD
426 }
427 BYZANTIUM | CONSTANTINOPLE | PETERSBURG => Self::BYZANTIUM,
428 ISTANBUL | MUIR_GLACIER => Self::ISTANBUL,
429 BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN,
430 CANCUN => Self::CANCUN,
431 PRAGUE => Self::PRAGUE,
432 OSAKA | AMSTERDAM => Self::OSAKA,
433 }
434 }
435}
436
437#[inline]
443pub const fn u64_to_address(x: u64) -> Address {
444 let x = x.to_be_bytes();
445 Address::new([
446 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],
447 ])
448}
449
450#[cfg(test)]
451mod test {
452 use super::*;
453
454 fn temp_precompile(_input: &[u8], _gas_limit: u64) -> PrecompileResult {
455 PrecompileResult::Err(PrecompileError::OutOfGas)
456 }
457
458 #[test]
459 fn test_optimized_access() {
460 let mut precompiles = Precompiles::istanbul().clone();
461 assert!(precompiles.optimized_access[9].is_some());
462 assert!(precompiles.optimized_access[10].is_none());
463
464 precompiles.extend([Precompile::new(
465 PrecompileId::Custom("test".into()),
466 u64_to_address(100),
467 temp_precompile,
468 )]);
469 precompiles.extend([Precompile::new(
470 PrecompileId::Custom("test".into()),
471 u64_to_address(101),
472 temp_precompile,
473 )]);
474
475 assert_eq!(
476 precompiles.optimized_access[100]
477 .as_ref()
478 .unwrap()
479 .execute(&[], u64::MAX),
480 PrecompileResult::Err(PrecompileError::OutOfGas)
481 );
482
483 assert_eq!(
484 precompiles
485 .get(&Address::left_padding_from(&[101]))
486 .unwrap()
487 .execute(&[], u64::MAX),
488 PrecompileResult::Err(PrecompileError::OutOfGas)
489 );
490 }
491
492 #[test]
493 fn test_difference_precompile_sets() {
494 let difference = Precompiles::istanbul().difference(Precompiles::berlin());
495 assert!(difference.is_empty());
496 }
497
498 #[test]
499 fn test_intersection_precompile_sets() {
500 let intersection = Precompiles::homestead().intersection(Precompiles::byzantium());
501
502 assert_eq!(intersection.len(), 4)
503 }
504}