use eth_types::{
geth_types::{Account, BlockConstants, Transaction, Withdrawal},
Address, Error, GethExecTrace, Word,
};
use serde::Serialize;
use std::collections::BTreeMap;
#[derive(Debug, Default, Clone, Serialize)]
pub struct TraceConfig {
pub chain_id: Word,
pub history_hashes: Vec<Word>,
pub block_constants: BlockConstants,
pub accounts: BTreeMap<Address, Account>,
pub transactions: Vec<Transaction>,
pub withdrawals: Vec<Withdrawal>,
pub logger_config: LoggerConfig,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct LoggerConfig {
pub enable_memory: bool,
pub disable_stack: bool,
pub disable_storage: bool,
pub enable_return_data: bool,
}
impl Default for LoggerConfig {
fn default() -> Self {
Self {
enable_memory: false,
disable_stack: false,
disable_storage: false,
enable_return_data: true,
}
}
}
impl LoggerConfig {
pub fn enable_memory() -> Self {
Self {
enable_memory: true,
..Self::default()
}
}
}
pub fn trace(config: &TraceConfig) -> Result<Vec<GethExecTrace>, Error> {
let trace_string = geth_utils::trace(&serde_json::to_string(&config).unwrap()).map_err(
|error| match error {
geth_utils::Error::TracingError(error) => Error::TracingError(error),
},
)?;
let trace: Vec<GethExecTrace> =
serde_json::from_str(&trace_string).map_err(Error::SerdeError)?;
for trace in trace.iter() {
let error = &trace.return_value;
let allowed_cases = error.contains("nonce too low")
|| error.contains("nonce too high")
|| error.contains("intrinsic gas too low")
|| error.contains("insufficient funds for gas * price + value")
|| error.contains("insufficient funds for transfer");
if trace.invalid && !allowed_cases {
return Err(Error::TracingError(error.clone()));
}
}
Ok(trace)
}