use crate::{Context, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed, Transfer};
use alloc::{collections::BTreeMap, vec::Vec};
use primitive_types::{H160, H256};
pub type PrecompileResult = Result<PrecompileOutput, PrecompileFailure>;
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct PrecompileOutput {
pub exit_status: ExitSucceed,
pub output: Vec<u8>,
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum PrecompileFailure {
Error { exit_status: ExitError },
Revert {
exit_status: ExitRevert,
output: Vec<u8>,
},
Fatal { exit_status: ExitFatal },
}
impl From<ExitError> for PrecompileFailure {
fn from(error: ExitError) -> PrecompileFailure {
PrecompileFailure::Error { exit_status: error }
}
}
pub trait PrecompileHandle {
fn call(
&mut self,
to: H160,
transfer: Option<Transfer>,
input: Vec<u8>,
gas_limit: Option<u64>,
is_static: bool,
context: &Context,
) -> (ExitReason, Vec<u8>);
fn record_cost(&mut self, cost: u64) -> Result<(), ExitError>;
fn record_external_cost(
&mut self,
ref_time: Option<u64>,
proof_size: Option<u64>,
storage_growth: Option<u64>,
) -> Result<(), ExitError>;
fn refund_external_cost(&mut self, ref_time: Option<u64>, proof_size: Option<u64>);
fn remaining_gas(&self) -> u64;
fn log(&mut self, address: H160, topics: Vec<H256>, data: Vec<u8>) -> Result<(), ExitError>;
fn code_address(&self) -> H160;
fn input(&self) -> &[u8];
fn context(&self) -> &Context;
fn is_static(&self) -> bool;
fn gas_limit(&self) -> Option<u64>;
}
pub trait PrecompileSet {
fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;
fn is_precompile(&self, address: H160, remaining_gas: u64) -> IsPrecompileResult;
}
pub enum IsPrecompileResult {
Answer {
is_precompile: bool,
extra_cost: u64,
},
OutOfGas,
}
impl PrecompileSet for () {
fn execute(&self, _: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
None
}
fn is_precompile(&self, _: H160, _: u64) -> IsPrecompileResult {
IsPrecompileResult::Answer {
is_precompile: false,
extra_cost: 0,
}
}
}
pub type PrecompileFn =
fn(&[u8], Option<u64>, &Context, bool) -> Result<(PrecompileOutput, u64), PrecompileFailure>;
impl PrecompileSet for BTreeMap<H160, PrecompileFn> {
fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
let address = handle.code_address();
self.get(&address).map(|precompile| {
let input = handle.input();
let gas_limit = handle.gas_limit();
let context = handle.context();
let is_static = handle.is_static();
match (*precompile)(input, gas_limit, context, is_static) {
Ok((output, cost)) => {
handle.record_cost(cost)?;
Ok(output)
}
Err(err) => Err(err),
}
})
}
fn is_precompile(&self, address: H160, _: u64) -> IsPrecompileResult {
IsPrecompileResult::Answer {
is_precompile: self.contains_key(&address),
extra_cost: 0,
}
}
}