use clap::Parser;
use database::BenchmarkDB;
use inspector::{inspector_handler, inspectors::TracerEip3155, InspectorContext, InspectorMainEvm};
use revm::{
bytecode::{Bytecode, BytecodeDecodeError},
handler::EthHandler,
primitives::{address, hex, Address, TxKind},
Context, Database, EvmExec, MainEvm,
};
use std::io::Error as IoError;
use std::path::PathBuf;
use std::time::Duration;
use std::{borrow::Cow, fs};
#[derive(Debug, thiserror::Error)]
pub enum Errors {
#[error("The specified path does not exist")]
PathNotExists,
#[error("Invalid bytecode")]
InvalidBytecode,
#[error("Invalid input")]
InvalidInput,
#[error("EVM Error")]
EVMError,
#[error(transparent)]
Io(#[from] IoError),
#[error(transparent)]
BytecodeDecodeError(#[from] BytecodeDecodeError),
}
#[derive(Parser, Debug)]
pub struct Cmd {
#[arg(required_unless_present = "path")]
bytecode: Option<String>,
#[arg(long)]
path: Option<PathBuf>,
#[arg(long)]
bench: bool,
#[arg(long, default_value = "")]
input: String,
#[arg(long)]
state: bool,
#[arg(long)]
trace: bool,
}
impl Cmd {
pub fn run(&self) -> Result<(), Errors> {
const CALLER: Address = address!("0000000000000000000000000000000000000001");
let bytecode_str: Cow<'_, str> = if let Some(path) = &self.path {
if !path.exists() {
return Err(Errors::PathNotExists);
}
fs::read_to_string(path)?.into()
} else if let Some(bytecode) = &self.bytecode {
bytecode.as_str().into()
} else {
unreachable!()
};
let bytecode = hex::decode(bytecode_str.trim()).map_err(|_| Errors::InvalidBytecode)?;
let input = hex::decode(self.input.trim())
.map_err(|_| Errors::InvalidInput)?
.into();
let mut db = BenchmarkDB::new_bytecode(Bytecode::new_raw_checked(bytecode.into())?);
let nonce = db.basic(CALLER).unwrap().map_or(0, |account| account.nonce);
let mut evm = MainEvm::new(
Context::builder().with_db(db).modify_tx_chained(|tx| {
tx.caller = CALLER;
tx.transact_to = TxKind::Call(Address::ZERO);
tx.data = input;
tx.nonce = nonce;
}),
EthHandler::default(),
);
if self.bench {
let bench_options = microbench::Options::default().time(Duration::from_secs(3));
microbench::bench(&bench_options, "Run bytecode", || {
let _ = evm.transact().unwrap();
});
return Ok(());
}
let out = if self.trace {
let mut evm = InspectorMainEvm::new(
InspectorContext::new(evm.context, TracerEip3155::new(Box::new(std::io::stdout()))),
inspector_handler(),
);
evm.exec().map_err(|_| Errors::EVMError)?
} else {
let out = evm.transact().map_err(|_| Errors::EVMError)?;
println!("Result: {:#?}", out.result);
out
};
if self.state {
println!("State: {:#?}", out.state);
}
Ok(())
}
}