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 | HOMESTEAD | TANGERINE | SPURIOUS_DRAGON => Self::HOMESTEAD,
455 BYZANTIUM | PETERSBURG => Self::BYZANTIUM,
456 ISTANBUL => Self::ISTANBUL,
457 BERLIN | LONDON | MERGE | SHANGHAI => Self::BERLIN,
458 CANCUN => Self::CANCUN,
459 PRAGUE => Self::PRAGUE,
460 OSAKA | AMSTERDAM => Self::OSAKA,
461 }
462 }
463}
464
465#[inline]
471pub const fn u64_to_address(x: u64) -> Address {
472 let x = x.to_be_bytes();
473 Address::new([
474 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],
475 ])
476}
477
478#[cfg(test)]
479mod test {
480 use super::*;
481
482 fn temp_precompile(_input: &[u8], _gas_limit: u64, reservoir: u64) -> PrecompileResult {
483 Ok(PrecompileOutput::halt(PrecompileHalt::OutOfGas, reservoir))
484 }
485
486 #[test]
487 fn test_optimized_access() {
488 let mut precompiles = Precompiles::istanbul().clone();
489 assert!(precompiles.optimized_access[9].is_some());
490 assert!(precompiles.optimized_access[10].is_none());
491
492 precompiles.extend([Precompile::new(
493 PrecompileId::Custom("test".into()),
494 u64_to_address(100),
495 temp_precompile,
496 )]);
497 precompiles.extend([Precompile::new(
498 PrecompileId::Custom("test".into()),
499 u64_to_address(101),
500 temp_precompile,
501 )]);
502
503 let output = precompiles.optimized_access[100]
504 .as_ref()
505 .unwrap()
506 .execute(&[], u64::MAX, 0)
507 .unwrap();
508 assert!(matches!(
509 output.status,
510 PrecompileStatus::Halt(PrecompileHalt::OutOfGas)
511 ));
512
513 let output = precompiles
514 .get(&Address::left_padding_from(&[101]))
515 .unwrap()
516 .execute(&[], u64::MAX, 0)
517 .unwrap();
518 assert!(matches!(
519 output.status,
520 PrecompileStatus::Halt(PrecompileHalt::OutOfGas)
521 ));
522 }
523
524 #[test]
525 fn test_difference_precompile_sets() {
526 let difference = Precompiles::istanbul().difference(Precompiles::berlin());
527 assert!(difference.is_empty());
528 }
529
530 #[test]
531 fn test_intersection_precompile_sets() {
532 let intersection = Precompiles::homestead().intersection(Precompiles::byzantium());
533
534 assert_eq!(intersection.len(), 4)
535 }
536}