diff --git a/Cargo.lock b/Cargo.lock index bb698ec97a..b7ee6e07af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3457,7 +3457,6 @@ dependencies = [ "auto_impl", "indicatif", "revm-bytecode", - "revm-context-interface", "revm-database-interface", "revm-primitives", "revm-state", @@ -3487,6 +3486,7 @@ name = "revm-handler" version = "1.0.0" dependencies = [ "revm-bytecode", + "revm-context", "revm-context-interface", "revm-database", "revm-handler-interface", @@ -3502,8 +3502,10 @@ dependencies = [ name = "revm-handler-interface" version = "1.0.0" dependencies = [ + "auto_impl", "revm-database", "revm-primitives", + "revm-specification", ] [[package]] @@ -3525,7 +3527,6 @@ dependencies = [ "revm-bytecode", "revm-context-interface", "revm-database-interface", - "revm-handler-interface", "revm-primitives", "revm-specification", "serde", @@ -3543,7 +3544,6 @@ dependencies = [ "once_cell", "revm", "revm-database", - "revm-inspector", "revm-precompile", "rstest 0.23.0", "serde", diff --git a/bins/revme/src/cmd/bench/analysis.rs b/bins/revme/src/cmd/bench/analysis.rs index 246cd4240a..88219799c7 100644 --- a/bins/revme/src/cmd/bench/analysis.rs +++ b/bins/revme/src/cmd/bench/analysis.rs @@ -1,9 +1,8 @@ use database::BenchmarkDB; use revm::{ bytecode::Bytecode, - handler::EthHandler, primitives::{address, bytes, hex, Bytes, TxKind}, - Context, MainEvm, + transact_main, Context, }; const BYTES: &str = include_str!("analysis.hex"); @@ -12,7 +11,7 @@ pub fn run() { let bytecode = Bytecode::new_raw(Bytes::from(hex::decode(BYTES).unwrap())); // BenchmarkDB is dummy state that implements Database trait. - let context = Context::builder() + let mut context = Context::builder() .with_db(BenchmarkDB::new_bytecode(bytecode)) .modify_tx_chained(|tx| { // Execution globals block hash/gas_limit/coinbase/timestamp.. @@ -21,6 +20,5 @@ pub fn run() { //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); tx.data = bytes!("8035F0CE"); }); - let mut evm = MainEvm::new(context, EthHandler::default()); - let _ = evm.transact().unwrap(); + let _ = transact_main(&mut context); } diff --git a/bins/revme/src/cmd/bench/burntpix.rs b/bins/revme/src/cmd/bench/burntpix.rs index c1a6158719..b8f922a823 100644 --- a/bins/revme/src/cmd/bench/burntpix.rs +++ b/bins/revme/src/cmd/bench/burntpix.rs @@ -12,10 +12,9 @@ use database::CacheDB; use revm::{ context_interface::result::{ExecutionResult, Output}, database_interface::EmptyDB, - handler::EthHandler, primitives::{address, hex, keccak256, Address, Bytes, TxKind, B256, U256}, state::{AccountInfo, Bytecode}, - Context, MainEvm, + transact_main, Context, }; use std::fs::File; @@ -37,16 +36,15 @@ pub fn run() { let db = init_db(); - let context = Context::builder().with_db(db).modify_tx_chained(|tx| { + let mut context = Context::builder().with_db(db).modify_tx_chained(|tx| { tx.caller = address!("1000000000000000000000000000000000000000"); tx.kind = TxKind::Call(BURNTPIX_MAIN_ADDRESS); tx.data = run_call_data.clone().into(); tx.gas_limit = u64::MAX; }); - let mut evm = MainEvm::new(context, EthHandler::default()); let started = Instant::now(); - let tx_result = evm.transact().unwrap().result; + let tx_result = transact_main(&mut context).unwrap().result; let return_data = match tx_result { ExecutionResult::Success { output, gas_used, .. diff --git a/bins/revme/src/cmd/bench/snailtracer.rs b/bins/revme/src/cmd/bench/snailtracer.rs index 4d3a75a8fe..8fd548614a 100644 --- a/bins/revme/src/cmd/bench/snailtracer.rs +++ b/bins/revme/src/cmd/bench/snailtracer.rs @@ -1,13 +1,12 @@ use database::BenchmarkDB; use revm::{ bytecode::Bytecode, - handler::EthHandler, primitives::{address, bytes, hex, Bytes, TxKind}, - Context, MainEvm, + transact_main, Context, }; pub fn simple_example(bytecode: Bytecode) { - let context = Context::builder() + let mut context = Context::builder() .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) .modify_tx_chained(|tx| { // Execution globals block hash/gas_limit/coinbase/timestamp.. @@ -16,8 +15,7 @@ pub fn simple_example(bytecode: Bytecode) { tx.data = bytes!("30627b7c"); tx.gas_limit = 1_000_000_000; }); - let mut evm = MainEvm::new(context, EthHandler::default()); - let _ = evm.transact().unwrap(); + let _ = transact_main(&mut context).unwrap(); } pub fn run() { diff --git a/bins/revme/src/cmd/bench/transfer.rs b/bins/revme/src/cmd/bench/transfer.rs index 6363c00c3c..9ffffdc150 100644 --- a/bins/revme/src/cmd/bench/transfer.rs +++ b/bins/revme/src/cmd/bench/transfer.rs @@ -1,13 +1,12 @@ use database::BenchmarkDB; use revm::{ bytecode::Bytecode, - handler::EthHandler, primitives::{TxKind, U256}, - Context, MainEvm, + transact_main, Context, }; pub fn run() { - let context = Context::builder() + let mut context = Context::builder() .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) .modify_tx_chained(|tx| { // Execution globals block hash/gas_limit/coinbase/timestamp.. @@ -21,7 +20,5 @@ pub fn run() { .unwrap(), ); }); - let mut evm = MainEvm::new(context, EthHandler::default()); - - let _ = evm.transact().unwrap(); + let _ = transact_main(&mut context); } diff --git a/bins/revme/src/cmd/evmrunner.rs b/bins/revme/src/cmd/evmrunner.rs index 2ad8a2a9fc..a379596bb4 100644 --- a/bins/revme/src/cmd/evmrunner.rs +++ b/bins/revme/src/cmd/evmrunner.rs @@ -1,14 +1,10 @@ use clap::Parser; use database::BenchmarkDB; -use inspector::{ - inspector_context::InspectorContext, inspector_handler, inspectors::TracerEip3155, - InspectorMainEvm, -}; +use inspector::{inspect_main, inspector_context::InspectorContext, inspectors::TracerEip3155}; use revm::{ bytecode::{Bytecode, BytecodeDecodeError}, - handler::EthHandler, primitives::{address, hex, Address, TxKind}, - Context, Database, EvmExec, MainEvm, + transact_main, Context, Database, }; use std::io::Error as IoError; use std::path::PathBuf; @@ -86,36 +82,32 @@ impl Cmd { // BenchmarkDB is dummy state that implements Database trait. // The bytecode is deployed at zero address. - let mut evm = MainEvm::new( - Context::builder().with_db(db).modify_tx_chained(|tx| { - tx.caller = CALLER; - tx.kind = TxKind::Call(Address::ZERO); - tx.data = input; - tx.nonce = nonce; - }), - EthHandler::default(), - ); + let mut ctx = Context::builder().with_db(db).modify_tx_chained(|tx| { + tx.caller = CALLER; + tx.kind = TxKind::Call(Address::ZERO); + tx.data = input; + tx.nonce = nonce; + }); 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(); + let _ = transact_main(&mut ctx).unwrap(); }); return Ok(()); } let out = if self.trace { - let mut evm = InspectorMainEvm::new( - InspectorContext::new(evm.context, TracerEip3155::new(Box::new(std::io::stdout()))), - inspector_handler(), - ); - - evm.exec().map_err(|_| Errors::EVMError)? + inspect_main(&mut InspectorContext::new( + &mut ctx, + TracerEip3155::new(Box::new(std::io::stdout())), + )) + .map_err(|_| Errors::EVMError)? } else { - let out = evm.transact().map_err(|_| Errors::EVMError)?; + let out = transact_main(&mut ctx).map_err(|_| Errors::EVMError)?; println!("Result: {:#?}", out.result); out }; diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 9d5f01c4a9..f02597a60a 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -5,8 +5,7 @@ use super::{ use database::State; use indicatif::{ProgressBar, ProgressDrawTarget}; use inspector::{ - inspector_context::InspectorContext, inspector_handler, inspectors::TracerEip3155, - InspectorMainEvm, + inspect_main_commit, inspector_context::InspectorContext, inspectors::TracerEip3155, }; use revm::{ bytecode::Bytecode, @@ -17,10 +16,9 @@ use revm::{ Cfg, }, database_interface::EmptyDB, - handler::EthHandler, primitives::{keccak256, Bytes, TxKind, B256}, specification::{eip4844::TARGET_BLOB_GAS_PER_BLOCK_CANCUN, hardfork::SpecId}, - Context, DatabaseCommit, EvmCommit, MainEvm, + transact_main_commit, Context, }; use serde_json::json; use statetest_types::{SpecName, Test, TestSuite}; @@ -361,7 +359,6 @@ pub fn execute_test_suite( } for (index, test) in tests.into_iter().enumerate() { - // TODO : TX TYPE needs to be set let Some(tx_type) = unit.transaction.tx_type(test.indexes.data) else { if test.expect_exception.is_some() { continue; @@ -416,35 +413,29 @@ pub fn execute_test_suite( .with_cached_prestate(cache) .with_bundle_update() .build(); - let mut evm = MainEvm::new( - Context::builder() - .with_block(&block) - .with_tx(&tx) - .with_cfg(&cfg) - .with_db(&mut state), - EthHandler::default(), - ); + let mut ctx = Context::builder() + .with_block(&block) + .with_tx(&tx) + .with_cfg(&cfg) + .with_db(&mut state); // Do the deed let (e, exec_result) = if trace { - let mut evm = InspectorMainEvm::new( - InspectorContext::new( - Context::builder() - .with_block(&block) - .with_tx(&tx) - .with_cfg(&cfg) - .with_db(&mut state), - TracerEip3155::new(Box::new(stderr())).without_summary(), - ), - inspector_handler(), + let mut ctx = &mut InspectorContext::new( + Context::builder() + .with_block(&block) + .with_tx(&tx) + .with_cfg(&cfg) + .with_db(&mut state), + TracerEip3155::new(Box::new(stderr())).without_summary(), ); let timer = Instant::now(); - let res = evm.exec_commit(); + let res = inspect_main_commit(&mut ctx); *elapsed.lock().unwrap() += timer.elapsed(); let spec = cfg.spec(); - let db = evm.context.inner.journaled_state.database; + let db = &mut ctx.inner.journaled_state.database; // Dump state and traces if test failed let output = check_evm_execution( &test, @@ -461,11 +452,11 @@ pub fn execute_test_suite( (e, res) } else { let timer = Instant::now(); - let res = evm.exec_commit(); + let res = transact_main_commit(&mut ctx); *elapsed.lock().unwrap() += timer.elapsed(); let spec = cfg.spec(); - let db = evm.context.journaled_state.database; + let db = ctx.journaled_state.database; // Dump state and traces if test failed let output = check_evm_execution( &test, @@ -503,30 +494,23 @@ pub fn execute_test_suite( println!("\nTraces:"); - let mut evm = InspectorMainEvm::new( - InspectorContext::new( - Context::builder() - .with_db(&mut state) - .with_block(&block) - .with_tx(&tx) - .with_cfg(&cfg), - TracerEip3155::new(Box::new(stderr())).without_summary(), - ), - inspector_handler(), + let mut ctx = InspectorContext::new( + Context::builder() + .with_db(&mut state) + .with_block(&block) + .with_tx(&tx) + .with_cfg(&cfg), + TracerEip3155::new(Box::new(stderr())).without_summary(), ); - let res = evm.transact(); - let _ = res.map(|r| { - evm.context.inner.journaled_state.database.commit(r.state); - r.result - }); + let _ = inspect_main_commit(&mut ctx); println!("\nExecution result: {exec_result:#?}"); println!("\nExpected exception: {:?}", test.expect_exception); println!("\nState before: {cache_state:#?}"); println!( "\nState after: {:#?}", - evm.context.inner.journaled_state.database.cache + ctx.inner.journaled_state.database.cache ); println!("\nSpecification: {:?}", cfg.spec); println!("\nTx: {tx:#?}"); diff --git a/crates/bytecode/src/opcode.rs b/crates/bytecode/src/opcode.rs index e33f3eb349..47d7abe0e6 100644 --- a/crates/bytecode/src/opcode.rs +++ b/crates/bytecode/src/opcode.rs @@ -784,7 +784,7 @@ mod tests { assert_eq!( opcode.map(|opcode| opcode.terminating).unwrap_or_default(), opcodes[i], - "Opcode {:?} terminating chack failed.", + "Opcode {:?} terminating check failed.", opcode ); } diff --git a/crates/context/interface/src/context.rs b/crates/context/interface/src/context.rs index 3378775607..a704965032 100644 --- a/crates/context/interface/src/context.rs +++ b/crates/context/interface/src/context.rs @@ -1,9 +1,12 @@ +use auto_impl::auto_impl; + /// Some actions on top of context with just Getter traits would require borrowing the context /// with a both mutable and immutable reference. /// /// To allow doing this action more efficiently, we introduce a new trait that does this directly. /// /// Used for loading access list and applying EIP-7702 authorization list. +#[auto_impl(&mut,Box)] pub trait PerformantContextAccess { type Error; diff --git a/crates/context/interface/src/host.rs b/crates/context/interface/src/host.rs index 89dd40eba3..fce7a768f5 100644 --- a/crates/context/interface/src/host.rs +++ b/crates/context/interface/src/host.rs @@ -8,7 +8,6 @@ use auto_impl::auto_impl; use primitives::{Address, Bytes, Log, B256, U256}; /// EVM context host. -// TODO : Move to context-interface #[auto_impl(&mut, Box)] pub trait Host: TransactionGetter + BlockGetter + CfgGetter { /// Load an account code. diff --git a/crates/context/interface/src/journaled_state.rs b/crates/context/interface/src/journaled_state.rs index 6681a3b6a6..9f6734845b 100644 --- a/crates/context/interface/src/journaled_state.rs +++ b/crates/context/interface/src/journaled_state.rs @@ -71,7 +71,6 @@ pub trait Journal { fn touch_account(&mut self, address: Address); - // TODO : Instruction result is not known fn transfer( &mut self, from: &Address, @@ -133,7 +132,7 @@ pub trait Journal { /// Does cleanup and returns modified state. /// /// This resets the [Journal] to its initial state. - fn finalize(&mut self) -> Result::Error>; + fn finalize(&mut self) -> Self::FinalOutput; } /// Transfer and creation result diff --git a/crates/context/interface/src/result.rs b/crates/context/interface/src/result.rs index 29bd64d6d1..c8cd35703b 100644 --- a/crates/context/interface/src/result.rs +++ b/crates/context/interface/src/result.rs @@ -21,6 +21,19 @@ pub struct ResultAndState { pub state: EvmState, } +impl ResultAndState { + /// Maps a `DBError` to a new error type using the provided closure, leaving other variants unchanged. + pub fn map_haltreason(self, op: F) -> ResultAndState + where + F: FnOnce(HaltReasonT) -> OHR, + { + ResultAndState { + result: self.result.map_haltreason(op), + state: self.state, + } + } +} + /// Result of a transaction execution #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -53,6 +66,33 @@ impl ExecutionResult { matches!(self, Self::Success { .. }) } + /// Maps a `DBError` to a new error type using the provided closure, leaving other variants unchanged. + pub fn map_haltreason(self, op: F) -> ExecutionResult + where + F: FnOnce(HaltReasonT) -> OHR, + { + match self { + Self::Success { + reason, + gas_used, + gas_refunded, + logs, + output, + } => ExecutionResult::Success { + reason, + gas_used, + gas_refunded, + logs, + output, + }, + Self::Revert { gas_used, output } => ExecutionResult::Revert { gas_used, output }, + Self::Halt { reason, gas_used } => ExecutionResult::Halt { + reason: op(reason), + gas_used, + }, + } + } + /// Returns created address if execution is Create transaction /// and Contract was created. pub fn created_address(&self) -> Option
{ diff --git a/crates/context/interface/src/transaction.rs b/crates/context/interface/src/transaction.rs index a4790e055f..983ee0126e 100644 --- a/crates/context/interface/src/transaction.rs +++ b/crates/context/interface/src/transaction.rs @@ -95,8 +95,6 @@ pub trait Transaction { /// Total gas for all blobs. Max number of blocks is already checked /// so we dont need to check for overflow. - /// - /// TODO remove this fn total_blob_gas(&self) -> u64 { GAS_PER_BLOB * self.blob_versioned_hashes().len() as u64 } @@ -108,8 +106,6 @@ pub trait Transaction { /// /// See EIP-4844: /// - /// - /// TODO remove it, make a utility trait. fn calc_max_data_fee(&self) -> U256 { let blob_gas = U256::from(self.total_blob_gas()); let max_blob_fee = U256::from(self.max_fee_per_blob_gas()); diff --git a/crates/context/src/journaled_state.rs b/crates/context/src/journaled_state.rs index c8d8451123..6c8ef8e7ef 100644 --- a/crates/context/src/journaled_state.rs +++ b/crates/context/src/journaled_state.rs @@ -150,7 +150,6 @@ impl Journal for JournaledState { to: &Address, balance: U256, ) -> Result, DB::Error> { - // TODO : Handle instruction result self.transfer(from, to, balance) } @@ -217,7 +216,7 @@ impl Journal for JournaledState { self.create_account_checkpoint(caller, address, balance, spec_id) } - fn finalize(&mut self) -> Result::Error> { + fn finalize(&mut self) -> Self::FinalOutput { let Self { state, transient_storage, @@ -237,7 +236,7 @@ impl Journal for JournaledState { let state = mem::take(state); let logs = mem::take(logs); - Ok((state, logs)) + (state, logs) } } diff --git a/crates/database/Cargo.toml b/crates/database/Cargo.toml index cb27f93992..d547c778f0 100644 --- a/crates/database/Cargo.toml +++ b/crates/database/Cargo.toml @@ -25,7 +25,6 @@ all = "warn" state.workspace = true primitives.workspace = true database-interface.workspace = true -context-interface.workspace = true bytecode.workspace = true auto_impl = "1.2" diff --git a/crates/database/src/states/cache.rs b/crates/database/src/states/cache.rs index 2b9f29413c..9105f974cd 100644 --- a/crates/database/src/states/cache.rs +++ b/crates/database/src/states/cache.rs @@ -19,7 +19,6 @@ pub struct CacheState { /// Block state account with account state pub accounts: HashMap, /// Created contracts - // TODO : Add bytecode counter for number of bytecodes added/removed. pub contracts: HashMap, /// Has EIP-161 state clear enabled (Spurious Dragon hardfork) pub has_state_clear: bool, diff --git a/crates/database/src/states/plain_account.rs b/crates/database/src/states/plain_account.rs index 547896e148..0d54a46854 100644 --- a/crates/database/src/states/plain_account.rs +++ b/crates/database/src/states/plain_account.rs @@ -1,7 +1,7 @@ use primitives::{HashMap, U256}; use state::{AccountInfo, EvmStorageSlot}; -// TODO : Rename this to BundleAccount. As for the block level we have original state. +// Plain account of StateDatabase. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct PlainAccount { pub info: AccountInfo, diff --git a/crates/handler/Cargo.toml b/crates/handler/Cargo.toml index 3037d07d0c..3ce214fc13 100644 --- a/crates/handler/Cargo.toml +++ b/crates/handler/Cargo.toml @@ -26,6 +26,7 @@ all = "warn" interpreter.workspace = true precompile.workspace = true context-interface.workspace = true +context.workspace = true primitives.workspace = true state.workspace = true specification.workspace = true diff --git a/crates/handler/interface/Cargo.toml b/crates/handler/interface/Cargo.toml index 5c7c59b98c..af2f083308 100644 --- a/crates/handler/interface/Cargo.toml +++ b/crates/handler/interface/Cargo.toml @@ -24,6 +24,8 @@ all = "warn" [dependencies] # revm primitives.workspace = true +auto_impl.workspace = true +specification.workspace = true [dev-dependencies] database.workspace = true diff --git a/crates/handler/interface/src/execution.rs b/crates/handler/interface/src/execution.rs deleted file mode 100644 index 19e4bd3e54..0000000000 --- a/crates/handler/interface/src/execution.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::util::FrameOrFrameResult; -pub use crate::{Frame, FrameOrResultGen}; -pub use std::{vec, vec::Vec}; - -pub trait ExecutionHandler { - type Context; - type Error; - type Frame: Frame; - type ExecResult; - - /// Execute call. - fn init_first_frame( - &mut self, - context: &mut Self::Context, - gas_limit: u64, - ) -> Result, Self::Error>; - - /// Execute create. - fn last_frame_result( - &self, - context: &mut Self::Context, - frame_result: ::FrameResult, - ) -> Result; - - fn run( - &self, - context: &mut Self::Context, - frame: Self::Frame, - ) -> Result { - let mut frame_stack: Vec<::Frame> = vec![frame]; - loop { - let frame = frame_stack.last_mut().unwrap(); - let call_or_result = frame.run(context)?; - - let mut result = match call_or_result { - FrameOrResultGen::Frame(init) => match frame.init(context, init)? { - FrameOrResultGen::Frame(new_frame) => { - frame_stack.push(new_frame); - continue; - } - // Dont pop the frame as new frame was not created. - FrameOrResultGen::Result(result) => result, - }, - FrameOrResultGen::Result(result) => { - // Pop frame that returned result - frame_stack.pop(); - result - } - }; - - let Some(frame) = frame_stack.last_mut() else { - Self::Frame::final_return(context, &mut result)?; - return self.last_frame_result(context, result); - }; - frame.return_result(context, result)?; - } - } -} diff --git a/crates/handler/interface/src/frame.rs b/crates/handler/interface/src/frame.rs index 6012946dc1..6d20521d69 100644 --- a/crates/handler/interface/src/frame.rs +++ b/crates/handler/interface/src/frame.rs @@ -1,36 +1,42 @@ -use crate::FrameOrResultGen; +use crate::{FrameInitOrResult, FrameOrResult}; /// Call frame trait pub trait Frame: Sized { type Context; type FrameInit; + type FrameContext; type FrameResult; type Error; fn init_first( context: &mut Self::Context, + frame_context: &mut Self::FrameContext, frame_input: Self::FrameInit, - ) -> Result, Self::Error>; + ) -> Result, Self::Error>; fn final_return( context: &mut Self::Context, + frame_context: &mut Self::FrameContext, result: &mut Self::FrameResult, ) -> Result<(), Self::Error>; fn init( &self, context: &mut Self::Context, + frame_context: &mut Self::FrameContext, frame_input: Self::FrameInit, - ) -> Result, Self::Error>; + ) -> Result, Self::Error>; fn run( &mut self, context: &mut Self::Context, - ) -> Result, Self::Error>; + frame_context: &mut Self::FrameContext, + ) -> Result, Self::Error>; fn return_result( &mut self, context: &mut Self::Context, + frame_context: &mut Self::FrameContext, result: Self::FrameResult, ) -> Result<(), Self::Error>; } diff --git a/crates/handler/interface/src/handler.rs b/crates/handler/interface/src/handler.rs deleted file mode 100644 index c33ead7c20..0000000000 --- a/crates/handler/interface/src/handler.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::{ExecutionHandler, PostExecutionHandler, PreExecutionHandler, ValidationHandler}; - -pub trait Handler { - type Validation: ValidationHandler; - type PreExecution: PreExecutionHandler; - type Execution: ExecutionHandler; - type PostExecution: PostExecutionHandler; - - fn validation(&mut self) -> &mut Self::Validation; - fn pre_execution(&mut self) -> &mut Self::PreExecution; - fn execution(&mut self) -> &mut Self::Execution; - fn post_execution(&mut self) -> &mut Self::PostExecution; -} diff --git a/crates/handler/interface/src/item_or_result.rs b/crates/handler/interface/src/item_or_result.rs new file mode 100644 index 0000000000..ed07a470c1 --- /dev/null +++ b/crates/handler/interface/src/item_or_result.rs @@ -0,0 +1,26 @@ +use crate::Frame; + +pub enum ItemOrResult { + Item(ITEM), + Result(RES), +} + +impl ItemOrResult { + pub fn map_frame(self, f: impl FnOnce(ITEM) -> OITEM) -> ItemOrResult { + match self { + ItemOrResult::Item(item) => ItemOrResult::Item(f(item)), + ItemOrResult::Result(result) => ItemOrResult::Result(result), + } + } + + pub fn map_result(self, f: impl FnOnce(RES) -> ORES) -> ItemOrResult { + match self { + ItemOrResult::Item(item) => ItemOrResult::Item(item), + ItemOrResult::Result(result) => ItemOrResult::Result(f(result)), + } + } +} + +pub type FrameOrResult = ItemOrResult::FrameResult>; +pub type FrameInitOrResult = + ItemOrResult<::FrameInit, ::FrameResult>; diff --git a/crates/handler/interface/src/lib.rs b/crates/handler/interface/src/lib.rs index fc133891e5..81ac9cef59 100644 --- a/crates/handler/interface/src/lib.rs +++ b/crates/handler/interface/src/lib.rs @@ -5,20 +5,10 @@ #[cfg(not(feature = "std"))] extern crate alloc as std; -pub mod execution; pub mod frame; -pub mod handler; -pub mod post_execution; -pub mod pre_execution; +pub mod item_or_result; pub mod precompile_provider; -pub mod util; -pub mod validation; -pub use execution::ExecutionHandler; pub use frame::Frame; -pub use handler::Handler; -pub use post_execution::PostExecutionHandler; -pub use pre_execution::PreExecutionHandler; -pub use precompile_provider::PrecompileProvider; -pub use util::FrameOrResultGen; -pub use validation::{InitialAndFloorGas, ValidationHandler}; +pub use item_or_result::{FrameInitOrResult, FrameOrResult, ItemOrResult}; +pub use precompile_provider::{PrecompileProvider, PrecompileProviderGetter}; diff --git a/crates/handler/interface/src/post_execution.rs b/crates/handler/interface/src/post_execution.rs deleted file mode 100644 index 73bd7ec991..0000000000 --- a/crates/handler/interface/src/post_execution.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::InitialAndFloorGas; - -pub trait PostExecutionHandler { - type Context; - type Error; - type ExecResult; - type Output; - - /// Calculate final refund. - fn eip7623_check_gas_floor( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - init_and_floor_gas: InitialAndFloorGas, - ); - - /// Calculate final refund. - fn refund( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - eip7702_refund: i64, - ); - - /// Reimburse the caller with balance it didn't spent. - fn reimburse_caller( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - ) -> Result<(), Self::Error>; - - /// Reward beneficiary with transaction rewards. - fn reward_beneficiary( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - ) -> Result<(), Self::Error>; - - /// Main return handle, takes state from journal and transforms internal result to [`Output`][PostExecutionHandler::Output]. - fn output( - &self, - context: &mut Self::Context, - result: Self::ExecResult, - ) -> Result; - - /// Called when execution ends. - /// - /// End handle in comparison to output handle will be called every time after execution. - /// - /// While [`output`][PostExecutionHandler::output] will be omitted in case of the error. - fn end( - &self, - _context: &mut Self::Context, - end_output: Result, - ) -> Result { - end_output - } - - /// Clean handler. - /// - /// This handle is called every time regardless of the result of the transaction. - fn clear(&self, context: &mut Self::Context); -} diff --git a/crates/handler/interface/src/pre_execution.rs b/crates/handler/interface/src/pre_execution.rs deleted file mode 100644 index 9afe508d99..0000000000 --- a/crates/handler/interface/src/pre_execution.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub trait PreExecutionHandler { - type Context; - type Error; - - fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error>; - - fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result; - - fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error>; -} diff --git a/crates/handler/interface/src/precompile_provider.rs b/crates/handler/interface/src/precompile_provider.rs index b2f8142bd1..e7a15f5885 100644 --- a/crates/handler/interface/src/precompile_provider.rs +++ b/crates/handler/interface/src/precompile_provider.rs @@ -1,12 +1,16 @@ +use auto_impl::auto_impl; use primitives::{Address, Bytes}; +use specification::hardfork::SpecId; +use std::boxed::Box; +#[auto_impl(&mut, Box)] pub trait PrecompileProvider: Clone { type Context; type Output; type Error; + type Spec: Into; - /// Create a new precompile. - fn new(context: &mut Self::Context) -> Self; + fn set_spec(&mut self, spec: Self::Spec); /// Run the precompile. fn run( @@ -18,8 +22,14 @@ pub trait PrecompileProvider: Clone { ) -> Result, Self::Error>; /// Get the warm addresses. - fn warm_addresses(&self) -> impl Iterator; + fn warm_addresses(&self) -> Box + '_>; /// Check if the address is a precompile. fn contains(&self, address: &Address) -> bool; } + +pub trait PrecompileProviderGetter { + type PrecompileProvider: PrecompileProvider; + + fn precompiles(&mut self) -> &mut Self::PrecompileProvider; +} diff --git a/crates/handler/interface/src/util.rs b/crates/handler/interface/src/util.rs deleted file mode 100644 index 560d8341bc..0000000000 --- a/crates/handler/interface/src/util.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::Frame; - -pub enum FrameOrResultGen { - Frame(Frame), - Result(Result), -} - -impl FrameOrResultGen { - pub fn map_frame(self, f: impl FnOnce(F) -> F2) -> FrameOrResultGen { - match self { - FrameOrResultGen::Frame(frame) => FrameOrResultGen::Frame(f(frame)), - FrameOrResultGen::Result(result) => FrameOrResultGen::Result(result), - } - } - - pub fn map_result(self, f: impl FnOnce(R) -> R2) -> FrameOrResultGen { - match self { - FrameOrResultGen::Frame(frame) => FrameOrResultGen::Frame(frame), - FrameOrResultGen::Result(result) => FrameOrResultGen::Result(f(result)), - } - } -} - -pub type FrameOrFrameResult = FrameOrResultGen::FrameResult>; diff --git a/crates/handler/interface/src/validation.rs b/crates/handler/interface/src/validation.rs deleted file mode 100644 index 5f45c15677..0000000000 --- a/crates/handler/interface/src/validation.rs +++ /dev/null @@ -1,26 +0,0 @@ -pub trait ValidationHandler { - type Context; - type Error; - - /// Validate env. - fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error>; - - /// Validate transactions against state. - fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error>; - - /// Validate initial gas. - fn validate_initial_tx_gas( - &self, - context: &Self::Context, - ) -> Result; -} - -/// Init and floor gas from transaction -#[derive(Clone, Copy, Debug, Default)] -pub struct InitialAndFloorGas { - /// Initial gas for transaction. - pub initial_gas: u64, - /// If transaction is a Call and Prague is enabled - /// floor_gas is at least amount of gas that is going to be spent. - pub floor_gas: u64, -} diff --git a/crates/handler/src/execution.rs b/crates/handler/src/execution.rs index 6b3bbd3e52..9218ad025a 100644 --- a/crates/handler/src/execution.rs +++ b/crates/handler/src/execution.rs @@ -1,12 +1,7 @@ -use super::{frame_data::FrameResult, EthFrame, EthPrecompileProvider}; +use super::frame_data::FrameResult; use bytecode::EOF_MAGIC_BYTES; -use context_interface::{ - result::InvalidTransaction, BlockGetter, Cfg, CfgGetter, ErrorGetter, JournalDBError, - JournalGetter, Transaction, TransactionGetter, -}; -use handler_interface::{util::FrameOrFrameResult, ExecutionHandler, Frame as FrameTrait}; +use context_interface::{Transaction, TransactionGetter}; use interpreter::{ - interpreter::{EthInstructionProvider, EthInterpreter}, CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, EOFCreateInputs, EOFCreateKind, FrameInput, Gas, }; @@ -14,204 +9,60 @@ use primitives::TxKind; use specification::hardfork::SpecId; use std::boxed::Box; -#[derive(Default)] -pub struct EthExecution< - CTX, - ERROR, - FRAME = EthFrame< - CTX, - ERROR, - EthInterpreter<()>, - EthPrecompileProvider, - EthInstructionProvider, CTX>, - >, -> { - _phantom: core::marker::PhantomData<(CTX, FRAME, ERROR)>, -} - -impl ExecutionHandler for EthExecution -where - CTX: EthExecutionContext, - ERROR: EthExecutionError, - FRAME: - FrameTrait, -{ - type Context = CTX; - type Error = ERROR; - type Frame = FRAME; - type ExecResult = FrameResult; - - fn init_first_frame( - &mut self, - context: &mut Self::Context, - gas_limit: u64, - ) -> Result, Self::Error> { - // Make new frame action. - let spec = context.cfg().spec().into(); - let tx = context.tx(); - let input = tx.input().clone(); - - let init_frame: FrameInput = match tx.kind() { - TxKind::Call(target_address) => FrameInput::Call(Box::new(CallInputs { - input, - gas_limit, - target_address, - bytecode_address: target_address, - caller: tx.caller(), - value: CallValue::Transfer(tx.value()), - scheme: CallScheme::Call, - is_static: false, - is_eof: false, - return_memory_offset: 0..0, - })), - TxKind::Create => { - // If first byte of data is magic 0xEF00, then it is EOFCreate. - if spec.is_enabled_in(SpecId::OSAKA) && input.starts_with(&EOF_MAGIC_BYTES) { - FrameInput::EOFCreate(Box::new(EOFCreateInputs::new( - tx.caller(), - tx.value(), - gas_limit, - EOFCreateKind::Tx { initdata: input }, - ))) - } else { - FrameInput::Create(Box::new(CreateInputs { - caller: tx.caller(), - scheme: CreateScheme::Create, - value: tx.value(), - init_code: input, - gas_limit, - })) - } +pub fn create_init_frame(tx: &impl Transaction, spec: SpecId, gas_limit: u64) -> FrameInput { + // Make new frame action. + let input = tx.input().clone(); + + match tx.kind() { + TxKind::Call(target_address) => FrameInput::Call(Box::new(CallInputs { + input, + gas_limit, + target_address, + bytecode_address: target_address, + caller: tx.caller(), + value: CallValue::Transfer(tx.value()), + scheme: CallScheme::Call, + is_static: false, + is_eof: false, + return_memory_offset: 0..0, + })), + TxKind::Create => { + // If first byte of data is magic 0xEF00, then it is EOFCreate. + if spec.is_enabled_in(SpecId::OSAKA) && input.starts_with(&EOF_MAGIC_BYTES) { + FrameInput::EOFCreate(Box::new(EOFCreateInputs::new( + tx.caller(), + tx.value(), + gas_limit, + EOFCreateKind::Tx { initdata: input }, + ))) + } else { + FrameInput::Create(Box::new(CreateInputs { + caller: tx.caller(), + scheme: CreateScheme::Create, + value: tx.value(), + init_code: input, + gas_limit, + })) } - }; - FRAME::init_first(context, init_frame) - } - - fn last_frame_result( - &self, - context: &mut Self::Context, - mut frame_result: ::FrameResult, - ) -> Result { - let instruction_result = frame_result.interpreter_result().result; - let gas = frame_result.gas_mut(); - let remaining = gas.remaining(); - let refunded = gas.refunded(); - - // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - *gas = Gas::new_spent(context.tx().gas_limit()); - - if instruction_result.is_ok_or_revert() { - gas.erase_cost(remaining); - } - - if instruction_result.is_ok() { - gas.record_refund(refunded); } - - Ok(frame_result) } } -impl EthExecution { - pub fn new() -> Self { - Self { - _phantom: core::marker::PhantomData, - } - } +/// TODO : Frame result should be a generic trait with needed functions. +pub fn last_frame_result(context: CTX, frame_result: &mut FrameResult) { + let instruction_result = frame_result.interpreter_result().result; + let gas = frame_result.gas_mut(); + let remaining = gas.remaining(); + let refunded = gas.refunded(); - pub fn new_boxed() -> Box { - Box::new(Self::new()) - } -} + // Spend the gas limit. Gas is reimbursed when the tx returns successfully. + *gas = Gas::new_spent(context.tx().gas_limit()); -pub trait EthExecutionContext: - TransactionGetter - + ErrorGetter> - + BlockGetter - + JournalGetter - + CfgGetter -{ -} - -impl< - ERROR, - T: TransactionGetter - + ErrorGetter> - + BlockGetter - + JournalGetter - + CfgGetter, - > EthExecutionContext for T -{ -} - -pub trait EthExecutionError: - From + From> -{ -} + if instruction_result.is_ok_or_revert() { + gas.erase_cost(remaining); + } -impl + From>> - EthExecutionError for T -{ + if instruction_result.is_ok() { + gas.record_refund(refunded); + } } - -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::handler::mainnet::refund; -// use interpreter::InstructionResult; -// use primitives::Bytes; -// use specification::hardfork::CancunSpec; -// use context_interface::{default::EnvWiring, DefaultEthereumWiring}; - -// /// Creates frame result. -// fn call_last_frame_return(instruction_result: InstructionResult, gas: Gas) -> Gas { -// let mut env = Envcontext_interface::::default(); -// env.tx.gas_limit = 100; - -// let mut context = Context::builder(); -// context.evm.inner.env = Box::new(env); -// let mut first_frame = FrameResult::Call(CallOutcome::new( -// InterpreterResult { -// result: instruction_result, -// output: Bytes::new(), -// gas, -// }, -// 0..0, -// )); -// last_frame_return::(&mut context, &mut first_frame).unwrap(); -// refund::(&mut context, first_frame.gas_mut(), 0); -// *first_frame.gas() -// } - -// #[test] -// fn test_consume_gas() { -// let gas = call_last_frame_return(InstructionResult::Stop, Gas::new(90)); -// assert_eq!(gas.remaining(), 90); -// assert_eq!(gas.spent(), 10); -// assert_eq!(gas.refunded(), 0); -// } - -// #[test] -// fn test_consume_gas_with_refund() { -// let mut return_gas = Gas::new(90); -// return_gas.record_refund(30); - -// let gas = call_last_frame_return(InstructionResult::Stop, return_gas); -// assert_eq!(gas.remaining(), 90); -// assert_eq!(gas.spent(), 10); -// assert_eq!(gas.refunded(), 2); - -// let gas = call_last_frame_return(InstructionResult::Revert, return_gas); -// assert_eq!(gas.remaining(), 90); -// assert_eq!(gas.spent(), 10); -// assert_eq!(gas.refunded(), 0); -// } - -// #[test] -// fn test_revert_gas() { -// let gas = call_last_frame_return(InstructionResult::Revert, Gas::new(90)); -// assert_eq!(gas.remaining(), 90); -// assert_eq!(gas.spent(), 10); -// assert_eq!(gas.refunded(), 0); -// } -// } diff --git a/crates/handler/src/frame.rs b/crates/handler/src/frame.rs index bc82401937..3d15e7ba21 100644 --- a/crates/handler/src/frame.rs +++ b/crates/handler/src/frame.rs @@ -6,10 +6,10 @@ use context_interface::{ TransactionGetter, }; use core::{cell::RefCell, cmp::min}; -use handler_interface::{Frame, FrameOrResultGen, PrecompileProvider}; +use handler_interface::{Frame, ItemOrResult, PrecompileProvider, PrecompileProviderGetter}; use interpreter::{ gas, - interpreter::{EthInterpreter, ExtBytecode, InstructionProvider}, + interpreter::{EthInterpreter, ExtBytecode, InstructionProvider, InstructionProviderGetter}, interpreter_types::{LoopControl, ReturnData, RuntimeFlag}, return_ok, return_revert, CallInputs, CallOutcome, CallValue, CreateInputs, CreateOutcome, CreateScheme, EOFCreateInputs, EOFCreateKind, FrameInput, Gas, Host, InputsImpl, @@ -26,24 +26,20 @@ use state::Bytecode; use std::borrow::ToOwned; use std::{rc::Rc, sync::Arc}; -pub struct EthFrame { - _phantom: core::marker::PhantomData (CTX, ERROR)>, +pub struct EthFrame { + _phantom: core::marker::PhantomData<(FRAMECTX, CTX, ERROR)>, data: FrameData, - // TODO : Include this + /// Depth of the call frame. depth: usize, /// Journal checkpoint. pub checkpoint: JournalCheckpoint, /// Interpreter. pub interpreter: Interpreter, - /// Precompiles provider. - pub precompiles: PRECOMPILE, - /// Instruction provider. - pub instructions: INSTRUCTIONS, // This is worth making as a generic type FrameSharedContext. pub memory: Rc>, } -impl EthFrame +impl EthFrame where CTX: JournalGetter, IW: InterpreterTypes, @@ -53,8 +49,6 @@ where depth: usize, interpreter: Interpreter, checkpoint: JournalCheckpoint, - precompiles: PRECOMP, - instructions: INST, memory: Rc>, ) -> Self { Self { @@ -63,34 +57,38 @@ where depth, interpreter, checkpoint, - precompiles, - instructions, memory, } } } -impl - EthFrame, PRECOMPILE, INSTRUCTION> +impl EthFrame, FRAMECTX> where CTX: EthFrameContext, - ERROR: EthFrameError, - PRECOMPILE: PrecompileProvider, + ERROR: From> + From, + FRAMECTX: PrecompileProviderGetter< + PrecompileProvider: PrecompileProvider< + Context = CTX, + Error = ERROR, + Output = InterpreterResult, + >, + > + InstructionProviderGetter< + InstructionProvider: InstructionProvider, Host = CTX>, + >, { /// Make call frame #[inline] pub fn make_call_frame( context: &mut CTX, + frame_context: &mut FRAMECTX, depth: usize, memory: Rc>, inputs: &CallInputs, - mut precompile: PRECOMPILE, - instructions: INSTRUCTION, - ) -> Result, ERROR> { + ) -> Result, ERROR> { let gas = Gas::new(inputs.gas_limit); let return_result = |instruction_result: InstructionResult| { - Ok(FrameOrResultGen::Result(FrameResult::Call(CallOutcome { + Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome { result: InterpreterResult { result: instruction_result, gas, @@ -128,7 +126,7 @@ where } let is_ext_delegate_call = inputs.scheme.is_ext_delegate_call(); if !is_ext_delegate_call { - if let Some(result) = precompile.run( + if let Some(result) = frame_context.precompiles().run( context, &inputs.bytecode_address, &inputs.input, @@ -139,7 +137,7 @@ where } else { context.journal().checkpoint_revert(checkpoint); } - return Ok(FrameOrResultGen::Result(FrameResult::Call(CallOutcome { + return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome { result, memory_offset: inputs.return_memory_offset.clone(), }))); @@ -180,7 +178,7 @@ where call_value: inputs.value.get(), }; - Ok(FrameOrResultGen::Frame(Self::new( + Ok(ItemOrResult::Item(Self::new( FrameData::Call(CallFrame { return_memory_range: inputs.return_memory_offset.clone(), }), @@ -195,8 +193,6 @@ where inputs.gas_limit, ), checkpoint, - precompile, - instructions, memory, ))) } @@ -208,21 +204,17 @@ where depth: usize, memory: Rc>, inputs: &CreateInputs, - precompile: PRECOMPILE, - instructions: INSTRUCTION, - ) -> Result, ERROR> { + ) -> Result, ERROR> { let spec = context.cfg().spec().into(); let return_error = |e| { - Ok(FrameOrResultGen::Result(FrameResult::Create( - CreateOutcome { - result: InterpreterResult { - result: e, - gas: Gas::new(inputs.gas_limit), - output: Bytes::new(), - }, - address: None, + Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome { + result: InterpreterResult { + result: e, + gas: Gas::new(inputs.gas_limit), + output: Bytes::new(), }, - ))) + address: None, + }))) }; // Check depth @@ -255,13 +247,12 @@ where } // Create address - // TODO : Incorporating code hash inside interpreter. It was a request by foundry. - let mut _init_code_hash = B256::ZERO; + let mut init_code_hash = B256::ZERO; let created_address = match inputs.scheme { CreateScheme::Create => inputs.caller.create(old_nonce), CreateScheme::Create2 { salt } => { - _init_code_hash = keccak256(&inputs.init_code); - inputs.caller.create2(salt.to_be_bytes(), _init_code_hash) + init_code_hash = keccak256(&inputs.init_code); + inputs.caller.create2(salt.to_be_bytes(), init_code_hash) } }; @@ -279,7 +270,10 @@ where Err(e) => return return_error(e.into()), }; - let bytecode = ExtBytecode::new(Bytecode::new_legacy(inputs.init_code.clone())); + let bytecode = ExtBytecode::new_with_hash( + Bytecode::new_legacy(inputs.init_code.clone()), + init_code_hash, + ); let interpreter_input = InputsImpl { target_address: created_address, @@ -288,7 +282,7 @@ where call_value: inputs.value, }; - Ok(FrameOrResultGen::Frame(Self::new( + Ok(ItemOrResult::Item(Self::new( FrameData::Create(CreateFrame { created_address }), depth, Interpreter::new( @@ -301,8 +295,6 @@ where inputs.gas_limit, ), checkpoint, - precompile, - instructions, memory, ))) } @@ -314,12 +306,10 @@ where depth: usize, memory: Rc>, inputs: &EOFCreateInputs, - precompile: PRECOMPILE, - instructions: INSTRUCTION, - ) -> Result, ERROR> { + ) -> Result, ERROR> { let spec = context.cfg().spec().into(); let return_error = |e| { - Ok(FrameOrResultGen::Result(FrameResult::EOFCreate( + Ok(ItemOrResult::Result(FrameResult::EOFCreate( CreateOutcome { result: InterpreterResult { result: e, @@ -405,7 +395,7 @@ where call_value: inputs.value, }; - Ok(FrameOrResultGen::Frame(Self::new( + Ok(ItemOrResult::Item(Self::new( FrameData::EOFCreate(EOFCreateFrame { created_address }), depth, Interpreter::new( @@ -418,8 +408,6 @@ where inputs.gas_limit, ), checkpoint, - precompile, - instructions, memory, ))) } @@ -428,61 +416,96 @@ where depth: usize, frame_init: FrameInput, memory: Rc>, - precompile: PRECOMPILE, - instructions: INSTRUCTION, context: &mut CTX, - ) -> Result, ERROR> { + frame_context: &mut FRAMECTX, + ) -> Result, ERROR> { match frame_init { FrameInput::Call(inputs) => { - Self::make_call_frame(context, depth, memory, &inputs, precompile, instructions) + Self::make_call_frame(context, frame_context, depth, memory, &inputs) } - FrameInput::Create(inputs) => { - Self::make_create_frame(context, depth, memory, &inputs, precompile, instructions) + FrameInput::Create(inputs) => Self::make_create_frame(context, depth, memory, &inputs), + FrameInput::EOFCreate(inputs) => { + Self::make_eofcreate_frame(context, depth, memory, &inputs) } - FrameInput::EOFCreate(inputs) => Self::make_eofcreate_frame( - context, - depth, - memory, - &inputs, - precompile, - instructions, - ), } } } -impl Frame - for EthFrame, PRECOMPILE, INSTRUCTION> +pub struct FrameContext { + pub precompiles: PRECOMPILE, + pub instructions: INSTRUCTION, +} + +impl + FrameContext +{ + pub fn new(precompiles: PRECOMPILE, instructions: INSTRUCTION) -> Self { + Self { + precompiles, + instructions, + } + } +} + +impl PrecompileProviderGetter + for FrameContext +{ + type PrecompileProvider = PRECOMPILES; + + fn precompiles(&mut self) -> &mut Self::PrecompileProvider { + &mut self.precompiles + } +} + +impl InstructionProviderGetter + for FrameContext +{ + type InstructionProvider = INSTRUCTIONS; + + fn instructions(&mut self) -> &mut Self::InstructionProvider { + &mut self.instructions + } +} + +impl Frame for EthFrame, FRAMECTX> where CTX: EthFrameContext, - ERROR: EthFrameError, - PRECOMPILE: PrecompileProvider, - INSTRUCTION: InstructionProvider, Host = CTX>, + ERROR: From> + From, + FRAMECTX: PrecompileProviderGetter< + PrecompileProvider: PrecompileProvider< + Context = CTX, + Error = ERROR, + Output = InterpreterResult, + >, + > + InstructionProviderGetter< + InstructionProvider: InstructionProvider, Host = CTX>, + >, { type Context = CTX; type Error = ERROR; type FrameInit = FrameInput; + type FrameContext = FRAMECTX; type FrameResult = FrameResult; fn init_first( context: &mut Self::Context, + frame_context: &mut Self::FrameContext, frame_input: Self::FrameInit, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { let memory = Rc::new(RefCell::new(SharedMemory::new())); - let precompiles = PRECOMPILE::new(context); - let instructions = INSTRUCTION::new(context); // Load precompiles addresses as warm. - for address in precompiles.warm_addresses() { + for address in frame_context.precompiles().warm_addresses() { context.journal().warm_account(address); } memory.borrow_mut().new_context(); - Self::init_with_context(0, frame_input, memory, precompiles, instructions, context) + Self::init_with_context(0, frame_input, memory, context, frame_context) } fn final_return( _context: &mut Self::Context, + _frame_context: &mut Self::FrameContext, _result: &mut Self::FrameResult, ) -> Result<(), Self::Error> { Ok(()) @@ -491,32 +514,33 @@ where fn init( &self, context: &mut CTX, + frame_context: &mut Self::FrameContext, frame_init: Self::FrameInit, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { self.memory.borrow_mut().new_context(); Self::init_with_context( self.depth + 1, frame_init, self.memory.clone(), - self.precompiles.clone(), - self.instructions.clone(), context, + frame_context, ) } fn run( &mut self, context: &mut Self::Context, - ) -> Result, Self::Error> { + frame_context: &mut Self::FrameContext, + ) -> Result, Self::Error> { let spec = context.cfg().spec().into(); // Run interpreter - let next_action = self.interpreter.run(self.instructions.table(), context); + let next_action = self + .interpreter + .run(frame_context.instructions().table(), context); let mut interpreter_result = match next_action { - InterpreterAction::NewFrame(new_frame) => { - return Ok(FrameOrResultGen::Frame(new_frame)) - } + InterpreterAction::NewFrame(new_frame) => return Ok(ItemOrResult::Item(new_frame)), InterpreterAction::Return { result } => result, InterpreterAction::None => unreachable!("InterpreterAction::None is not expected"), }; @@ -531,7 +555,7 @@ where } else { context.journal().checkpoint_revert(self.checkpoint); } - FrameOrResultGen::Result(FrameResult::Call(CallOutcome::new( + ItemOrResult::Result(FrameResult::Call(CallOutcome::new( interpreter_result, frame.return_memory_range.clone(), ))) @@ -547,7 +571,7 @@ where spec, ); - FrameOrResultGen::Result(FrameResult::Create(CreateOutcome::new( + ItemOrResult::Result(FrameResult::Create(CreateOutcome::new( interpreter_result, Some(frame.created_address), ))) @@ -562,7 +586,7 @@ where max_code_size, ); - FrameOrResultGen::Result(FrameResult::EOFCreate(CreateOutcome::new( + ItemOrResult::Result(FrameResult::EOFCreate(CreateOutcome::new( interpreter_result, Some(frame.created_address), ))) @@ -575,6 +599,7 @@ where fn return_result( &mut self, context: &mut Self::Context, + _frame_context: &mut Self::FrameContext, result: Self::FrameResult, ) -> Result<(), Self::Error> { self.memory.borrow_mut().free_context(); @@ -816,13 +841,3 @@ impl< > EthFrameContext for CTX { } - -pub trait EthFrameError: - From> + From -{ -} - -impl> + From> EthFrameError - for T -{ -} diff --git a/crates/handler/src/frame_data.rs b/crates/handler/src/frame_data.rs index 3e0f193bf8..3b36cbf7e4 100644 --- a/crates/handler/src/frame_data.rs +++ b/crates/handler/src/frame_data.rs @@ -4,24 +4,24 @@ use interpreter::{CallOutcome, CreateOutcome, Gas, InstructionResult, Interprete use primitives::Address; /// Call Frame -//#[derive(Debug)] -//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CallFrame { /// Call frame has return memory range where output will be stored. pub return_memory_range: Range, } /// Create Frame -//#[derive(Debug)] -//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CreateFrame { /// Create frame has a created address. pub created_address: Address, } /// Eof Create Frame -//#[derive(Debug)] -//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct EOFCreateFrame { pub created_address: Address, } @@ -29,8 +29,8 @@ pub struct EOFCreateFrame { /// Frame Data /// /// [`FrameData`] bundles different types of frames. -//#[derive(Debug)] -//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum FrameData { Call(CallFrame), Create(CreateFrame), diff --git a/crates/handler/src/handler.rs b/crates/handler/src/handler.rs new file mode 100644 index 0000000000..fa8103c9b6 --- /dev/null +++ b/crates/handler/src/handler.rs @@ -0,0 +1,313 @@ +pub mod types; + +pub use types::{EthContext, EthError, EthHandlerImpl}; + +use crate::{execution, post_execution, pre_execution, validation, FrameContext, FrameResult}; +use context_interface::{ + result::{HaltReasonTrait, ResultAndState}, + Cfg, CfgGetter, ErrorGetter, Journal, JournalGetter, Transaction, TransactionGetter, +}; +use handler_interface::{ + Frame, FrameInitOrResult, FrameOrResult, ItemOrResult, PrecompileProvider, +}; +use interpreter::{interpreter::InstructionProvider, FrameInput, InitialAndFloorGas}; +use std::{vec, vec::Vec}; + +pub trait EthHandler { + type Context: EthContext; + type Error: EthError; + type Precompiles: PrecompileProvider; + type Instructions: InstructionProvider; + // TODO `FrameResult` should be a generic trait. + // TODO `FrameInit` should be a generic. + type Frame: Frame< + Context = Self::Context, + Error = Self::Error, + FrameResult = FrameResult, + FrameInit = FrameInput, + FrameContext = FrameContext, + >; + // TODO `HaltReason` should be a ExecResult trait, returned by the handler. + type HaltReason: HaltReasonTrait; + + fn run( + &mut self, + context: &mut Self::Context, + ) -> Result, Self::Error> { + let init_and_floor_gas = self.validate(context)?; + let eip7702_refund = self.pre_execution(context)? as i64; + let exec_result = self.execution(context, &init_and_floor_gas)?; + self.post_execution(context, exec_result, init_and_floor_gas, eip7702_refund) + } + + fn frame_context( + &mut self, + context: &mut Self::Context, + ) -> ::FrameContext; + + /// Call all validation functions + fn validate(&self, context: &mut Self::Context) -> Result { + self.validate_env(context)?; + self.validate_tx_against_state(context)?; + self.validate_initial_tx_gas(context) + } + + /// Call all Pre execution functions. + fn pre_execution(&self, context: &mut Self::Context) -> Result { + self.load_accounts(context)?; + self.deduct_caller(context)?; + let gas = self.apply_eip7702_auth_list(context)?; + Ok(gas) + } + + fn execution( + &mut self, + context: &mut Self::Context, + init_and_floor_gas: &InitialAndFloorGas, + ) -> Result { + let gas_limit = context.tx().gas_limit() - init_and_floor_gas.initial_gas; + + // Make a context! + let mut frame_context = self.frame_context(context); + // Create first frame action + let first_frame = self.create_first_frame(context, &mut frame_context, gas_limit)?; + let mut frame_result = match first_frame { + ItemOrResult::Item(frame) => self.run_exec_loop(context, &mut frame_context, frame)?, + ItemOrResult::Result(result) => result, + }; + + self.last_frame_result(context, &mut frame_context, &mut frame_result)?; + Ok(frame_result) + } + + fn post_execution( + &self, + context: &mut Self::Context, + mut exec_result: FrameResult, + init_and_floor_gas: InitialAndFloorGas, + eip7702_gas_refund: i64, + ) -> Result, Self::Error> { + // Calculate final refund and add EIP-7702 refund to gas. + self.refund(context, &mut exec_result, eip7702_gas_refund); + // Check if gas floor is met and spent at least a floor gas. + self.eip7623_check_gas_floor(context, &mut exec_result, init_and_floor_gas); + // Reimburse the caller + self.reimburse_caller(context, &mut exec_result)?; + // Reward beneficiary + self.reward_beneficiary(context, &mut exec_result)?; + // Returns output of transaction. + self.output(context, exec_result) + } + + /* VALIDATION */ + + /// Validate env. + fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { + validation::validate_env(context) + } + + /// Validate transactions against state. + fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + validation::validate_tx_against_state(context) + } + + /// Validate initial gas. + fn validate_initial_tx_gas( + &self, + context: &Self::Context, + ) -> Result { + validation::validate_initial_tx_gas(context.tx(), context.cfg().spec().into()) + .map_err(From::from) + } + + /* PRE EXECUTION */ + + fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + pre_execution::load_accounts(context) + } + + fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result { + pre_execution::apply_eip7702_auth_list(context) + } + + fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + pre_execution::deduct_caller(context).map_err(From::from) + } + + /* EXECUTION */ + fn create_first_frame( + &mut self, + context: &mut Self::Context, + frame_context: &mut ::FrameContext, + gas_limit: u64, + ) -> Result, Self::Error> { + let init_frame = + execution::create_init_frame(context.tx(), context.cfg().spec().into(), gas_limit); + self.frame_init_first(context, frame_context, init_frame) + } + + fn last_frame_result( + &self, + context: &mut Self::Context, + _frame_context: &mut ::FrameContext, + frame_result: &mut ::FrameResult, + ) -> Result<(), Self::Error> { + execution::last_frame_result(context, frame_result); + Ok(()) + } + + /* FRAMES */ + + fn frame_init_first( + &mut self, + context: &mut Self::Context, + frame_context: &mut ::FrameContext, + frame_input: ::FrameInit, + ) -> Result, Self::Error> { + Self::Frame::init_first(context, frame_context, frame_input) + } + + fn frame_init( + &self, + frame: &Self::Frame, + context: &mut Self::Context, + frame_context: &mut ::FrameContext, + frame_input: ::FrameInit, + ) -> Result, Self::Error> { + Frame::init(frame, context, frame_context, frame_input) + } + + fn frame_call( + &mut self, + frame: &mut Self::Frame, + context: &mut Self::Context, + frame_context: &mut ::FrameContext, + ) -> Result, Self::Error> { + Frame::run(frame, context, frame_context) + } + + fn frame_return_result( + &mut self, + frame: &mut Self::Frame, + context: &mut Self::Context, + frame_context: &mut ::FrameContext, + result: ::FrameResult, + ) -> Result<(), Self::Error> { + Self::Frame::return_result(frame, context, frame_context, result) + } + + fn frame_final_return( + context: &mut Self::Context, + frame_context: &mut ::FrameContext, + result: &mut ::FrameResult, + ) -> Result<(), Self::Error> { + Self::Frame::final_return(context, frame_context, result)?; + Ok(()) + } + + fn run_exec_loop( + &self, + context: &mut Self::Context, + frame_context: &mut ::FrameContext, + frame: Self::Frame, + ) -> Result { + let mut frame_stack: Vec = vec![frame]; + loop { + let frame = frame_stack.last_mut().unwrap(); + let call_or_result = frame.run(context, frame_context)?; + + let mut result = match call_or_result { + ItemOrResult::Item(init) => match frame.init(context, frame_context, init)? { + ItemOrResult::Item(new_frame) => { + frame_stack.push(new_frame); + continue; + } + // Dont pop the frame as new frame was not created. + ItemOrResult::Result(result) => result, + }, + ItemOrResult::Result(result) => { + // Pop frame that returned result + frame_stack.pop(); + result + } + }; + + let Some(frame) = frame_stack.last_mut() else { + Self::Frame::final_return(context, frame_context, &mut result)?; + return Ok(result); + }; + frame.return_result(context, frame_context, result)?; + } + } + + /* POST EXECUTION */ + + /// Calculate final refund. + fn eip7623_check_gas_floor( + &self, + _context: &mut Self::Context, + exec_result: &mut ::FrameResult, + init_and_floor_gas: InitialAndFloorGas, + ) { + post_execution::eip7623_check_gas_floor(exec_result.gas_mut(), init_and_floor_gas) + } + + /// Calculate final refund. + fn refund( + &self, + context: &mut Self::Context, + exec_result: &mut ::FrameResult, + eip7702_refund: i64, + ) { + let spec = context.cfg().spec().into(); + post_execution::refund(spec, exec_result.gas_mut(), eip7702_refund) + } + + /// Reimburse the caller with balance it didn't spent. + fn reimburse_caller( + &self, + context: &mut Self::Context, + exec_result: &mut ::FrameResult, + ) -> Result<(), Self::Error> { + post_execution::reimburse_caller(context, exec_result.gas_mut()).map_err(From::from) + } + + /// Reward beneficiary with transaction rewards. + fn reward_beneficiary( + &self, + context: &mut Self::Context, + exec_result: &mut ::FrameResult, + ) -> Result<(), Self::Error> { + post_execution::reward_beneficiary(context, exec_result.gas_mut()).map_err(From::from) + } + + /// Main return handle, takes state from journal and transforms internal result to output. + fn output( + &self, + context: &mut Self::Context, + result: ::FrameResult, + ) -> Result, Self::Error> { + context.take_error()?; + Ok(post_execution::output(context, result)) + } + + /// Called when execution ends. + /// + /// End handle in comparison to output handle will be called every time after execution. + /// + /// While output will be omitted in case of the error. + fn end( + &self, + _context: &mut Self::Context, + end_output: Result, Self::Error>, + ) -> Result, Self::Error> { + end_output + } + + /// Clean handler. It resets internal Journal state to default one. + /// + /// This handle is called every time regardless of the result of the transaction. + fn clear(&self, context: &mut Self::Context) { + context.journal().clear(); + } +} diff --git a/crates/handler/src/handler/types.rs b/crates/handler/src/handler/types.rs new file mode 100644 index 0000000000..8ea7a68990 --- /dev/null +++ b/crates/handler/src/handler/types.rs @@ -0,0 +1,144 @@ +use context::Context; +use context_interface::{ + result::{HaltReason, InvalidHeader, InvalidTransaction}, + Block, BlockGetter, Cfg, CfgGetter, Database, DatabaseGetter, ErrorGetter, Journal, + JournalDBError, JournalGetter, PerformantContextAccess, Transaction, TransactionGetter, +}; +use handler_interface::{Frame, PrecompileProvider}; +use interpreter::{ + interpreter::{EthInstructionProvider, EthInterpreter, InstructionProvider}, + FrameInput, Host, +}; +use precompile::PrecompileErrors; +use primitives::Log; +use specification::hardfork::SpecId; +use state::EvmState; +use std::vec::Vec; + +use crate::{EthPrecompileProvider, FrameContext, FrameResult}; + +use super::EthHandler; + +pub struct EthHandlerImpl { + pub precompiles: PRECOMPILES, + pub instructions: INSTRUCTIONS, + pub _phantom: core::marker::PhantomData<(CTX, ERROR, FRAME, PRECOMPILES, INSTRUCTIONS)>, +} + +impl + EthHandlerImpl +where + PRECOMPILES: PrecompileProvider, + INSTRUCTIONS: InstructionProvider, +{ + pub fn crete_frame_context(&self) -> FrameContext { + FrameContext { + precompiles: self.precompiles.clone(), + instructions: self.instructions.clone(), + } + } +} + +impl EthHandler + for EthHandlerImpl +where + CTX: EthContext, + ERROR: EthError, + PRECOMPILES: PrecompileProvider< + Context = CTX, + Error = ERROR, + Spec = <::Cfg as Cfg>::Spec, + >, + INSTRUCTIONS: InstructionProvider, + // TODO `FrameResult` should be a generic trait. + // TODO `FrameInit` should be a generic. + FRAME: Frame< + Context = CTX, + Error = ERROR, + FrameResult = FrameResult, + FrameInit = FrameInput, + FrameContext = FrameContext, + >, +{ + type Context = CTX; + type Error = ERROR; + type Frame = FRAME; + type Precompiles = PRECOMPILES; + type Instructions = INSTRUCTIONS; + type HaltReason = HaltReason; + + fn frame_context( + &mut self, + context: &mut Self::Context, + ) -> ::FrameContext { + self.precompiles.set_spec(context.cfg().spec()); + self.crete_frame_context() + } +} + +impl Default + for EthHandlerImpl< + CTX, + ERROR, + FRAME, + EthPrecompileProvider, + EthInstructionProvider, + > +{ + fn default() -> Self { + Self { + precompiles: EthPrecompileProvider::new(SpecId::LATEST), + instructions: EthInstructionProvider::new(), + _phantom: core::marker::PhantomData, + } + } +} + +pub trait EthContext: + TransactionGetter + + BlockGetter + + DatabaseGetter + + CfgGetter + + PerformantContextAccess> + + ErrorGetter> + + JournalGetter)>> + + Host +{ +} + +pub trait EthError: + From + From + From> + From +{ +} + +impl< + CTX: JournalGetter, + T: From + + From + + From> + + From, + > EthError for T +{ +} + +impl< + BLOCK: Block, + TX: Transaction, + CFG: Cfg, + DB: Database, + JOURNAL: Journal)>, + CHAIN, + > EthContext for Context +{ +} + +impl< + BLOCK: Block, + TX: Transaction, + CFG: Cfg, + DB: Database, + JOURNAL: Journal)>, + CHAIN, + > EthContext for &mut Context +{ +} diff --git a/crates/handler/src/lib.rs b/crates/handler/src/lib.rs index f3a261fa73..bb7aae1987 100644 --- a/crates/handler/src/lib.rs +++ b/crates/handler/src/lib.rs @@ -7,132 +7,17 @@ extern crate alloc as std; // Mainnet related handlers. -mod execution; +pub mod execution; mod frame; mod frame_data; -mod post_execution; -mod pre_execution; +pub mod handler; +pub mod post_execution; +pub mod pre_execution; mod precompile_provider; -mod validation; +pub mod validation; // Public exports - -pub use execution::{EthExecution, EthExecutionContext, EthExecutionError}; -pub use frame::{return_create, return_eofcreate, EthFrame, EthFrameContext, EthFrameError}; +pub use frame::{return_create, return_eofcreate, EthFrame, EthFrameContext, FrameContext}; pub use frame_data::{FrameData, FrameResult}; -pub use post_execution::{EthPostExecution, EthPostExecutionContext, EthPostExecutionError}; -pub use pre_execution::{ - apply_eip7702_auth_list, EthPreExecution, EthPreExecutionContext, EthPreExecutionError, -}; -use precompile::PrecompileErrors; +pub use handler::{EthContext, EthError, EthHandler, EthHandlerImpl}; pub use precompile_provider::EthPrecompileProvider; -use primitives::Log; -use state::EvmState; -use std::vec::Vec; -pub use validation::{ - validate_eip4844_tx, validate_initial_tx_gas, validate_priority_fee_tx, - validate_tx_against_account, validate_tx_env, EthValidation, EthValidationContext, - EthValidationError, -}; - -// Imports - -use context_interface::{ - journaled_state::Journal, - result::{HaltReason, InvalidHeader, InvalidTransaction}, -}; -use context_interface::{ - BlockGetter, CfgGetter, ErrorGetter, JournalDBError, JournalGetter, TransactionGetter, -}; -use handler_interface::{ - ExecutionHandler, Handler, PostExecutionHandler, PreExecutionHandler, ValidationHandler, -}; -use interpreter::Host; - -#[derive(Default)] -pub struct EthHandler< - CTX, - ERROR, - VAL = EthValidation, - PREEXEC = EthPreExecution, - EXEC = EthExecution, - POSTEXEC = EthPostExecution, -> { - pub validation: VAL, - pub pre_execution: PREEXEC, - pub execution: EXEC, - pub post_execution: POSTEXEC, - _phantom: core::marker::PhantomData (CTX, ERROR)>, -} - -impl Default for EthHandler { - fn default() -> Self { - Self { - validation: EthValidation::new(), - pre_execution: EthPreExecution::new(), - execution: EthExecution::new(), - post_execution: EthPostExecution::new(), - _phantom: core::marker::PhantomData, - } - } -} - -impl - EthHandler -{ - pub fn new( - validation: VAL, - pre_execution: PREEXEC, - execution: EXEC, - post_execution: POSTEXEC, - ) -> Self { - Self { - validation, - pre_execution, - execution, - post_execution, - _phantom: core::marker::PhantomData, - } - } -} - -impl Handler - for EthHandler -where - CTX: TransactionGetter - + BlockGetter - + JournalGetter - + CfgGetter - + ErrorGetter> - + JournalGetter)>> - + Host, - ERROR: From - + From - + From> - + From, - VAL: ValidationHandler, - PREEXEC: PreExecutionHandler, - EXEC: ExecutionHandler, - POSTEXEC: PostExecutionHandler, -{ - type Validation = VAL; - type PreExecution = PREEXEC; - type Execution = EXEC; - type PostExecution = POSTEXEC; - - fn validation(&mut self) -> &mut Self::Validation { - &mut self.validation - } - - fn pre_execution(&mut self) -> &mut Self::PreExecution { - &mut self.pre_execution - } - - fn execution(&mut self) -> &mut Self::Execution { - &mut self.execution - } - - fn post_execution(&mut self) -> &mut Self::PostExecution { - &mut self.post_execution - } -} diff --git a/crates/handler/src/post_execution.rs b/crates/handler/src/post_execution.rs index 9520a30455..7701d7b90c 100644 --- a/crates/handler/src/post_execution.rs +++ b/crates/handler/src/post_execution.rs @@ -1,214 +1,134 @@ +use super::frame_data::FrameResult; use context_interface::{ journaled_state::Journal, result::{ExecutionResult, HaltReasonTrait, ResultAndState}, - Block, BlockGetter, Cfg, CfgGetter, ErrorGetter, JournalDBError, JournalGetter, Transaction, + Block, BlockGetter, Cfg, CfgGetter, JournalDBError, JournalGetter, Transaction, TransactionGetter, }; -use handler_interface::PostExecutionHandler; -use interpreter::SuccessOrHalt; +use interpreter::{Gas, InitialAndFloorGas, SuccessOrHalt}; use primitives::{Log, U256}; use specification::hardfork::SpecId; use state::EvmState; -use std::{boxed::Box, vec::Vec}; - -use super::frame_data::FrameResult; - -#[derive(Default)] -pub struct EthPostExecution { - pub _phantom: core::marker::PhantomData<(CTX, ERROR, HALTREASON)>, -} - -impl EthPostExecution { - /// Create new instance of post execution handler. - pub fn new() -> Self { - Self { - _phantom: core::marker::PhantomData, - } - } - - /// Create new boxed instance of post execution handler. - /// - /// Boxed instance is useful to erase FORK type. - pub fn new_boxed() -> Box { - Box::new(Self::new()) +use std::vec::Vec; + +pub fn eip7623_check_gas_floor(gas: &mut Gas, init_and_floor_gas: InitialAndFloorGas) { + // EIP-7623: Increase calldata cost + // spend at least a gas_floor amount of gas. + if gas.spent_sub_refunded() < init_and_floor_gas.floor_gas { + gas.set_spent(init_and_floor_gas.floor_gas); + // clear refund + gas.set_refund(0); } } -impl PostExecutionHandler for EthPostExecution -where - CTX: EthPostExecutionContext, - ERROR: EthPostExecutionError, - HALTREASON: HaltReasonTrait, -{ - type Context = CTX; - type Error = ERROR; - type ExecResult = FrameResult; - type Output = ResultAndState; - - fn eip7623_check_gas_floor( - &self, - _context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - init_and_floor_gas: handler_interface::InitialAndFloorGas, - ) { - let gas = exec_result.gas_mut(); - // EIP-7623: Increase calldata cost - // spend at least a gas_floor amount of gas. - if gas.spent_sub_refunded() < init_and_floor_gas.floor_gas { - gas.set_spent(init_and_floor_gas.floor_gas); - // clear refund - gas.set_refund(0); - } - } - - fn refund( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - eip7702_refund: i64, - ) { - let gas = exec_result.gas_mut(); - gas.record_refund(eip7702_refund); - - // Calculate gas refund for transaction. - // If spec is set to london, it will decrease the maximum refund amount to 5th part of - // gas spend. (Before london it was 2th part of gas spend) - gas.set_final_refund(context.cfg().spec().into().is_enabled_in(SpecId::LONDON)); - } - - fn reimburse_caller( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - ) -> Result<(), Self::Error> { - let basefee = context.block().basefee() as u128; - let caller = context.tx().caller(); - let effective_gas_price = context.tx().effective_gas_price(basefee); - let gas = exec_result.gas(); +pub fn refund(spec: SpecId, gas: &mut Gas, eip7702_refund: i64) { + gas.record_refund(eip7702_refund); + // Calculate gas refund for transaction. + // If spec is set to london, it will decrease the maximum refund amount to 5th part of + // gas spend. (Before london it was 2th part of gas spend) + gas.set_final_refund(spec.is_enabled_in(SpecId::LONDON)); +} - // Return balance of not spend gas. - let caller_account = context.journal().load_account(caller)?; +pub fn reimburse_caller( + context: &mut CTX, + gas: &mut Gas, +) -> Result<(), JournalDBError> { + let basefee = context.block().basefee() as u128; + let caller = context.tx().caller(); + let effective_gas_price = context.tx().effective_gas_price(basefee); + + // Return balance of not spend gas. + let caller_account = context.journal().load_account(caller)?; + + let reimbursed = + effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128); + caller_account.data.info.balance = caller_account + .data + .info + .balance + .saturating_add(U256::from(reimbursed)); + + Ok(()) +} - let reimbursed = - effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128); - caller_account.data.info.balance = caller_account +pub fn reward_beneficiary( + context: &mut CTX, + gas: &mut Gas, +) -> Result<(), JournalDBError> { + let block = context.block(); + let tx = context.tx(); + let beneficiary = block.beneficiary(); + let basefee = block.basefee() as u128; + let effective_gas_price = tx.effective_gas_price(basefee); + + // Transfer fee to coinbase/beneficiary. + // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. + let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) { + effective_gas_price.saturating_sub(basefee) + } else { + effective_gas_price + }; + + let coinbase_account = context.journal().load_account(beneficiary)?; + + coinbase_account.data.mark_touch(); + coinbase_account.data.info.balance = + coinbase_account .data .info .balance - .saturating_add(U256::from(reimbursed)); + .saturating_add(U256::from( + coinbase_gas_price * (gas.spent() - gas.refunded() as u64) as u128, + )); - Ok(()) - } - - fn reward_beneficiary( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - ) -> Result<(), Self::Error> { - let block = context.block(); - let tx = context.tx(); - let beneficiary = block.beneficiary(); - let basefee = block.basefee() as u128; - let effective_gas_price = tx.effective_gas_price(basefee); - let gas = exec_result.gas(); - - // Transfer fee to coinbase/beneficiary. - // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. - let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) { - effective_gas_price.saturating_sub(basefee) - } else { - effective_gas_price - }; - - let coinbase_account = context.journal().load_account(beneficiary)?; - - coinbase_account.data.mark_touch(); - coinbase_account.data.info.balance = - coinbase_account - .data - .info - .balance - .saturating_add(U256::from( - coinbase_gas_price * (gas.spent() - gas.refunded() as u64) as u128, - )); - - Ok(()) - } - - fn output( - &self, - context: &mut Self::Context, - result: Self::ExecResult, - ) -> Result { - context.take_error()?; - - // Used gas with refund calculated. - let gas_refunded = result.gas().refunded() as u64; - let final_gas_used = result.gas().spent() - gas_refunded; - let output = result.output(); - let instruction_result = result.into_interpreter_result(); - - // Reset journal and return present state. - let (state, logs) = context.journal().finalize()?; - - let result = match SuccessOrHalt::::from(instruction_result.result) { - SuccessOrHalt::Success(reason) => ExecutionResult::Success { - reason, - gas_used: final_gas_used, - gas_refunded, - logs, - output, - }, - SuccessOrHalt::Revert => ExecutionResult::Revert { - gas_used: final_gas_used, - output: output.into_data(), - }, - SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { - reason, - gas_used: final_gas_used, - }, - // Only two internal return flags. - flag @ (SuccessOrHalt::FatalExternalError | SuccessOrHalt::Internal(_)) => { - panic!( - "Encountered unexpected internal return flag: {:?} with instruction result: {:?}", - flag, instruction_result - ) - } - }; - - Ok(ResultAndState { result, state }) - } - - fn clear(&self, context: &mut Self::Context) { - // Clear error and journaled state. - // TODO : Check effects of removal of take_error - // let _ = context.evm.take_error(); - context.journal().clear(); - } + Ok(()) } -/// Trait for post execution context. +/// Calculate last gas spent and transform internal reason to external. /// -// TODO : Generalize FinalOutput. -pub trait EthPostExecutionContext: - TransactionGetter - + ErrorGetter> - + BlockGetter - + JournalGetter)>> - + CfgGetter -{ -} +/// TODO make Journal FinalOutput more generic. +pub fn output< + CTX: JournalGetter)>>, + HALTREASON: HaltReasonTrait, +>( + context: &mut CTX, + // TODO, make this more generic and nice. + // FrameResult should be a generic that returns gas and interpreter result. + result: FrameResult, +) -> ResultAndState { + // Used gas with refund calculated. + let gas_refunded = result.gas().refunded() as u64; + let final_gas_used = result.gas().spent() - gas_refunded; + let output = result.output(); + let instruction_result = result.into_interpreter_result(); + + // Reset journal and return present state. + let (state, logs) = context.journal().finalize(); + + let result = match SuccessOrHalt::::from(instruction_result.result) { + SuccessOrHalt::Success(reason) => ExecutionResult::Success { + reason, + gas_used: final_gas_used, + gas_refunded, + logs, + output, + }, + SuccessOrHalt::Revert => ExecutionResult::Revert { + gas_used: final_gas_used, + output: output.into_data(), + }, + SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { + reason, + gas_used: final_gas_used, + }, + // Only two internal return flags. + flag @ (SuccessOrHalt::FatalExternalError | SuccessOrHalt::Internal(_)) => { + panic!( + "Encountered unexpected internal return flag: {:?} with instruction result: {:?}", + flag, instruction_result + ) + } + }; -impl< - CTX: TransactionGetter - + ErrorGetter> - + BlockGetter - + JournalGetter)>> - + CfgGetter, - > EthPostExecutionContext for CTX -{ + ResultAndState { result, state } } - -pub trait EthPostExecutionError: From> {} - -impl>> EthPostExecutionError for ERROR {} diff --git a/crates/handler/src/pre_execution.rs b/crates/handler/src/pre_execution.rs index 857ddf3dfc..fe6bde7538 100644 --- a/crates/handler/src/pre_execution.rs +++ b/crates/handler/src/pre_execution.rs @@ -10,105 +10,76 @@ use context_interface::{ Block, BlockGetter, Cfg, CfgGetter, Database, DatabaseGetter, JournalDBError, JournalGetter, PerformantContextAccess, TransactionGetter, }; -use handler_interface::PreExecutionHandler; use primitives::{Address, BLOCKHASH_STORAGE_ADDRESS, KECCAK_EMPTY, U256}; use specification::{eip7702, hardfork::SpecId}; -use std::{boxed::Box, vec::Vec}; +use std::vec::Vec; -#[derive(Default)] -pub struct EthPreExecution { - pub _phantom: core::marker::PhantomData<(CTX, ERROR)>, -} - -impl EthPreExecution { - pub fn new() -> Self { - Self { - _phantom: core::marker::PhantomData, - } +pub fn load_accounts< + CTX: PerformantContextAccess + BlockGetter + JournalGetter + CfgGetter, + ERROR: From> + From<::Error>, +>( + context: &mut CTX, +) -> Result<(), ERROR> { + let spec = context.cfg().spec().into(); + // Set journaling state flag. + context.journal().set_spec_id(spec); + + // Load coinbase + // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm + if spec.is_enabled_in(SpecId::SHANGHAI) { + let coinbase = context.block().beneficiary(); + context.journal().warm_account(coinbase); } - pub fn new_boxed() -> Box { - Box::new(Self::new()) + // Load blockhash storage address + // EIP-2935: Serve historical block hashes from state + if spec.is_enabled_in(SpecId::PRAGUE) { + context.journal().warm_account(BLOCKHASH_STORAGE_ADDRESS); } -} -impl PreExecutionHandler for EthPreExecution -where - CTX: EthPreExecutionContext, - ERROR: EthPreExecutionError, -{ - type Context = CTX; - type Error = ERROR; - - fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let spec = context.cfg().spec().into(); - // Set journaling state flag. - context.journal().set_spec_id(spec); - - // Load coinbase - // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if spec.is_enabled_in(SpecId::SHANGHAI) { - let coinbase = context.block().beneficiary(); - context.journal().warm_account(coinbase); - } + // Load access list + context.load_access_list()?; - // Load blockhash storage address - // EIP-2935: Serve historical block hashes from state - if spec.is_enabled_in(SpecId::PRAGUE) { - context.journal().warm_account(BLOCKHASH_STORAGE_ADDRESS); - } - - // Load access list - context.load_access_list()?; + Ok(()) +} - Ok(()) +#[inline] +pub fn deduct_caller( + context: &mut CTX, +) -> Result<(), JournalDBError> { + let basefee = context.block().basefee(); + let blob_price = context.block().blob_gasprice().unwrap_or_default(); + let effective_gas_price = context.tx().effective_gas_price(basefee as u128); + // Subtract gas costs from the caller's account. + // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. + let mut gas_cost = (context.tx().gas_limit() as u128).saturating_mul(effective_gas_price); + + // EIP-4844 + if context.tx().tx_type() == TransactionType::Eip4844 { + let blob_gas = context.tx().total_blob_gas() as u128; + gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); } - fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result { - let spec = context.cfg().spec().into(); - if spec.is_enabled_in(SpecId::PRAGUE) { - apply_eip7702_auth_list::(context) - } else { - Ok(0) - } + let is_call = context.tx().kind().is_call(); + let caller = context.tx().caller(); + + // Load caller's account. + let caller_account = context.journal().load_account(caller)?.data; + // Set new caller account balance. + caller_account.info.balance = caller_account + .info + .balance + .saturating_sub(U256::from(gas_cost)); + + // Bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. + if is_call { + // Nonce is already checked + caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); } - #[inline] - fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let basefee = context.block().basefee(); - let blob_price = context.block().blob_gasprice().unwrap_or_default(); - let effective_gas_price = context.tx().effective_gas_price(basefee as u128); - // Subtract gas costs from the caller's account. - // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = (context.tx().gas_limit() as u128).saturating_mul(effective_gas_price); - - // EIP-4844 - if context.tx().tx_type() == TransactionType::Eip4844 { - let blob_gas = context.tx().total_blob_gas() as u128; - gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); - } - - let is_call = context.tx().kind().is_call(); - let caller = context.tx().caller(); - - // Load caller's account. - let caller_account = context.journal().load_account(caller)?.data; - // Set new caller account balance. - caller_account.info.balance = caller_account - .info - .balance - .saturating_sub(U256::from(gas_cost)); - - // Bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. - if is_call { - // Nonce is already checked - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - } - - // Touch account so we know it is changed. - caller_account.mark_touch(); - Ok(()) - } + // Touch account so we know it is changed. + caller_account.mark_touch(); + Ok(()) } /// Apply EIP-7702 auth list and return number gas refund on already created accounts. @@ -119,12 +90,17 @@ pub fn apply_eip7702_auth_list< >( context: &mut CTX, ) -> Result { - // Return if there is no auth list. + let spec = context.cfg().spec().into(); let tx = context.tx(); + if !spec.is_enabled_in(SpecId::PRAGUE) { + return Ok(0); + } + // Return if there is no auth list. if tx.tx_type() != TransactionType::Eip7702 { return Ok(0); } + #[derive(Debug)] struct Authorization { authority: Option
, address: Address, @@ -225,13 +201,3 @@ impl< > EthPreExecutionContext for CTX { } - -pub trait EthPreExecutionError: - From + From> -{ -} - -impl + From>> - EthPreExecutionError for T -{ -} diff --git a/crates/handler/src/precompile_provider.rs b/crates/handler/src/precompile_provider.rs index 9fcbe764f0..a61ad97927 100644 --- a/crates/handler/src/precompile_provider.rs +++ b/crates/handler/src/precompile_provider.rs @@ -1,9 +1,12 @@ -use context_interface::{Cfg, CfgGetter}; +use context::Cfg; +use context_interface::CfgGetter; use handler_interface::PrecompileProvider; use interpreter::{Gas, InstructionResult, InterpreterResult}; use precompile::PrecompileErrors; use precompile::{PrecompileSpecId, Precompiles}; use primitives::{Address, Bytes}; +use specification::hardfork::SpecId; +use std::boxed::Box; pub struct EthPrecompileProvider { pub precompiles: &'static Precompiles, @@ -19,6 +22,15 @@ impl Clone for EthPrecompileProvider { } } +impl EthPrecompileProvider { + pub fn new(spec: SpecId) -> Self { + Self { + precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)), + _phantom: core::marker::PhantomData, + } + } +} + impl PrecompileProvider for EthPrecompileProvider where CTX: CfgGetter, @@ -27,13 +39,10 @@ where type Context = CTX; type Error = ERROR; type Output = InterpreterResult; + type Spec = <::Cfg as Cfg>::Spec; - fn new(context: &mut Self::Context) -> Self { - let spec = context.cfg().spec().into(); - Self { - precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)), - _phantom: core::marker::PhantomData, - } + fn set_spec(&mut self, spec: Self::Spec) { + self.precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec.into())); } fn run( @@ -72,8 +81,8 @@ where Ok(Some(result)) } - fn warm_addresses(&self) -> impl Iterator { - self.precompiles.addresses().cloned() + fn warm_addresses(&self) -> Box> { + Box::new(self.precompiles.addresses().cloned()) } fn contains(&self, address: &Address) -> bool { diff --git a/crates/handler/src/validation.rs b/crates/handler/src/validation.rs index cd6498f1a7..420528cc3a 100644 --- a/crates/handler/src/validation.rs +++ b/crates/handler/src/validation.rs @@ -5,77 +5,44 @@ use context_interface::{ Block, BlockGetter, Cfg, CfgGetter, JournalDBError, JournalGetter, TransactionGetter, }; use core::cmp::{self, Ordering}; -use handler_interface::{InitialAndFloorGas, ValidationHandler}; -use interpreter::gas::{self}; +use interpreter::gas::{self, InitialAndFloorGas}; use primitives::{B256, U256}; use specification::{eip4844, hardfork::SpecId}; -use state::Account; +use state::AccountInfo; use std::boxed::Box; -pub struct EthValidation { - pub _phantom: core::marker::PhantomData (CTX, ERROR)>, -} - -impl Default for EthValidation { - fn default() -> Self { - Self { - _phantom: core::marker::PhantomData, - } - } -} - -impl EthValidation { - pub fn new() -> Self { - Self { - _phantom: core::marker::PhantomData, - } +pub fn validate_env< + CTX: CfgGetter + BlockGetter + TransactionGetter, + ERROR: From + From, +>( + context: CTX, +) -> Result<(), ERROR> { + let spec = context.cfg().spec().into(); + // `prevrandao` is required for the merge + if spec.is_enabled_in(SpecId::MERGE) && context.block().prevrandao().is_none() { + return Err(InvalidHeader::PrevrandaoNotSet.into()); } - - pub fn new_boxed() -> Box { - Box::new(Self::new()) + // `excess_blob_gas` is required for Cancun + if spec.is_enabled_in(SpecId::CANCUN) && context.block().blob_excess_gas_and_price().is_none() { + return Err(InvalidHeader::ExcessBlobGasNotSet.into()); } + validate_tx_env::(context, spec).map_err(Into::into) } -impl ValidationHandler for EthValidation -where - CTX: EthValidationContext, - ERROR: From + From + From>, -{ - type Context = CTX; - type Error = ERROR; - - fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { - let spec = context.cfg().spec().into(); - // `prevrandao` is required for the merge - if spec.is_enabled_in(SpecId::MERGE) && context.block().prevrandao().is_none() { - return Err(InvalidHeader::PrevrandaoNotSet.into()); - } - // `excess_blob_gas` is required for Cancun - if spec.is_enabled_in(SpecId::CANCUN) - && context.block().blob_excess_gas_and_price().is_none() - { - return Err(InvalidHeader::ExcessBlobGasNotSet.into()); - } - validate_tx_env::<&Self::Context, InvalidTransaction>(context, spec).map_err(Into::into) - } - - fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let tx_caller = context.tx().caller(); +pub fn validate_tx_against_state< + CTX: TransactionGetter + JournalGetter + CfgGetter, + ERROR: From + From>, +>( + mut context: CTX, +) -> Result<(), ERROR> { + let tx_caller = context.tx().caller(); - // Load acc - let account = &mut context.journal().load_account_code(tx_caller)?; - let account = account.data.clone(); + // Load acc + let account = context.journal().load_account_code(tx_caller)?; + let account = account.data.info.clone(); - validate_tx_against_account::(&account, context) - } - - fn validate_initial_tx_gas( - &self, - context: &Self::Context, - ) -> Result { - let spec = context.cfg().spec().into(); - validate_initial_tx_gas(context.tx(), spec).map_err(From::from) - } + validate_tx_against_account::(&account, context)?; + Ok(()) } /// Validate transaction that has EIP-1559 priority fee @@ -139,10 +106,7 @@ pub fn validate_eip4844_tx( pub fn validate_tx_env( context: CTX, spec_id: SpecId, -) -> Result<(), Error> -where - Error: From, -{ +) -> Result<(), InvalidTransaction> { // Check if the transaction's chain id is correct let tx_type = context.tx().tx_type(); let tx = context.tx(); @@ -159,40 +123,40 @@ where // EIP-155: Simple replay attack protection if let Some(chain_id) = tx.chain_id() { if chain_id != context.cfg().chain_id() { - return Err(InvalidTransaction::InvalidChainId.into()); + return Err(InvalidTransaction::InvalidChainId); } } // Gas price must be at least the basefee. if let Some(base_fee) = base_fee { if tx.gas_price() < base_fee { - return Err(InvalidTransaction::GasPriceLessThanBasefee.into()); + return Err(InvalidTransaction::GasPriceLessThanBasefee); } } } TransactionType::Eip2930 => { // Enabled in BERLIN hardfork if !spec_id.is_enabled_in(SpecId::BERLIN) { - return Err(InvalidTransaction::Eip2930NotSupported.into()); + return Err(InvalidTransaction::Eip2930NotSupported); } if Some(context.cfg().chain_id()) != tx.chain_id() { - return Err(InvalidTransaction::InvalidChainId.into()); + return Err(InvalidTransaction::InvalidChainId); } // Gas price must be at least the basefee. if let Some(base_fee) = base_fee { if tx.gas_price() < base_fee { - return Err(InvalidTransaction::GasPriceLessThanBasefee.into()); + return Err(InvalidTransaction::GasPriceLessThanBasefee); } } } TransactionType::Eip1559 => { if !spec_id.is_enabled_in(SpecId::LONDON) { - return Err(InvalidTransaction::Eip1559NotSupported.into()); + return Err(InvalidTransaction::Eip1559NotSupported); } if Some(context.cfg().chain_id()) != tx.chain_id() { - return Err(InvalidTransaction::InvalidChainId.into()); + return Err(InvalidTransaction::InvalidChainId); } validate_priority_fee_tx( @@ -203,11 +167,11 @@ where } TransactionType::Eip4844 => { if !spec_id.is_enabled_in(SpecId::CANCUN) { - return Err(InvalidTransaction::Eip4844NotSupported.into()); + return Err(InvalidTransaction::Eip4844NotSupported); } if Some(context.cfg().chain_id()) != tx.chain_id() { - return Err(InvalidTransaction::InvalidChainId.into()); + return Err(InvalidTransaction::InvalidChainId); } validate_priority_fee_tx( @@ -226,11 +190,11 @@ where TransactionType::Eip7702 => { // Check if EIP-7702 transaction is enabled. if !spec_id.is_enabled_in(SpecId::PRAGUE) { - return Err(InvalidTransaction::Eip7702NotSupported.into()); + return Err(InvalidTransaction::Eip7702NotSupported); } if Some(context.cfg().chain_id()) != tx.chain_id() { - return Err(InvalidTransaction::InvalidChainId.into()); + return Err(InvalidTransaction::InvalidChainId); } validate_priority_fee_tx( @@ -242,7 +206,7 @@ where let auth_list_len = tx.authorization_list_len(); // The transaction is considered invalid if the length of authorization_list is zero. if auth_list_len == 0 { - return Err(InvalidTransaction::EmptyAuthorizationList.into()); + return Err(InvalidTransaction::EmptyAuthorizationList); } } TransactionType::Custom => { @@ -253,14 +217,14 @@ where // Check if gas_limit is more than block_gas_limit if !context.cfg().is_block_gas_limit_disabled() && tx.gas_limit() > context.block().gas_limit() { - return Err(InvalidTransaction::CallerGasLimitMoreThanBlock.into()); + return Err(InvalidTransaction::CallerGasLimitMoreThanBlock); } // EIP-3860: Limit and meter initcode if spec_id.is_enabled_in(SpecId::SHANGHAI) && tx.kind().is_create() { let max_initcode_size = context.cfg().max_code_size().saturating_mul(2); if context.tx().input().len() > max_initcode_size { - return Err(InvalidTransaction::CreateInitCodeSizeLimit.into()); + return Err(InvalidTransaction::CreateInitCodeSizeLimit); } } @@ -269,37 +233,34 @@ where /// Validate account against the transaction. #[inline] -pub fn validate_tx_against_account( - account: &Account, - context: &CTX, -) -> Result<(), ERROR> -where - ERROR: From, -{ +pub fn validate_tx_against_account( + account: &AccountInfo, + context: CTX, +) -> Result<(), InvalidTransaction> { let tx = context.tx(); let tx_type = context.tx().tx_type(); // EIP-3607: Reject transactions from senders with deployed code // This EIP is introduced after london but there was no collision in past // so we can leave it enabled always if !context.cfg().is_eip3607_disabled() { - let bytecode = &account.info.code.as_ref().unwrap(); + let bytecode = &account.code.as_ref().unwrap(); // Allow EOAs whose code is a valid delegation designation, // i.e. 0xef0100 || address, to continue to originate transactions. if !bytecode.is_empty() && !bytecode.is_eip7702() { - return Err(InvalidTransaction::RejectCallerWithCode.into()); + return Err(InvalidTransaction::RejectCallerWithCode); } } // Check that the transaction's nonce is correct if !context.cfg().is_nonce_check_disabled() { let tx = tx.nonce(); - let state = account.info.nonce; + let state = account.nonce; match tx.cmp(&state) { Ordering::Greater => { - return Err(InvalidTransaction::NonceTooHigh { tx, state }.into()); + return Err(InvalidTransaction::NonceTooHigh { tx, state }); } Ordering::Less => { - return Err(InvalidTransaction::NonceTooLow { tx, state }.into()); + return Err(InvalidTransaction::NonceTooLow { tx, state }); } _ => {} } @@ -320,29 +281,25 @@ where // Check if account has enough balance for `gas_limit * max_fee`` and value transfer. // Transfer will be done inside `*_inner` functions. - if balance_check > account.info.balance && !context.cfg().is_balance_check_disabled() { + if balance_check > account.balance && !context.cfg().is_balance_check_disabled() { return Err(InvalidTransaction::LackOfFundForMaxFee { fee: Box::new(balance_check), - balance: Box::new(account.info.balance), - } - .into()); + balance: Box::new(account.balance), + }); } Ok(()) } /// Validate initial transaction gas. -pub fn validate_initial_tx_gas( - tx: TransactionT, - spec_id: SpecId, -) -> Result -where - TransactionT: Transaction, -{ +pub fn validate_initial_tx_gas( + tx: impl Transaction, + spec: SpecId, +) -> Result { let (accounts, storages) = tx.access_list_nums().unwrap_or_default(); let gas = gas::calculate_initial_tx_gas( - spec_id, + spec, tx.input(), tx.kind().is_create(), accounts as u64, @@ -357,7 +314,7 @@ where // EIP-7623: Increase calldata cost // floor gas should be less than gas limit. - if spec_id.is_enabled_in(SpecId::PRAGUE) && gas.floor_gas > tx.gas_limit() { + if spec.is_enabled_in(SpecId::PRAGUE) && gas.floor_gas > tx.gas_limit() { return Err(InvalidTransaction::GasFloorMoreThanGasLimit); }; @@ -365,6 +322,7 @@ where } /// Helper trait that summarizes ValidationHandler requirements from Context. +/// pub trait EthValidationContext: TransactionGetter + BlockGetter + JournalGetter + CfgGetter { diff --git a/crates/inspector/src/inspector.rs b/crates/inspector/src/inspector.rs index 19746ef499..c93a52f699 100644 --- a/crates/inspector/src/inspector.rs +++ b/crates/inspector/src/inspector.rs @@ -1,30 +1,26 @@ use crate::{ - inspector_context::InspectorContext, inspector_instruction::InspectorInstructionProvider, journal::{JournalExt, JournalExtGetter}, }; use auto_impl::auto_impl; use revm::{ context_interface::{ - BlockGetter, CfgGetter, ErrorGetter, Journal, JournalDBError, JournalGetter, - TransactionGetter, + result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, ResultAndState}, + DatabaseGetter, Journal, }, - database_interface::{Database, EmptyDB}, + database_interface::Database, handler::{ - EthExecution, EthFrame, EthHandler, EthPostExecution, EthPreExecution, - EthPrecompileProvider, EthValidation, FrameResult, + handler::{EthContext, EthError, EthHandler as EthHandlerNew, EthHandlerImpl}, + EthFrame, EthPrecompileProvider, FrameContext, FrameResult, }, - handler_interface::{Frame, FrameOrResultGen, PrecompileProvider}, + handler_interface::{Frame, ItemOrResult, PrecompileProvider}, interpreter::{ - interpreter::EthInterpreter, - interpreter_types::{Jumps, LoopControl}, - table::CustomInstruction, - CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, FrameInput, Host, - Instruction, InstructionResult, Interpreter, InterpreterResult, InterpreterTypes, + interpreter::EthInterpreter, CallInputs, CallOutcome, CreateInputs, CreateOutcome, + EOFCreateInputs, FrameInput, Interpreter, InterpreterTypes, }, - precompile::PrecompileErrors, primitives::{Address, Log, U256}, - Context, Error, Evm, + specification::hardfork::SpecId, + Context, DatabaseCommit, }; /// EVM [Interpreter] callbacks. @@ -162,6 +158,7 @@ pub trait GetInspector { fn get_inspector(&mut self) -> &mut impl Inspector; } +#[auto_impl(&mut, Box)] pub trait InspectorCtx { type IT: InterpreterTypes; @@ -191,176 +188,192 @@ impl + JournalExt, } } -#[derive(Clone)] -pub struct InspectorInstruction { - pub instruction: fn(&mut Interpreter, &mut HOST), +pub struct InspectorHandlerImpl { + pub handler: HANDLER, + _phantom: core::marker::PhantomData<(CTX, ERROR, FRAME, PRECOMPILES, INSTRUCTIONS)>, } -impl CustomInstruction for InspectorInstruction -where - HOST: InspectorCtx, +impl + InspectorHandlerImpl { - type Wire = IT; - type Host = HOST; - - fn exec(&self, interpreter: &mut Interpreter, host: &mut Self::Host) { - // SAFETY: As the PC was already incremented we need to subtract 1 to preserve the - // old Inspector behavior. - interpreter.bytecode.relative_jump(-1); - - // Call step. - host.step(interpreter); - if interpreter.control.instruction_result() != InstructionResult::Continue { - return; + pub fn new(handler: HANDLER) -> Self { + Self { + handler, + _phantom: core::marker::PhantomData, } - - // Reset PC to previous value. - interpreter.bytecode.relative_jump(1); - - // Execute instruction. - (self.instruction)(interpreter, host); - - // Call step_end. - host.step_end(interpreter); } +} - fn from_base(instruction: Instruction) -> Self { - Self { instruction } - } +pub trait FrameInterpreterGetter { + type IT: InterpreterTypes; + + fn interpreter(&mut self) -> &mut Interpreter; } -pub struct InspectorEthFrame -where - CTX: Host, +impl FrameInterpreterGetter + for EthFrame { - // TODO : For now, hardcode the InstructionProvider. But in future this should be configurable as generic parameter. - pub eth_frame: EthFrame< - CTX, - ERROR, - EthInterpreter<()>, - PRECOMPILE, - InspectorInstructionProvider, CTX>, - >, + type IT = IW; + + fn interpreter(&mut self) -> &mut Interpreter { + &mut self.interpreter + } } -impl Frame for InspectorEthFrame +impl EthHandlerNew + for InspectorHandlerImpl where - CTX: TransactionGetter - + ErrorGetter> - + BlockGetter - + JournalGetter - + CfgGetter - + JournalExtGetter - + Host - + InspectorCtx, - ERROR: From> + From, - PRECOMPILE: PrecompileProvider, + CTX: EthContext + InspectorCtx + JournalExtGetter, + INTR: InterpreterTypes, + ERROR: EthError, + // TODO `FrameResult` should be a generic trait. + // TODO `FrameInit` should be a generic. + FRAME: Frame< + Context = CTX, + Error = ERROR, + FrameResult = FrameResult, + FrameInit = FrameInput, + FrameContext = FrameContext>, + > + FrameInterpreterGetter, + PRECOMPILES: PrecompileProvider, + HANDLER: EthHandlerNew, { type Context = CTX; type Error = ERROR; - type FrameInit = FrameInput; - type FrameResult = FrameResult; + type Frame = FRAME; + type Precompiles = PRECOMPILES; + type Instructions = InspectorInstructionProvider; + type HaltReason = ::HaltReason; - fn init_first( - context: &mut CTX, - mut frame_input: Self::FrameInit, - ) -> Result, Self::Error> { + fn frame_context( + &mut self, + context: &mut Self::Context, + ) -> ::FrameContext { + FrameContext::new( + self.handler.frame_context(context).precompiles, + InspectorInstructionProvider::new(), + ) + } + + fn frame_init_first( + &mut self, + context: &mut Self::Context, + frame_context: &mut <::Frame as Frame>::FrameContext, + mut frame_input: <::Frame as Frame>::FrameInit, + ) -> Result< + ItemOrResult< + ::Frame, + <::Frame as Frame>::FrameResult, + >, + Self::Error, + > { if let Some(output) = context.frame_start(&mut frame_input) { - return Ok(FrameOrResultGen::Result(output)); + return Ok(ItemOrResult::Result(output)); } - let mut ret = EthFrame::init_first(context, frame_input) - .map(|frame| frame.map_frame(|eth_frame| Self { eth_frame })); + let mut ret = self + .handler + .frame_init_first(context, frame_context, frame_input); match &mut ret { - Ok(FrameOrResultGen::Result(res)) => { + Ok(ItemOrResult::Result(res)) => { context.frame_end(res); } - Ok(FrameOrResultGen::Frame(frame)) => { - context.initialize_interp(&mut frame.eth_frame.interpreter); + Ok(ItemOrResult::Item(frame)) => { + context.initialize_interp(frame.interpreter()); } _ => (), } ret } - fn final_return( - context: &mut Self::Context, - result: &mut Self::FrameResult, - ) -> Result<(), Self::Error> { - context.frame_end(result); - Ok(()) - } - - fn init( + fn frame_init( &self, - context: &mut CTX, - mut frame_input: Self::FrameInit, - ) -> Result, Self::Error> { + frame: &Self::Frame, + context: &mut Self::Context, + frame_context: &mut <::Frame as Frame>::FrameContext, + mut frame_input: <::Frame as Frame>::FrameInit, + ) -> Result< + ItemOrResult< + ::Frame, + <::Frame as Frame>::FrameResult, + >, + Self::Error, + > { if let Some(output) = context.frame_start(&mut frame_input) { - return Ok(FrameOrResultGen::Result(output)); + return Ok(ItemOrResult::Result(output)); } let mut ret = self - .eth_frame - .init(context, frame_input) - .map(|frame| frame.map_frame(|eth_frame| Self { eth_frame })); - - if let Ok(FrameOrResultGen::Frame(frame)) = &mut ret { - context.initialize_interp(&mut frame.eth_frame.interpreter); + .handler + .frame_init(frame, context, frame_context, frame_input); + match &mut ret { + Ok(ItemOrResult::Result(res)) => { + context.frame_end(res); + } + Ok(ItemOrResult::Item(frame)) => { + context.initialize_interp(frame.interpreter()); + } + _ => (), } ret } - fn run( + fn frame_return_result( &mut self, - context: &mut CTX, - ) -> Result, Self::Error> { - self.eth_frame.run(context) + frame: &mut Self::Frame, + context: &mut Self::Context, + frame_context: &mut <::Frame as Frame>::FrameContext, + mut result: <::Frame as Frame>::FrameResult, + ) -> Result<(), Self::Error> { + context.frame_end(&mut result); + self.handler + .frame_return_result(frame, context, frame_context, result) } - fn return_result( - &mut self, - context: &mut CTX, - mut result: Self::FrameResult, + fn frame_final_return( + context: &mut Self::Context, + _frame_context: &mut <::Frame as Frame>::FrameContext, + result: &mut <::Frame as Frame>::FrameResult, ) -> Result<(), Self::Error> { - context.frame_end(&mut result); - self.eth_frame.return_result(context, result) + context.frame_end(result); + Ok(()) } } -pub type InspCtxType = InspectorContext; - -pub type InspectorMainEvm = Evm< - Error, - InspCtxType, - EthHandler< - InspCtxType, - Error, - EthValidation, Error>, - EthPreExecution, Error>, - InspectorEthExecution, Error>, - >, ->; - -/// Function to create Inspector Handler. -pub fn inspector_handler() -> InspectorHandler -{ - EthHandler::new( - EthValidation::new(), - EthPreExecution::new(), - EthExecution::<_, _, InspectorEthFrame<_, _, PRECOMPILE>>::new(), - EthPostExecution::new(), - ) +pub fn inspect_main< + DB: Database, + CTX: EthContext + + JournalExtGetter + + DatabaseGetter + + InspectorCtx, +>( + ctx: &mut CTX, +) -> Result, EVMError<::Error, InvalidTransaction>> { + InspectorHandlerImpl::< + _, + _, + EthFrame<_, _, _, _>, + _, + _, + InspectorInstructionProvider, + >::new(EthHandlerImpl { + precompiles: EthPrecompileProvider::new(SpecId::LATEST), + instructions: InspectorInstructionProvider::new(), + _phantom: core::marker::PhantomData, + }) + .run(ctx) } -/// Composed type for Inspector Execution handler. -pub type InspectorEthExecution> = - EthExecution>; - -/// Composed type for Inspector Handler. -pub type InspectorHandler = EthHandler< - CTX, - ERROR, - EthValidation, - EthPreExecution, - InspectorEthExecution, ->; +pub fn inspect_main_commit< + DB: Database + DatabaseCommit, + CTX: EthContext + + JournalExtGetter + + DatabaseGetter + + InspectorCtx, +>( + ctx: &mut CTX, +) -> Result, EVMError<::Error, InvalidTransaction>> { + inspect_main(ctx).map(|res| { + ctx.db().commit(res.state); + res.result + }) +} diff --git a/crates/inspector/src/inspector_context.rs b/crates/inspector/src/inspector_context.rs index 2bb39169b5..74faa10abc 100644 --- a/crates/inspector/src/inspector_context.rs +++ b/crates/inspector/src/inspector_context.rs @@ -5,7 +5,7 @@ use revm::{ PerformantContextAccess, TransactionGetter, }, database_interface::Database, - handler::FrameResult, + handler::{handler::EthContext, FrameResult}, interpreter::{ interpreter::EthInterpreter, FrameInput, Host, Interpreter, SStoreResult, SelfDestructResult, StateLoad, @@ -27,6 +27,22 @@ where pub frame_input_stack: Vec, } +impl< + INSP: Inspector, + DB: Database, + CTX: EthContext + DatabaseGetter, + > EthContext for InspectorContext +{ +} + +impl< + INSP: Inspector, + DB: Database, + CTX: EthContext + DatabaseGetter, + > EthContext for &mut InspectorContext +{ +} + impl InspectorContext where CTX: BlockGetter @@ -109,7 +125,7 @@ where INSP: GetInspector, CTX: DatabaseGetter, { - type IT = EthInterpreter<()>; + type IT = EthInterpreter; fn step(&mut self, interp: &mut Interpreter) { self.inspector.get_inspector().step(interp, &mut self.inner); diff --git a/crates/inspector/src/inspector_instruction.rs b/crates/inspector/src/inspector_instruction.rs index 5a00ff3171..a979d205ca 100644 --- a/crates/inspector/src/inspector_instruction.rs +++ b/crates/inspector/src/inspector_instruction.rs @@ -5,7 +5,7 @@ use revm::{ interpreter::{ instructions::host::{log, selfdestruct}, interpreter::InstructionProvider, - interpreter_types::LoopControl, + interpreter_types::{Jumps, LoopControl}, table::{self, CustomInstruction}, Host, Instruction, InstructionResult, Interpreter, InterpreterTypes, }, @@ -15,9 +15,47 @@ use std::rc::Rc; use crate::{ journal::{JournalExt, JournalExtGetter}, - InspectorCtx, InspectorInstruction, + InspectorCtx, }; +#[derive(Clone)] +pub struct InspectorInstruction { + pub instruction: fn(&mut Interpreter, &mut HOST), +} + +impl CustomInstruction for InspectorInstruction +where + HOST: InspectorCtx, +{ + type Wire = IT; + type Host = HOST; + + fn exec(&self, interpreter: &mut Interpreter, host: &mut Self::Host) { + // SAFETY: As the PC was already incremented we need to subtract 1 to preserve the + // old Inspector behavior. + interpreter.bytecode.relative_jump(-1); + + // Call step. + host.step(interpreter); + if interpreter.control.instruction_result() != InstructionResult::Continue { + return; + } + + // Reset PC to previous value. + interpreter.bytecode.relative_jump(1); + + // Execute instruction. + (self.instruction)(interpreter, host); + + // Call step_end. + host.step_end(interpreter); + } + + fn from_base(instruction: Instruction) -> Self { + Self { instruction } + } +} + pub struct InspectorInstructionProvider { instruction_table: Rc<[InspectorInstruction; 256]>, } @@ -33,15 +71,12 @@ where } } -impl InstructionProvider for InspectorInstructionProvider +impl InspectorInstructionProvider where WIRE: InterpreterTypes, HOST: Host + JournalExtGetter + JournalGetter + InspectorCtx, { - type WIRE = WIRE; - type Host = HOST; - - fn new(_context: &mut Self::Host) -> Self { + pub fn new() -> Self { let main_table = table::make_instruction_table::(); let mut table: [MaybeUninit>; 256] = unsafe { MaybeUninit::uninit().assume_init() }; @@ -103,7 +138,7 @@ where table[OpCode::SELFDESTRUCT.as_usize()] = InspectorInstruction { instruction: |interp, context| { - selfdestruct::(interp, context); + selfdestruct::(interp, context); if interp.control.instruction_result() == InstructionResult::SelfDestruct { match context.journal_ext().last_journal().last() { Some(JournalEntry::AccountDestroyed { @@ -129,8 +164,27 @@ where instruction_table: Rc::new(table), } } +} + +impl InstructionProvider for InspectorInstructionProvider +where + WIRE: InterpreterTypes, + HOST: Host + JournalExtGetter + JournalGetter + InspectorCtx, +{ + type WIRE = WIRE; + type Host = HOST; fn table(&mut self) -> &[impl CustomInstruction; 256] { self.instruction_table.as_ref() } } + +impl Default for InspectorInstructionProvider +where + WIRE: InterpreterTypes, + HOST: Host + JournalExtGetter + JournalGetter + InspectorCtx, +{ + fn default() -> Self { + Self::new() + } +} diff --git a/crates/interpreter/CHANGELOG.md b/crates/interpreter/CHANGELOG.md index ddcf245bb0..f4cc1df6ea 100644 --- a/crates/interpreter/CHANGELOG.md +++ b/crates/interpreter/CHANGELOG.md @@ -129,7 +129,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - parse opcodes from strings ([#1358](https://github.com/bluealloy/revm/pull/1358)) - *(interpreter)* add helpers for spending all gas ([#1360](https://github.com/bluealloy/revm/pull/1360)) - add helper methods to CallInputs ([#1345](https://github.com/bluealloy/revm/pull/1345)) -- *(revm)* make `FrameOrResult` serializable ([#1282](https://github.com/bluealloy/revm/pull/1282)) +- *(revm)* make `ItemOrResult` serializable ([#1282](https://github.com/bluealloy/revm/pull/1282)) - add flag to force hashbrown usage ([#1284](https://github.com/bluealloy/revm/pull/1284)) - EOF (Ethereum Object Format) ([#1143](https://github.com/bluealloy/revm/pull/1143)) - *(interpreter)* derive Eq for InterpreterAction ([#1262](https://github.com/bluealloy/revm/pull/1262)) diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index 32bd8efd19..efd3dbf2a9 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -27,7 +27,6 @@ bytecode.workspace = true primitives.workspace = true specification.workspace = true context-interface.workspace = true -handler-interface.workspace = true # optional serde = { version = "1.0", default-features = false, features = [ diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index d622625e94..ea72672a48 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -1,7 +1,6 @@ use super::constants::*; use crate::{num_words, tri, SStoreResult, SelfDestructResult, StateLoad}; use context_interface::journaled_state::AccountLoad; -use handler_interface::InitialAndFloorGas; use primitives::U256; use specification::{eip7702, hardfork::SpecId}; @@ -347,6 +346,16 @@ pub const fn memory_gas(num_words: usize) -> u64 { .saturating_add(num_words.saturating_mul(num_words) / 512) } +/// Init and floor gas from transaction +#[derive(Clone, Copy, Debug, Default)] +pub struct InitialAndFloorGas { + /// Initial gas for transaction. + pub initial_gas: u64, + /// If transaction is a Call and Prague is enabled + /// floor_gas is at least amount of gas that is going to be spent. + pub floor_gas: u64, +} + /// Initial gas that is deducted for transaction to be included. /// Initial gas contains initial stipend gas, gas for access list and input data. /// diff --git a/crates/interpreter/src/instructions.rs b/crates/interpreter/src/instructions.rs index baed840373..1e6e850706 100644 --- a/crates/interpreter/src/instructions.rs +++ b/crates/interpreter/src/instructions.rs @@ -226,7 +226,6 @@ mod tests { // use crate::DummyHost; // use bytecode::opcode::*; - // TODO : Define EthEthereumWire // #[test] // fn all_instructions_and_opcodes_used() { // // known unknown instruction we compare it with other instructions from table. diff --git a/crates/interpreter/src/instructions/stack.rs b/crates/interpreter/src/instructions/stack.rs index cd881fdd8a..566dae809f 100644 --- a/crates/interpreter/src/instructions/stack.rs +++ b/crates/interpreter/src/instructions/stack.rs @@ -35,7 +35,6 @@ pub fn push( _host: &mut H, ) { gas!(interpreter, gas::VERYLOW); - // TODO : Check performance degradation. push!(interpreter, U256::ZERO); popn_top!([], top, interpreter); diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 827d8b7589..ca7968c23c 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -92,11 +92,15 @@ pub trait InstructionProvider: Clone { type WIRE: InterpreterTypes; type Host; - fn new(context: &mut Self::Host) -> Self; - fn table(&mut self) -> &[impl CustomInstruction; 256]; } +pub trait InstructionProviderGetter { + type InstructionProvider: InstructionProvider; + + fn instructions(&mut self) -> &mut Self::InstructionProvider; +} + pub struct EthInstructionProvider { instruction_table: Rc<[Instruction; 256]>, } @@ -112,22 +116,27 @@ where } } -impl InstructionProvider for EthInstructionProvider +impl EthInstructionProvider where WIRE: InterpreterTypes, HOST: Host, { - type WIRE = WIRE; - type Host = HOST; - - fn new(_context: &mut Self::Host) -> Self { + pub fn new() -> Self { Self { instruction_table: Rc::new(crate::table::make_instruction_table::()), } } +} - // TODO : Make impl a associate type. With this associate type we can implement. - // InspectorInstructionProvider over generic type. +impl InstructionProvider for EthInstructionProvider +where + WIRE: InterpreterTypes, + HOST: Host, +{ + type WIRE = WIRE; + type Host = HOST; + + /// Returns the instruction table. fn table(&mut self) -> &[impl CustomInstruction; 256] { self.instruction_table.as_ref() } @@ -244,21 +253,15 @@ impl InterpreterResult { } } -// /// Resize the memory to the new size. Returns whether the gas was enough to resize the memory. -// #[inline(never)] -// #[cold] -// #[must_use] -// pub fn resize_memory(memory: &mut SharedMemory, gas: &mut Gas, new_size: usize) -> bool { -// let new_words = num_words(new_size as u64); -// let new_cost = gas::memory_gas(new_words); -// let current_cost = memory.current_expansion_cost(); -// let cost = new_cost - current_cost; -// let success = gas.record_cost(cost); -// if success { -// memory.resize((new_words as usize) * 32); -// } -// success -// } +impl Default for EthInstructionProvider +where + WIRE: InterpreterTypes, + HOST: Host, +{ + fn default() -> Self { + Self::new() + } +} #[cfg(test)] mod tests { diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index db4d1ff731..8d8f4c3a60 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -269,7 +269,7 @@ impl Stack { unsafe { // Note: `ptr::swap_nonoverlapping` is more efficient than `slice::swap` or `ptr::swap` // because it operates under the assumption that the pointers do not overlap, - // eliminating an intemediate copy, + // eliminating an intermediate copy, // which is a condition we know to be true in this context. let top = self.data.as_mut_ptr().add(len - 1); core::ptr::swap_nonoverlapping(top.sub(n), top.sub(n_m_index), 1); diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index 46e0490699..05bec1a828 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -30,7 +30,7 @@ pub use context_interface::{ host::{DummyHost, Host, SStoreResult, SelfDestructResult, StateLoad}, CreateScheme, }; -pub use gas::Gas; +pub use gas::{Gas, InitialAndFloorGas}; pub use instruction_result::*; pub use interpreter::{ num_words, InputsImpl, Interpreter, InterpreterResult, MemoryGetter, SharedMemory, Stack, diff --git a/crates/optimism/Cargo.toml b/crates/optimism/Cargo.toml index 8190befb22..de26584f1b 100644 --- a/crates/optimism/Cargo.toml +++ b/crates/optimism/Cargo.toml @@ -25,7 +25,7 @@ all = "warn" # revm revm.workspace = true precompile = { workspace = true, features = ["secp256r1"] } -inspector.workspace = true +#inspector.workspace = true # static precompile sets. once_cell = { version = "1.19", default-features = false, features = ["alloc"] } diff --git a/crates/optimism/src/evm.rs b/crates/optimism/src/evm.rs deleted file mode 100644 index d1674a7806..0000000000 --- a/crates/optimism/src/evm.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::{ - handler::{ - precompiles::OpPrecompileProvider, OpExecution, OpHandler, OpPreExecution, OpValidation, - }, - L1BlockInfo, OpSpec, OpTransaction, -}; -use inspector::{inspector_context::InspectorContext, InspectorEthFrame}; -use revm::{ - context::{block::BlockEnv, tx::TxEnv, CfgEnv, Context}, - context_interface::result::{EVMError, InvalidTransaction}, - database_interface::Database, - Evm, JournaledState, -}; - -/// Optimism Error -pub type OpError = EVMError<::Error, InvalidTransaction>; - -/// Optimism Context -pub type OpContext = Context, CfgEnv, DB, L1BlockInfo>; - -/// Optimism EVM type -pub type OpEvm = Evm, OpContext, OpHandler, OpError>>; - -pub type InspCtxType = InspectorContext< - INSP, - DB, - Context, DB, JournaledState, L1BlockInfo>, ->; - -pub type InspectorOpEvm = Evm< - OpError, - InspCtxType, - OpHandler< - InspCtxType, - OpError, - OpValidation, OpError>, - OpPreExecution, OpError>, - OpExecution< - InspCtxType, - OpError, - InspectorEthFrame< - InspCtxType, - OpError, - OpPrecompileProvider, OpError>, - >, - >, - >, ->; diff --git a/crates/optimism/src/handler.rs b/crates/optimism/src/handler.rs index 9b5256b43c..dde96adb25 100644 --- a/crates/optimism/src/handler.rs +++ b/crates/optimism/src/handler.rs @@ -10,25 +10,16 @@ use crate::{ }, L1BlockInfoGetter, OpSpec, OpSpecId, OptimismHaltReason, BASE_FEE_RECIPIENT, L1_FEE_RECIPIENT, }; -use precompiles::OpPrecompileProvider; use revm::{ context_interface::{ result::{ExecutionResult, FromStringError, InvalidTransaction, ResultAndState}, - Block, Cfg, CfgGetter, DatabaseGetter, Journal, Transaction, TransactionGetter, - }, - handler::{ - EthExecution, EthExecutionContext, EthExecutionError, EthFrame, EthFrameContext, - EthFrameError, EthHandler, EthPostExecution, EthPostExecutionContext, - EthPostExecutionError, EthPreExecution, EthPreExecutionContext, EthPreExecutionError, - EthValidation, EthValidationContext, EthValidationError, FrameResult, - }, - handler_interface::{ - util::FrameOrFrameResult, ExecutionHandler, Frame, InitialAndFloorGas, - PostExecutionHandler, PreExecutionHandler, ValidationHandler, + Block, Cfg, CfgGetter, Journal, Transaction, TransactionGetter, }, + handler::{EthContext, EthError, EthHandler, EthHandlerImpl, FrameContext, FrameResult}, + handler_interface::{Frame, PrecompileProvider}, interpreter::{ - interpreter::{EthInstructionProvider, EthInterpreter}, - FrameInput, Gas, + interpreter::{EthInterpreter, InstructionProvider}, + FrameInput, Gas, Host, }, primitives::{hash_map::HashMap, U256}, specification::hardfork::SpecId, @@ -36,33 +27,59 @@ use revm::{ Database, }; -pub type OpHandler< - CTX, - ERROR, - VAL = OpValidation, - PREEXEC = OpPreExecution, - EXEC = OpExecution, - POSTEXEC = OpPostExecution, -> = EthHandler; - -pub struct OpValidation { - pub eth: EthValidation, +pub struct OpHandlerNew { + pub eth: EthHandlerImpl, } -impl ValidationHandler for OpValidation +impl + OpHandlerNew where - CTX: EthValidationContext + OpTxGetter, + PRECOMPILES: PrecompileProvider, + INSTRUCTIONS: InstructionProvider, +{ + pub fn crete_frame_context(&self) -> FrameContext { + self.eth.crete_frame_context() + } +} + +pub trait IsTxError { + fn is_tx_error(&self) -> bool; +} + +impl EthHandler + for OpHandlerNew +where + CTX: EthContext + OpTxGetter + L1BlockInfoGetter, // Have Cfg with OpSpec ::Cfg: Cfg, - // Have transaction with OpTransactionType - //::Transaction: Transaction, - // Add additional error type. - ERROR: EthValidationError + From, + ERROR: EthError + From + IsTxError + FromStringError, + PRECOMPILES: PrecompileProvider, + INSTRUCTIONS: InstructionProvider, + // TODO `FrameResult` should be a generic trait. + // TODO `FrameInit` should be a generic. + FRAME: Frame< + Context = CTX, + Error = ERROR, + FrameResult = FrameResult, + FrameInit = FrameInput, + FrameContext = FrameContext, + >, { type Context = CTX; type Error = ERROR; + type Frame = FRAME; + type Precompiles = PRECOMPILES; + type Instructions = INSTRUCTIONS; + type HaltReason = OptimismHaltReason; + + fn frame_context( + &mut self, + context: &mut Self::Context, + ) -> ::FrameContext { + self.eth.precompiles.set_spec(context.cfg().spec()); + self.crete_frame_context() + } - /// Validate env. fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { // Do not perform any extra validation for deposit transactions, they are pre-verified on L1. let tx_type = context.tx().tx_type(); @@ -78,7 +95,6 @@ where self.eth.validate_env(context) } - /// Validate transactions against state. fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { if context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE { return Ok(()); @@ -86,28 +102,6 @@ where self.eth.validate_tx_against_state(context) } - /// Validate initial gas. - fn validate_initial_tx_gas( - &self, - context: &Self::Context, - ) -> Result { - self.eth.validate_initial_tx_gas(context) - } -} - -pub struct OpPreExecution { - pub eth: EthPreExecution, -} - -impl PreExecutionHandler for OpPreExecution -where - CTX: EthPreExecutionContext + DatabaseGetter + OpTxGetter + L1BlockInfoGetter, - ::Cfg: Cfg, - ERROR: EthPreExecutionError + From<<::Database as Database>::Error>, -{ - type Context = CTX; - type Error = ERROR; - fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { // The L1-cost fee is only computed for Optimism non-deposit transactions. let spec = context.cfg().spec(); @@ -122,10 +116,6 @@ where self.eth.load_accounts(context) } - fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result { - self.eth.apply_eip7702_auth_list(context) - } - fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { let caller = context.tx().caller(); let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; @@ -171,48 +161,13 @@ where } Ok(()) } -} - -pub struct OpExecution< - CTX, - ERROR, - FRAME = EthFrame< - CTX, - ERROR, - EthInterpreter<()>, - OpPrecompileProvider, - EthInstructionProvider, CTX>, - >, -> { - pub eth: EthExecution, -} - -impl ExecutionHandler for OpExecution -where - CTX: EthExecutionContext + EthFrameContext + OpTxGetter, - ERROR: EthExecutionError + EthFrameError, - ::Cfg: Cfg, - //::Transaction: Transaction, - FRAME: Frame, -{ - type Context = CTX; - type Error = ERROR; - type Frame = FRAME; - type ExecResult = FrameResult; - - fn init_first_frame( - &mut self, - context: &mut Self::Context, - gas_limit: u64, - ) -> Result, Self::Error> { - self.eth.init_first_frame(context, gas_limit) - } fn last_frame_result( &self, context: &mut Self::Context, - mut frame_result: ::FrameResult, - ) -> Result { + _frame_context: &mut ::FrameContext, + frame_result: &mut ::FrameResult, + ) -> Result<(), Self::Error> { let tx = context.tx(); let is_deposit = tx.tx_type() == DEPOSIT_TRANSACTION_TYPE; let tx_gas_limit = tx.gas_limit(); @@ -270,48 +225,13 @@ where gas.erase_cost(remaining); } } - Ok(frame_result) - } -} - -pub struct OpPostExecution { - pub eth: EthPostExecution, -} - -pub trait IsTxError { - fn is_tx_error(&self) -> bool; -} - -impl PostExecutionHandler for OpPostExecution -where - CTX: EthPostExecutionContext + OpTxGetter + L1BlockInfoGetter + DatabaseGetter, - ERROR: EthPostExecutionError - + EthFrameError - + From - + FromStringError - + IsTxError, - ::Cfg: Cfg, - //::Transaction: Transaction, -{ - type Context = CTX; - type Error = ERROR; - type ExecResult = FrameResult; - type Output = ResultAndState; - - fn eip7623_check_gas_floor( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - init_and_floor_gas: InitialAndFloorGas, - ) { - self.eth - .eip7623_check_gas_floor(context, exec_result, init_and_floor_gas); + Ok(()) } fn refund( &self, context: &mut Self::Context, - exec_result: &mut Self::ExecResult, + exec_result: &mut ::FrameResult, eip7702_refund: i64, ) { exec_result.gas_mut().record_refund(eip7702_refund); @@ -328,18 +248,10 @@ where } } - fn reimburse_caller( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - ) -> Result<(), Self::Error> { - self.eth.reimburse_caller(context, exec_result) - } - fn reward_beneficiary( &self, context: &mut Self::Context, - exec_result: &mut Self::ExecResult, + exec_result: &mut ::FrameResult, ) -> Result<(), Self::Error> { self.eth.reward_beneficiary(context, exec_result)?; @@ -380,9 +292,10 @@ where fn output( &self, context: &mut Self::Context, - result: Self::ExecResult, - ) -> Result { + result: ::FrameResult, + ) -> Result, Self::Error> { let result = self.eth.output(context, result)?; + let result = result.map_haltreason(OptimismHaltReason::Base); if result.result.is_halt() { // Post-regolith, if the transaction is a deposit transaction and it halts, // we bubble up to the global return handler. The mint value will be persisted @@ -395,15 +308,11 @@ where Ok(result) } - fn clear(&self, context: &mut Self::Context) { - self.eth.clear(context); - } - fn end( &self, context: &mut Self::Context, - end_output: Result, - ) -> Result { + end_output: Result, Self::Error>, + ) -> Result, Self::Error> { //end_output let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; @@ -466,16 +375,6 @@ where } } -// /// Optimism end handle changes output if the transaction is a deposit transaction. -// /// Deposit transaction can't be reverted and is always successful. -// #[inline] -// pub fn end( -// context: &mut Context, -// evm_output: EVMResult, -// ) -> EVMResult { - -// } - // #[cfg(test)] // mod tests { // use super::*; diff --git a/crates/optimism/src/handler/precompiles.rs b/crates/optimism/src/handler/precompiles.rs index 73a0d63738..1519d08138 100644 --- a/crates/optimism/src/handler/precompiles.rs +++ b/crates/optimism/src/handler/precompiles.rs @@ -29,43 +29,9 @@ impl OpPrecompileProvider { }, } } -} - -/// Returns precompiles for Fjor spec. -pub fn fjord() -> &'static Precompiles { - static INSTANCE: OnceBox = OnceBox::new(); - INSTANCE.get_or_init(|| { - let mut precompiles = Precompiles::cancun().clone(); - // EIP-7212: secp256r1 P256verify - precompiles.extend([crate::bn128::pair::GRANITE]); - Box::new(precompiles) - }) -} - -/// Returns precompiles for Granite spec. -pub fn granite() -> &'static Precompiles { - static INSTANCE: OnceBox = OnceBox::new(); - INSTANCE.get_or_init(|| { - let mut precompiles = Precompiles::cancun().clone(); - // Restrict bn256Pairing input size - precompiles.extend([secp256r1::P256VERIFY]); - Box::new(precompiles) - }) -} - -impl PrecompileProvider for OpPrecompileProvider -where - CTX: CfgGetter, - ::Cfg: Cfg, - ERROR: From, -{ - type Context = CTX; - type Error = ERROR; - type Output = InterpreterResult; #[inline] - fn new(context: &mut Self::Context) -> Self { - let spec = context.cfg().spec(); + pub fn new_with_spec(spec: OpSpec) -> Self { match spec { // No changes spec @ (OpSpec::Eth( @@ -101,6 +67,45 @@ where | OpSpec::Eth(SpecId::PRAGUE | SpecId::OSAKA | SpecId::LATEST) => Self::new(granite()), } } +} + +/// Returns precompiles for Fjor spec. +pub fn fjord() -> &'static Precompiles { + static INSTANCE: OnceBox = OnceBox::new(); + INSTANCE.get_or_init(|| { + let mut precompiles = Precompiles::cancun().clone(); + // EIP-7212: secp256r1 P256verify + precompiles.extend([crate::bn128::pair::GRANITE]); + Box::new(precompiles) + }) +} + +/// Returns precompiles for Granite spec. +pub fn granite() -> &'static Precompiles { + static INSTANCE: OnceBox = OnceBox::new(); + INSTANCE.get_or_init(|| { + let mut precompiles = Precompiles::cancun().clone(); + // Restrict bn256Pairing input size + precompiles.extend([secp256r1::P256VERIFY]); + Box::new(precompiles) + }) +} + +impl PrecompileProvider for OpPrecompileProvider +where + CTX: CfgGetter, + ::Cfg: Cfg, + ERROR: From, +{ + type Context = CTX; + type Error = ERROR; + type Output = InterpreterResult; + type Spec = OpSpec; + + #[inline] + fn set_spec(&mut self, spec: Self::Spec) { + *self = Self::new_with_spec(spec); + } #[inline] fn run( @@ -115,7 +120,7 @@ where } #[inline] - fn warm_addresses(&self) -> impl Iterator { + fn warm_addresses(&self) -> Box + '_> { self.precompile_provider.warm_addresses() } diff --git a/crates/optimism/src/lib.rs b/crates/optimism/src/lib.rs index 2468597be1..4e5b4de60e 100644 --- a/crates/optimism/src/lib.rs +++ b/crates/optimism/src/lib.rs @@ -6,7 +6,6 @@ extern crate alloc as std; pub mod bn128; -pub mod evm; pub mod fast_lz; pub mod handler; pub mod l1block; diff --git a/crates/optimism/src/spec.rs b/crates/optimism/src/spec.rs index 98c1e64851..38e4079c87 100644 --- a/crates/optimism/src/spec.rs +++ b/crates/optimism/src/spec.rs @@ -51,6 +51,14 @@ impl From for OpSpec { OpSpec::Eth(spec) } } +impl From for SpecId { + fn from(spec: OpSpec) -> Self { + match spec { + OpSpec::Eth(spec) => spec, + OpSpec::Op(spec) => spec.into_eth_spec(), + } + } +} impl TryFrom<&str> for OpSpecId { type Error = (); diff --git a/crates/revm/CHANGELOG.md b/crates/revm/CHANGELOG.md index 3810eda0a1..15c4ceb8f3 100644 --- a/crates/revm/CHANGELOG.md +++ b/crates/revm/CHANGELOG.md @@ -134,7 +134,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add uniswap V2 WETH-USDC swap example ([#1353](https://github.com/bluealloy/revm/pull/1353)) - *(interpreter)* add helpers for spending all gas ([#1360](https://github.com/bluealloy/revm/pull/1360)) - add helper methods to CallInputs ([#1345](https://github.com/bluealloy/revm/pull/1345)) -- *(revm)* make `FrameOrResult` serializable ([#1282](https://github.com/bluealloy/revm/pull/1282)) +- *(revm)* make `ItemOrResult` serializable ([#1282](https://github.com/bluealloy/revm/pull/1282)) - add flag to force hashbrown usage ([#1284](https://github.com/bluealloy/revm/pull/1284)) - EOF (Ethereum Object Format) ([#1143](https://github.com/bluealloy/revm/pull/1143)) - *(`db`)* Introduce `alloydb` ([#1257](https://github.com/bluealloy/revm/pull/1257)) diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index ef962a13d7..994fe73e8b 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -35,13 +35,6 @@ context-interface.workspace = true handler.workspace = true handler-interface.workspace = true -# Optional -# TODO : Check if needed. -# serde = { version = "1.0", default-features = false, features = [ -# "derive", -# "rc", -# ], optional = true } - [dev-dependencies] database.workspace = true alloy-sol-types = { version = "0.8.2", default-features = false, features = [ diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 72d503b278..597b190bd5 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,297 +1,30 @@ -use crate::{exec::EvmCommit, EvmExec}; -use context::{block::BlockEnv, tx::TxEnv, CfgEnv, Context, JournaledState}; use context_interface::{ - block::BlockSetter, - context::PerformantContextAccess, - journaled_state::Journal, - result::{ - EVMError, ExecutionResult, HaltReasonTrait, InvalidHeader, InvalidTransaction, - ResultAndState, - }, - transaction::TransactionSetter, - BlockGetter, CfgGetter, DatabaseGetter, ErrorGetter, JournalDBError, JournalGetter, - Transaction, TransactionGetter, + result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, ResultAndState}, + DatabaseGetter, }; use database_interface::{Database, DatabaseCommit}; -use handler::{EthHandler, FrameResult}; -use handler_interface::{ - ExecutionHandler, Frame, FrameOrResultGen, Handler, InitialAndFloorGas, PostExecutionHandler, - PreExecutionHandler, ValidationHandler, +use handler::{ + handler::{EthContext, EthHandler, EthHandlerImpl}, + EthFrame, EthPrecompileProvider, }; -use interpreter::Host; -use precompile::PrecompileErrors; -use primitives::Log; -use state::EvmState; -use std::vec::Vec; -/// Main EVM structure -pub struct Evm> { - pub context: CTX, - pub handler: HANDLER, - pub _error: core::marker::PhantomData ERROR>, +pub fn transact_main>( + ctx: &mut CTX, +) -> Result, EVMError<::Error, InvalidTransaction>> { + EthHandlerImpl::, EthPrecompileProvider, _>::default() + .run(ctx) } -impl Evm { - pub fn new(context: CTX, handler: HANDLER) -> Self { - Self { - context, - handler, - _error: core::marker::PhantomData, - } - } -} - -impl EvmCommit - for Evm> -where - CTX: TransactionSetter - + BlockSetter - + JournalGetter - + CfgGetter - + DatabaseGetter - + ErrorGetter> - + JournalGetter< - Journal: Journal< - FinalOutput = (EvmState, Vec), - Database = ::Database, - >, - > + Host - + PerformantContextAccess::Database as Database>::Error>, - ERROR: From - + From - + From> - + From, - VAL: ValidationHandler, - PREEXEC: PreExecutionHandler, - EXEC: ExecutionHandler< - Context = CTX, - Error = ERROR, - ExecResult = FrameResult, - Frame: Frame, - >, - POSTEXEC: PostExecutionHandler< - Context = CTX, - Error = ERROR, - ExecResult = FrameResult, - // TODO make output more generics - Output = ResultAndState, - >, - HALT: HaltReasonTrait, -{ - type CommitOutput = Result, ERROR>; - - fn exec_commit(&mut self) -> Self::CommitOutput { - let res = self.transact(); - res.map(|r| { - self.context.db().commit(r.state); - r.result - }) - } -} - -impl EvmExec - for Evm> -where - CTX: TransactionSetter - + BlockSetter - + JournalGetter - + CfgGetter - + DatabaseGetter - + ErrorGetter> - + JournalGetter< - Journal: Journal< - FinalOutput = (EvmState, Vec), - Database = ::Database, - >, - > + Host - + PerformantContextAccess::Database as Database>::Error>, - ERROR: From - + From - + From> - + From, - VAL: ValidationHandler, - PREEXEC: PreExecutionHandler, - EXEC: ExecutionHandler< - Context = CTX, - Error = ERROR, - ExecResult = FrameResult, - Frame: Frame, - >, - POSTEXEC: PostExecutionHandler, -{ - type Transaction = ::Transaction; - - type Block = ::Block; - - type Output = Result<::Output, ERROR>; - - fn set_block(&mut self, block: Self::Block) { - self.context.set_block(block); - } - - fn set_tx(&mut self, tx: Self::Transaction) { - self.context.set_tx(tx); - } - - fn exec(&mut self) -> Self::Output { - self.transact() - } -} - -/// Mainnet Error. -pub type Error = EVMError<::Error, InvalidTransaction>; - -/// Mainnet Contexts. -pub type EthContext> = - Context; - -/// Mainnet EVM type. -pub type MainEvm = Evm, EthContext>; - -impl - Evm> -where - CTX: TransactionGetter - + BlockGetter - + JournalGetter - + CfgGetter - + DatabaseGetter - + ErrorGetter> - + JournalGetter< - Journal: Journal< - FinalOutput = (EvmState, Vec), - Database = ::Database, - >, - > + Host - + PerformantContextAccess::Database as Database>::Error>, - ERROR: From - + From - + From> - + From, - VAL: ValidationHandler, - PREEXEC: PreExecutionHandler, - EXEC: ExecutionHandler< - Context = CTX, - Error = ERROR, - ExecResult = FrameResult, - Frame: Frame, - >, - POSTEXEC: PostExecutionHandler, -{ - /// Pre verify transaction by checking Environment, initial gas spend and if caller - /// has enough balance to pay for the gas. - #[inline] - pub fn preverify_transaction(&mut self) -> Result<(), ERROR> { - let output = self.preverify_transaction_inner().map(|_| ()); - self.clear(); - output - } - - /// Calls clear handle of post execution to clear the state for next execution. - fn clear(&mut self) { - self.handler.post_execution().clear(&mut self.context); - } - - /// Transact pre-verified transaction - /// - /// This function will not validate the transaction. - #[inline] - pub fn transact_preverified( - &mut self, - ) -> Result<::Output, ERROR> { - let initial_gas_spend = self - .handler - .validation() - .validate_initial_tx_gas(&self.context) - .inspect_err(|_| { - self.clear(); - })?; - let init_and_floor_gas = self.transact_preverified_inner(initial_gas_spend); - let output = self - .handler - .post_execution() - .end(&mut self.context, init_and_floor_gas); - self.clear(); - output - } - - /// Pre verify transaction inner. - #[inline] - fn preverify_transaction_inner(&mut self) -> Result { - self.handler.validation().validate_env(&self.context)?; - let initial_gas_spend = self - .handler - .validation() - .validate_initial_tx_gas(&self.context)?; - self.handler - .validation() - .validate_tx_against_state(&mut self.context)?; - Ok(initial_gas_spend) - } - - /// Transact transaction - /// - /// This function will validate the transaction. - #[inline] - pub fn transact(&mut self) -> Result<::Output, ERROR> { - let initial_gas_spend = self.preverify_transaction_inner().inspect_err(|_| { - self.clear(); - })?; - - let init_and_floor_gas = self.transact_preverified_inner(initial_gas_spend); - let output = self - .handler - .post_execution() - .end(&mut self.context, init_and_floor_gas); - self.clear(); - output - } - - /// Transact pre-verified transaction. - fn transact_preverified_inner( - &mut self, - init_and_floor_gas: InitialAndFloorGas, - ) -> Result<::Output, ERROR> { - let context = &mut self.context; - let pre_exec = self.handler.pre_execution(); - - // Load access list and beneficiary if needed. - pre_exec.load_accounts(context)?; - - // Deduce caller balance with its limit. - pre_exec.deduct_caller(context)?; - - let gas_limit = context.tx().gas_limit() - init_and_floor_gas.initial_gas; - - // Apply EIP-7702 auth list. - let eip7702_gas_refund = pre_exec.apply_eip7702_auth_list(context)? as i64; - - // Start execution - - //let instructions = self.handler.take_instruction_table(); - let exec = self.handler.execution(); - - // Create first frame action - let first_frame = exec.init_first_frame(context, gas_limit)?; - let frame_result = match first_frame { - FrameOrResultGen::Frame(frame) => exec.run(context, frame)?, - FrameOrResultGen::Result(result) => result, - }; - - let mut exec_result = exec.last_frame_result(context, frame_result)?; - - let post_exec = self.handler.post_execution(); - // Calculate final refund and add EIP-7702 refund to gas. - post_exec.refund(context, &mut exec_result, eip7702_gas_refund); - // Check gas floor - post_exec.eip7623_check_gas_floor(context, &mut exec_result, init_and_floor_gas); - // Reimburse the caller - post_exec.reimburse_caller(context, &mut exec_result)?; - // Reward beneficiary - post_exec.reward_beneficiary(context, &mut exec_result)?; - // Returns output of transaction. - post_exec.output(context, exec_result) - } +pub fn transact_main_commit< + DB: Database + DatabaseCommit, + CTX: EthContext + DatabaseGetter, +>( + ctx: &mut CTX, +) -> Result, EVMError<::Error, InvalidTransaction>> { + transact_main(ctx).map(|r| { + ctx.db().commit(r.state); + r.result + }) } /* diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 865c02a66d..bebfc2f30a 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -2,8 +2,8 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(not(feature = "std"))] -extern crate alloc as std; +//#[cfg(not(feature = "std"))] +//extern crate alloc as std; // reexport dependencies pub use bytecode; @@ -28,5 +28,5 @@ mod exec; pub use context::journaled_state::{JournalEntry, JournaledState}; pub use context::Context; pub use database_interface::{Database, DatabaseCommit, DatabaseRef}; -pub use evm::{Error, EthContext, Evm, MainEvm}; +pub use evm::{transact_main, transact_main_commit}; pub use exec::{EvmCommit, EvmExec}; diff --git a/examples/block_traces/src/main.rs b/examples/block_traces/src/main.rs index ddcde54d89..d174721037 100644 --- a/examples/block_traces/src/main.rs +++ b/examples/block_traces/src/main.rs @@ -9,19 +9,8 @@ use alloy_provider::{ }; use database::{AlloyDB, CacheDB, StateBuilder}; use indicatif::ProgressBar; -use inspector::{ - inspector_context::InspectorContext, inspectors::TracerEip3155, InspectorEthFrame, - InspectorMainEvm, -}; -use revm::{ - database_interface::WrapDatabaseAsync, - handler::{ - EthExecution, EthHandler, EthPostExecution, EthPreExecution, EthPrecompileProvider, - EthValidation, - }, - primitives::TxKind, - Context, EvmCommit, -}; +use inspector::{inspect_main, inspector_context::InspectorContext, inspectors::TracerEip3155}; +use revm::{database_interface::WrapDatabaseAsync, primitives::TxKind, Context}; use std::io::BufWriter; use std::io::Write; use std::sync::Arc; @@ -49,6 +38,8 @@ impl Write for FlushWriter { } } +pub fn inspect_ctx_insp() {} + #[tokio::main] async fn main() -> anyhow::Result<()> { // Set up the HTTP transport which is consumed by the RPC client. @@ -83,31 +74,22 @@ async fn main() -> anyhow::Result<()> { let state_db = WrapDatabaseAsync::new(AlloyDB::new(client, prev_id)).unwrap(); let cache_db: CacheDB<_> = CacheDB::new(state_db); let mut state = StateBuilder::new_with_database(cache_db).build(); - let mut evm = InspectorMainEvm::new( - InspectorContext::new( - Context::builder() - .with_db(&mut state) - .modify_block_chained(|b| { - b.number = block.header.number; - b.beneficiary = block.header.beneficiary; - b.timestamp = block.header.timestamp; - - b.difficulty = block.header.difficulty; - b.gas_limit = block.header.gas_limit; - b.basefee = block.header.base_fee_per_gas.unwrap_or_default(); - }) - .modify_cfg_chained(|c| { - c.chain_id = chain_id; - }), - TracerEip3155::new(Box::new(stdout())), - ), - EthHandler::new( - EthValidation::new(), - EthPreExecution::new(), - EthExecution::<_, _, InspectorEthFrame<_, _, EthPrecompileProvider<_, _>>>::new(), - EthPostExecution::new(), - ), - ); + let mut ctx = Context::builder() + .with_db(&mut state) + .modify_block_chained(|b| { + b.number = block.header.number; + b.beneficiary = block.header.beneficiary; + b.timestamp = block.header.timestamp; + + b.difficulty = block.header.difficulty; + b.gas_limit = block.header.gas_limit; + b.basefee = block.header.base_fee_per_gas.unwrap_or_default(); + }) + .modify_cfg_chained(|c| { + c.chain_id = chain_id; + }); + let mut inspector = TracerEip3155::new(Box::new(stdout())); + let mut ctx = InspectorContext::new(&mut ctx, &mut inspector); let txs = block.transactions.len(); println!("Found {txs} transactions."); @@ -124,7 +106,7 @@ async fn main() -> anyhow::Result<()> { }; for tx in transactions { - evm.context.inner.modify_tx(|etx| { + ctx.inner.modify_tx(|etx| { etx.caller = tx.from; etx.gas_limit = tx.gas_limit(); etx.gas_price = tx.gas_price().unwrap_or(tx.inner.max_fee_per_gas()); @@ -160,9 +142,9 @@ async fn main() -> anyhow::Result<()> { let writer = FlushWriter::new(Arc::clone(&inner)); // Inspect and commit the transaction to the EVM - evm.context.inspector.set_writer(Box::new(writer)); + ctx.inspector.set_writer(Box::new(writer)); - let res = evm.exec_commit(); + let res = inspect_main(&mut ctx); if let Err(error) = res { println!("Got error: {:?}", error); diff --git a/examples/cheatcode_inspector/src/main.rs b/examples/cheatcode_inspector/src/main.rs index 10d9898388..eb2e620f19 100644 --- a/examples/cheatcode_inspector/src/main.rs +++ b/examples/cheatcode_inspector/src/main.rs @@ -9,13 +9,11 @@ use std::{convert::Infallible, fmt::Debug}; use database::InMemoryDB; use inspector::{ - inspector_context::InspectorContext, inspector_handler, inspectors::TracerEip3155, - journal::JournalExt, GetInspector, Inspector, InspectorHandler, + inspect_main, inspector_context::InspectorContext, inspectors::TracerEip3155, + journal::JournalExt, GetInspector, Inspector, }; -use revm::bytecode::Bytecode; -use revm::interpreter::{interpreter::EthInterpreter, CallInputs, CallOutcome, InterpreterResult}; -use revm::primitives::{Log, U256}; use revm::{ + bytecode::Bytecode, context::{BlockEnv, Cfg, CfgEnv, TxEnv}, context_interface::{ host::{SStoreResult, SelfDestructResult}, @@ -25,10 +23,12 @@ use revm::{ }, handler::EthPrecompileProvider, handler_interface::PrecompileProvider, + interpreter::{interpreter::EthInterpreter, CallInputs, CallOutcome, InterpreterResult}, precompile::{Address, HashSet, B256}, + primitives::{Log, U256}, specification::hardfork::SpecId, state::{Account, EvmState, TransientStorage}, - Context, Database, DatabaseCommit, Evm, JournalEntry, JournaledState, + Context, Database, DatabaseCommit, JournalEntry, JournaledState, }; /// Backend for cheatcodes. @@ -212,7 +212,7 @@ impl Journal for Backend { self.journaled_state.depth } - fn finalize(&mut self) -> Result::Error> { + fn finalize(&mut self) -> Self::FinalOutput { let JournaledState { state, transient_storage, @@ -231,7 +231,7 @@ impl Journal for Backend { let state = std::mem::take(state); let logs = std::mem::take(logs); - Ok((state, logs)) + (state, logs) } } @@ -317,17 +317,8 @@ impl DatabaseExt for Backend { BlockT: Block, TxT: Transaction, CfgT: Cfg, - PrecompileT: PrecompileProvider< - Context = InspectorContext< - InspectorT, - InMemoryDB, - Context, - >, - Output = InterpreterResult, - Error = EVMError, - >, { - commit_transaction::(self, env, inspector)?; + commit_transaction::(self, env, inspector)?; self.method_with_inspector_counter += 1; Ok(()) } @@ -348,16 +339,6 @@ impl DatabaseExt for Backend { BlockT, TxT, CfgT, - // Since we can't have a generic precompiles type param as explained in the trait definition, we're using - // concrete type here. - EthPrecompileProvider< - InspectorContext< - TracerEip3155, EthInterpreter>, - InMemoryDB, - Context, - >, - EVMError, - >, >(self, env, inspector)?; self.method_without_inspector_counter += 1; @@ -459,7 +440,7 @@ impl Env { /// Executes a transaction and runs the inspector using the `Backend` as the state. /// Mimics `commit_transaction` -fn commit_transaction( +fn commit_transaction( backend: &mut Backend, env: Env, inspector: InspectorT, @@ -473,15 +454,6 @@ where BlockT: Block, TxT: Transaction, CfgT: Cfg, - PrecompileT: PrecompileProvider< - Context = InspectorContext< - InspectorT, - InMemoryDB, - Context, - >, - Output = InterpreterResult, - Error = EVMError, - >, { // Create new journaled state and backend with the same DB and journaled state as the original for the transaction. // This new backend and state will be discarded after the transaction is done and the changes are applied to the @@ -498,26 +470,13 @@ where error: Ok(()), }; - let inspector_context = InspectorContext::< + let mut inspector_context = InspectorContext::< InspectorT, InMemoryDB, Context, >::new(context, inspector); - let mut evm = Evm::new( - inspector_context, - inspector_handler::< - InspectorContext< - InspectorT, - InMemoryDB, - Context, - >, - EVMError, - PrecompileT, - >(), - ); - - let result = evm.transact()?; + let result = inspect_main(&mut inspector_context)?; // Persist the changes to the original backend. backend.journaled_state.database.commit(result.state); @@ -543,15 +502,6 @@ fn update_state(state: &mut EvmState, db: &mut DB) -> Result<(), D } fn main() -> anyhow::Result<()> { - type InspectorT<'cheatcodes> = &'cheatcodes mut Cheatcodes; - type ErrorT = EVMError; - type InspectorContextT<'cheatcodes> = InspectorContext< - InspectorT<'cheatcodes>, - InMemoryDB, - Context, - >; - type PrecompileT<'cheatcodes> = EthPrecompileProvider, ErrorT>; - let backend = Backend::new(SpecId::LATEST, InMemoryDB::default()); let mut inspector = Cheatcodes::::default(); let env = Env::mainnet(); @@ -564,32 +514,18 @@ fn main() -> anyhow::Result<()> { chain: (), error: Ok(()), }; - let inspector_context = InspectorContext::< - InspectorT<'_>, - InMemoryDB, - Context, - >::new(context, &mut inspector); - let handler = inspector_handler::, ErrorT, PrecompileT<'_>>(); - - let mut evm = Evm::< - ErrorT, - InspectorContextT<'_>, - InspectorHandler, ErrorT, PrecompileT<'_>>, - >::new(inspector_context, handler); + let mut context = InspectorContext::new(context, &mut inspector); - evm.transact()?; + inspect_main(&mut context)?; // Sanity check - assert_eq!(evm.context.inspector.call_count, 2); + assert_eq!(context.inspector.call_count, 2); assert_eq!( - evm.context - .inner - .journaled_state - .method_with_inspector_counter, + context.inner.journaled_state.method_with_inspector_counter, 1 ); assert_eq!( - evm.context + context .inner .journaled_state .method_without_inspector_counter, diff --git a/examples/contract_deployment/src/main.rs b/examples/contract_deployment/src/main.rs index c8cd211f12..3e0309c63c 100644 --- a/examples/contract_deployment/src/main.rs +++ b/examples/contract_deployment/src/main.rs @@ -8,9 +8,8 @@ use revm::{ context::Context, context_interface::result::{ExecutionResult, Output}, database_interface::EmptyDB, - handler::EthHandler, primitives::{hex, Bytes, TxKind, U256}, - EvmCommit, MainEvm, + transact_main, transact_main_commit, }; /// Load number parameter and set to storage with slot 0 @@ -48,18 +47,15 @@ const RUNTIME_BYTECODE: &[u8] = &[opcode::PUSH0, opcode::SLOAD]; fn main() -> anyhow::Result<()> { let param = 0x42; let bytecode: Bytes = [INIT_CODE, RET, RUNTIME_BYTECODE, &[param]].concat().into(); - let mut evm = MainEvm::new( - Context::builder() - .modify_tx_chained(|tx| { - tx.kind = TxKind::Create; - tx.data = bytecode.clone(); - }) - .with_db(CacheDB::::default()), - EthHandler::default(), - ); + let mut ctx = Context::builder() + .modify_tx_chained(|tx| { + tx.kind = TxKind::Create; + tx.data = bytecode.clone(); + }) + .with_db(CacheDB::::default()); println!("bytecode: {}", hex::encode(bytecode)); - let ref_tx = evm.exec_commit()?; + let ref_tx = transact_main_commit(&mut ctx)?; let ExecutionResult::Success { output: Output::Create(_, Some(address)), .. @@ -69,13 +65,13 @@ fn main() -> anyhow::Result<()> { }; println!("Created contract at {address}"); - evm.context.modify_tx(|tx| { + ctx.modify_tx(|tx| { tx.kind = TxKind::Call(address); tx.data = Default::default(); tx.nonce += 1; }); - let result = evm.transact()?; + let result = transact_main(&mut ctx)?; let Some(storage0) = result .state .get(&address) diff --git a/examples/erc20_gas/src/exec.rs b/examples/erc20_gas/src/exec.rs new file mode 100644 index 0000000000..1d8fdc4655 --- /dev/null +++ b/examples/erc20_gas/src/exec.rs @@ -0,0 +1,27 @@ +use crate::handler::Erc20MainetHandler; +use revm::{ + context_interface::{ + result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, ResultAndState}, + DatabaseGetter, + }, + database_interface::{Database, DatabaseCommit}, + handler::handler::{EthContext, EthHandler}, +}; + +pub fn transact_erc20evm>( + ctx: &mut CTX, +) -> Result, EVMError<::Error, InvalidTransaction>> { + Erc20MainetHandler::::new().run(ctx) +} + +pub fn transact_erc20evm_commit< + DB: Database + DatabaseCommit, + CTX: EthContext + DatabaseGetter, +>( + ctx: &mut CTX, +) -> Result, EVMError<::Error, InvalidTransaction>> { + transact_erc20evm(ctx).map(|r| { + ctx.db().commit(r.state); + r.result + }) +} diff --git a/examples/erc20_gas/src/handler.rs b/examples/erc20_gas/src/handler.rs new file mode 100644 index 0000000000..f4b3099807 --- /dev/null +++ b/examples/erc20_gas/src/handler.rs @@ -0,0 +1,189 @@ +use revm::{ + context::Cfg, + context_interface::{ + result::{HaltReason, InvalidTransaction}, + Block, CfgGetter, Journal, Transaction, TransactionType, + }, + handler::{EthContext, EthError, EthFrame, EthHandler, EthPrecompileProvider, FrameContext}, + handler_interface::Frame, + interpreter::{ + interpreter::{EthInstructionProvider, EthInterpreter}, + Host, + }, + precompile::PrecompileErrors, + primitives::U256, + specification::hardfork::SpecId, +}; +use std::cmp::Ordering; + +use crate::{erc_address_storage, token_operation, TOKEN, TREASURY}; + +pub struct Erc20MainetHandler> { + frame_context: FrameContext< + EthPrecompileProvider, + EthInstructionProvider, + >, +} + +impl> Erc20MainetHandler { + pub fn new() -> Self { + Self { + frame_context: FrameContext::new( + EthPrecompileProvider::new(SpecId::LATEST), + EthInstructionProvider::default(), + ), + } + } +} + +impl> Default + for Erc20MainetHandler +{ + fn default() -> Self { + Self::new() + } +} + +impl EthHandler for Erc20MainetHandler +where + CTX: EthContext, + ERROR: EthError, +{ + type Context = CTX; + type Error = ERROR; + type Precompiles = EthPrecompileProvider; + type Instructions = EthInstructionProvider; + type Frame = + EthFrame>; + type HaltReason = HaltReason; + + fn frame_context( + &mut self, + _context: &mut Self::Context, + ) -> ::FrameContext { + FrameContext { + precompiles: self.frame_context.precompiles.clone(), + instructions: self.frame_context.instructions.clone(), + } + } + + fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + let caller = context.tx().caller(); + let caller_nonce = context.journal().load_account(caller)?.data.info.nonce; + let _ = context.journal().load_account(TOKEN)?.data.clone(); + + if !context.cfg().is_nonce_check_disabled() { + let tx_nonce = context.tx().nonce(); + let state_nonce = caller_nonce; + match tx_nonce.cmp(&state_nonce) { + Ordering::Less => { + return Err(ERROR::from(InvalidTransaction::NonceTooLow { + tx: tx_nonce, + state: state_nonce, + })) + } + Ordering::Greater => { + return Err(ERROR::from(InvalidTransaction::NonceTooHigh { + tx: tx_nonce, + state: state_nonce, + })) + } + _ => (), + } + } + + let mut balance_check = U256::from(context.tx().gas_limit()) + .checked_mul(U256::from(context.tx().max_fee_per_gas())) + .and_then(|gas_cost| gas_cost.checked_add(context.tx().value())) + .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; + + if context.tx().tx_type() == TransactionType::Eip4844 { + let tx = context.tx(); + let data_fee = tx.calc_max_data_fee(); + balance_check = balance_check + .checked_add(data_fee) + .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; + } + + let account_balance_slot = erc_address_storage(caller); + let account_balance = context + .journal() + .sload(TOKEN, account_balance_slot) + .map(|v| v.data) + .unwrap_or_default(); + + if account_balance < balance_check && !context.cfg().is_balance_check_disabled() { + return Err(InvalidTransaction::LackOfFundForMaxFee { + fee: Box::new(balance_check), + balance: Box::new(account_balance), + } + .into()); + }; + + Ok(()) + } + + fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + // load and touch token account + let _ = context.journal().load_account(TOKEN)?.data; + context.journal().touch_account(TOKEN); + + let basefee = context.block().basefee() as u128; + let blob_price = context.block().blob_gasprice().unwrap_or_default(); + let effective_gas_price = context.tx().effective_gas_price(basefee); + + let mut gas_cost = (context.tx().gas_limit() as u128).saturating_mul(effective_gas_price); + + if context.tx().tx_type() == TransactionType::Eip4844 { + let blob_gas = context.tx().total_blob_gas() as u128; + gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); + } + + let caller = context.tx().caller(); + println!("Deduct caller: {:?} for amount: {gas_cost:?}", caller); + token_operation::(context, caller, TREASURY, U256::from(gas_cost))?; + + Ok(()) + } + + fn reimburse_caller( + &self, + context: &mut Self::Context, + exec_result: &mut ::FrameResult, + ) -> Result<(), Self::Error> { + let basefee = context.block().basefee() as u128; + let caller = context.tx().caller(); + let effective_gas_price = context.tx().effective_gas_price(basefee); + let gas = exec_result.gas(); + + let reimbursement = + effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128); + token_operation::(context, TREASURY, caller, U256::from(reimbursement))?; + + Ok(()) + } + + fn reward_beneficiary( + &self, + context: &mut Self::Context, + exec_result: &mut ::FrameResult, + ) -> Result<(), Self::Error> { + let tx = context.tx(); + let beneficiary = context.block().beneficiary(); + let basefee = context.block().basefee() as u128; + let effective_gas_price = tx.effective_gas_price(basefee); + let gas = exec_result.gas(); + + let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) { + effective_gas_price.saturating_sub(basefee) + } else { + effective_gas_price + }; + + let reward = + coinbase_gas_price.saturating_mul((gas.spent() - gas.refunded() as u64) as u128); + token_operation::(context, TREASURY, beneficiary, U256::from(reward))?; + + Ok(()) + } +} diff --git a/examples/erc20_gas/src/handlers/mod.rs b/examples/erc20_gas/src/handlers/mod.rs deleted file mode 100644 index cc25fb1e21..0000000000 --- a/examples/erc20_gas/src/handlers/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -pub mod post_execution; -pub mod pre_execution; -pub mod validation; - -pub use post_execution::Erc20PostExecution; -pub use pre_execution::Erc20PreExecution; -pub use validation::Erc20Validation; - -use revm::{ - context::{block::BlockEnv, tx::TxEnv, CfgEnv, Context}, - context_interface::result::{EVMError, InvalidTransaction}, - database_interface::Database, - handler::{EthExecution, EthHandler}, - Evm, -}; - -pub type Erc20GasError = EVMError<::Error, InvalidTransaction>; - -pub type Erc20GasContext = Context; - -pub type CustomHandler< - CTX, - ERROR, - VAL = Erc20Validation, - PREEXEC = Erc20PreExecution, - EXEC = EthExecution, - POSTEXEC = Erc20PostExecution, -> = EthHandler; - -pub type CustomEvm = Evm< - Erc20GasError, - Erc20GasContext, - CustomHandler, Erc20GasError>, ->; diff --git a/examples/erc20_gas/src/handlers/post_execution.rs b/examples/erc20_gas/src/handlers/post_execution.rs deleted file mode 100644 index 3335033606..0000000000 --- a/examples/erc20_gas/src/handlers/post_execution.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::{token_operation, TREASURY}; -use revm::{ - context::Cfg, - context_interface::{ - result::{HaltReason, HaltReasonTrait, InvalidHeader, InvalidTransaction, ResultAndState}, - Block, JournalDBError, Transaction, TransactionGetter, - }, - handler::{EthPostExecution, EthPostExecutionContext, EthPostExecutionError, FrameResult}, - handler_interface::{InitialAndFloorGas, PostExecutionHandler}, - precompile::PrecompileErrors, - primitives::U256, - specification::hardfork::SpecId, -}; - -pub struct Erc20PostExecution { - inner: EthPostExecution, -} - -impl Erc20PostExecution { - pub fn new() -> Self { - Self { - inner: EthPostExecution::new(), - } - } -} - -impl Default for Erc20PostExecution { - fn default() -> Self { - Self::new() - } -} - -impl PostExecutionHandler for Erc20PostExecution -where - CTX: EthPostExecutionContext, - ERROR: EthPostExecutionError - + From - + From - + From> - + From, - HALTREASON: HaltReasonTrait, -{ - type Context = CTX; - type Error = ERROR; - type ExecResult = FrameResult; - type Output = ResultAndState; - - fn eip7623_check_gas_floor( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - init_and_floor_gas: InitialAndFloorGas, - ) { - self.inner - .eip7623_check_gas_floor(context, exec_result, init_and_floor_gas) - } - - fn refund( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - eip7702_refund: i64, - ) { - self.inner.refund(context, exec_result, eip7702_refund) - } - - fn reimburse_caller( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - ) -> Result<(), Self::Error> { - let basefee = context.block().basefee() as u128; - let caller = context.tx().caller(); - let effective_gas_price = context.tx().effective_gas_price(basefee); - let gas = exec_result.gas(); - - let reimbursement = - effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128); - token_operation::(context, TREASURY, caller, U256::from(reimbursement))?; - - Ok(()) - } - - fn reward_beneficiary( - &self, - context: &mut Self::Context, - exec_result: &mut Self::ExecResult, - ) -> Result<(), Self::Error> { - let tx = context.tx(); - let beneficiary = context.block().beneficiary(); - let basefee = context.block().basefee() as u128; - let effective_gas_price = tx.effective_gas_price(basefee); - let gas = exec_result.gas(); - - let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) { - effective_gas_price.saturating_sub(basefee) - } else { - effective_gas_price - }; - - let reward = - coinbase_gas_price.saturating_mul((gas.spent() - gas.refunded() as u64) as u128); - token_operation::(context, TREASURY, beneficiary, U256::from(reward))?; - - Ok(()) - } - - fn output( - &self, - context: &mut Self::Context, - result: Self::ExecResult, - ) -> Result { - self.inner.output(context, result) - } - - fn clear(&self, context: &mut Self::Context) { - self.inner.clear(context) - } -} diff --git a/examples/erc20_gas/src/handlers/pre_execution.rs b/examples/erc20_gas/src/handlers/pre_execution.rs deleted file mode 100644 index eef01ee7b1..0000000000 --- a/examples/erc20_gas/src/handlers/pre_execution.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::{token_operation, TREASURY}; -use revm::{ - context_interface::{ - result::InvalidHeader, Block, Transaction, TransactionGetter, TransactionType, - }, - handler::{EthPreExecution, EthPreExecutionContext, EthPreExecutionError}, - handler_interface::PreExecutionHandler, - precompile::PrecompileErrors, - primitives::U256, -}; - -pub struct Erc20PreExecution { - inner: EthPreExecution, -} - -impl Erc20PreExecution { - pub fn new() -> Self { - Self { - inner: EthPreExecution::new(), - } - } -} - -impl Default for Erc20PreExecution { - fn default() -> Self { - Self::new() - } -} - -impl PreExecutionHandler for Erc20PreExecution -where - CTX: EthPreExecutionContext, - ERROR: EthPreExecutionError + From + From, -{ - type Context = CTX; - type Error = ERROR; - - fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - self.inner.load_accounts(context) - } - - fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result { - self.inner.apply_eip7702_auth_list(context) - } - - fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let basefee = context.block().basefee() as u128; - let blob_price = context.block().blob_gasprice().unwrap_or_default(); - let effective_gas_price = context.tx().effective_gas_price(basefee); - - let mut gas_cost = (context.tx().gas_limit() as u128).saturating_mul(effective_gas_price); - - if context.tx().tx_type() == TransactionType::Eip4844 { - let blob_gas = context.tx().total_blob_gas() as u128; - gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); - } - - let caller = context.tx().caller(); - token_operation::(context, caller, TREASURY, U256::from(gas_cost))?; - - Ok(()) - } -} diff --git a/examples/erc20_gas/src/handlers/validation.rs b/examples/erc20_gas/src/handlers/validation.rs deleted file mode 100644 index 68b37eb009..0000000000 --- a/examples/erc20_gas/src/handlers/validation.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::TOKEN; -use alloy_sol_types::SolValue; -use revm::{ - context::Cfg, - context_interface::{ - result::InvalidTransaction, Journal, Transaction, TransactionGetter, TransactionType, - }, - handler::{EthValidation, EthValidationContext, EthValidationError}, - handler_interface::{InitialAndFloorGas, ValidationHandler}, - primitives::{keccak256, U256}, -}; -use std::cmp::Ordering; - -pub struct Erc20Validation { - inner: EthValidation, -} - -impl Erc20Validation { - pub fn new() -> Self { - Self { - inner: EthValidation::new(), - } - } -} - -impl Default for Erc20Validation { - fn default() -> Self { - Self::new() - } -} - -impl ValidationHandler for Erc20Validation -where - CTX: EthValidationContext, - ERROR: EthValidationError, -{ - type Context = CTX; - type Error = ERROR; - - fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { - self.inner.validate_env(context) - } - - fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let caller = context.tx().caller(); - let caller_nonce = context.journal().load_account(caller)?.data.info.nonce; - let token_account = context.journal().load_account(TOKEN)?.data.clone(); - - if !context.cfg().is_nonce_check_disabled() { - let tx_nonce = context.tx().nonce(); - let state_nonce = caller_nonce; - match tx_nonce.cmp(&state_nonce) { - Ordering::Less => { - return Err(ERROR::from(InvalidTransaction::NonceTooLow { - tx: tx_nonce, - state: state_nonce, - })) - } - Ordering::Greater => { - return Err(ERROR::from(InvalidTransaction::NonceTooHigh { - tx: tx_nonce, - state: state_nonce, - })) - } - _ => (), - } - } - - let mut balance_check = U256::from(context.tx().gas_limit()) - .checked_mul(U256::from(context.tx().max_fee_per_gas())) - .and_then(|gas_cost| gas_cost.checked_add(context.tx().value())) - .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - - if context.tx().tx_type() == TransactionType::Eip4844 { - let tx = context.tx(); - let data_fee = tx.calc_max_data_fee(); - balance_check = balance_check - .checked_add(data_fee) - .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - } - - let account_balance_slot: U256 = keccak256((caller, U256::from(3)).abi_encode()).into(); - let account_balance = token_account - .storage - .get(&account_balance_slot) - .expect("Balance slot not found") - .present_value(); - - if account_balance < balance_check && !context.cfg().is_balance_check_disabled() { - return Err(InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(balance_check), - balance: Box::new(account_balance), - } - .into()); - }; - - Ok(()) - } - - fn validate_initial_tx_gas( - &self, - context: &Self::Context, - ) -> Result { - self.inner.validate_initial_tx_gas(context) - } -} diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index ddb372bd68..a018c6876a 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -5,32 +5,33 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] use alloy_provider::{network::Ethereum, ProviderBuilder, RootProvider}; -use alloy_sol_types::{sol, SolCall, SolValue}; +use alloy_sol_types::SolValue; use alloy_transport_http::Http; -use anyhow::{anyhow, Result}; +use anyhow::Result; use database::{AlloyDB, BlockId, CacheDB}; +use exec::transact_erc20evm_commit; use reqwest::{Client, Url}; use revm::{ context_interface::{ - result::{ExecutionResult, InvalidHeader, InvalidTransaction, Output}, + result::{InvalidHeader, InvalidTransaction}, Journal, JournalDBError, JournalGetter, }, database_interface::WrapDatabaseAsync, - handler::EthExecution, precompile::PrecompileErrors, primitives::{address, keccak256, Address, Bytes, TxKind, U256}, - state::{AccountInfo, EvmStorageSlot}, - Context, EvmCommit, MainEvm, + specification::hardfork::SpecId, + state::AccountInfo, + Context, Database, }; -pub mod handlers; -use handlers::{CustomEvm, CustomHandler, Erc20PostExecution, Erc20PreExecution, Erc20Validation}; +pub mod exec; +pub mod handler; type AlloyCacheDB = CacheDB, Ethereum, RootProvider>>>>; // Constants -pub const TOKEN: Address = address!("1234567890123456789012345678901234567890"); +pub const TOKEN: Address = address!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"); pub const TREASURY: Address = address!("0000000000000000000000000000000000000001"); #[tokio::main] @@ -48,35 +49,32 @@ async fn main() -> Result<()> { // Random empty account: To let account_to = address!("21a4B6F62E51e59274b6Be1705c7c68781B87C77"); - let usdc = address!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"); - // USDC has 6 decimals let hundred_tokens = U256::from(100_000_000_000_000_000u128); - let balance_slot = keccak256((account, U256::from(3)).abi_encode()).into(); - + let balance_slot = erc_address_storage(account); + println!("Balance slot: {balance_slot}"); cache_db - .insert_account_storage(usdc, balance_slot, hundred_tokens) + .insert_account_storage(TOKEN, balance_slot, hundred_tokens * U256::from(2)) .unwrap(); cache_db.insert_account_info( account, AccountInfo { nonce: 0, - balance: hundred_tokens, + balance: hundred_tokens * U256::from(2), code_hash: keccak256(Bytes::new()), code: None, }, ); - let balance_before = balance_of(usdc, account, &mut cache_db).unwrap(); + let balance_before = balance_of(account, &mut cache_db).unwrap(); + println!("Balance before: {balance_before}"); // Transfer 100 tokens from account to account_to // Magic happens here with custom handlers - transfer(account, account_to, hundred_tokens, usdc, &mut cache_db)?; + transfer(account, account_to, hundred_tokens, &mut cache_db)?; - let balance_after = balance_of(usdc, account, &mut cache_db)?; - - println!("Balance before: {balance_before}"); + let balance_after = balance_of(account, &mut cache_db)?; println!("Balance after: {balance_after}"); Ok(()) @@ -96,14 +94,8 @@ where + From> + From, { - let token_account = context.journal().load_account(TOKEN)?.data; - - let sender_balance_slot: U256 = keccak256((sender, U256::from(3)).abi_encode()).into(); - let sender_balance = token_account - .storage - .get(&sender_balance_slot) - .expect("Balance slot not found") - .present_value(); + let sender_balance_slot = erc_address_storage(sender); + let sender_balance = context.journal().sload(TOKEN, sender_balance_slot)?.data; if sender_balance < amount { return Err(ERROR::from( @@ -112,102 +104,47 @@ where } // Subtract the amount from the sender's balance let sender_new_balance = sender_balance.saturating_sub(amount); - token_account.storage.insert( - sender_balance_slot, - EvmStorageSlot::new_changed(sender_balance, sender_new_balance), - ); + context + .journal() + .sstore(TOKEN, sender_balance_slot, sender_new_balance)?; // Add the amount to the recipient's balance - let recipient_balance_slot: U256 = keccak256((recipient, U256::from(3)).abi_encode()).into(); - let recipient_balance = token_account - .storage - .get(&recipient_balance_slot) - .expect("To balance slot not found") - .present_value(); + let recipient_balance_slot = erc_address_storage(recipient); + let recipient_balance = context.journal().sload(TOKEN, recipient_balance_slot)?.data; + let recipient_new_balance = recipient_balance.saturating_add(amount); - token_account.storage.insert( - recipient_balance_slot, - EvmStorageSlot::new_changed(recipient_balance, recipient_new_balance), - ); + context + .journal() + .sstore(TOKEN, recipient_balance_slot, recipient_new_balance)?; Ok(()) } -fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> Result { - sol! { - function balanceOf(address account) public returns (uint256); - } - - let encoded = balanceOfCall { account: address }.abi_encode(); - - let mut evm = MainEvm::new( - Context::builder() - .with_db(alloy_db) - .modify_tx_chained(|tx| { - // 0x1 because calling USDC proxy from zero address fails - tx.caller = address!("0000000000000000000000000000000000000001"); - tx.kind = TxKind::Call(token); - tx.data = encoded.into(); - tx.value = U256::from(0); - }), - CustomHandler::default(), - ); - - let ref_tx = evm.exec_commit().unwrap(); - let value = match ref_tx { - ExecutionResult::Success { - output: Output::Call(value), - .. - } => value, - result => return Err(anyhow!("'balanceOf' execution failed: {result:?}")), - }; - - let balance = ::abi_decode(&value, false)?; - - Ok(balance) +fn balance_of(address: Address, alloy_db: &mut AlloyCacheDB) -> Result { + let slot = erc_address_storage(address); + alloy_db.storage(TOKEN, slot).map_err(From::from) } -fn transfer( - from: Address, - to: Address, - amount: U256, - token: Address, - cache_db: &mut AlloyCacheDB, -) -> Result<()> { - sol! { - function transfer(address to, uint amount) external returns (bool); - } - - let encoded = transferCall { to, amount }.abi_encode(); - - let mut evm = CustomEvm::new( - Context::builder() - .with_db(cache_db) - .modify_tx_chained(|tx| { - tx.caller = from; - tx.kind = TxKind::Call(token); - tx.data = encoded.into(); - tx.value = U256::from(0); - }), - CustomHandler::new( - Erc20Validation::new(), - Erc20PreExecution::new(), - EthExecution::new(), - Erc20PostExecution::new(), - ), - ); - let ref_tx = evm.exec_commit().unwrap(); - let success: bool = match ref_tx { - ExecutionResult::Success { - output: Output::Call(value), - .. - } => ::abi_decode(&value, false)?, - result => return Err(anyhow!("'transfer' execution failed: {result:?}")), - }; - - if !success { - return Err(anyhow!("'transfer' failed")); - } +fn transfer(from: Address, to: Address, amount: U256, cache_db: &mut AlloyCacheDB) -> Result<()> { + let mut ctx = Context::builder() + .with_db(cache_db) + .modify_cfg_chained(|cfg| { + cfg.spec = SpecId::CANCUN; + }) + .modify_tx_chained(|tx| { + tx.caller = from; + tx.kind = TxKind::Call(to); + tx.value = amount; + tx.gas_price = 2; + }) + .modify_block_chained(|b| { + b.basefee = 1; + }); + transact_erc20evm_commit(&mut ctx).unwrap(); Ok(()) } + +pub fn erc_address_storage(address: Address) -> U256 { + keccak256((address, U256::from(4)).abi_encode()).into() +} diff --git a/examples/uniswap_get_reserves/src/main.rs b/examples/uniswap_get_reserves/src/main.rs index 7ab56c7c79..09c07de66d 100644 --- a/examples/uniswap_get_reserves/src/main.rs +++ b/examples/uniswap_get_reserves/src/main.rs @@ -3,19 +3,13 @@ use alloy_eips::BlockId; use alloy_provider::ProviderBuilder; -use alloy_sol_types::sol; -use alloy_sol_types::SolCall; +use alloy_sol_types::{sol, SolCall}; use database::{AlloyDB, CacheDB}; -use revm::database_interface::WrapDatabaseAsync; -use revm::handler::EthHandler; -use revm::Context; -use revm::EvmExec; use revm::{ context_interface::result::{ExecutionResult, Output}, - database_interface::DatabaseRef, - database_interface::EmptyDB, + database_interface::{DatabaseRef, EmptyDB, WrapDatabaseAsync}, primitives::{address, TxKind, U256}, - MainEvm, + transact_main, Context, }; #[tokio::main] @@ -71,25 +65,22 @@ async fn main() -> anyhow::Result<()> { .unwrap(); // Initialise an empty (default) EVM - let mut evm = MainEvm::new( - Context::builder() - .with_db(cache_db) - .modify_tx_chained(|tx| { - // fill in missing bits of env struct - // change that to whatever caller you want to be - tx.caller = address!("0000000000000000000000000000000000000000"); - // account you want to transact with - tx.kind = TxKind::Call(pool_address); - // calldata formed via abigen - tx.data = encoded.into(); - // transaction value in wei - tx.value = U256::from(0); - }), - EthHandler::default(), - ); + let mut ctx = Context::builder() + .with_db(cache_db) + .modify_tx_chained(|tx| { + // fill in missing bits of env struct + // change that to whatever caller you want to be + tx.caller = address!("0000000000000000000000000000000000000000"); + // account you want to transact with + tx.kind = TxKind::Call(pool_address); + // calldata formed via abigen + tx.data = encoded.into(); + // transaction value in wei + tx.value = U256::from(0); + }); // Execute transaction without writing to the DB - let ref_tx = evm.exec().unwrap(); + let ref_tx = transact_main(&mut ctx).unwrap(); // Select ExecutionResult struct let result = ref_tx.result; diff --git a/examples/uniswap_v2_usdc_swap/src/main.rs b/examples/uniswap_v2_usdc_swap/src/main.rs index fd97dc1f8f..8ec72fbcd9 100644 --- a/examples/uniswap_v2_usdc_swap/src/main.rs +++ b/examples/uniswap_v2_usdc_swap/src/main.rs @@ -11,10 +11,9 @@ use reqwest::Client; use revm::{ context_interface::result::{ExecutionResult, Output}, database_interface::WrapDatabaseAsync, - handler::EthHandler, primitives::{address, keccak256, Address, Bytes, TxKind, U256}, state::AccountInfo, - Context, EvmCommit, EvmExec, MainEvm, + transact_main, transact_main_commit, Context, }; use std::ops::Div; @@ -97,20 +96,17 @@ fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> let encoded = balanceOfCall { account: address }.abi_encode(); - let mut evm = MainEvm::new( - Context::builder() - .with_db(alloy_db) - .modify_tx_chained(|tx| { - // 0x1 because calling USDC proxy from zero address fails - tx.caller = address!("0000000000000000000000000000000000000001"); - tx.kind = TxKind::Call(token); - tx.data = encoded.into(); - tx.value = U256::from(0); - }), - EthHandler::default(), - ); - - let ref_tx = evm.exec().unwrap(); + let mut ctx = Context::builder() + .with_db(alloy_db) + .modify_tx_chained(|tx| { + // 0x1 because calling USDC proxy from zero address fails + tx.caller = address!("0000000000000000000000000000000000000001"); + tx.kind = TxKind::Call(token); + tx.data = encoded.into(); + tx.value = U256::from(0); + }); + + let ref_tx = transact_main(&mut ctx).unwrap(); let result = ref_tx.result; let value = match result { @@ -144,19 +140,16 @@ async fn get_amount_out( } .abi_encode(); - let mut evm = MainEvm::new( - Context::builder() - .with_db(cache_db) - .modify_tx_chained(|tx| { - tx.caller = address!("0000000000000000000000000000000000000000"); - tx.kind = TxKind::Call(uniswap_v2_router); - tx.data = encoded.into(); - tx.value = U256::from(0); - }), - EthHandler::default(), - ); - - let ref_tx = evm.transact().unwrap(); + let mut ctx = Context::builder() + .with_db(cache_db) + .modify_tx_chained(|tx| { + tx.caller = address!("0000000000000000000000000000000000000000"); + tx.kind = TxKind::Call(uniswap_v2_router); + tx.data = encoded.into(); + tx.value = U256::from(0); + }); + + let ref_tx = transact_main(&mut ctx).unwrap(); let result = ref_tx.result; let value = match result { @@ -179,19 +172,16 @@ fn get_reserves(pair_address: Address, cache_db: &mut AlloyCacheDB) -> Result<(U let encoded = getReservesCall {}.abi_encode(); - let mut evm = MainEvm::new( - Context::builder() - .with_db(cache_db) - .modify_tx_chained(|tx| { - tx.caller = address!("0000000000000000000000000000000000000000"); - tx.kind = TxKind::Call(pair_address); - tx.data = encoded.into(); - tx.value = U256::from(0); - }), - EthHandler::default(), - ); - - let ref_tx = evm.transact().unwrap(); + let mut ctx = Context::builder() + .with_db(cache_db) + .modify_tx_chained(|tx| { + tx.caller = address!("0000000000000000000000000000000000000000"); + tx.kind = TxKind::Call(pair_address); + tx.data = encoded.into(); + tx.value = U256::from(0); + }); + + let ref_tx = transact_main(&mut ctx).unwrap(); let result = ref_tx.result; let value = match result { @@ -230,20 +220,17 @@ fn swap( } .abi_encode(); - let mut evm = MainEvm::new( - Context::builder() - .with_db(cache_db) - .modify_tx_chained(|tx| { - tx.caller = from; - tx.kind = TxKind::Call(pool_address); - tx.data = encoded.into(); - tx.value = U256::from(0); - tx.nonce = 1; - }), - EthHandler::default(), - ); - - let ref_tx = evm.exec_commit().unwrap(); + let mut ctx = Context::builder() + .with_db(cache_db) + .modify_tx_chained(|tx| { + tx.caller = from; + tx.kind = TxKind::Call(pool_address); + tx.data = encoded.into(); + tx.value = U256::from(0); + tx.nonce = 1; + }); + + let ref_tx = transact_main_commit(&mut ctx).unwrap(); match ref_tx { ExecutionResult::Success { .. } => {} @@ -266,19 +253,16 @@ fn transfer( let encoded = transferCall { to, amount }.abi_encode(); - let mut evm = MainEvm::new( - Context::builder() - .with_db(cache_db) - .modify_tx_chained(|tx| { - tx.caller = from; - tx.kind = TxKind::Call(token); - tx.data = encoded.into(); - tx.value = U256::from(0); - }), - EthHandler::default(), - ); - - let ref_tx = evm.exec_commit().unwrap(); + let mut ctx = Context::builder() + .with_db(cache_db) + .modify_tx_chained(|tx| { + tx.caller = from; + tx.kind = TxKind::Call(token); + tx.data = encoded.into(); + tx.value = U256::from(0); + }); + + let ref_tx = transact_main_commit(&mut ctx).unwrap(); let success: bool = match ref_tx { ExecutionResult::Success { output: Output::Call(value), diff --git a/graph.png b/graph.png new file mode 100644 index 0000000000..4f52c51627 Binary files /dev/null and b/graph.png differ