revme/cmd/
evmrunner.rs

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/// Evm runner command allows running arbitrary evm bytecode
31///
32/// Bytecode can be provided from cli or from file with `--path` option.
33#[derive(Parser, Debug)]
34pub struct Cmd {
35    /// Hex-encoded EVM bytecode to be executed
36    #[arg(required_unless_present = "path")]
37    bytecode: Option<String>,
38    /// Path to a file containing the hex-encoded EVM bytecode to be executed
39    ///
40    /// Overrides the positional `bytecode` argument.
41    #[arg(long)]
42    path: Option<PathBuf>,
43    /// Whether to run in benchmarking mode
44    #[arg(long)]
45    bench: bool,
46    /// Hex-encoded input/calldata bytes
47    #[arg(long, default_value = "")]
48    input: String,
49    /// Whether to print the state
50    #[arg(long)]
51    state: bool,
52    /// Whether to print the trace
53    #[arg(long)]
54    trace: bool,
55}
56
57impl Cmd {
58    /// Runs evm runner command.
59    pub fn run(&self) -> Result<(), Errors> {
60        let bytecode_str: Cow<'_, str> = if let Some(path) = &self.path {
61            // Check if path exists.
62            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        // BenchmarkDB is dummy state that implements Database trait.
85        // The bytecode is deployed at zero address.
86        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}