use auto_impl::auto_impl;
use context::{Cfg, LocalContextTr};
use context_interface::ContextTr;
use interpreter::{CallInput, Gas, InputsImpl, InstructionResult, InterpreterResult};
use precompile::PrecompileError;
use precompile::{PrecompileSpecId, Precompiles};
use primitives::{hardfork::SpecId, Address, Bytes};
use std::boxed::Box;
use std::string::String;
#[auto_impl(&mut, Box)]
pub trait PrecompileProvider<CTX: ContextTr> {
type Output;
fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool;
fn run(
&mut self,
context: &mut CTX,
address: &Address,
inputs: &InputsImpl,
is_static: bool,
gas_limit: u64,
) -> Result<Option<Self::Output>, String>;
fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>>;
fn contains(&self, address: &Address) -> bool;
}
#[derive(Debug)]
pub struct EthPrecompiles {
pub precompiles: &'static Precompiles,
pub spec: SpecId,
}
impl EthPrecompiles {
pub fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
Box::new(self.precompiles.addresses().cloned())
}
pub fn contains(&self, address: &Address) -> bool {
self.precompiles.contains(address)
}
}
impl Clone for EthPrecompiles {
fn clone(&self) -> Self {
Self {
precompiles: self.precompiles,
spec: self.spec,
}
}
}
impl Default for EthPrecompiles {
fn default() -> Self {
let spec = SpecId::default();
Self {
precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
spec,
}
}
}
impl<CTX: ContextTr> PrecompileProvider<CTX> for EthPrecompiles {
type Output = InterpreterResult;
fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool {
let spec = spec.into();
if spec == self.spec {
return false;
}
self.precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec));
self.spec = spec;
true
}
fn run(
&mut self,
context: &mut CTX,
address: &Address,
inputs: &InputsImpl,
_is_static: bool,
gas_limit: u64,
) -> Result<Option<InterpreterResult>, String> {
let Some(precompile) = self.precompiles.get(address) else {
return Ok(None);
};
let mut result = InterpreterResult {
result: InstructionResult::Return,
gas: Gas::new(gas_limit),
output: Bytes::new(),
};
let r;
let input_bytes = match &inputs.input {
CallInput::SharedBuffer(range) => {
if let Some(slice) = context.local().shared_memory_buffer_slice(range.clone()) {
r = slice;
r.as_ref()
} else {
&[]
}
}
CallInput::Bytes(bytes) => bytes.0.iter().as_slice(),
};
match (*precompile)(input_bytes, gas_limit) {
Ok(output) => {
let underflow = result.gas.record_cost(output.gas_used);
assert!(underflow, "Gas underflow is not possible");
result.result = InstructionResult::Return;
result.output = output.bytes;
}
Err(PrecompileError::Fatal(e)) => return Err(e),
Err(e) => {
result.result = if e.is_oog() {
InstructionResult::PrecompileOOG
} else {
InstructionResult::PrecompileError
};
}
}
Ok(Some(result))
}
fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
self.warm_addresses()
}
fn contains(&self, address: &Address) -> bool {
self.contains(address)
}
}