Skip to content

Commit

Permalink
feat: add evm script (#1039)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
charles-cooper authored Mar 1, 2024
1 parent ef64e4d commit a89daef
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions bins/revme/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ version = "0.2.2"

[dependencies]
hash-db = "0.15"
hex = "0.4"
hashbrown = "0.14"
indicatif = "0.17"
microbench = "0.5"
plain_hasher = "0.2"
revm = { path = "../../crates/revm", version = "6.1.0", default-features = false, features = [
"ethersdb",
Expand Down
8 changes: 8 additions & 0 deletions bins/revme/src/cmd.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod evmrunner;
pub mod format_kzg_setup;
pub mod statetest;

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

#[derive(Debug, thiserror::Error)]
Expand All @@ -21,13 +26,16 @@ pub enum Error {
Statetest(#[from] statetest::Error),
#[error(transparent)]
KzgErrors(#[from] format_kzg_setup::KzgErrors),
#[error(transparent)]
EvmRunnerErrors(#[from] evmrunner::Errors),
}

impl MainCmd {
pub fn run(&self) -> Result<(), Error> {
match self {
Self::Statetest(cmd) => cmd.run().map_err(Into::into),
Self::FormatKzgSetup(cmd) => cmd.run().map_err(Into::into),
Self::Evm(cmd) => cmd.run().map_err(Into::into),
}
}
}
105 changes: 105 additions & 0 deletions bins/revme/src/cmd/evmrunner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use revm::{
db::BenchmarkDB,
primitives::{Address, Bytecode, TransactTo},
Evm,
};
use std::io::Error as IoError;
use std::path::PathBuf;
use std::time::Duration;
use std::{borrow::Cow, fs};
use structopt::StructOpt;

extern crate alloc;

#[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(IoError),
}

impl From<IoError> for Errors {
fn from(e: IoError) -> Self {
Errors::Io(e)
}
}

/// Evm runner command allows running arbitrary evm bytecode.
/// Bytecode can be provided from cli or from file with --path option.
#[derive(StructOpt, Debug)]
pub struct Cmd {
/// Bytecode to be executed.
#[structopt(default_value = "")]
bytecode: String,
/// Path to file containing the evm bytecode.
/// Overrides the bytecode option.
#[structopt(long)]
path: Option<PathBuf>,
/// Run in benchmarking mode.
#[structopt(long)]
bench: bool,
/// Input bytes.
#[structopt(long, default_value = "")]
input: String,
/// Print the state.
#[structopt(long)]
state: bool,
}

impl Cmd {
/// Run statetest command.
pub fn run(&self) -> Result<(), Errors> {
let bytecode_str: Cow<'_, str> = if let Some(path) = &self.path {
// check if path exists.
if !path.exists() {
return Err(Errors::PathNotExists);
}
fs::read_to_string(path)?.to_owned().into()
} else {
self.bytecode.as_str().into()
};

let bytecode = hex::decode(bytecode_str.trim()).map_err(|_| Errors::InvalidBytecode)?;
let input = hex::decode(self.input.trim())
.map_err(|_| Errors::InvalidInput)?
.into();
// BenchmarkDB is dummy state that implements Database trait.
// the bytecode is deployed at zero address.
let mut evm = Evm::builder()
.with_db(BenchmarkDB::new_bytecode(Bytecode::new_raw(
bytecode.into(),
)))
.modify_tx_env(|tx| {
// execution globals block hash/gas_limit/coinbase/timestamp..
tx.caller = "0x0000000000000000000000000000000000000001"
.parse()
.unwrap();
tx.transact_to = TransactTo::Call(Address::ZERO);
tx.data = input;
})
.build();

if self.bench {
// Microbenchmark
let bench_options = microbench::Options::default().time(Duration::from_secs(3));

microbench::bench(&bench_options, "Run bytecode", || {
let _ = evm.transact().unwrap();
});
} else {
let out = evm.transact().map_err(|_| Errors::EVMError)?;
println!("Result: {:#?}", out.result);
if self.state {
println!("State: {:#?}", out.state);
}
}
Ok(())
}
}

0 comments on commit a89daef

Please sign in to comment.