mod data;
use ethereum::Log;
use evm::{executor::stack::PrecompileFailure, ExitError};
use primitive_types::{H160, H256};
pub use data::{Address, EvmData, EvmDataReader, EvmDataWriter};
pub use evm_precompiles_derive::generate_function_selector;
pub type EvmResult<T = ()> = Result<T, PrecompileFailure>;
#[macro_export(crate)]
macro_rules! err {
($e: expr) => {
PrecompileFailure::Error { exit_status: $e }
};
}
pub fn error<T: Into<std::borrow::Cow<'static, str>>>(text: T) -> PrecompileFailure {
err!(ExitError::Other(text.into()))
}
#[derive(Clone, Debug)]
pub struct LogsBuilder {
address: H160,
logs: Vec<Log>,
}
impl LogsBuilder {
pub fn new(address: H160) -> Self {
Self {
logs: vec![],
address,
}
}
pub fn build(self) -> Vec<Log> {
self.logs
}
pub fn log0<D>(mut self, data: D) -> Self
where
D: Into<Vec<u8>>,
{
self.logs.push(Log {
address: self.address,
data: data.into(),
topics: vec![],
});
self
}
pub fn log1<D, T0>(mut self, topic0: T0, data: D) -> Self
where
D: Into<Vec<u8>>,
T0: Into<H256>,
{
self.logs.push(Log {
address: self.address,
data: data.into(),
topics: vec![topic0.into()],
});
self
}
pub fn log2<D, T0, T1>(mut self, topic0: T0, topic1: T1, data: D) -> Self
where
D: Into<Vec<u8>>,
T0: Into<H256>,
T1: Into<H256>,
{
self.logs.push(Log {
address: self.address,
data: data.into(),
topics: vec![topic0.into(), topic1.into()],
});
self
}
pub fn log3<D, T0, T1, T2>(mut self, topic0: T0, topic1: T1, topic2: T2, data: D) -> Self
where
D: Into<Vec<u8>>,
T0: Into<H256>,
T1: Into<H256>,
T2: Into<H256>,
{
self.logs.push(Log {
address: self.address,
data: data.into(),
topics: vec![topic0.into(), topic1.into(), topic2.into()],
});
self
}
pub fn log4<D, T0, T1, T2, T3>(
mut self,
topic0: T0,
topic1: T1,
topic2: T2,
topic3: T3,
data: D,
) -> Self
where
D: Into<Vec<u8>>,
T0: Into<H256>,
T1: Into<H256>,
T2: Into<H256>,
T3: Into<H256>,
{
self.logs.push(Log {
address: self.address,
data: data.into(),
topics: vec![topic0.into(), topic1.into(), topic2.into(), topic3.into()],
});
self
}
}
#[derive(Clone, Copy, Debug)]
pub struct Gasometer {
target_gas: Option<u64>,
used_gas: u64,
}
impl Gasometer {
pub fn new(target_gas: Option<u64>) -> Self {
Self {
target_gas,
used_gas: 0,
}
}
pub fn used_gas(&self) -> u64 {
self.used_gas
}
pub fn record_cost(&mut self, cost: u64) -> EvmResult {
self.used_gas = self
.used_gas
.checked_add(cost)
.ok_or(err!(ExitError::OutOfGas))?;
match self.target_gas {
Some(gas_limit) if self.used_gas > gas_limit => Err(err!(ExitError::OutOfGas)),
_ => Ok(()),
}
}
pub fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult {
const G_LOG: u64 = 375;
const G_LOGDATA: u64 = 8;
const G_LOGTOPIC: u64 = 375;
let topic_cost = G_LOGTOPIC
.checked_mul(topics as u64)
.ok_or(err!(ExitError::OutOfGas))?;
let data_cost = G_LOGDATA
.checked_mul(data_len as u64)
.ok_or(err!(ExitError::OutOfGas))?;
self.record_cost(G_LOG)?;
self.record_cost(topic_cost)?;
self.record_cost(data_cost)?;
Ok(())
}
pub fn record_log_costs(&mut self, logs: &[Log]) -> EvmResult {
for log in logs {
self.record_log_costs_manual(log.topics.len(), log.data.len())?;
}
Ok(())
}
pub fn remaining_gas(&self) -> EvmResult<Option<u64>> {
Ok(match self.target_gas {
None => None,
Some(gas_limit) => Some(
gas_limit
.checked_sub(self.used_gas)
.ok_or(err!(ExitError::OutOfGas))?,
),
})
}
}