#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(not(feature = "std"), no_std)]
#[macro_use]
#[cfg(not(feature = "std"))]
extern crate alloc as std;
pub mod blake2;
pub mod bls12_381;
pub mod bls12_381_const;
pub mod bls12_381_utils;
pub mod bn254;
pub mod hash;
mod id;
pub mod identity;
pub mod interface;
pub mod kzg_point_evaluation;
pub mod modexp;
pub mod secp256k1;
pub mod secp256r1;
pub mod utilities;
pub use id::PrecompileId;
pub use interface::*;
cfg_if::cfg_if! {
if #[cfg(feature = "bn")]{
use ark_bn254 as _;
use ark_ff as _;
use ark_ec as _;
use ark_serialize as _;
}
}
use arrayref as _;
cfg_if::cfg_if! {
if #[cfg(feature = "blst")]{
use ark_bls12_381 as _;
use ark_ff as _;
use ark_ec as _;
use ark_serialize as _;
}
}
#[cfg(feature = "gmp")]
use aurora_engine_modexp as _;
use core::hash::Hash;
use primitives::{
hardfork::SpecId, short_address, Address, HashMap, HashSet, OnceLock, SHORT_ADDRESS_CAP,
};
use std::vec::Vec;
pub fn calc_linear_cost_u32(len: usize, base: u64, word: u64) -> u64 {
(len as u64).div_ceil(32) * word + base
}
#[derive(Clone, Debug)]
pub struct Precompiles {
inner: HashMap<Address, Precompile>,
addresses: HashSet<Address>,
optimized_access: Vec<Option<Precompile>>,
all_short_addresses: bool,
}
impl Default for Precompiles {
fn default() -> Self {
Self {
inner: HashMap::default(),
addresses: HashSet::default(),
optimized_access: vec![None; SHORT_ADDRESS_CAP],
all_short_addresses: true,
}
}
}
impl Precompiles {
pub fn new(spec: PrecompileSpecId) -> &'static Self {
match spec {
PrecompileSpecId::HOMESTEAD => Self::homestead(),
PrecompileSpecId::BYZANTIUM => Self::byzantium(),
PrecompileSpecId::ISTANBUL => Self::istanbul(),
PrecompileSpecId::BERLIN => Self::berlin(),
PrecompileSpecId::CANCUN => Self::cancun(),
PrecompileSpecId::PRAGUE => Self::prague(),
PrecompileSpecId::OSAKA => Self::osaka(),
}
}
pub fn homestead() -> &'static Self {
static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
INSTANCE.get_or_init(|| {
let mut precompiles = Precompiles::default();
precompiles.extend([
secp256k1::ECRECOVER,
hash::SHA256,
hash::RIPEMD160,
identity::FUN,
]);
precompiles
})
}
pub fn inner(&self) -> &HashMap<Address, Precompile> {
&self.inner
}
pub fn byzantium() -> &'static Self {
static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
INSTANCE.get_or_init(|| {
let mut precompiles = Self::homestead().clone();
precompiles.extend([
modexp::BYZANTIUM,
bn254::add::BYZANTIUM,
bn254::mul::BYZANTIUM,
bn254::pair::BYZANTIUM,
]);
precompiles
})
}
pub fn istanbul() -> &'static Self {
static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
INSTANCE.get_or_init(|| {
let mut precompiles = Self::byzantium().clone();
precompiles.extend([
bn254::add::ISTANBUL,
bn254::mul::ISTANBUL,
bn254::pair::ISTANBUL,
blake2::FUN,
]);
precompiles
})
}
pub fn berlin() -> &'static Self {
static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
INSTANCE.get_or_init(|| {
let mut precompiles = Self::istanbul().clone();
precompiles.extend([
modexp::BERLIN,
]);
precompiles
})
}
pub fn cancun() -> &'static Self {
static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
INSTANCE.get_or_init(|| {
let mut precompiles = Self::berlin().clone();
precompiles.extend([
kzg_point_evaluation::POINT_EVALUATION,
]);
precompiles
})
}
pub fn prague() -> &'static Self {
static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
INSTANCE.get_or_init(|| {
let mut precompiles = Self::cancun().clone();
precompiles.extend(bls12_381::precompiles());
precompiles
})
}
pub fn osaka() -> &'static Self {
static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
INSTANCE.get_or_init(|| {
let mut precompiles = Self::prague().clone();
precompiles.extend([modexp::OSAKA, secp256r1::P256VERIFY_OSAKA]);
precompiles
})
}
pub fn latest() -> &'static Self {
Self::osaka()
}
#[inline]
pub fn addresses(&self) -> impl ExactSizeIterator<Item = &Address> {
self.inner.keys()
}
#[inline]
pub fn into_addresses(self) -> impl ExactSizeIterator<Item = Address> {
self.inner.into_keys()
}
#[inline]
pub fn contains(&self, address: &Address) -> bool {
self.inner.contains_key(address)
}
#[inline]
pub fn get(&self, address: &Address) -> Option<&Precompile> {
if let Some(short_address) = short_address(address) {
return self.optimized_access[short_address].as_ref();
}
self.inner.get(address)
}
#[inline]
pub fn get_mut(&mut self, address: &Address) -> Option<&mut Precompile> {
self.inner.get_mut(address)
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn addresses_set(&self) -> &HashSet<Address> {
&self.addresses
}
#[inline]
pub fn extend(&mut self, other: impl IntoIterator<Item = Precompile>) {
let items: Vec<Precompile> = other.into_iter().collect::<Vec<_>>();
for item in items.iter() {
if let Some(short_address) = short_address(item.address()) {
self.optimized_access[short_address] = Some(item.clone());
} else {
self.all_short_addresses = false;
}
}
self.addresses.extend(items.iter().map(|p| *p.address()));
self.inner
.extend(items.into_iter().map(|p| (*p.address(), p.clone())));
}
pub fn difference(&self, other: &Self) -> Self {
let Self { inner, .. } = self;
let inner = inner
.iter()
.filter(|(a, _)| !other.inner.contains_key(*a))
.map(|(a, p)| (*a, p.clone()))
.collect::<HashMap<_, _>>();
let mut precompiles = Self::default();
precompiles.extend(inner.into_iter().map(|p| p.1));
precompiles
}
pub fn intersection(&self, other: &Self) -> Self {
let Self { inner, .. } = self;
let inner = inner
.iter()
.filter(|(a, _)| other.inner.contains_key(*a))
.map(|(a, p)| (*a, p.clone()))
.collect::<HashMap<_, _>>();
let mut precompiles = Self::default();
precompiles.extend(inner.into_iter().map(|p| p.1));
precompiles
}
}
#[derive(Clone, Debug)]
pub struct Precompile {
id: PrecompileId,
address: Address,
fn_: PrecompileFn,
}
impl From<(PrecompileId, Address, PrecompileFn)> for Precompile {
fn from((id, address, fn_): (PrecompileId, Address, PrecompileFn)) -> Self {
Precompile { id, address, fn_ }
}
}
impl From<Precompile> for (PrecompileId, Address, PrecompileFn) {
fn from(value: Precompile) -> Self {
(value.id, value.address, value.fn_)
}
}
impl Precompile {
pub const fn new(id: PrecompileId, address: Address, fn_: PrecompileFn) -> Self {
Self { id, address, fn_ }
}
#[inline]
pub fn id(&self) -> &PrecompileId {
&self.id
}
#[inline]
pub fn address(&self) -> &Address {
&self.address
}
#[inline]
pub fn precompile(&self) -> &PrecompileFn {
&self.fn_
}
#[inline]
pub fn into_precompile(self) -> PrecompileFn {
self.fn_
}
#[inline]
pub fn execute(&self, input: &[u8], gas_limit: u64) -> PrecompileResult {
(self.fn_)(input, gas_limit)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum PrecompileSpecId {
HOMESTEAD,
BYZANTIUM,
ISTANBUL,
BERLIN,
CANCUN,
PRAGUE,
OSAKA,
}
impl From<SpecId> for PrecompileSpecId {
fn from(spec_id: SpecId) -> Self {
Self::from_spec_id(spec_id)
}
}
impl PrecompileSpecId {
pub const fn from_spec_id(spec_id: primitives::hardfork::SpecId) -> Self {
use primitives::hardfork::SpecId::*;
match spec_id {
FRONTIER | FRONTIER_THAWING | HOMESTEAD | DAO_FORK | TANGERINE | SPURIOUS_DRAGON => {
Self::HOMESTEAD
}
BYZANTIUM | CONSTANTINOPLE | PETERSBURG => Self::BYZANTIUM,
ISTANBUL | MUIR_GLACIER => Self::ISTANBUL,
BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN,
CANCUN => Self::CANCUN,
PRAGUE => Self::PRAGUE,
OSAKA | AMSTERDAM => Self::OSAKA,
}
}
}
#[inline]
pub const fn u64_to_address(x: u64) -> Address {
let x = x.to_be_bytes();
Address::new([
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],
])
}
#[cfg(test)]
mod test {
use super::*;
fn temp_precompile(_input: &[u8], _gas_limit: u64) -> PrecompileResult {
PrecompileResult::Err(PrecompileError::OutOfGas)
}
#[test]
fn test_optimized_access() {
let mut precompiles = Precompiles::istanbul().clone();
assert!(precompiles.optimized_access[9].is_some());
assert!(precompiles.optimized_access[10].is_none());
precompiles.extend([Precompile::new(
PrecompileId::Custom("test".into()),
u64_to_address(100),
temp_precompile,
)]);
precompiles.extend([Precompile::new(
PrecompileId::Custom("test".into()),
u64_to_address(101),
temp_precompile,
)]);
assert_eq!(
precompiles.optimized_access[100]
.as_ref()
.unwrap()
.execute(&[], u64::MAX),
PrecompileResult::Err(PrecompileError::OutOfGas)
);
assert_eq!(
precompiles
.get(&Address::left_padding_from(&[101]))
.unwrap()
.execute(&[], u64::MAX),
PrecompileResult::Err(PrecompileError::OutOfGas)
);
}
#[test]
fn test_difference_precompile_sets() {
let difference = Precompiles::istanbul().difference(Precompiles::berlin());
assert!(difference.is_empty());
}
#[test]
fn test_intersection_precompile_sets() {
let intersection = Precompiles::homestead().intersection(Precompiles::byzantium());
assert_eq!(intersection.len(), 4)
}
}