1use clap::Parser;
2use context::TxEnv;
3use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET};
4use inspector::{inspectors::TracerEip3155, InspectEvm};
5use revm::{
6 bytecode::{Bytecode, BytecodeDecodeError},
7 primitives::{hex, TxKind},
8 Context, Database, ExecuteEvm, MainBuilder, MainContext,
9};
10use std::path::PathBuf;
11use std::{borrow::Cow, fs};
12use std::{io::Error as IoError, time::Instant};
13
14#[derive(Debug, thiserror::Error)]
15pub enum Errors {
16 #[error("The specified path does not exist")]
17 PathNotExists,
18 #[error("Invalid bytecode")]
19 InvalidBytecode,
20 #[error("Invalid input")]
21 InvalidInput,
22 #[error("EVM Error")]
23 EVMError,
24 #[error(transparent)]
25 Io(#[from] IoError),
26 #[error(transparent)]
27 BytecodeDecodeError(#[from] BytecodeDecodeError),
28}
29
30#[derive(Parser, Debug)]
34pub struct Cmd {
35 #[arg(required_unless_present = "path")]
37 bytecode: Option<String>,
38 #[arg(long)]
42 path: Option<PathBuf>,
43 #[arg(long)]
45 bench: bool,
46 #[arg(long, default_value = "")]
48 input: String,
49 #[arg(long)]
51 state: bool,
52 #[arg(long)]
54 trace: bool,
55}
56
57impl Cmd {
58 pub fn run(&self) -> Result<(), Errors> {
60 let bytecode_str: Cow<'_, str> = if let Some(path) = &self.path {
61 if !path.exists() {
63 return Err(Errors::PathNotExists);
64 }
65 fs::read_to_string(path)?.into()
66 } else if let Some(bytecode) = &self.bytecode {
67 bytecode.as_str().into()
68 } else {
69 unreachable!()
70 };
71
72 let bytecode = hex::decode(bytecode_str.trim()).map_err(|_| Errors::InvalidBytecode)?;
73 let input = hex::decode(self.input.trim())
74 .map_err(|_| Errors::InvalidInput)?
75 .into();
76
77 let mut db = BenchmarkDB::new_bytecode(Bytecode::new_raw_checked(bytecode.into())?);
78
79 let nonce = db
80 .basic(BENCH_CALLER)
81 .unwrap()
82 .map_or(0, |account| account.nonce);
83
84 let mut evm = Context::mainnet()
87 .with_db(db)
88 .build_mainnet_with_inspector(TracerEip3155::new(Box::new(std::io::stdout())));
89
90 let tx = TxEnv {
91 caller: BENCH_CALLER,
92 kind: TxKind::Call(BENCH_TARGET),
93 data: input,
94 nonce,
95 ..Default::default()
96 };
97
98 if self.bench {
99 let mut criterion = criterion::Criterion::default()
100 .warm_up_time(std::time::Duration::from_millis(300))
101 .measurement_time(std::time::Duration::from_secs(2))
102 .without_plots();
103 let mut criterion_group = criterion.benchmark_group("revme");
104 criterion_group.bench_function("evm", |b| {
105 b.iter(|| {
106 let _ = evm.transact_finalize(tx.clone()).unwrap();
107 })
108 });
109 criterion_group.finish();
110
111 return Ok(());
112 }
113
114 let time = Instant::now();
115 let state = if self.trace {
116 evm.inspect_tx_finalize(tx.clone())
117 .map_err(|_| Errors::EVMError)?
118 .state
119 } else {
120 let out = evm
121 .transact_finalize(tx.clone())
122 .map_err(|_| Errors::EVMError)?;
123 println!("Result: {:#?}", out.result);
124 out.state
125 };
126 let time = time.elapsed();
127
128 if self.state {
129 println!("State: {:#?}", state);
130 }
131
132 println!("Elapsed: {:?}", time);
133 Ok(())
134 }
135}