Skip to content

Commit a89daef

Browse files
feat: add evm script (#1039)
* feat: add evm script add a script which can run arbitrary binaries * fix lint * move evm runner script to `revme` subcommand * cargo fmt * cli byecode,input,path, state options added, output result
1 parent ef64e4d commit a89daef

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bins/revme/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ version = "0.2.2"
1010

1111
[dependencies]
1212
hash-db = "0.15"
13+
hex = "0.4"
1314
hashbrown = "0.14"
1415
indicatif = "0.17"
16+
microbench = "0.5"
1517
plain_hasher = "0.2"
1618
revm = { path = "../../crates/revm", version = "6.1.0", default-features = false, features = [
1719
"ethersdb",

bins/revme/src/cmd.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod evmrunner;
12
pub mod format_kzg_setup;
23
pub mod statetest;
34

@@ -13,6 +14,10 @@ pub enum MainCmd {
1314
about = "Format kzg settings from a trusted setup file (.txt) into binary format (.bin)"
1415
)]
1516
FormatKzgSetup(format_kzg_setup::Cmd),
17+
#[structopt(
18+
about = "Evm runner command allows running arbitrary evm bytecode.\nBytecode can be provided from cli or from file with --path option."
19+
)]
20+
Evm(evmrunner::Cmd),
1621
}
1722

1823
#[derive(Debug, thiserror::Error)]
@@ -21,13 +26,16 @@ pub enum Error {
2126
Statetest(#[from] statetest::Error),
2227
#[error(transparent)]
2328
KzgErrors(#[from] format_kzg_setup::KzgErrors),
29+
#[error(transparent)]
30+
EvmRunnerErrors(#[from] evmrunner::Errors),
2431
}
2532

2633
impl MainCmd {
2734
pub fn run(&self) -> Result<(), Error> {
2835
match self {
2936
Self::Statetest(cmd) => cmd.run().map_err(Into::into),
3037
Self::FormatKzgSetup(cmd) => cmd.run().map_err(Into::into),
38+
Self::Evm(cmd) => cmd.run().map_err(Into::into),
3139
}
3240
}
3341
}

bins/revme/src/cmd/evmrunner.rs

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use revm::{
2+
db::BenchmarkDB,
3+
primitives::{Address, Bytecode, TransactTo},
4+
Evm,
5+
};
6+
use std::io::Error as IoError;
7+
use std::path::PathBuf;
8+
use std::time::Duration;
9+
use std::{borrow::Cow, fs};
10+
use structopt::StructOpt;
11+
12+
extern crate alloc;
13+
14+
#[derive(Debug, thiserror::Error)]
15+
pub 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(IoError),
26+
}
27+
28+
impl From<IoError> for Errors {
29+
fn from(e: IoError) -> Self {
30+
Errors::Io(e)
31+
}
32+
}
33+
34+
/// Evm runner command allows running arbitrary evm bytecode.
35+
/// Bytecode can be provided from cli or from file with --path option.
36+
#[derive(StructOpt, Debug)]
37+
pub struct Cmd {
38+
/// Bytecode to be executed.
39+
#[structopt(default_value = "")]
40+
bytecode: String,
41+
/// Path to file containing the evm bytecode.
42+
/// Overrides the bytecode option.
43+
#[structopt(long)]
44+
path: Option<PathBuf>,
45+
/// Run in benchmarking mode.
46+
#[structopt(long)]
47+
bench: bool,
48+
/// Input bytes.
49+
#[structopt(long, default_value = "")]
50+
input: String,
51+
/// Print the state.
52+
#[structopt(long)]
53+
state: bool,
54+
}
55+
56+
impl Cmd {
57+
/// Run statetest command.
58+
pub fn run(&self) -> Result<(), Errors> {
59+
let bytecode_str: Cow<'_, str> = if let Some(path) = &self.path {
60+
// check if path exists.
61+
if !path.exists() {
62+
return Err(Errors::PathNotExists);
63+
}
64+
fs::read_to_string(path)?.to_owned().into()
65+
} else {
66+
self.bytecode.as_str().into()
67+
};
68+
69+
let bytecode = hex::decode(bytecode_str.trim()).map_err(|_| Errors::InvalidBytecode)?;
70+
let input = hex::decode(self.input.trim())
71+
.map_err(|_| Errors::InvalidInput)?
72+
.into();
73+
// BenchmarkDB is dummy state that implements Database trait.
74+
// the bytecode is deployed at zero address.
75+
let mut evm = Evm::builder()
76+
.with_db(BenchmarkDB::new_bytecode(Bytecode::new_raw(
77+
bytecode.into(),
78+
)))
79+
.modify_tx_env(|tx| {
80+
// execution globals block hash/gas_limit/coinbase/timestamp..
81+
tx.caller = "0x0000000000000000000000000000000000000001"
82+
.parse()
83+
.unwrap();
84+
tx.transact_to = TransactTo::Call(Address::ZERO);
85+
tx.data = input;
86+
})
87+
.build();
88+
89+
if self.bench {
90+
// Microbenchmark
91+
let bench_options = microbench::Options::default().time(Duration::from_secs(3));
92+
93+
microbench::bench(&bench_options, "Run bytecode", || {
94+
let _ = evm.transact().unwrap();
95+
});
96+
} else {
97+
let out = evm.transact().map_err(|_| Errors::EVMError)?;
98+
println!("Result: {:#?}", out.result);
99+
if self.state {
100+
println!("State: {:#?}", out.state);
101+
}
102+
}
103+
Ok(())
104+
}
105+
}

0 commit comments

Comments
 (0)