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 primitives;
27
28pub use id::PrecompileId;
29pub use interface::*;
30
31cfg_if::cfg_if! {
33 if #[cfg(feature = "bn")]{
34 use ark_bn254 as _;
35 use ark_ff as _;
36 use ark_ec as _;
37 use ark_serialize as _;
38 }
39}
40
41use arrayref as _;
42
43cfg_if::cfg_if! {
45 if #[cfg(feature = "blst")]{
46 use ark_bls12_381 as _;
47 use ark_ff as _;
48 use ark_ec as _;
49 use ark_serialize as _;
50 }
51}
52
53#[cfg(feature = "gmp")]
55use aurora_engine_modexp as _;
56
57#[cfg(feature = "p256-aws-lc-rs")]
59use p256 as _;
60
61use core::hash::Hash;
62use primitives::{
63 hardfork::SpecId, short_address, Address, AddressMap, AddressSet, HashMap, OnceLock,
64 SHORT_ADDRESS_CAP,
65};
66use std::vec::Vec;
67
68#[inline]
70pub fn calc_linear_cost(len: usize, base: u64, word: u64) -> u64 {
71 (len as u64).div_ceil(32) * word + base
72}
73
74#[deprecated(note = "please use `calc_linear_cost` instead")]
76pub fn calc_linear_cost_u32(len: usize, base: u64, word: u64) -> u64 {
77 calc_linear_cost(len, base, word)
78}
79
80#[derive(Clone, Debug)]
82pub struct Precompiles {
83 inner: AddressMap<Precompile>,
85 addresses: AddressSet,
87 optimized_access: Vec<Option<Precompile>>,
89 all_short_addresses: bool,
91}
92
93impl Default for Precompiles {
94 fn default() -> Self {
95 Self {
96 inner: HashMap::default(),
97 addresses: AddressSet::default(),
98 optimized_access: vec![None; SHORT_ADDRESS_CAP],
99 all_short_addresses: true,
100 }
101 }
102}
103
104impl Precompiles {
105 pub fn new(spec: PrecompileSpecId) -> &'static Self {
107 match spec {
108 PrecompileSpecId::HOMESTEAD => Self::homestead(),
109 PrecompileSpecId::BYZANTIUM => Self::byzantium(),
110 PrecompileSpecId::ISTANBUL => Self::istanbul(),
111 PrecompileSpecId::BERLIN => Self::berlin(),
112 PrecompileSpecId::CANCUN => Self::cancun(),
113 PrecompileSpecId::PRAGUE => Self::prague(),
114 PrecompileSpecId::OSAKA => Self::osaka(),
115 }
116 }
117
118 pub fn homestead() -> &'static Self {
120 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
121 INSTANCE.get_or_init(|| {
122 let mut precompiles = Precompiles::default();
123 precompiles.extend([
124 secp256k1::ECRECOVER,
125 hash::SHA256,
126 hash::RIPEMD160,
127 identity::FUN,
128 ]);
129 precompiles
130 })
131 }
132
133 pub fn inner(&self) -> &AddressMap<Precompile> {
135 &self.inner
136 }
137
138 pub fn byzantium() -> &'static Self {
140 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
141 INSTANCE.get_or_init(|| {
142 let mut precompiles = Self::homestead().clone();
143 precompiles.extend([
144 modexp::BYZANTIUM,
146 bn254::add::BYZANTIUM,
149 bn254::mul::BYZANTIUM,
150 bn254::pair::BYZANTIUM,
151 ]);
152 precompiles
153 })
154 }
155
156 pub fn istanbul() -> &'static Self {
158 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
159 INSTANCE.get_or_init(|| {
160 let mut precompiles = Self::byzantium().clone();
161 precompiles.extend([
162 bn254::add::ISTANBUL,
164 bn254::mul::ISTANBUL,
165 bn254::pair::ISTANBUL,
166 blake2::FUN,
168 ]);
169 precompiles
170 })
171 }
172
173 pub fn berlin() -> &'static Self {
175 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
176 INSTANCE.get_or_init(|| {
177 let mut precompiles = Self::istanbul().clone();
178 precompiles.extend([
179 modexp::BERLIN,
181 ]);
182 precompiles
183 })
184 }
185
186 pub fn cancun() -> &'static Self {
191 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
192 INSTANCE.get_or_init(|| {
193 let mut precompiles = Self::berlin().clone();
194 precompiles.extend([
195 kzg_point_evaluation::POINT_EVALUATION,
197 ]);
198 precompiles
199 })
200 }
201
202 pub fn prague() -> &'static Self {
204 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
205 INSTANCE.get_or_init(|| {
206 let mut precompiles = Self::cancun().clone();
207 precompiles.extend(bls12_381::precompiles());
208 precompiles
209 })
210 }
211
212 pub fn osaka() -> &'static Self {
214 static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
215 INSTANCE.get_or_init(|| {
216 let mut precompiles = Self::prague().clone();
217 precompiles.extend([modexp::OSAKA, secp256r1::P256VERIFY_OSAKA]);
218 precompiles
219 })
220 }
221
222 pub fn latest() -> &'static Self {
224 Self::osaka()
225 }
226
227 #[inline]
229 pub fn addresses(&self) -> impl ExactSizeIterator<Item = &Address> {
230 self.inner.keys()
231 }
232
233 #[inline]
235 pub fn into_addresses(self) -> impl ExactSizeIterator<Item = Address> {
236 self.inner.into_keys()
237 }
238
239 #[inline]
241 pub fn contains(&self, address: &Address) -> bool {
242 self.inner.contains_key(address)
243 }
244
245 #[inline]
247 pub fn get(&self, address: &Address) -> Option<&Precompile> {
248 if let Some(short_address) = short_address(address) {
249 return self.optimized_access[short_address].as_ref();
250 }
251 self.inner.get(address)
252 }
253
254 #[inline]
256 pub fn get_mut(&mut self, address: &Address) -> Option<&mut Precompile> {
257 self.inner.get_mut(address)
258 }
259
260 pub fn is_empty(&self) -> bool {
262 self.inner.is_empty()
263 }
264
265 pub fn len(&self) -> usize {
267 self.inner.len()
268 }
269
270 pub fn addresses_set(&self) -> &AddressSet {
272 &self.addresses
273 }
274
275 #[inline]
279 pub fn extend(&mut self, other: impl IntoIterator<Item = Precompile>) {
280 let iter = other.into_iter();
281 let (lower, _) = iter.size_hint();
282 self.addresses.reserve(lower);
283 self.inner.reserve(lower);
284 for item in iter {
285 let address = *item.address();
286 if let Some(short_idx) = short_address(&address) {
287 self.optimized_access[short_idx] = Some(item.clone());
288 } else {
289 self.all_short_addresses = false;
290 }
291 self.addresses.insert(address);
292 self.inner.insert(address, item);
293 }
294 }
295
296 pub fn difference(&self, other: &Self) -> Self {
300 let Self { inner, .. } = self;
301
302 let inner = inner
303 .iter()
304 .filter(|(a, _)| !other.inner.contains_key(*a))
305 .map(|(a, p)| (*a, p.clone()))
306 .collect::<AddressMap<_>>();
307
308 let mut precompiles = Self::default();
309 precompiles.extend(inner.into_iter().map(|p| p.1));
310 precompiles
311 }
312
313 pub fn intersection(&self, other: &Self) -> Self {
317 let Self { inner, .. } = self;
318
319 let inner = inner
320 .iter()
321 .filter(|(a, _)| other.inner.contains_key(*a))
322 .map(|(a, p)| (*a, p.clone()))
323 .collect::<AddressMap<_>>();
324
325 let mut precompiles = Self::default();
326 precompiles.extend(inner.into_iter().map(|p| p.1));
327 precompiles
328 }
329}
330
331#[derive(Clone, Debug)]
333pub struct Precompile {
334 id: PrecompileId,
336 address: Address,
338 fn_: PrecompileFn,
340}
341
342impl From<(PrecompileId, Address, PrecompileFn)> for Precompile {
343 fn from((id, address, fn_): (PrecompileId, Address, PrecompileFn)) -> Self {
344 Precompile { id, address, fn_ }
345 }
346}
347
348impl From<Precompile> for (PrecompileId, Address, PrecompileFn) {
349 fn from(value: Precompile) -> Self {
350 (value.id, value.address, value.fn_)
351 }
352}
353
354impl Precompile {
355 pub const fn new(id: PrecompileId, address: Address, fn_: PrecompileFn) -> Self {
357 Self { id, address, fn_ }
358 }
359
360 #[inline]
362 pub fn id(&self) -> &PrecompileId {
363 &self.id
364 }
365
366 #[inline]
368 pub fn address(&self) -> &Address {
369 &self.address
370 }
371
372 #[inline]
374 pub fn precompile(&self) -> &PrecompileFn {
375 &self.fn_
376 }
377
378 #[inline]
380 pub fn into_precompile(self) -> PrecompileFn {
381 self.fn_
382 }
383
384 #[inline]
386 pub fn execute(&self, input: &[u8], gas_limit: u64) -> PrecompileResult {
387 (self.fn_)(input, gas_limit)
388 }
389}
390
391#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
393pub enum PrecompileSpecId {
394 HOMESTEAD,
396 BYZANTIUM,
401 ISTANBUL,
406 BERLIN,
409 CANCUN,
412 PRAGUE,
421 OSAKA,
425}
426
427impl From<SpecId> for PrecompileSpecId {
428 fn from(spec_id: SpecId) -> Self {
429 Self::from_spec_id(spec_id)
430 }
431}
432
433impl PrecompileSpecId {
434 pub const fn from_spec_id(spec_id: primitives::hardfork::SpecId) -> Self {
436 use primitives::hardfork::SpecId::*;
437 match spec_id {
438 FRONTIER | FRONTIER_THAWING | HOMESTEAD | DAO_FORK | TANGERINE | SPURIOUS_DRAGON => {
439 Self::HOMESTEAD
440 }
441 BYZANTIUM | CONSTANTINOPLE | PETERSBURG => Self::BYZANTIUM,
442 ISTANBUL | MUIR_GLACIER => Self::ISTANBUL,
443 BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN,
444 CANCUN => Self::CANCUN,
445 PRAGUE => Self::PRAGUE,
446 OSAKA | AMSTERDAM => Self::OSAKA,
447 }
448 }
449}
450
451#[inline]
457pub const fn u64_to_address(x: u64) -> Address {
458 let x = x.to_be_bytes();
459 Address::new([
460 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],
461 ])
462}
463
464#[cfg(test)]
465mod test {
466 use super::*;
467
468 fn temp_precompile(_input: &[u8], _gas_limit: u64) -> PrecompileResult {
469 PrecompileResult::Err(PrecompileError::OutOfGas)
470 }
471
472 #[test]
473 fn test_optimized_access() {
474 let mut precompiles = Precompiles::istanbul().clone();
475 assert!(precompiles.optimized_access[9].is_some());
476 assert!(precompiles.optimized_access[10].is_none());
477
478 precompiles.extend([Precompile::new(
479 PrecompileId::Custom("test".into()),
480 u64_to_address(100),
481 temp_precompile,
482 )]);
483 precompiles.extend([Precompile::new(
484 PrecompileId::Custom("test".into()),
485 u64_to_address(101),
486 temp_precompile,
487 )]);
488
489 assert_eq!(
490 precompiles.optimized_access[100]
491 .as_ref()
492 .unwrap()
493 .execute(&[], u64::MAX),
494 PrecompileResult::Err(PrecompileError::OutOfGas)
495 );
496
497 assert_eq!(
498 precompiles
499 .get(&Address::left_padding_from(&[101]))
500 .unwrap()
501 .execute(&[], u64::MAX),
502 PrecompileResult::Err(PrecompileError::OutOfGas)
503 );
504 }
505
506 #[test]
507 fn test_difference_precompile_sets() {
508 let difference = Precompiles::istanbul().difference(Precompiles::berlin());
509 assert!(difference.is_empty());
510 }
511
512 #[test]
513 fn test_intersection_precompile_sets() {
514 let intersection = Precompiles::homestead().intersection(Precompiles::byzantium());
515
516 assert_eq!(intersection.len(), 4)
517 }
518}