From 74bf32a60fec4ba0339b75c56c5093af6e9221c3 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Thu, 1 Feb 2024 10:00:09 -0500 Subject: [PATCH 1/5] feat: add evm script add a script which can run arbitrary binaries --- bins/revm-test/src/bin/evm.rs | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 bins/revm-test/src/bin/evm.rs diff --git a/bins/revm-test/src/bin/evm.rs b/bins/revm-test/src/bin/evm.rs new file mode 100644 index 0000000000..567bedd5b3 --- /dev/null +++ b/bins/revm-test/src/bin/evm.rs @@ -0,0 +1,48 @@ +use revm::{ + db::BenchmarkDB, + primitives::{Bytecode, TransactTo, U256}, + Evm, +}; +use std::time::Duration; +use std::fs; +use std::env; +extern crate alloc; + + +fn main() { + let args: Vec = env::args().collect(); + let file_path = &args[1]; + let contents = fs::read_to_string(file_path).unwrap_or_else(|error| { + panic!("Couldn't read file: {:?}", error); + }); + let contents_str = contents.to_string(); + let bytecode = hex::decode(contents_str.trim()).unwrap_or_else(|error| { + panic!("Couldn't decode contents: {:?}", error); + }); + + let ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; + + // 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( + ZERO_ADDRESS + .parse() + .unwrap(), + ); + }) + .build(); + + // Microbenchmark + let bench_options = microbench::Options::default().time(Duration::from_secs(3)); + + microbench::bench(&bench_options, "Run bytecode", || { + let _ = evm.transact().unwrap(); + }); +} From 97fbbe69640a6cd30ff7fb99bffd05ab107127c4 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Fri, 2 Feb 2024 08:09:26 -0500 Subject: [PATCH 2/5] fix lint --- bins/revm-test/src/bin/evm.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bins/revm-test/src/bin/evm.rs b/bins/revm-test/src/bin/evm.rs index 567bedd5b3..d4a53cb4ed 100644 --- a/bins/revm-test/src/bin/evm.rs +++ b/bins/revm-test/src/bin/evm.rs @@ -1,6 +1,6 @@ use revm::{ db::BenchmarkDB, - primitives::{Bytecode, TransactTo, U256}, + primitives::{Bytecode, TransactTo}, Evm, }; use std::time::Duration; @@ -20,7 +20,7 @@ fn main() { panic!("Couldn't decode contents: {:?}", error); }); - let ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; + let zero_address = "0x0000000000000000000000000000000000000000"; // BenchmarkDB is dummy state that implements Database trait. // the bytecode is deployed at zero address. @@ -32,7 +32,7 @@ fn main() { .parse() .unwrap(); tx.transact_to = TransactTo::Call( - ZERO_ADDRESS + zero_address .parse() .unwrap(), ); From 566c32ff90f883481f6d60c321eba84ffc79bf3c Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Thu, 29 Feb 2024 15:50:55 -0500 Subject: [PATCH 3/5] move evm runner script to `revme` subcommand --- Cargo.lock | 2 + bins/revm-test/src/bin/evm.rs | 48 ------------------ bins/revme/Cargo.toml | 2 + bins/revme/src/cmd.rs | 6 +++ bins/revme/src/cmd/evmrunner.rs | 89 +++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 48 deletions(-) delete mode 100644 bins/revm-test/src/bin/evm.rs create mode 100644 bins/revme/src/cmd/evmrunner.rs diff --git a/Cargo.lock b/Cargo.lock index fb382c4fe8..c4bd9e8c97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2395,8 +2395,10 @@ dependencies = [ "alloy-rlp", "hash-db", "hashbrown", + "hex", "indicatif", "k256", + "microbench", "plain_hasher", "revm", "serde", diff --git a/bins/revm-test/src/bin/evm.rs b/bins/revm-test/src/bin/evm.rs deleted file mode 100644 index d4a53cb4ed..0000000000 --- a/bins/revm-test/src/bin/evm.rs +++ /dev/null @@ -1,48 +0,0 @@ -use revm::{ - db::BenchmarkDB, - primitives::{Bytecode, TransactTo}, - Evm, -}; -use std::time::Duration; -use std::fs; -use std::env; -extern crate alloc; - - -fn main() { - let args: Vec = env::args().collect(); - let file_path = &args[1]; - let contents = fs::read_to_string(file_path).unwrap_or_else(|error| { - panic!("Couldn't read file: {:?}", error); - }); - let contents_str = contents.to_string(); - let bytecode = hex::decode(contents_str.trim()).unwrap_or_else(|error| { - panic!("Couldn't decode contents: {:?}", error); - }); - - let zero_address = "0x0000000000000000000000000000000000000000"; - - // 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( - zero_address - .parse() - .unwrap(), - ); - }) - .build(); - - // Microbenchmark - let bench_options = microbench::Options::default().time(Duration::from_secs(3)); - - microbench::bench(&bench_options, "Run bytecode", || { - let _ = evm.transact().unwrap(); - }); -} diff --git a/bins/revme/Cargo.toml b/bins/revme/Cargo.toml index 59118f5fae..6792e8c30a 100644 --- a/bins/revme/Cargo.toml +++ b/bins/revme/Cargo.toml @@ -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", diff --git a/bins/revme/src/cmd.rs b/bins/revme/src/cmd.rs index 821e39ee71..cfdcce0cb0 100644 --- a/bins/revme/src/cmd.rs +++ b/bins/revme/src/cmd.rs @@ -1,5 +1,6 @@ pub mod format_kzg_setup; pub mod statetest; +pub mod evmrunner; use structopt::{clap::AppSettings, StructOpt}; @@ -13,6 +14,8 @@ pub enum MainCmd { about = "Format kzg settings from a trusted setup file (.txt) into binary format (.bin)" )] FormatKzgSetup(format_kzg_setup::Cmd), + #[structopt(about = "Run evm script directly")] + Evm(evmrunner::Cmd), } #[derive(Debug, thiserror::Error)] @@ -21,6 +24,8 @@ pub enum Error { Statetest(#[from] statetest::Error), #[error(transparent)] KzgErrors(#[from] format_kzg_setup::KzgErrors), + #[error(transparent)] + EvmRunnerErrors(#[from] evmrunner::Errors), } impl MainCmd { @@ -28,6 +33,7 @@ impl MainCmd { 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), } } } diff --git a/bins/revme/src/cmd/evmrunner.rs b/bins/revme/src/cmd/evmrunner.rs new file mode 100644 index 0000000000..5219df6e94 --- /dev/null +++ b/bins/revme/src/cmd/evmrunner.rs @@ -0,0 +1,89 @@ +use revm::{ + db::BenchmarkDB, + primitives::{Bytecode, TransactTo}, + Evm, +}; +use std::time::Duration; +use std::fs; +use std::path::PathBuf; +use core::fmt::Display; +use structopt::StructOpt; + +extern crate alloc; + +#[derive(Debug)] +pub enum Errors { + PathNotExists, + InvalidFile, + EVMError, +} + +impl std::error::Error for Errors {} + +impl Display for Errors { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Errors::PathNotExists => write!(f, "The specified path does not exist"), + Errors::InvalidFile => write!(f, "Invalid EVM script"), + Errors::EVMError => write!(f, "VM error"), + } + } +} + +/// EvmRunner command +#[derive(StructOpt, Debug)] +pub struct Cmd { + /// Path to file containing the evm script. + #[structopt(required = true)] + path: PathBuf, + /// Run in benchmarking mode + #[structopt(long)] + bench: bool, +} + +impl Cmd { + /// Run statetest command. + pub fn run(&self) -> Result<(), Errors> { + // check if path exists. + if !self.path.exists() { + return Err(Errors::PathNotExists); + } + + + let contents = fs::read_to_string(&self.path).map_err(|_| Errors::InvalidFile)?; + let contents_str = contents.to_string(); + let bytecode = hex::decode(contents_str.trim()).map_err(|_| Errors::InvalidFile)?; + + let zero_address = "0x0000000000000000000000000000000000000000"; + + // 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( + zero_address + .parse() + .unwrap(), + ); + }) + .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 { + evm.transact().map_err(|_| Errors::EVMError)?; + // TODO: print the result + } + Ok(()) + } +} From dd07953e9373ac615d14fba5c230c33fb069a476 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Thu, 29 Feb 2024 16:10:55 -0500 Subject: [PATCH 4/5] cargo fmt --- bins/revme/src/cmd.rs | 2 +- bins/revme/src/cmd/evmrunner.rs | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/bins/revme/src/cmd.rs b/bins/revme/src/cmd.rs index cfdcce0cb0..b2e5c93768 100644 --- a/bins/revme/src/cmd.rs +++ b/bins/revme/src/cmd.rs @@ -1,6 +1,6 @@ +pub mod evmrunner; pub mod format_kzg_setup; pub mod statetest; -pub mod evmrunner; use structopt::{clap::AppSettings, StructOpt}; diff --git a/bins/revme/src/cmd/evmrunner.rs b/bins/revme/src/cmd/evmrunner.rs index 5219df6e94..06a7d3e36f 100644 --- a/bins/revme/src/cmd/evmrunner.rs +++ b/bins/revme/src/cmd/evmrunner.rs @@ -1,12 +1,12 @@ +use core::fmt::Display; use revm::{ db::BenchmarkDB, primitives::{Bytecode, TransactTo}, Evm, }; -use std::time::Duration; use std::fs; use std::path::PathBuf; -use core::fmt::Display; +use std::time::Duration; use structopt::StructOpt; extern crate alloc; @@ -19,7 +19,7 @@ pub enum Errors { } impl std::error::Error for Errors {} - + impl Display for Errors { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { @@ -48,7 +48,6 @@ impl Cmd { if !self.path.exists() { return Err(Errors::PathNotExists); } - let contents = fs::read_to_string(&self.path).map_err(|_| Errors::InvalidFile)?; let contents_str = contents.to_string(); @@ -59,17 +58,15 @@ impl Cmd { // 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()))) + .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( - zero_address - .parse() - .unwrap(), - ); + tx.transact_to = TransactTo::Call(zero_address.parse().unwrap()); }) .build(); From 9eb286fba1b271706d450a0485ed9a8c4d7093a0 Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 1 Mar 2024 04:55:46 +0100 Subject: [PATCH 5/5] cli byecode,input,path, state options added, output result --- bins/revme/src/cmd.rs | 4 +- bins/revme/src/cmd/evmrunner.rs | 83 ++++++++++++++++++++------------- 2 files changed, 54 insertions(+), 33 deletions(-) diff --git a/bins/revme/src/cmd.rs b/bins/revme/src/cmd.rs index b2e5c93768..3734b69bd9 100644 --- a/bins/revme/src/cmd.rs +++ b/bins/revme/src/cmd.rs @@ -14,7 +14,9 @@ pub enum MainCmd { about = "Format kzg settings from a trusted setup file (.txt) into binary format (.bin)" )] FormatKzgSetup(format_kzg_setup::Cmd), - #[structopt(about = "Run evm script directly")] + #[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), } diff --git a/bins/revme/src/cmd/evmrunner.rs b/bins/revme/src/cmd/evmrunner.rs index 06a7d3e36f..1414a97054 100644 --- a/bins/revme/src/cmd/evmrunner.rs +++ b/bins/revme/src/cmd/evmrunner.rs @@ -1,60 +1,75 @@ -use core::fmt::Display; use revm::{ db::BenchmarkDB, - primitives::{Bytecode, TransactTo}, + primitives::{Address, Bytecode, TransactTo}, Evm, }; -use std::fs; +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)] +#[derive(Debug, thiserror::Error)] pub enum Errors { + #[error("The specified path does not exist")] PathNotExists, - InvalidFile, + #[error("Invalid bytecode")] + InvalidBytecode, + #[error("Invalid input")] + InvalidInput, + #[error("EVM Error")] EVMError, + #[error(transparent)] + Io(IoError), } -impl std::error::Error for Errors {} - -impl Display for Errors { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Errors::PathNotExists => write!(f, "The specified path does not exist"), - Errors::InvalidFile => write!(f, "Invalid EVM script"), - Errors::EVMError => write!(f, "VM error"), - } +impl From for Errors { + fn from(e: IoError) -> Self { + Errors::Io(e) } } -/// EvmRunner command +/// 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 { - /// Path to file containing the evm script. - #[structopt(required = true)] - path: PathBuf, - /// Run in benchmarking mode + /// Bytecode to be executed. + #[structopt(default_value = "")] + bytecode: String, + /// Path to file containing the evm bytecode. + /// Overrides the bytecode option. + #[structopt(long)] + path: Option, + /// 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> { - // check if path exists. - if !self.path.exists() { - return Err(Errors::PathNotExists); - } - - let contents = fs::read_to_string(&self.path).map_err(|_| Errors::InvalidFile)?; - let contents_str = contents.to_string(); - let bytecode = hex::decode(contents_str.trim()).map_err(|_| Errors::InvalidFile)?; - - let zero_address = "0x0000000000000000000000000000000000000000"; + 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() @@ -66,7 +81,8 @@ impl Cmd { tx.caller = "0x0000000000000000000000000000000000000001" .parse() .unwrap(); - tx.transact_to = TransactTo::Call(zero_address.parse().unwrap()); + tx.transact_to = TransactTo::Call(Address::ZERO); + tx.data = input; }) .build(); @@ -78,8 +94,11 @@ impl Cmd { let _ = evm.transact().unwrap(); }); } else { - evm.transact().map_err(|_| Errors::EVMError)?; - // TODO: print the result + let out = evm.transact().map_err(|_| Errors::EVMError)?; + println!("Result: {:#?}", out.result); + if self.state { + println!("State: {:#?}", out.state); + } } Ok(()) }