use crate::{Bytes, Env};
use core::fmt::{self};
use dyn_clone::DynClone;
use std::{boxed::Box, string::String, sync::Arc};
pub type PrecompileResult = Result<PrecompileOutput, PrecompileErrors>;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PrecompileOutput {
pub gas_used: u64,
pub bytes: Bytes,
}
impl PrecompileOutput {
pub fn new(gas_used: u64, bytes: Bytes) -> Self {
Self { gas_used, bytes }
}
}
pub type StandardPrecompileFn = fn(&Bytes, u64) -> PrecompileResult;
pub type EnvPrecompileFn = fn(&Bytes, u64, env: &Env) -> PrecompileResult;
pub trait StatefulPrecompile: Sync + Send {
fn call(&self, bytes: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult;
}
pub trait StatefulPrecompileMut: DynClone + Send + Sync {
fn call_mut(&mut self, bytes: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult;
}
dyn_clone::clone_trait_object!(StatefulPrecompileMut);
pub type StatefulPrecompileArc = Arc<dyn StatefulPrecompile>;
pub type StatefulPrecompileBox = Box<dyn StatefulPrecompileMut>;
#[derive(Clone)]
pub enum Precompile {
Standard(StandardPrecompileFn),
Env(EnvPrecompileFn),
Stateful(StatefulPrecompileArc),
StatefulMut(StatefulPrecompileBox),
}
impl From<StandardPrecompileFn> for Precompile {
fn from(p: StandardPrecompileFn) -> Self {
Precompile::Standard(p)
}
}
impl From<EnvPrecompileFn> for Precompile {
fn from(p: EnvPrecompileFn) -> Self {
Precompile::Env(p)
}
}
impl From<StatefulPrecompileArc> for Precompile {
fn from(p: StatefulPrecompileArc) -> Self {
Precompile::Stateful(p)
}
}
impl From<StatefulPrecompileBox> for Precompile {
fn from(p: StatefulPrecompileBox) -> Self {
Precompile::StatefulMut(p)
}
}
impl fmt::Debug for Precompile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Precompile::Standard(_) => f.write_str("Standard"),
Precompile::Env(_) => f.write_str("Env"),
Precompile::Stateful(_) => f.write_str("Stateful"),
Precompile::StatefulMut(_) => f.write_str("StatefulMut"),
}
}
}
impl Precompile {
pub fn new_stateful<P: StatefulPrecompile + 'static>(p: P) -> Self {
Self::Stateful(Arc::new(p))
}
pub fn new_stateful_mut<P: StatefulPrecompileMut + 'static>(p: P) -> Self {
Self::StatefulMut(Box::new(p))
}
pub fn call(&mut self, bytes: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult {
match *self {
Precompile::Standard(p) => p(bytes, gas_limit),
Precompile::Env(p) => p(bytes, gas_limit, env),
Precompile::Stateful(ref p) => p.call(bytes, gas_limit, env),
Precompile::StatefulMut(ref mut p) => p.call_mut(bytes, gas_limit, env),
}
}
pub fn call_ref(&self, bytes: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult {
match *self {
Precompile::Standard(p) => p(bytes, gas_limit),
Precompile::Env(p) => p(bytes, gas_limit, env),
Precompile::Stateful(ref p) => p.call(bytes, gas_limit, env),
Precompile::StatefulMut(_) => Err(PrecompileErrors::Fatal {
msg: "call_ref on mutable stateful precompile".into(),
}),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PrecompileErrors {
Error(PrecompileError),
Fatal { msg: String },
}
#[cfg(feature = "std")]
impl std::error::Error for PrecompileErrors {}
impl fmt::Display for PrecompileErrors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Error(e) => e.fmt(f),
Self::Fatal { msg } => f.write_str(msg),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PrecompileError {
OutOfGas,
Blake2WrongLength,
Blake2WrongFinalIndicatorFlag,
ModexpExpOverflow,
ModexpBaseOverflow,
ModexpModOverflow,
Bn128FieldPointNotAMember,
Bn128AffineGFailedToCreate,
Bn128PairLength,
BlobInvalidInputLength,
BlobMismatchedVersion,
BlobVerifyKzgProofFailed,
Other(String),
}
impl PrecompileError {
pub fn other(err: impl Into<String>) -> Self {
Self::Other(err.into())
}
pub fn is_oog(&self) -> bool {
matches!(self, Self::OutOfGas)
}
}
impl From<PrecompileError> for PrecompileErrors {
fn from(err: PrecompileError) -> Self {
PrecompileErrors::Error(err)
}
}
#[cfg(feature = "std")]
impl std::error::Error for PrecompileError {}
impl fmt::Display for PrecompileError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::OutOfGas => "out of gas",
Self::Blake2WrongLength => "wrong input length for blake2",
Self::Blake2WrongFinalIndicatorFlag => "wrong final indicator flag for blake2",
Self::ModexpExpOverflow => "modexp exp overflow",
Self::ModexpBaseOverflow => "modexp base overflow",
Self::ModexpModOverflow => "modexp mod overflow",
Self::Bn128FieldPointNotAMember => "field point not a member of bn128 curve",
Self::Bn128AffineGFailedToCreate => "failed to create affine g point for bn128 curve",
Self::Bn128PairLength => "bn128 invalid pair length",
Self::BlobInvalidInputLength => "invalid blob input length",
Self::BlobMismatchedVersion => "mismatched blob version",
Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed",
Self::Other(s) => s,
};
f.write_str(s)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn stateful_precompile_mut() {
#[derive(Default, Clone)]
struct MyPrecompile {}
impl StatefulPrecompileMut for MyPrecompile {
fn call_mut(
&mut self,
_bytes: &Bytes,
_gas_limit: u64,
_env: &Env,
) -> PrecompileResult {
Err(PrecompileError::OutOfGas.into())
}
}
let mut p = Precompile::new_stateful_mut(MyPrecompile::default());
match &mut p {
Precompile::StatefulMut(p) => {
let _ = p.call_mut(&Bytes::new(), 0, &Env::default());
}
_ => panic!("not a state"),
}
}
}