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
11#[cfg_attr(
12 all(
13 any(target_arch = "x86", target_arch = "x86_64"),
14 target_feature = "avx2"
15 ),
16 expect(unreachable_code)
17)]
18pub mod blake2;
19pub mod bls12_381;
20pub mod bls12_381_const;
21pub mod bls12_381_utils;
22pub mod bn254;
23pub mod hash;
24mod id;
25pub mod identity;
26pub mod interface;
27pub mod kzg_point_evaluation;
28pub mod modexp;
29pub mod secp256k1;
30pub mod secp256r1;
31pub mod utilities;
32
33pub use primitives;
34
35pub use id::PrecompileId;
36pub use interface::*;
37
38use core::fmt::{self, Debug};
39
40cfg_if::cfg_if! {
42 if #[cfg(feature = "bn")]{
43 use ark_bn254 as _;
44 use ark_ff as _;
45 use ark_ec as _;
46 use ark_serialize as _;
47 }
48}
49
50use arrayref as _;
51
52cfg_if::cfg_if! {
54 if #[cfg(feature = "blst")]{
55 use ark_bls12_381 as _;
56 use ark_ff as _;
57 use ark_ec as _;
58 use ark_serialize as _;
59 }
60}
61
62#[cfg(feature = "gmp")]
64use aurora_engine_modexp as _;
65
66#[cfg(feature = "p256-aws-lc-rs")]
68use p256 as _;
69
70use core::hash::Hash;
71use primitives::{
72 hardfork::SpecId, short_address, Address, AddressMap, AddressSet, HashMap, OnceLock,
73 SHORT_ADDRESS_CAP,
74};
75use std::boxed::Box;
76
77#[inline]
79pub const fn calc_linear_cost(len: usize, base: u64, word: u64) -> u64 {
80 (len as u64).div_ceil(32) * word + base
81}
82
83#[deprecated(note = "please use `calc_linear_cost` instead")]
85pub const fn calc_linear_cost_u32(len: usize, base: u64, word: u64) -> u64 {
86 calc_linear_cost(len, base, word)
87}
88
89#[derive(Clone, Debug)]
91pub struct Precompiles {
92 inner: AddressMap<Precompile>,
94 addresses: AddressSet,
96 optimized_access: Box<[Option<Precompile>; SHORT_ADDRESS_CAP]>,
98}
99
100impl Default for Precompiles {
101 fn default() -> Self {
102 Self {
103 inner: HashMap::default(),
104 addresses: AddressSet::default(),
105 optimized_access: Box::new([const { None }; SHORT_ADDRESS_CAP]),
106 }
107 }
108}
109
110impl Precompiles {
111 pub fn new(spec: PrecompileSpecId) -> &'static Self {
113 static INSTANCES: [OnceLock<Precompiles>; PrecompileSpecId::NEXT as usize + 1] =
114 [const { OnceLock::new() }; PrecompileSpecId::NEXT as usize + 1];
115 INSTANCES[spec as usize].get_or_init(|| init_precompiles(spec))
116 }
117
118 pub fn homestead() -> &'static Self {
120 Self::new(PrecompileSpecId::HOMESTEAD)
121 }
122
123 pub fn byzantium() -> &'static Self {
125 Self::new(PrecompileSpecId::BYZANTIUM)
126 }
127
128 pub fn istanbul() -> &'static Self {
130 Self::new(PrecompileSpecId::ISTANBUL)
131 }
132
133 pub fn berlin() -> &'static Self {
135 Self::new(PrecompileSpecId::BERLIN)
136 }
137
138 pub fn cancun() -> &'static Self {
143 Self::new(PrecompileSpecId::CANCUN)
144 }
145
146 pub fn prague() -> &'static Self {
148 Self::new(PrecompileSpecId::PRAGUE)
149 }
150
151 pub fn osaka() -> &'static Self {
153 Self::new(PrecompileSpecId::OSAKA)
154 }
155
156 pub fn latest() -> &'static Self {
158 Self::new(PrecompileSpecId::NEXT)
159 }
160
161 #[inline]
163 pub const fn inner(&self) -> &AddressMap<Precompile> {
164 &self.inner
165 }
166
167 #[inline]
169 pub fn addresses(&self) -> impl ExactSizeIterator<Item = &Address> {
170 self.inner.keys()
171 }
172
173 #[inline]
175 pub fn into_addresses(self) -> impl ExactSizeIterator<Item = Address> {
176 self.inner.into_keys()
177 }
178
179 #[inline]
181 pub fn contains(&self, address: &Address) -> bool {
182 self.inner.contains_key(address)
183 }
184
185 #[inline]
187 pub fn get(&self, address: &Address) -> Option<&Precompile> {
188 if let Some(short_address) = short_address(address) {
189 return self.optimized_access[short_address].as_ref();
190 }
191 self.inner.get(address)
192 }
193
194 #[inline]
196 pub fn get_mut(&mut self, address: &Address) -> Option<&mut Precompile> {
197 self.inner.get_mut(address)
198 }
199
200 #[inline]
202 pub fn is_empty(&self) -> bool {
203 self.inner.is_empty()
204 }
205
206 #[inline]
208 pub fn len(&self) -> usize {
209 self.inner.len()
210 }
211
212 #[inline]
214 pub const fn addresses_set(&self) -> &AddressSet {
215 &self.addresses
216 }
217
218 pub fn extend(&mut self, other: impl IntoIterator<Item = Precompile>) {
222 let iter = other.into_iter();
223 let (lower, _) = iter.size_hint();
224 self.addresses.reserve(lower);
225 self.inner.reserve(lower);
226 for item in iter {
227 let address = *item.address();
228 if let Some(short_idx) = short_address(&address) {
229 self.optimized_access[short_idx] = Some(item.clone());
230 }
231 self.addresses.insert(address);
232 self.inner.insert(address, item);
233 }
234 }
235
236 pub fn difference(&self, other: &Self) -> Self {
240 let Self { inner, .. } = self;
241
242 let inner = inner
243 .iter()
244 .filter(|(a, _)| !other.inner.contains_key(*a))
245 .map(|(a, p)| (*a, p.clone()))
246 .collect::<AddressMap<_>>();
247
248 let mut precompiles = Self::default();
249 precompiles.extend(inner.into_iter().map(|p| p.1));
250 precompiles
251 }
252
253 pub fn intersection(&self, other: &Self) -> Self {
257 let Self { inner, .. } = self;
258
259 let inner = inner
260 .iter()
261 .filter(|(a, _)| other.inner.contains_key(*a))
262 .map(|(a, p)| (*a, p.clone()))
263 .collect::<AddressMap<_>>();
264
265 let mut precompiles = Self::default();
266 precompiles.extend(inner.into_iter().map(|p| p.1));
267 precompiles
268 }
269}
270
271fn init_precompiles(spec: PrecompileSpecId) -> Precompiles {
272 use PrecompileSpecId::*;
273
274 let mut precompiles = Precompiles::default();
275
276 precompiles.extend([
278 secp256k1::ECRECOVER,
279 hash::SHA256,
280 hash::RIPEMD160,
281 identity::FUN,
282 ]);
283
284 if spec.is_enabled_in(BYZANTIUM) {
285 precompiles.extend([
289 modexp::BYZANTIUM,
290 bn254::add::BYZANTIUM,
291 bn254::mul::BYZANTIUM,
292 bn254::pair::BYZANTIUM,
293 ]);
294 }
295
296 if spec.is_enabled_in(ISTANBUL) {
297 precompiles.extend([
300 bn254::add::ISTANBUL,
301 bn254::mul::ISTANBUL,
302 bn254::pair::ISTANBUL,
303 blake2::FUN,
304 ]);
305 }
306
307 if spec.is_enabled_in(BERLIN) {
308 precompiles.extend([modexp::BERLIN]);
310 }
311
312 if spec.is_enabled_in(CANCUN) {
313 precompiles.extend([kzg_point_evaluation::POINT_EVALUATION]);
315 }
316
317 if spec.is_enabled_in(PRAGUE) {
318 precompiles.extend(bls12_381::precompiles());
320 }
321
322 if spec.is_enabled_in(OSAKA) {
323 precompiles.extend([modexp::OSAKA, secp256r1::P256VERIFY_OSAKA]);
326 }
327
328 precompiles
329}
330
331#[derive(Clone)]
333pub struct Precompile {
334 id: PrecompileId,
336 address: Address,
338 fn_: PrecompileFn,
340}
341
342impl Debug for Precompile {
343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 write!(
345 f,
346 "Precompile {{ id: {:?}, address: {:?} }}",
347 self.id, self.address
348 )
349 }
350}
351
352impl From<(PrecompileId, Address, PrecompileFn)> for Precompile {
353 fn from((id, address, fn_): (PrecompileId, Address, PrecompileFn)) -> Self {
354 Precompile { id, address, fn_ }
355 }
356}
357
358impl From<Precompile> for (PrecompileId, Address) {
359 fn from(value: Precompile) -> Self {
360 (value.id, value.address)
361 }
362}
363
364impl Precompile {
365 pub const fn new(id: PrecompileId, address: Address, fn_: PrecompileFn) -> Self {
367 Self { id, address, fn_ }
368 }
369
370 #[inline]
372 pub const fn id(&self) -> &PrecompileId {
373 &self.id
374 }
375
376 #[inline]
378 pub const fn address(&self) -> &Address {
379 &self.address
380 }
381
382 #[inline]
387 pub fn execute(&self, input: &[u8], gas_limit: u64, reservoir: u64) -> PrecompileResult {
388 (self.fn_)(input, gas_limit, reservoir)
389 }
390}
391
392#[repr(u8)]
394#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
395pub enum PrecompileSpecId {
396 HOMESTEAD,
398 BYZANTIUM,
403 ISTANBUL,
408 BERLIN,
411 CANCUN,
414 PRAGUE,
423 OSAKA,
427}
428
429impl From<SpecId> for PrecompileSpecId {
430 fn from(spec_id: SpecId) -> Self {
431 Self::from_spec_id(spec_id)
432 }
433}
434
435impl PrecompileSpecId {
436 #[doc(alias = "MAX")]
442 pub const NEXT: Self = Self::OSAKA;
443
444 #[inline]
446 pub const fn is_enabled_in(self, other: Self) -> bool {
447 self as u8 >= other as u8
448 }
449
450 pub const fn from_spec_id(spec_id: SpecId) -> Self {
452 use SpecId::*;
453 match spec_id {
454 FRONTIER | FRONTIER_THAWING | HOMESTEAD | DAO_FORK | TANGERINE | SPURIOUS_DRAGON => {
455 Self::HOMESTEAD
456 }
457 BYZANTIUM | CONSTANTINOPLE | PETERSBURG => Self::BYZANTIUM,
458 ISTANBUL | MUIR_GLACIER => Self::ISTANBUL,
459 BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN,
460 CANCUN => Self::CANCUN,
461 PRAGUE => Self::PRAGUE,
462 OSAKA | AMSTERDAM => Self::OSAKA,
463 }
464 }
465}
466
467#[inline]
473pub const fn u64_to_address(x: u64) -> Address {
474 let x = x.to_be_bytes();
475 Address::new([
476 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],
477 ])
478}
479
480#[cfg(test)]
481mod test {
482 use super::*;
483
484 fn temp_precompile(_input: &[u8], _gas_limit: u64, reservoir: u64) -> PrecompileResult {
485 Ok(PrecompileOutput::halt(PrecompileHalt::OutOfGas, reservoir))
486 }
487
488 #[test]
489 fn test_optimized_access() {
490 let mut precompiles = Precompiles::istanbul().clone();
491 assert!(precompiles.optimized_access[9].is_some());
492 assert!(precompiles.optimized_access[10].is_none());
493
494 precompiles.extend([Precompile::new(
495 PrecompileId::Custom("test".into()),
496 u64_to_address(100),
497 temp_precompile,
498 )]);
499 precompiles.extend([Precompile::new(
500 PrecompileId::Custom("test".into()),
501 u64_to_address(101),
502 temp_precompile,
503 )]);
504
505 let output = precompiles.optimized_access[100]
506 .as_ref()
507 .unwrap()
508 .execute(&[], u64::MAX, 0)
509 .unwrap();
510 assert!(matches!(
511 output.status,
512 PrecompileStatus::Halt(PrecompileHalt::OutOfGas)
513 ));
514
515 let output = precompiles
516 .get(&Address::left_padding_from(&[101]))
517 .unwrap()
518 .execute(&[], u64::MAX, 0)
519 .unwrap();
520 assert!(matches!(
521 output.status,
522 PrecompileStatus::Halt(PrecompileHalt::OutOfGas)
523 ));
524 }
525
526 #[test]
527 fn test_difference_precompile_sets() {
528 let difference = Precompiles::istanbul().difference(Precompiles::berlin());
529 assert!(difference.is_empty());
530 }
531
532 #[test]
533 fn test_intersection_precompile_sets() {
534 let intersection = Precompiles::homestead().intersection(Precompiles::byzantium());
535
536 assert_eq!(intersection.len(), 4)
537 }
538}