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
44 #[arg(long)]
46 bench: bool,
47
48 #[arg(long, default_value = "")]
50 input: String,
51 #[arg(long, default_value = "1000000000")]
53 gas_limit: u64,
54
55 #[arg(long)]
57 state: bool,
58 #[arg(long)]
60 trace: bool,
61}
62
63impl Cmd {
64 pub fn run(&self) -> Result<(), Errors> {
66 let bytecode_str: Cow<'_, str> = if let Some(path) = &self.path {
67 if !path.exists() {
69 return Err(Errors::PathNotExists);
70 }
71 fs::read_to_string(path)?.into()
72 } else if let Some(bytecode) = &self.bytecode {
73 bytecode.as_str().into()
74 } else {
75 unreachable!()
76 };
77
78 let bytecode = hex::decode(bytecode_str.trim()).map_err(|_| Errors::InvalidBytecode)?;
79 let input = hex::decode(self.input.trim())
80 .map_err(|_| Errors::InvalidInput)?
81 .into();
82
83 let mut db = BenchmarkDB::new_bytecode(Bytecode::new_raw_checked(bytecode.into())?);
84
85 let nonce = db
86 .basic(BENCH_CALLER)
87 .unwrap()
88 .map_or(0, |account| account.nonce);
89
90 let mut evm = Context::mainnet()
93 .with_db(db)
94 .build_mainnet_with_inspector(TracerEip3155::new(Box::new(std::io::stdout())));
95
96 let tx = TxEnv::builder()
97 .caller(BENCH_CALLER)
98 .kind(TxKind::Call(BENCH_TARGET))
99 .data(input)
100 .nonce(nonce)
101 .gas_limit(self.gas_limit)
102 .build()
103 .unwrap();
104
105 if self.bench {
106 let mut criterion = criterion::Criterion::default()
107 .warm_up_time(std::time::Duration::from_millis(300))
108 .measurement_time(std::time::Duration::from_secs(2))
109 .without_plots();
110 let mut criterion_group = criterion.benchmark_group("revme");
111 criterion_group.bench_function("evm", |b| {
112 b.iter_batched(
113 || tx.clone(),
114 |input| evm.transact(input).unwrap(),
115 criterion::BatchSize::SmallInput,
116 );
117 });
118 criterion_group.finish();
119
120 return Ok(());
121 }
122
123 let time = Instant::now();
124 let r = if self.trace {
125 evm.inspect_tx(tx)
126 } else {
127 evm.transact(tx)
128 }
129 .map_err(|_| Errors::EVMError)?;
130 let time = time.elapsed();
131
132 println!("Result: {:#?}", r.result);
133 if self.state {
134 println!("State: {:#?}", r.state);
135 }
136
137 println!("Elapsed: {time:?}");
138 Ok(())
139 }
140}