diff --git a/Cargo.lock b/Cargo.lock index 2081056e53..2900b65770 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1507,16 +1507,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "example-cheatcode-inspector" -version = "0.0.0" -dependencies = [ - "anyhow", - "revm", - "revm-database", - "revm-inspector", -] - [[package]] name = "example-contract-deployment" version = "0.0.0" @@ -3394,7 +3384,6 @@ dependencies = [ "revm-database", "revm-database-interface", "revm-handler", - "revm-handler-interface", "revm-interpreter", "revm-precompile", "revm-primitives", @@ -3419,6 +3408,7 @@ dependencies = [ name = "revm-context" version = "1.0.0" dependencies = [ + "auto_impl", "cfg-if", "derive-where", "revm-bytecode", @@ -3485,11 +3475,11 @@ dependencies = [ name = "revm-handler" version = "1.0.0" dependencies = [ + "auto_impl", "revm-bytecode", "revm-context", "revm-context-interface", "revm-database", - "revm-handler-interface", "revm-interpreter", "revm-precompile", "revm-primitives", @@ -3498,21 +3488,10 @@ dependencies = [ "serde", ] -[[package]] -name = "revm-handler-interface" -version = "1.0.0" -dependencies = [ - "auto_impl", - "revm-database", - "revm-primitives", - "revm-specification", -] - [[package]] name = "revm-inspector" version = "1.0.0" dependencies = [ - "auto_impl", "revm", "revm-database", "serde", @@ -3541,12 +3520,10 @@ dependencies = [ "alloy-sol-types", "anyhow", "auto_impl", - "derive_more", "indicatif", "once_cell", "revm", "revm-database", - "revm-inspector", "revm-precompile", "rstest 0.23.0", "serde", diff --git a/Cargo.toml b/Cargo.toml index fcfc566312..3834780532 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ members = [ "crates/specification", "crates/context", "crates/context/interface", - "crates/handler/interface", "crates/handler", # variants @@ -27,7 +26,7 @@ members = [ # examples "examples/block_traces", - "examples/cheatcode_inspector", + #"examples/cheatcode_inspector", "examples/contract_deployment", "examples/database_components", "examples/uniswap_get_reserves", @@ -54,8 +53,6 @@ statetest-types = { path = "crates/statetest-types", package = "revm-statetest-t context = { path = "crates/context", package = "revm-context", version = "1.0.0", default-features = false } context-interface = { path = "crates/context/interface", package = "revm-context-interface", version = "1.0.0", default-features = false } handler = { path = "crates/handler", package = "revm-handler", version = "1.0.0", default-features = false } -handler-interface = { path = "crates/handler/interface", package = "revm-handler-interface", version = "1.0.0", default-features = false } - # misc cfg-if = { version = "1.0", default-features = false } auto_impl = { version = "1.2.0" } diff --git a/bins/revme/src/cmd/bench/analysis.rs b/bins/revme/src/cmd/bench/analysis.rs index ee397d4a64..5b375dd20b 100644 --- a/bins/revme/src/cmd/bench/analysis.rs +++ b/bins/revme/src/cmd/bench/analysis.rs @@ -4,7 +4,7 @@ use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; use revm::{ bytecode::Bytecode, primitives::{bytes, hex, Bytes, TxKind}, - Context, ExecuteEvm, + Context, ExecuteEvm, MainBuilder, MainContext, }; const BYTES: &str = include_str!("analysis.hex"); @@ -13,21 +13,22 @@ pub fn run() { let bytecode = Bytecode::new_raw(Bytes::from(hex::decode(BYTES).unwrap())); // BenchmarkDB is dummy state that implements Database trait. - let mut context = Context::builder() + let context = Context::mainnet() .with_db(BenchmarkDB::new_bytecode(bytecode)) .modify_tx_chained(|tx| { // Execution globals block hash/gas_limit/coinbase/timestamp.. tx.caller = BENCH_CALLER; tx.kind = TxKind::Call(BENCH_TARGET); - //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); tx.data = bytes!("8035F0CE"); }); + let mut evm = context.build_mainnet(); + let time = Instant::now(); - let _ = context.exec_previous(); + let _ = evm.transact_previous(); println!("First init: {:?}", time.elapsed()); let time = Instant::now(); - let _ = context.exec_previous(); + let _ = evm.transact_previous(); println!("Run: {:?}", time.elapsed()); } diff --git a/bins/revme/src/cmd/bench/burntpix.rs b/bins/revme/src/cmd/bench/burntpix.rs index 35929f8b9f..40e1c72dc9 100644 --- a/bins/revme/src/cmd/bench/burntpix.rs +++ b/bins/revme/src/cmd/bench/burntpix.rs @@ -14,7 +14,7 @@ use revm::{ database_interface::EmptyDB, primitives::{hex, keccak256, Address, Bytes, TxKind, B256, U256}, state::{AccountInfo, Bytecode}, - transact_main, Context, + Context, ExecuteEvm, MainBuilder, MainContext, }; use std::fs::File; @@ -36,15 +36,18 @@ pub fn run() { let db = init_db(); - let mut context = Context::builder().with_db(db).modify_tx_chained(|tx| { - tx.caller = BENCH_CALLER; - tx.kind = TxKind::Call(BURNTPIX_MAIN_ADDRESS); - tx.data = run_call_data.clone().into(); - tx.gas_limit = u64::MAX; - }); + let mut evm = Context::mainnet() + .with_db(db) + .modify_tx_chained(|tx| { + tx.caller = BENCH_CALLER; + tx.kind = TxKind::Call(BURNTPIX_MAIN_ADDRESS); + tx.data = run_call_data.clone().into(); + tx.gas_limit = u64::MAX; + }) + .build_mainnet(); let started = Instant::now(); - let tx_result = transact_main(&mut context).unwrap().result; + let tx_result = evm.transact_previous().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 828a9d1183..41bd92d3cd 100644 --- a/bins/revme/src/cmd/bench/snailtracer.rs +++ b/bins/revme/src/cmd/bench/snailtracer.rs @@ -2,11 +2,11 @@ use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; use revm::{ bytecode::Bytecode, primitives::{bytes, hex, Bytes, TxKind}, - transact_main, Context, + Context, ExecuteEvm, MainBuilder, MainContext, }; pub fn simple_example(bytecode: Bytecode) { - let mut context = Context::builder() + let mut evm = Context::mainnet() .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) .modify_tx_chained(|tx| { // Execution globals block hash/gas_limit/coinbase/timestamp.. @@ -14,8 +14,9 @@ pub fn simple_example(bytecode: Bytecode) { tx.kind = TxKind::Call(BENCH_TARGET); tx.data = bytes!("30627b7c"); tx.gas_limit = 1_000_000_000; - }); - let _ = transact_main(&mut context).unwrap(); + }) + .build_mainnet(); + let _ = evm.transact_previous().unwrap(); } pub fn run() { diff --git a/bins/revme/src/cmd/bench/transfer.rs b/bins/revme/src/cmd/bench/transfer.rs index d7a60da10a..0cd26dc1b9 100644 --- a/bins/revme/src/cmd/bench/transfer.rs +++ b/bins/revme/src/cmd/bench/transfer.rs @@ -1,26 +1,29 @@ -use std::time::Instant; - use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; use revm::{ bytecode::Bytecode, primitives::{TxKind, U256}, - Context, ExecuteEvm, + Context, ExecuteEvm, MainBuilder, MainContext, }; +use std::time::Instant; pub fn run() { - let mut context = Context::builder() + let time = Instant::now(); + let mut evm = Context::mainnet() .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) .modify_tx_chained(|tx| { // Execution globals block hash/gas_limit/coinbase/timestamp.. tx.caller = BENCH_CALLER; tx.kind = TxKind::Call(BENCH_TARGET); tx.value = U256::from(10); - }); + }) + .build_mainnet(); + println!("Init: {:?}", time.elapsed()); + let time = Instant::now(); - let _ = context.exec_previous(); - println!("First init: {:?}", time.elapsed()); + let _ = evm.transact_previous(); + println!("First run: {:?}", time.elapsed()); let time = Instant::now(); - let _ = context.exec_previous(); - println!("Run: {:?}", time.elapsed()); + let _ = evm.transact_previous(); + println!("Second run: {:?}", time.elapsed()); } diff --git a/bins/revme/src/cmd/evmrunner.rs b/bins/revme/src/cmd/evmrunner.rs index 35f7650512..89fbc40a16 100644 --- a/bins/revme/src/cmd/evmrunner.rs +++ b/bins/revme/src/cmd/evmrunner.rs @@ -1,10 +1,10 @@ use clap::Parser; use database::BenchmarkDB; -use inspector::{exec::InspectEvm, inspectors::TracerEip3155}; +use inspector::inspectors::TracerEip3155; use revm::{ bytecode::{Bytecode, BytecodeDecodeError}, primitives::{address, hex, Address, TxKind}, - transact_main, Context, Database, ExecuteEvm, + Context, Database, ExecuteEvm, InspectEvm, MainBuilder, MainContext, }; use std::io::Error as IoError; use std::path::PathBuf; @@ -82,30 +82,31 @@ impl Cmd { // BenchmarkDB is dummy state that implements Database trait. // The bytecode is deployed at zero address. - 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; - }); + let mut evm = Context::mainnet() + .with_db(db) + .modify_tx_chained(|tx| { + tx.caller = CALLER; + tx.kind = TxKind::Call(Address::ZERO); + tx.data = input; + tx.nonce = nonce; + }) + .build_mainnet_with_inspector(TracerEip3155::new(Box::new(std::io::stdout()))); if self.bench { // Microbenchmark let bench_options = microbench::Options::default().time(Duration::from_secs(3)); microbench::bench(&bench_options, "Run bytecode", || { - let _ = ctx.exec_previous().unwrap(); + let _ = evm.transact_previous().unwrap(); }); return Ok(()); } let out = if self.trace { - let inspector = TracerEip3155::new(Box::new(std::io::stdout())); - ctx.inspect_previous(inspector) - .map_err(|_| Errors::EVMError)? + evm.inspect_previous().map_err(|_| Errors::EVMError)? } else { - let out = transact_main(&mut ctx).map_err(|_| Errors::EVMError)?; + let out = evm.transact_previous().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 5db4610714..a688488506 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -4,7 +4,7 @@ use super::{ }; use database::State; use indicatif::{ProgressBar, ProgressDrawTarget}; -use inspector::{exec::InspectCommitEvm, inspectors::TracerEip3155}; +use inspector::inspectors::TracerEip3155; use revm::{ bytecode::Bytecode, context::{block::BlockEnv, cfg::CfgEnv, tx::TxEnv}, @@ -16,7 +16,7 @@ use revm::{ database_interface::EmptyDB, primitives::{keccak256, Bytes, TxKind, B256}, specification::{eip4844::TARGET_BLOB_GAS_PER_BLOCK_CANCUN, hardfork::SpecId}, - Context, ExecuteCommitEvm, + Context, ExecuteCommitEvm, InspectCommitEvm, MainBuilder, MainContext, }; use serde_json::json; use statetest_types::{SpecName, Test, TestSuite}; @@ -411,28 +411,30 @@ pub fn execute_test_suite( .with_cached_prestate(cache) .with_bundle_update() .build(); - let mut ctx = Context::builder() + let mut evm = Context::mainnet() .with_block(&block) .with_tx(&tx) .with_cfg(&cfg) - .with_db(&mut state); + .with_db(&mut state) + .build_mainnet(); // Do the deed let (e, exec_result) = if trace { - let mut ctx = Context::builder() + let mut evm = Context::mainnet() .with_block(&block) .with_tx(&tx) .with_cfg(&cfg) - .with_db(&mut state); + .with_db(&mut state) + .build_mainnet_with_inspector( + TracerEip3155::buffered(stderr()).without_summary(), + ); let timer = Instant::now(); - let res = ctx.inspect_commit_previous( - TracerEip3155::buffered(stderr()).without_summary(), - ); + let res = evm.inspect_commit_previous(); *elapsed.lock().unwrap() += timer.elapsed(); let spec = cfg.spec(); - let db = &mut ctx.journaled_state.database; + let db = &mut evm.data.ctx.journaled_state.database; // Dump state and traces if test failed let output = check_evm_execution( &test, @@ -449,11 +451,11 @@ pub fn execute_test_suite( (e, res) } else { let timer = Instant::now(); - let res = ctx.exec_commit_previous(); + let res = evm.transact_commit_previous(); *elapsed.lock().unwrap() += timer.elapsed(); let spec = cfg.spec(); - let db = ctx.journaled_state.database; + let db = evm.data.ctx.journaled_state.database; // Dump state and traces if test failed let output = check_evm_execution( &test, @@ -491,19 +493,24 @@ pub fn execute_test_suite( println!("\nTraces:"); - let mut ctx = Context::builder() + let mut evm = Context::mainnet() .with_db(&mut state) .with_block(&block) .with_tx(&tx) - .with_cfg(&cfg); + .with_cfg(&cfg) + .build_mainnet_with_inspector( + TracerEip3155::buffered(stderr()).without_summary(), + ); - let _ = ctx - .inspect_commit_previous(TracerEip3155::buffered(stderr()).without_summary()); + let _ = evm.inspect_commit_previous(); println!("\nExecution result: {exec_result:#?}"); println!("\nExpected exception: {:?}", test.expect_exception); println!("\nState before: {cache_state:#?}"); - println!("\nState after: {:#?}", ctx.journaled_state.database.cache); + println!( + "\nState after: {:#?}", + evm.data.ctx.journaled_state.database.cache + ); println!("\nSpecification: {:?}", cfg.spec); println!("\nTx: {tx:#?}"); println!("Block: {block:#?}"); diff --git a/crates/context/Cargo.toml b/crates/context/Cargo.toml index bd08b24cc0..6d7a4758c0 100644 --- a/crates/context/Cargo.toml +++ b/crates/context/Cargo.toml @@ -30,6 +30,7 @@ database-interface.workspace = true state.workspace = true specification.workspace = true bytecode.workspace = true +auto_impl.workspace = true # misc derive-where.workspace = true diff --git a/crates/context/interface/src/block.rs b/crates/context/interface/src/block.rs index 994e1e26e4..63ee7f0d3c 100644 --- a/crates/context/interface/src/block.rs +++ b/crates/context/interface/src/block.rs @@ -4,7 +4,6 @@ pub use blob::{calc_blob_gasprice, calc_excess_blob_gas, BlobExcessGasAndPrice}; use auto_impl::auto_impl; use primitives::{Address, B256, U256}; -use std::boxed::Box; /// Trait for retrieving block information required for execution. #[auto_impl(&, &mut, Box, Arc)] @@ -69,26 +68,3 @@ pub trait Block { self.blob_excess_gas_and_price().map(|a| a.excess_blob_gas) } } - -#[auto_impl(&, &mut, Box, Arc)] -pub trait BlockGetter { - type Block: Block; - - fn block(&self) -> &Self::Block; -} - -pub trait BlockSetter: BlockGetter { - fn set_block(&mut self, block: ::Block); -} - -impl BlockSetter for &mut T { - fn set_block(&mut self, block: ::Block) { - (**self).set_block(block) - } -} - -impl BlockSetter for Box { - fn set_block(&mut self, block: ::Block) { - (**self).set_block(block) - } -} diff --git a/crates/context/interface/src/cfg.rs b/crates/context/interface/src/cfg.rs index 1872f2ace2..31dd3ddef3 100644 --- a/crates/context/interface/src/cfg.rs +++ b/crates/context/interface/src/cfg.rs @@ -59,10 +59,3 @@ pub enum CreateScheme { salt: U256, }, } - -#[auto_impl(&, &mut, Box, Arc)] -pub trait CfgGetter { - type Cfg: Cfg; - - fn cfg(&self) -> &Self::Cfg; -} diff --git a/crates/context/interface/src/context.rs b/crates/context/interface/src/context.rs index a704965032..94a435d1bd 100644 --- a/crates/context/interface/src/context.rs +++ b/crates/context/interface/src/context.rs @@ -1,15 +1,23 @@ +use crate::{Block, Cfg, Database, Journal, Transaction}; 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; +#[auto_impl(&mut, Box)] +pub trait ContextTrait { + type Block: Block; + type Tx: Transaction; + type Cfg: Cfg; + type Db: Database; + type Journal: Journal; + type Chain; - /// Load access list - fn load_access_list(&mut self) -> Result<(), Self::Error>; + fn tx(&self) -> &Self::Tx; + fn block(&self) -> &Self::Block; + fn cfg(&self) -> &Self::Cfg; + fn journal(&mut self) -> &mut Self::Journal; + fn journal_ref(&self) -> &Self::Journal; + fn db(&mut self) -> &mut Self::Db; + fn db_ref(&self) -> &Self::Db; + fn chain(&mut self) -> &mut Self::Chain; + fn error(&mut self) -> &mut Result<(), ::Error>; + fn tx_journal(&mut self) -> (&mut Self::Tx, &mut Self::Journal); } diff --git a/crates/context/interface/src/errors.rs b/crates/context/interface/src/errors.rs deleted file mode 100644 index c5d2f461a9..0000000000 --- a/crates/context/interface/src/errors.rs +++ /dev/null @@ -1,9 +0,0 @@ -use auto_impl::auto_impl; - -// TODO : Change the name of the trait -#[auto_impl(&mut, Box)] -pub trait ErrorGetter { - type Error; - - fn take_error(&mut self) -> Result<(), Self::Error>; -} diff --git a/crates/context/interface/src/host.rs b/crates/context/interface/src/host.rs index a377210e18..3c93896a6e 100644 --- a/crates/context/interface/src/host.rs +++ b/crates/context/interface/src/host.rs @@ -1,22 +1,14 @@ -mod dummy; - pub use crate::journaled_state::StateLoad; use database_interface::Database; -pub use dummy::DummyHost; -use crate::{ - journaled_state::AccountLoad, Block, BlockGetter, CfgGetter, Journal, JournalGetter, - TransactionGetter, -}; +use crate::{context::ContextTrait, journaled_state::AccountLoad, Block, Journal}; use primitives::{Address, Bytes, Log, B256, BLOCK_HASH_HISTORY, U256}; -use std::boxed::Box; /// EVM context host. -pub trait Host: JournalGetter + TransactionGetter + BlockGetter + CfgGetter { - fn set_error( - &mut self, - error: <<::Journal as Journal>::Database as Database>::Error, - ); +pub trait Host: ContextTrait { + fn set_error(&mut self, error: ::Error) { + *self.error() = Err(error); + } /// Gets the block hash of the given block `number`. fn block_hash(&mut self, requested_number: u64) -> Option { @@ -126,23 +118,7 @@ pub trait Host: JournalGetter + TransactionGetter + BlockGetter + CfgGetter { } } -impl Host for &mut T { - fn set_error( - &mut self, - error: <<::Journal as Journal>::Database as Database>::Error, - ) { - (**self).set_error(error) - } -} - -impl Host for Box { - fn set_error( - &mut self, - error: <<::Journal as Journal>::Database as Database>::Error, - ) { - (**self).set_error(error) - } -} +impl Host for T {} /// Represents the result of an `sstore` operation. #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -204,20 +180,3 @@ pub struct SelfDestructResult { pub target_exists: bool, pub previously_destroyed: bool, } - -// TODO TEST -// #[cfg(test)] -// mod tests { -// use database_interface::EmptyDB; -// use context_interface::EthereumWiring; - -// use super::*; - -// fn assert_host() {} - -// #[test] -// fn object_safety() { -// assert_host::>>(); -// assert_host::>>(); -// } -// } diff --git a/crates/context/interface/src/host/dummy.rs b/crates/context/interface/src/host/dummy.rs deleted file mode 100644 index 3e897397ba..0000000000 --- a/crates/context/interface/src/host/dummy.rs +++ /dev/null @@ -1,115 +0,0 @@ -use super::Host; -use crate::{ - Block, BlockGetter, Cfg, CfgGetter, Journal, JournalGetter, Transaction, TransactionGetter, -}; -use database_interface::DatabaseGetter; - -/// A dummy [Host] implementation. -#[derive(Clone, Debug, Default)] -pub struct DummyHost -where - BLOCK: Block, - TX: Transaction, - CFG: Cfg, - JOURNAL: Journal, -{ - pub tx: TX, - pub block: BLOCK, - pub cfg: CFG, - pub journal: JOURNAL, -} - -impl DummyHost -where - BLOCK: Block, - TX: Transaction, - CFG: Cfg + Default, - JOURNAL: Journal + Default, -{ - /// Create a new dummy host with the given [`Transaction`] and [`Block`]. - #[inline] - pub fn new(tx: TX, block: BLOCK) -> Self { - Self { - tx, - block, - cfg: CFG::default(), - journal: JOURNAL::default(), - } - } - - /// Clears the storage and logs of the dummy host. - #[inline] - pub fn clear(&mut self) { - self.journal.clear(); - } -} - -impl BlockGetter - for DummyHost -{ - type Block = BLOCK; - - fn block(&self) -> &Self::Block { - &self.block - } -} - -impl TransactionGetter - for DummyHost -{ - type Transaction = TX; - - fn tx(&self) -> &Self::Transaction { - &self.tx - } -} - -impl CfgGetter - for DummyHost -{ - type Cfg = CFG; - - fn cfg(&self) -> &Self::Cfg { - &self.cfg - } -} - -impl DatabaseGetter - for DummyHost -{ - type Database = ::Database; - - fn db(&mut self) -> &mut Self::Database { - self.journal.db() - } - - fn db_ref(&self) -> &Self::Database { - self.journal.db_ref() - } -} - -impl JournalGetter - for DummyHost -{ - type Journal = JOURNAL; - - fn journal(&mut self) -> &mut Self::Journal { - &mut self.journal - } - - fn journal_ref(&self) -> &Self::Journal { - &self.journal - } -} - -impl Host - for DummyHost -{ - #[inline] - fn set_error( - &mut self, - error: <<::Journal as crate::Journal>::Database as database_interface::Database>::Error, - ) { - panic!("Error: {:?}", error); - } -} diff --git a/crates/context/interface/src/journaled_state.rs b/crates/context/interface/src/journaled_state.rs index a80639cd49..b8ee724800 100644 --- a/crates/context/interface/src/journaled_state.rs +++ b/crates/context/interface/src/journaled_state.rs @@ -1,11 +1,9 @@ +use crate::host::{SStoreResult, SelfDestructResult}; use core::ops::{Deref, DerefMut}; -use database_interface::{Database, DatabaseGetter}; +use database_interface::Database; use primitives::{Address, Bytes, HashSet, Log, B256, U256}; use specification::hardfork::SpecId; use state::{Account, Bytecode}; -use std::boxed::Box; - -use crate::host::{SStoreResult, SelfDestructResult}; pub trait Journal { type Database: Database; @@ -214,39 +212,3 @@ pub struct AccountLoad { /// Is account empty, if `true` account is not created pub is_empty: bool, } - -/// Helper that extracts database error from [`JournalGetter`]. -pub type JournalDBError = - <<::Journal as Journal>::Database as Database>::Error; - -pub trait JournalGetter: DatabaseGetter { - type Journal: Journal::Database>; - - fn journal(&mut self) -> &mut Self::Journal; - - fn journal_ref(&self) -> &Self::Journal; -} - -impl JournalGetter for &mut T { - type Journal = T::Journal; - - fn journal(&mut self) -> &mut Self::Journal { - T::journal(*self) - } - - fn journal_ref(&self) -> &Self::Journal { - T::journal_ref(*self) - } -} - -impl JournalGetter for Box { - type Journal = T::Journal; - - fn journal(&mut self) -> &mut Self::Journal { - T::journal(self.as_mut()) - } - - fn journal_ref(&self) -> &Self::Journal { - T::journal_ref(self.as_ref()) - } -} diff --git a/crates/context/interface/src/lib.rs b/crates/context/interface/src/lib.rs index c03aba433f..776da8f60e 100644 --- a/crates/context/interface/src/lib.rs +++ b/crates/context/interface/src/lib.rs @@ -8,16 +8,14 @@ extern crate alloc as std; pub mod block; pub mod cfg; pub mod context; -pub mod errors; pub mod host; pub mod journaled_state; pub mod result; pub mod transaction; -pub use block::{Block, BlockGetter}; -pub use cfg::{Cfg, CfgGetter, CreateScheme, TransactTo}; -pub use context::PerformantContextAccess; -pub use database_interface::{DBErrorMarker, Database, DatabaseGetter}; -pub use errors::ErrorGetter; -pub use journaled_state::{Journal, JournalDBError, JournalGetter}; -pub use transaction::{Transaction, TransactionGetter, TransactionType}; +pub use block::Block; +pub use cfg::{Cfg, CreateScheme, TransactTo}; +pub use context::ContextTrait; +pub use database_interface::{DBErrorMarker, Database}; +pub use journaled_state::Journal; +pub use transaction::{Transaction, TransactionType}; diff --git a/crates/context/interface/src/transaction.rs b/crates/context/interface/src/transaction.rs index 983ee0126e..6ff37941df 100644 --- a/crates/context/interface/src/transaction.rs +++ b/crates/context/interface/src/transaction.rs @@ -7,7 +7,6 @@ use auto_impl::auto_impl; use core::cmp::min; use core::fmt::Debug; use primitives::{Address, Bytes, TxKind, B256, U256}; -use std::boxed::Box; /// Transaction validity error types. pub trait TransactionError: Debug + core::error::Error {} @@ -152,19 +151,3 @@ pub trait TransactionGetter { fn tx(&self) -> &Self::Transaction; } - -pub trait TransactionSetter: TransactionGetter { - fn set_tx(&mut self, tx: ::Transaction); -} - -impl TransactionSetter for &mut T { - fn set_tx(&mut self, block: ::Transaction) { - (**self).set_tx(block) - } -} - -impl TransactionSetter for Box { - fn set_tx(&mut self, block: ::Transaction) { - (**self).set_tx(block) - } -} diff --git a/crates/context/src/context.rs b/crates/context/src/context.rs index 3c2917e5da..56704bf2d8 100644 --- a/crates/context/src/context.rs +++ b/crates/context/src/context.rs @@ -1,13 +1,7 @@ -pub mod performant_access; - use crate::{block::BlockEnv, cfg::CfgEnv, journaled_state::JournaledState, tx::TxEnv}; -use context_interface::{ - block::BlockSetter, transaction::TransactionSetter, Block, BlockGetter, Cfg, CfgGetter, - DatabaseGetter, ErrorGetter, Journal, JournalGetter, Transaction, TransactionGetter, -}; +use context_interface::{Block, Cfg, ContextTrait, Journal, Transaction}; use database_interface::{Database, EmptyDB}; use derive_where::derive_where; -use interpreter::Host; use specification::hardfork::SpecId; /// EVM context contains data that EVM needs for execution. @@ -34,15 +28,60 @@ pub struct Context< pub error: Result<(), ::Error>, } -impl Default for Context { - fn default() -> Self { - Self::new(EmptyDB::new(), SpecId::LATEST) +impl< + BLOCK: Block, + TX: Transaction, + DB: Database, + CFG: Cfg, + JOURNAL: Journal, + CHAIN, + > ContextTrait for Context +{ + type Block = BLOCK; + type Tx = TX; + type Cfg = CFG; + type Db = DB; + type Journal = JOURNAL; + type Chain = CHAIN; + + fn tx(&self) -> &Self::Tx { + &self.tx + } + + fn block(&self) -> &Self::Block { + &self.block + } + + fn cfg(&self) -> &Self::Cfg { + &self.cfg + } + + fn journal(&mut self) -> &mut Self::Journal { + &mut self.journaled_state } -} -impl Context { - pub fn builder() -> Self { - Self::new(EmptyDB::new(), SpecId::LATEST) + fn journal_ref(&self) -> &Self::Journal { + &self.journaled_state + } + + fn db(&mut self) -> &mut Self::Db { + self.journaled_state.db() + } + + fn db_ref(&self) -> &Self::Db { + self.journaled_state.db_ref() + } + + fn chain(&mut self) -> &mut Self::Chain { + &mut self.chain + } + + fn error(&mut self) -> &mut Result<(), ::Error> { + &mut self.error + } + + fn tx_journal(&mut self) -> (&mut Self::Tx, &mut Self::Journal) { + (&mut self.tx, &mut self.journaled_state) } } @@ -272,109 +311,3 @@ where f(&mut self.journaled_state); } } - -impl Host for Context -where - BLOCK: Block, - TX: Transaction, - CFG: Cfg, - DB: Database, - JOURNAL: Journal, -{ - fn set_error( - &mut self, - error: <<::Journal as Journal>::Database as Database>::Error, - ) { - self.error = Err(error); - } -} - -impl, CHAIN> CfgGetter - for Context -{ - type Cfg = CFG; - - fn cfg(&self) -> &Self::Cfg { - &self.cfg - } -} - -impl JournalGetter - for Context -where - DB: Database, - JOURNAL: Journal, -{ - type Journal = JOURNAL; - - fn journal(&mut self) -> &mut Self::Journal { - &mut self.journaled_state - } - - fn journal_ref(&self) -> &Self::Journal { - &self.journaled_state - } -} - -impl DatabaseGetter - for Context -where - DB: Database, - JOURNAL: Journal, -{ - type Database = DB; - - fn db(&mut self) -> &mut Self::Database { - self.journaled_state.db() - } - - fn db_ref(&self) -> &Self::Database { - self.journaled_state.db_ref() - } -} - -impl, CHAIN> ErrorGetter - for Context -{ - type Error = DB::Error; - - fn take_error(&mut self) -> Result<(), Self::Error> { - core::mem::replace(&mut self.error, Ok(())) - } -} - -impl, CHAIN> - TransactionGetter for Context -{ - type Transaction = TX; - - fn tx(&self) -> &Self::Transaction { - &self.tx - } -} - -impl, CHAIN> - TransactionSetter for Context -{ - fn set_tx(&mut self, tx: ::Transaction) { - self.tx = tx; - } -} - -impl, CHAIN> BlockGetter - for Context -{ - type Block = BLOCK; - - fn block(&self) -> &Self::Block { - &self.block - } -} - -impl, CHAIN> BlockSetter - for Context -{ - fn set_block(&mut self, block: ::Block) { - self.block = block; - } -} diff --git a/crates/context/src/context/performant_access.rs b/crates/context/src/context/performant_access.rs deleted file mode 100644 index 2644c2bf03..0000000000 --- a/crates/context/src/context/performant_access.rs +++ /dev/null @@ -1,28 +0,0 @@ -use super::Context; -use context_interface::{Block, Cfg, Database, Journal, PerformantContextAccess, Transaction}; -use primitives::U256; - -impl< - BLOCK: Block, - TX: Transaction, - CFG: Cfg, - DB: Database, - JOURNAL: Journal, - CHAIN, - > PerformantContextAccess for Context -{ - type Error = ::Error; - - fn load_access_list(&mut self) -> Result<(), Self::Error> { - let Some(access_list) = self.tx.access_list() else { - return Ok(()); - }; - for access_list in access_list { - self.journaled_state.warm_account_and_storage( - *access_list.0, - access_list.1.iter().map(|i| U256::from_be_bytes(i.0)), - )?; - } - Ok(()) - } -} diff --git a/crates/context/src/evm.rs b/crates/context/src/evm.rs new file mode 100644 index 0000000000..d71e05dfbf --- /dev/null +++ b/crates/context/src/evm.rs @@ -0,0 +1,52 @@ +use crate::setters::ContextSetters; +use core::ops::{Deref, DerefMut}; + +pub struct Evm { + pub data: EvmData, + pub enabled_inspection: bool, + pub instruction: I, + pub precompiles: P, +} + +pub struct EvmData { + pub ctx: CTX, + pub inspector: INSP, +} + +impl Evm { + pub fn new(ctx: CTX) -> Self { + Evm { + data: EvmData { ctx, inspector: () }, + enabled_inspection: false, + instruction: (), + precompiles: (), + } + } +} + +impl ContextSetters for Evm { + type Tx = ::Tx; + type Block = ::Block; + + fn set_tx(&mut self, tx: Self::Tx) { + self.data.ctx.set_tx(tx); + } + + fn set_block(&mut self, block: Self::Block) { + self.data.ctx.set_block(block); + } +} + +impl Deref for Evm { + type Target = CTX; + + fn deref(&self) -> &Self::Target { + &self.data.ctx + } +} + +impl DerefMut for Evm { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data.ctx + } +} diff --git a/crates/context/src/instructions.rs b/crates/context/src/instructions.rs deleted file mode 100644 index 192dc42be1..0000000000 --- a/crates/context/src/instructions.rs +++ /dev/null @@ -1,81 +0,0 @@ -use interpreter::{ - table::{make_instruction_table, InstructionTable}, - Host, Interpreter, InterpreterAction, InterpreterTypes, -}; -use std::boxed::Box; - -pub trait InstructionExecutor: Clone + Default { - type InterpreterTypes: InterpreterTypes; - type CTX; - type Output; - - fn run( - &mut self, - context: &mut Self::CTX, - interpreter: &mut Interpreter, - ) -> Self::Output; -} - -#[derive(Debug)] -pub struct EthInstructionExecutor { - instruction_table: Box>, -} - -pub trait InstructionExecutorGetter { - type InstructionExecutor: InstructionExecutor; - - fn executor(&mut self) -> &mut Self::InstructionExecutor; -} - -impl Clone for EthInstructionExecutor -where - WIRE: InterpreterTypes, -{ - fn clone(&self) -> Self { - Self { - instruction_table: self.instruction_table.clone(), - } - } -} - -impl EthInstructionExecutor -where - WIRE: InterpreterTypes, - HOST: Host, -{ - pub fn new() -> Self { - Self { - instruction_table: Box::new(make_instruction_table::()), - } - } -} - -impl InstructionExecutor for EthInstructionExecutor -where - IT: InterpreterTypes, - CTX: Host, -{ - type InterpreterTypes = IT; - type CTX = CTX; - /// TODO Interpreter action could be tied to InterpreterTypes so we can - /// set custom actions from instructions. - type Output = InterpreterAction; - - fn run( - &mut self, - context: &mut Self::CTX, - interpreter: &mut Interpreter, - ) -> Self::Output { - interpreter.run_plain(&self.instruction_table, context) - } -} - -impl Default for EthInstructionExecutor -where - WIRE: InterpreterTypes, - HOST: Host, -{ - fn default() -> Self { - Self::new() - } -} diff --git a/crates/context/src/lib.rs b/crates/context/src/lib.rs index 96400f2991..dcef198f0d 100644 --- a/crates/context/src/lib.rs +++ b/crates/context/src/lib.rs @@ -8,6 +8,7 @@ extern crate alloc as std; pub mod block; pub mod cfg; pub mod context; +pub mod evm; mod journal_init; pub mod journaled_state; pub mod tx; @@ -18,3 +19,5 @@ pub use context::*; pub use journal_init::JournalInit; pub use journaled_state::*; pub use tx::TxEnv; +pub mod setters; +pub use evm::{Evm, EvmData}; diff --git a/crates/context/src/setters.rs b/crates/context/src/setters.rs new file mode 100644 index 0000000000..fa2062cddc --- /dev/null +++ b/crates/context/src/setters.rs @@ -0,0 +1,33 @@ +use crate::Context; +use auto_impl::auto_impl; +use context_interface::{Block, Cfg, Database, Journal, Transaction}; + +#[auto_impl(&mut, Box)] +pub trait ContextSetters { + type Tx: Transaction; + type Block: Block; + + fn set_tx(&mut self, tx: Self::Tx); + fn set_block(&mut self, block: Self::Block); +} + +impl ContextSetters + for Context +where + BLOCK: Block, + TX: Transaction, + CFG: Cfg, + DB: Database, + JOURNAL: Journal, +{ + type Tx = TX; + type Block = BLOCK; + + fn set_tx(&mut self, tx: Self::Tx) { + self.tx = tx; + } + + fn set_block(&mut self, block: Self::Block) { + self.block = block; + } +} diff --git a/crates/database/interface/src/lib.rs b/crates/database/interface/src/lib.rs index a8772af3d4..e3bba2473b 100644 --- a/crates/database/interface/src/lib.rs +++ b/crates/database/interface/src/lib.rs @@ -124,12 +124,3 @@ impl DatabaseCommit for WrapDatabaseRef { self.0.commit(changes) } } - -#[auto_impl(&mut, Box)] -pub trait DatabaseGetter { - type Database: Database; - - fn db(&mut self) -> &mut Self::Database; - - fn db_ref(&self) -> &Self::Database; -} diff --git a/crates/handler/Cargo.toml b/crates/handler/Cargo.toml index 3ce214fc13..d50bd5c52c 100644 --- a/crates/handler/Cargo.toml +++ b/crates/handler/Cargo.toml @@ -30,8 +30,8 @@ context.workspace = true primitives.workspace = true state.workspace = true specification.workspace = true -bytecode.workspace = true -handler-interface.workspace = true +bytecode.workspace = true +auto_impl.workspace = true # Optional serde = { version = "1.0", default-features = false, features = [ diff --git a/crates/handler/interface/Cargo.toml b/crates/handler/interface/Cargo.toml deleted file mode 100644 index af2f083308..0000000000 --- a/crates/handler/interface/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "revm-handler-interface" -description = "Revm handler interface crates" -version = "1.0.0" -authors.workspace = true -edition.workspace = true -keywords.workspace = true -license.workspace = true -repository.workspace = true -readme.workspace = true - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] - -[lints.rust] -unreachable_pub = "warn" -unused_must_use = "deny" -rust_2018_idioms = "deny" - -[lints.rustdoc] -all = "warn" - -[dependencies] -# revm -primitives.workspace = true -auto_impl.workspace = true -specification.workspace = true - -[dev-dependencies] -database.workspace = true - -[features] -default = ["std"] -std = [] -serde = ["std", "primitives/serde"] diff --git a/crates/handler/interface/src/frame.rs b/crates/handler/interface/src/frame.rs deleted file mode 100644 index 2092eed3b2..0000000000 --- a/crates/handler/interface/src/frame.rs +++ /dev/null @@ -1,36 +0,0 @@ -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>; - - fn init( - &self, - context: &mut Self::Context, - frame_context: &mut Self::FrameContext, - frame_input: Self::FrameInit, - ) -> Result, Self::Error>; - - fn run( - &mut self, - context: &mut Self::Context, - 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/lib.rs b/crates/handler/interface/src/lib.rs deleted file mode 100644 index 81ac9cef59..0000000000 --- a/crates/handler/interface/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Optimism-specific constants, types, and helpers. -#![cfg_attr(not(test), warn(unused_crate_dependencies))] -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(not(feature = "std"))] -extern crate alloc as std; - -pub mod frame; -pub mod item_or_result; -pub mod precompile_provider; - -pub use frame::Frame; -pub use item_or_result::{FrameInitOrResult, FrameOrResult, ItemOrResult}; -pub use precompile_provider::{PrecompileProvider, PrecompileProviderGetter}; diff --git a/crates/handler/interface/src/precompile_provider.rs b/crates/handler/interface/src/precompile_provider.rs deleted file mode 100644 index 45f347698f..0000000000 --- a/crates/handler/interface/src/precompile_provider.rs +++ /dev/null @@ -1,35 +0,0 @@ -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 + Default { - type Context; - type Output; - type Error; - type Spec: Into; - - fn set_spec(&mut self, spec: Self::Spec); - - /// Run the precompile. - fn run( - &mut self, - context: &mut Self::Context, - address: &Address, - bytes: &Bytes, - gas_limit: u64, - ) -> Result, Self::Error>; - - /// Get the warm addresses. - 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/CHANGELOG.md b/crates/handler/src/evm.rs similarity index 100% rename from crates/handler/interface/CHANGELOG.md rename to crates/handler/src/evm.rs diff --git a/crates/handler/src/execution.rs b/crates/handler/src/execution.rs index 9218ad025a..c842c7ddb7 100644 --- a/crates/handler/src/execution.rs +++ b/crates/handler/src/execution.rs @@ -1,6 +1,7 @@ use super::frame_data::FrameResult; use bytecode::EOF_MAGIC_BYTES; -use context_interface::{Transaction, TransactionGetter}; +use context_interface::ContextTrait; +use context_interface::Transaction; use interpreter::{ CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, EOFCreateInputs, EOFCreateKind, FrameInput, Gas, @@ -49,7 +50,7 @@ pub fn create_init_frame(tx: &impl Transaction, spec: SpecId, gas_limit: u64) -> } /// TODO : Frame result should be a generic trait with needed functions. -pub fn last_frame_result(context: CTX, frame_result: &mut FrameResult) { +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(); diff --git a/crates/handler/src/frame.rs b/crates/handler/src/frame.rs index 0df9a004ac..7226ab4e20 100644 --- a/crates/handler/src/frame.rs +++ b/crates/handler/src/frame.rs @@ -1,24 +1,22 @@ -use crate::instructions::{InstructionExecutor, InstructionExecutorGetter}; - use super::frame_data::*; +use crate::{ + handler::EvmTrait, instructions::InstructionProvider, precompile_provider::PrecompileProvider, + FrameInitOrResult, FrameOrResult, ItemOrResult, +}; use bytecode::{Eof, EOF_MAGIC_BYTES}; +use context_interface::ContextTrait; use context_interface::{ journaled_state::{Journal, JournalCheckpoint}, - BlockGetter, Cfg, CfgGetter, ErrorGetter, JournalDBError, JournalGetter, Transaction, - TransactionGetter, + Cfg, Database, Transaction, }; use core::{cell::RefCell, cmp::min}; -use handler_interface::{ - Frame, FrameInitOrResult, ItemOrResult, PrecompileProvider, PrecompileProviderGetter, -}; use interpreter::{ gas, interpreter::{EthInterpreter, ExtBytecode}, interpreter_types::{LoopControl, ReturnData, RuntimeFlag}, return_ok, return_revert, CallInputs, CallOutcome, CallValue, CreateInputs, CreateOutcome, - CreateScheme, EOFCreateInputs, EOFCreateKind, FrameInput, Gas, Host, InputsImpl, - InstructionResult, Interpreter, InterpreterAction, InterpreterResult, InterpreterTypes, - SharedMemory, + CreateScheme, EOFCreateInputs, EOFCreateKind, FrameInput, Gas, InputsImpl, InstructionResult, + Interpreter, InterpreterAction, InterpreterResult, InterpreterTypes, SharedMemory, }; use precompile::PrecompileErrors; use primitives::{keccak256, Address, Bytes, B256, U256}; @@ -28,11 +26,41 @@ use specification::{ }; use state::Bytecode; use std::borrow::ToOwned; -use std::{rc::Rc, sync::Arc}; +use std::{boxed::Box, rc::Rc, sync::Arc}; + +/// Call frame trait +pub trait Frame: Sized { + type Context; + type FrameInit; + type FrameResult; + type Error; + + fn init_first( + context: &mut Self::Context, + frame_input: Self::FrameInit, + ) -> Result, Self::Error>; + + fn init( + &self, + context: &mut Self::Context, + frame_input: Self::FrameInit, + ) -> Result, Self::Error>; -pub struct EthFrame { - _phantom: core::marker::PhantomData<(FRAMECTX, CTX, ERROR)>, + fn run(&mut self, context: &mut Self::Context) -> Result, Self::Error>; + + fn return_result( + &mut self, + context: &mut Self::Context, + result: Self::FrameResult, + ) -> Result<(), Self::Error>; +} + +pub struct EthFrame { + phantom: core::marker::PhantomData<(CTX, ERROR)>, + /// Data of the frame. data: FrameData, + /// Input data for the frame. + pub input: FrameInput, /// Depth of the call frame. depth: usize, /// Journal checkpoint. @@ -43,20 +71,69 @@ pub struct EthFrame { pub memory: Rc>, } -impl EthFrame +impl Frame for EthFrame> +where + EVM: EvmTrait< + Precompiles: PrecompileProvider, + Instructions: InstructionProvider< + Context = EVM::Context, + InterpreterTypes = EthInterpreter<()>, + Output = InterpreterAction, + >, + >, + ERROR: From> + From, +{ + type Context = EVM; + type FrameInit = FrameInput; + type FrameResult = FrameResult; + type Error = ERROR; + + fn init_first( + context: &mut Self::Context, + frame_input: Self::FrameInit, + ) -> Result, Self::Error> { + EthFrame::init_first(context, frame_input) + } + + fn init( + &self, + context: &mut Self::Context, + frame_input: Self::FrameInit, + ) -> Result, Self::Error> { + self.init(context, frame_input) + } + + fn run(&mut self, context: &mut Self::Context) -> Result, Self::Error> { + let next_action = context.run_interpreter(&mut self.interpreter); + self.run_frame(context, next_action) + } + + fn return_result( + &mut self, + context: &mut Self::Context, + result: Self::FrameResult, + ) -> Result<(), Self::Error> { + self.return_result(context, result) + } +} + +pub type CtxTraitDbError = <::Db as Database>::Error; + +impl EthFrame where - CTX: JournalGetter, IW: InterpreterTypes, { pub fn new( data: FrameData, + input: FrameInput, depth: usize, interpreter: Interpreter, checkpoint: JournalCheckpoint, memory: Rc>, ) -> Self { Self { - _phantom: core::marker::PhantomData, + phantom: Default::default(), + input, data, depth, interpreter, @@ -66,35 +143,27 @@ where } } -impl EthFrame +impl EthFrame where - CTX: EthFrameContext, - ERROR: From> + From, - FRAMECTX: PrecompileProviderGetter< - PrecompileProvider: PrecompileProvider< - Context = CTX, - Error = ERROR, - Output = InterpreterResult, - >, - > + InstructionExecutorGetter< - InstructionExecutor: InstructionExecutor< - InterpreterTypes = EthInterpreter, - CTX = CTX, - Output = InterpreterAction, - >, - >, + EVM: EvmTrait< + Context: ContextTrait, + Precompiles: PrecompileProvider, + Instructions: InstructionProvider, + >, + ERROR: From> + From, { /// Make call frame #[inline] pub fn make_call_frame( - context: &mut CTX, - frame_context: &mut FRAMECTX, + evm: &mut EVM, depth: usize, memory: Rc>, - inputs: &CallInputs, + inputs: Box, ) -> Result, ERROR> { let gas = Gas::new(inputs.gas_limit); + let (context, precompiles) = evm.ctx_precompiles(); + let return_result = |instruction_result: InstructionResult| { Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome { result: InterpreterResult { @@ -134,7 +203,7 @@ where } let is_ext_delegate_call = inputs.scheme.is_ext_delegate_call(); if !is_ext_delegate_call { - if let Some(result) = frame_context.precompiles().run( + if let Some(result) = precompiles.run( context, &inputs.bytecode_address, &inputs.input, @@ -186,20 +255,22 @@ where input: inputs.input.clone(), call_value: inputs.value.get(), }; - + let is_static = inputs.is_static; + let gas_limit = inputs.gas_limit; Ok(ItemOrResult::Item(Self::new( FrameData::Call(CallFrame { return_memory_range: inputs.return_memory_offset.clone(), }), + FrameInput::Call(inputs), depth, Interpreter::new( memory.clone(), ExtBytecode::new_with_hash(bytecode, code_hash), interpreter_input, - inputs.is_static, + is_static, false, context.cfg().spec().into(), - inputs.gas_limit, + gas_limit, ), checkpoint, memory, @@ -209,11 +280,12 @@ where /// Make create frame. #[inline] pub fn make_create_frame( - context: &mut CTX, + evm: &mut EVM, depth: usize, memory: Rc>, - inputs: &CreateInputs, + inputs: Box, ) -> Result, ERROR> { + let context = evm.ctx(); let spec = context.cfg().spec().into(); let return_error = |e| { Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome { @@ -290,9 +362,10 @@ where input: Bytes::new(), call_value: inputs.value, }; - + let gas_limit = inputs.gas_limit; Ok(ItemOrResult::Item(Self::new( FrameData::Create(CreateFrame { created_address }), + FrameInput::Create(inputs), depth, Interpreter::new( memory.clone(), @@ -301,7 +374,7 @@ where false, false, spec, - inputs.gas_limit, + gas_limit, ), checkpoint, memory, @@ -311,11 +384,12 @@ where /// Make create frame. #[inline] pub fn make_eofcreate_frame( - context: &mut CTX, + evm: &mut EVM, depth: usize, memory: Rc>, - inputs: &EOFCreateInputs, + inputs: Box, ) -> Result, ERROR> { + let context = evm.ctx(); let spec = context.cfg().spec().into(); let return_error = |e| { Ok(ItemOrResult::Result(FrameResult::EOFCreate( @@ -404,8 +478,10 @@ where call_value: inputs.value, }; + let gas_limit = inputs.gas_limit; Ok(ItemOrResult::Item(Self::new( FrameData::EOFCreate(EOFCreateFrame { created_address }), + FrameInput::EOFCreate(inputs), depth, Interpreter::new( memory.clone(), @@ -414,7 +490,7 @@ where false, true, spec, - inputs.gas_limit, + gas_limit, ), checkpoint, memory, @@ -422,126 +498,65 @@ where } pub fn init_with_context( + evm: &mut EVM, depth: usize, frame_init: FrameInput, memory: Rc>, - context: &mut CTX, - frame_context: &mut FRAMECTX, ) -> Result, ERROR> { match frame_init { - FrameInput::Call(inputs) => { - Self::make_call_frame(context, frame_context, depth, memory, &inputs) - } - FrameInput::Create(inputs) => Self::make_create_frame(context, depth, memory, &inputs), - FrameInput::EOFCreate(inputs) => { - Self::make_eofcreate_frame(context, depth, memory, &inputs) - } - } - } -} - -pub struct FrameContext { - pub precompiles: PRECOMPILE, - pub instructions: INSTRUCTION, -} - -impl - FrameContext -{ - pub fn new(precompiles: PRECOMPILE, instructions: INSTRUCTION) -> Self { - Self { - precompiles, - instructions, + FrameInput::Call(inputs) => Self::make_call_frame(evm, depth, memory, inputs), + FrameInput::Create(inputs) => Self::make_create_frame(evm, depth, memory, inputs), + FrameInput::EOFCreate(inputs) => Self::make_eofcreate_frame(evm, depth, memory, inputs), } } } -impl PrecompileProviderGetter - for FrameContext -{ - type PrecompileProvider = PRECOMPILES; - - fn precompiles(&mut self) -> &mut Self::PrecompileProvider { - &mut self.precompiles - } -} - -impl InstructionExecutorGetter - for FrameContext -{ - type InstructionExecutor = INSTRUCTIONS; - - fn executor(&mut self) -> &mut Self::InstructionExecutor { - &mut self.instructions - } -} - -impl Frame for EthFrame, FRAMECTX> +impl EthFrame> where - CTX: EthFrameContext, - ERROR: From> + From, - FRAMECTX: PrecompileProviderGetter< - PrecompileProvider: PrecompileProvider< - Context = CTX, - Error = ERROR, - Output = InterpreterResult, - Spec = <::Cfg as Cfg>::Spec, - >, - > + InstructionExecutorGetter< - InstructionExecutor: InstructionExecutor< - InterpreterTypes = EthInterpreter<()>, - CTX = CTX, - Output = InterpreterAction, - >, + CTX: EvmTrait< + Context: ContextTrait, + Precompiles: PrecompileProvider, + Instructions: InstructionProvider< + Context = CTX::Context, + InterpreterTypes = EthInterpreter<()>, + Output = InterpreterAction, >, + >, + ERROR: From> + From, { - 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> { + pub fn init_first( + evm: &mut CTX, + frame_input: FrameInput, + ) -> Result, ERROR> { let memory = Rc::new(RefCell::new(SharedMemory::new())); - - frame_context.precompiles().set_spec(context.cfg().spec()); + let (context, precompiles) = evm.ctx_precompiles(); + precompiles.set_spec(context.cfg().spec()); context .journal() - .warm_precompiles(frame_context.precompiles().warm_addresses().collect()); + .warm_precompiles(precompiles.warm_addresses().collect()); memory.borrow_mut().new_context(); - Self::init_with_context(0, frame_input, memory, context, frame_context) + Self::init_with_context(evm, 0, frame_input, memory) } fn init( &self, context: &mut CTX, - frame_context: &mut Self::FrameContext, - frame_init: Self::FrameInit, - ) -> Result, Self::Error> { + frame_init: FrameInput, + ) -> Result, ERROR> { self.memory.borrow_mut().new_context(); - Self::init_with_context( - self.depth + 1, - frame_init, - self.memory.clone(), - context, - frame_context, - ) + Self::init_with_context(context, self.depth + 1, frame_init, self.memory.clone()) } - fn run( + pub fn run_frame( &mut self, - context: &mut Self::Context, - frame_context: &mut Self::FrameContext, - ) -> Result, Self::Error> { + evm: &mut CTX, + next_action: InterpreterAction, + ) -> Result, ERROR> { + let context = evm.ctx(); let spec = context.cfg().spec().into(); // Run interpreter - let next_action = frame_context.executor().run(context, &mut self.interpreter); let mut interpreter_result = match next_action { InterpreterAction::NewFrame(new_frame) => return Ok(ItemOrResult::Item(new_frame)), @@ -600,14 +615,9 @@ where Ok(result) } - fn return_result( - &mut self, - context: &mut Self::Context, - _frame_context: &mut Self::FrameContext, - result: Self::FrameResult, - ) -> Result<(), Self::Error> { + fn return_result(&mut self, context: &mut CTX, result: FrameResult) -> Result<(), ERROR> { self.memory.borrow_mut().free_context(); - context.take_error()?; + core::mem::replace(context.ctx().error(), Ok(()))?; // Insert result to the top frame. match result { @@ -824,24 +834,3 @@ pub fn return_eofcreate( // Eof bytecode is going to be hashed. journal.set_code(address, Bytecode::Eof(Arc::new(bytecode))); } - -pub trait EthFrameContext: - TransactionGetter - + Host - + ErrorGetter> - + BlockGetter - + JournalGetter - + CfgGetter -{ -} - -impl< - CTX: TransactionGetter - + ErrorGetter> - + BlockGetter - + JournalGetter - + CfgGetter - + Host, - > EthFrameContext for CTX -{ -} diff --git a/crates/handler/src/handler.rs b/crates/handler/src/handler.rs index c939759476..999cc2cb2c 100644 --- a/crates/handler/src/handler.rs +++ b/crates/handler/src/handler.rs @@ -1,228 +1,402 @@ -pub mod types; - -pub use types::{EthContext, EthError, MainnetHandler}; - +use crate::inspector::JournalExt; use crate::{ - execution, instructions::InstructionExecutor, post_execution, pre_execution, validation, - FrameContext, FrameResult, + execution, inspector::Inspector, instructions::InstructionProvider, post_execution, + pre_execution, validation, Frame, FrameInitOrResult, FrameOrResult, FrameResult, ItemOrResult, }; +use auto_impl::auto_impl; +use context::{Evm, JournalEntry}; +use context_interface::ContextTrait; use context_interface::{ - result::{HaltReasonTrait, ResultAndState}, - Cfg, CfgGetter, ErrorGetter, Journal, JournalGetter, Transaction, TransactionGetter, + result::{HaltReasonTrait, InvalidHeader, InvalidTransaction, ResultAndState}, + Cfg, Database, Journal, Transaction, }; -use handler_interface::{ - Frame, FrameInitOrResult, FrameOrResult, ItemOrResult, PrecompileProvider, +use core::mem; +use interpreter::table::InstructionTable; +use interpreter::InterpreterTypes; +use interpreter::{ + interpreter_types::{Jumps, LoopControl}, + FrameInput, Host, InitialAndFloorGas, InstructionResult, Interpreter, InterpreterAction, }; -use interpreter::{FrameInput, InitialAndFloorGas}; +use precompile::PrecompileErrors; +use primitives::Log; +use state::EvmState; use std::{vec, vec::Vec}; +pub trait EthTraitError: + From + + From + + From<<::Db as Database>::Error> + + From +{ +} + +impl< + EVM: EvmTrait, + T: From + + From + + From<<::Db as Database>::Error> + + From, + > EthTraitError for T +{ +} + +pub fn inspect_instructions( + context: &mut CTX, + interpreter: &mut Interpreter, + mut inspector: impl Inspector, + instructions: &InstructionTable, +) -> InterpreterAction +where + CTX: ContextTrait + Host, + IT: InterpreterTypes, +{ + interpreter.reset_control(); + + let mut log_num = context.journal().logs().len(); + // Main loop + while interpreter.control.instruction_result().is_continue() { + // Get current opcode. + let opcode = interpreter.bytecode.opcode(); + + // Call Inspector step. + inspector.step(interpreter, context); + if interpreter.control.instruction_result() != InstructionResult::Continue { + break; + } + + // SAFETY: In analysis we are doing padding of bytecode so that we are sure that last + // byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction + // it will do noop and just stop execution of this contract + interpreter.bytecode.relative_jump(1); + + // Execute instruction. + instructions[opcode as usize](interpreter, context); + + // check if new log is added + let new_log = context.journal().logs().len(); + if log_num < new_log { + // as there is a change in log number this means new log is added + let log = context.journal().logs().last().unwrap().clone(); + inspector.log(interpreter, context, log); + log_num = new_log; + } + + // Call step_end. + inspector.step_end(interpreter, context); + } + + let next_action = interpreter.take_next_action(); + + // handle selfdestruct + if let InterpreterAction::Return { result } = &next_action { + if result.result == InstructionResult::SelfDestruct { + match context.journal().last_journal().last() { + Some(JournalEntry::AccountDestroyed { + address, + target, + had_balance, + .. + }) => { + inspector.selfdestruct(*address, *target, *had_balance); + } + Some(JournalEntry::BalanceTransfer { + from, to, balance, .. + }) => { + inspector.selfdestruct(*from, *to, *balance); + } + _ => {} + } + } + } + + next_action +} + +impl EvmTrait for Evm +where + CTX: ContextTrait + Host, + INSP: Inspector, + I: InstructionProvider, +{ + type Context = CTX; + type Inspector = INSP; + type Instructions = I; + type Precompiles = P; + + #[inline] + fn run_interpreter( + &mut self, + interpreter: &mut Interpreter< + ::InterpreterTypes, + >, + ) -> ::Output { + let context = &mut self.data.ctx; + let instructions = &mut self.instruction; + let inspector = &mut self.data.inspector; + if self.enabled_inspection { + inspect_instructions( + context, + interpreter, + inspector, + instructions.instruction_table(), + ) + } else { + interpreter.run_plain(instructions.instruction_table(), context) + } + } + + #[inline] + fn enable_inspection(&mut self, enable: bool) { + self.enabled_inspection = enable; + } + + #[inline] + fn ctx(&mut self) -> &mut Self::Context { + &mut self.data.ctx + } + + #[inline] + fn ctx_ref(&self) -> &Self::Context { + &self.data.ctx + } + + #[inline] + fn ctx_inspector(&mut self) -> (&mut Self::Context, &mut Self::Inspector) { + (&mut self.data.ctx, &mut self.data.inspector) + } + + #[inline] + fn ctx_instructions(&mut self) -> (&mut Self::Context, &mut Self::Instructions) { + (&mut self.data.ctx, &mut self.instruction) + } + + #[inline] + fn ctx_precompiles(&mut self) -> (&mut Self::Context, &mut Self::Precompiles) { + (&mut self.data.ctx, &mut self.precompiles) + } +} + +#[auto_impl(&mut, Box)] +pub trait EvmTrait { + type Context: ContextTrait; + type Inspector; + type Instructions: InstructionProvider; + type Precompiles; + + fn run_interpreter( + &mut self, + interpreter: &mut Interpreter< + ::InterpreterTypes, + >, + ) -> ::Output; + + fn enable_inspection(&mut self, enable: bool); + + fn ctx(&mut self) -> &mut Self::Context; + + fn ctx_ref(&self) -> &Self::Context; + + fn ctx_inspector(&mut self) -> (&mut Self::Context, &mut Self::Inspector); + + fn ctx_instructions(&mut self) -> (&mut Self::Context, &mut Self::Instructions); + + fn ctx_precompiles(&mut self) -> (&mut Self::Context, &mut Self::Precompiles); +} + pub trait EthHandler { - type Context: EthContext; - type Error: EthError; - type Precompiles: PrecompileProvider; - type Instructions: InstructionExecutor; + type Evm: EvmTrait)>>>; + type Error: EthTraitError; // TODO `FrameResult` should be a generic trait. // TODO `FrameInit` should be a generic. type Frame: Frame< - Context = Self::Context, + Context = Self::Evm, Error = Self::Error, FrameResult = FrameResult, FrameInit = FrameInput, - FrameContext = FrameContext, >; - // TODO `HaltReason` should be a ExecResult trait, returned by the handler. + // TODO `HaltReason` should be part of the output. type HaltReason: HaltReasonTrait; + #[inline] fn run( &mut self, - context: &mut Self::Context, + evm: &mut Self::Evm, ) -> 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) + let init_and_floor_gas = self.validate(evm)?; + let eip7702_refund = self.pre_execution(evm)? as i64; + let exec_result = self.execution(evm, &init_and_floor_gas)?; + self.post_execution(evm, exec_result, init_and_floor_gas, eip7702_refund) } - - fn precompile(&self, _context: &mut Self::Context) -> Self::Precompiles { - Self::Precompiles::default() - } - - fn instructions(&self, _context: &mut Self::Context) -> Self::Instructions { - Self::Instructions::default() - } - - fn frame_context( - &mut self, - context: &mut Self::Context, - ) -> ::FrameContext { - FrameContext::new(self.precompile(context), self.instructions(context)) - } - /// 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) + #[inline] + fn validate(&self, evm: &mut Self::Evm) -> Result { + self.validate_env(evm)?; + self.validate_tx_against_state(evm)?; + self.validate_initial_tx_gas(evm) } /// 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)?; + #[inline] + fn pre_execution(&self, evm: &mut Self::Evm) -> Result { + self.load_accounts(evm)?; + self.deduct_caller(evm)?; + let gas = self.apply_eip7702_auth_list(evm)?; Ok(gas) } + #[inline] fn execution( &mut self, - context: &mut Self::Context, + evm: &mut Self::Evm, init_and_floor_gas: &InitialAndFloorGas, ) -> Result { - let gas_limit = context.tx().gas_limit() - init_and_floor_gas.initial_gas; + let gas_limit = evm.ctx().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 first_frame = self.create_first_frame(evm, gas_limit)?; let mut frame_result = match first_frame { - ItemOrResult::Item(frame) => self.run_exec_loop(context, &mut frame_context, frame)?, + ItemOrResult::Item(frame) => self.run_exec_loop(evm, frame)?, ItemOrResult::Result(result) => result, }; - self.last_frame_result(context, &mut frame_context, &mut frame_result)?; + self.last_frame_result(evm, &mut frame_result)?; Ok(frame_result) } + #[inline] fn post_execution( &self, - context: &mut Self::Context, + evm: &mut Self::Evm, 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); + self.refund(evm, &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); + self.eip7623_check_gas_floor(evm, &mut exec_result, init_and_floor_gas); // Reimburse the caller - self.reimburse_caller(context, &mut exec_result)?; + self.reimburse_caller(evm, &mut exec_result)?; // Reward beneficiary - self.reward_beneficiary(context, &mut exec_result)?; + self.reward_beneficiary(evm, &mut exec_result)?; // Returns output of transaction. - self.output(context, exec_result) + self.output(evm, exec_result) } /* VALIDATION */ /// Validate env. - fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { - validation::validate_env(context) + #[inline] + fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { + validation::validate_env(evm.ctx()) } /// Validate transactions against state. - fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - validation::validate_tx_against_state(context) + #[inline] + fn validate_tx_against_state(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { + validation::validate_tx_against_state(evm.ctx()) } /// 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) + #[inline] + fn validate_initial_tx_gas(&self, evm: &Self::Evm) -> Result { + let ctx = evm.ctx_ref(); + validation::validate_initial_tx_gas(ctx.tx(), ctx.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) + #[inline] + fn load_accounts(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { + pre_execution::load_accounts(evm.ctx()) } - fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result { - pre_execution::apply_eip7702_auth_list(context) + #[inline] + fn apply_eip7702_auth_list(&self, evm: &mut Self::Evm) -> Result { + pre_execution::apply_eip7702_auth_list(evm.ctx()) } - fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - pre_execution::deduct_caller(context).map_err(From::from) + #[inline] + fn deduct_caller(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { + pre_execution::deduct_caller(evm.ctx()).map_err(From::from) } /* EXECUTION */ + #[inline] fn create_first_frame( &mut self, - context: &mut Self::Context, - frame_context: &mut ::FrameContext, + evm: &mut Self::Evm, 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) + let ctx = evm.ctx_ref(); + let init_frame = execution::create_init_frame(ctx.tx(), ctx.cfg().spec().into(), gas_limit); + self.frame_init_first(evm, init_frame) } + #[inline] fn last_frame_result( &self, - context: &mut Self::Context, - _frame_context: &mut ::FrameContext, + evm: &mut Self::Evm, frame_result: &mut ::FrameResult, ) -> Result<(), Self::Error> { - execution::last_frame_result(context, frame_result); + execution::last_frame_result(evm.ctx(), frame_result); Ok(()) } /* FRAMES */ + #[inline] fn frame_init_first( &mut self, - context: &mut Self::Context, - frame_context: &mut ::FrameContext, + evm: &mut Self::Evm, frame_input: ::FrameInit, ) -> Result, Self::Error> { - Self::Frame::init_first(context, frame_context, frame_input) + Self::Frame::init_first(evm, frame_input) } + #[inline] fn frame_init( &mut self, frame: &Self::Frame, - context: &mut Self::Context, - frame_context: &mut ::FrameContext, + evm: &mut Self::Evm, frame_input: ::FrameInit, ) -> Result, Self::Error> { - Frame::init(frame, context, frame_context, frame_input) + Frame::init(frame, evm, frame_input) } + #[inline] fn frame_call( &mut self, frame: &mut Self::Frame, - context: &mut Self::Context, - frame_context: &mut ::FrameContext, + evm: &mut Self::Evm, ) -> Result, Self::Error> { - Frame::run(frame, context, frame_context) + Frame::run(frame, evm) } + #[inline] fn frame_return_result( &mut self, frame: &mut Self::Frame, - context: &mut Self::Context, - frame_context: &mut ::FrameContext, + evm: &mut Self::Evm, result: ::FrameResult, ) -> Result<(), Self::Error> { - Self::Frame::return_result(frame, context, frame_context, result) + Self::Frame::return_result(frame, evm, result) } + #[inline] fn run_exec_loop( &mut self, - context: &mut Self::Context, - frame_context: &mut ::FrameContext, + evm: &mut Self::Evm, frame: Self::Frame, ) -> Result { let mut frame_stack: Vec = vec![frame]; loop { let frame = frame_stack.last_mut().unwrap(); - let call_or_result = self.frame_call(frame, context, frame_context)?; + let call_or_result = self.frame_call(frame, evm)?; let result = match call_or_result { ItemOrResult::Item(init) => { - match self.frame_init(frame, context, frame_context, init)? { + match self.frame_init(frame, evm, init)? { ItemOrResult::Item(new_frame) => { frame_stack.push(new_frame); continue; @@ -241,16 +415,17 @@ pub trait EthHandler { let Some(frame) = frame_stack.last_mut() else { return Ok(result); }; - self.frame_return_result(frame, context, frame_context, result)?; + self.frame_return_result(frame, evm, result)?; } } /* POST EXECUTION */ /// Calculate final refund. + #[inline] fn eip7623_check_gas_floor( &self, - _context: &mut Self::Context, + _evm: &mut Self::Evm, exec_result: &mut ::FrameResult, init_and_floor_gas: InitialAndFloorGas, ) { @@ -258,42 +433,47 @@ pub trait EthHandler { } /// Calculate final refund. + #[inline] fn refund( &self, - context: &mut Self::Context, + evm: &mut Self::Evm, exec_result: &mut ::FrameResult, eip7702_refund: i64, ) { - let spec = context.cfg().spec().into(); + let spec = evm.ctx().cfg().spec().into(); post_execution::refund(spec, exec_result.gas_mut(), eip7702_refund) } /// Reimburse the caller with balance it didn't spent. + #[inline] fn reimburse_caller( &self, - context: &mut Self::Context, + evm: &mut Self::Evm, exec_result: &mut ::FrameResult, ) -> Result<(), Self::Error> { - post_execution::reimburse_caller(context, exec_result.gas_mut()).map_err(From::from) + post_execution::reimburse_caller(evm.ctx(), exec_result.gas_mut()).map_err(From::from) } /// Reward beneficiary with transaction rewards. + #[inline] fn reward_beneficiary( &self, - context: &mut Self::Context, + evm: &mut Self::Evm, exec_result: &mut ::FrameResult, ) -> Result<(), Self::Error> { - post_execution::reward_beneficiary(context, exec_result.gas_mut()).map_err(From::from) + post_execution::reward_beneficiary(evm.ctx(), exec_result.gas_mut()).map_err(From::from) } /// Main return handle, takes state from journal and transforms internal result to output. + #[inline] fn output( &self, - context: &mut Self::Context, + evm: &mut Self::Evm, result: ::FrameResult, ) -> Result, Self::Error> { - context.take_error()?; - Ok(post_execution::output(context, result)) + let ctx = evm.ctx(); + mem::replace(ctx.error(), Ok(()))?; + Ok(post_execution::output(ctx, result)) } /// Called when execution ends. @@ -301,9 +481,10 @@ pub trait EthHandler { /// End handle in comparison to output handle will be called every time after execution. /// /// While output will be omitted in case of the error. + #[inline] fn end( &self, - _context: &mut Self::Context, + _evm: &mut Self::Evm, end_output: Result, Self::Error>, ) -> Result, Self::Error> { end_output @@ -312,7 +493,8 @@ pub trait EthHandler { /// 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(); + #[inline] + fn clear(&self, evm: &mut Self::Evm) { + evm.ctx().journal().clear(); } } diff --git a/crates/handler/src/handler/types.rs b/crates/handler/src/handler/types.rs deleted file mode 100644 index 2c82e3f8a0..0000000000 --- a/crates/handler/src/handler/types.rs +++ /dev/null @@ -1,106 +0,0 @@ -use super::EthHandler; -use crate::{instructions::InstructionExecutor, EthPrecompileProvider, FrameContext, FrameResult}; -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::EthInterpreter, FrameInput, Host}; -use precompile::PrecompileErrors; -use primitives::Log; -use state::EvmState; -use std::vec::Vec; - -pub struct MainnetHandler { - pub _phantom: core::marker::PhantomData<(CTX, ERROR, FRAME, PRECOMPILES, INSTRUCTIONS)>, -} - -impl EthHandler - for MainnetHandler -where - CTX: EthContext, - ERROR: EthError, - PRECOMPILES: PrecompileProvider< - Context = CTX, - Error = ERROR, - Spec = <::Cfg as Cfg>::Spec, - >, - INSTRUCTIONS: InstructionExecutor, - // 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; -} - -impl Default - for MainnetHandler, INSTRUCTIONS> -{ - fn default() -> Self { - Self { - _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/inspector.rs b/crates/handler/src/inspector.rs new file mode 100644 index 0000000000..4a14e10909 --- /dev/null +++ b/crates/handler/src/inspector.rs @@ -0,0 +1,493 @@ +use crate::{ + execution, + handler::{EthHandler, EvmTrait}, + EthFrame, Frame, FrameOrResult, FrameResult, ItemOrResult, +}; +use auto_impl::auto_impl; +use context::{Cfg, JournalEntry, JournaledState}; +use context_interface::{result::ResultAndState, ContextTrait, Database, Transaction}; +use interpreter::{ + CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, FrameInput, + InitialAndFloorGas, Interpreter, InterpreterTypes, +}; +use primitives::{Address, Log, U256}; +use state::EvmState; +use std::{vec, vec::Vec}; + +/// EVM [Interpreter] callbacks. +#[auto_impl(&mut, Box)] +pub trait Inspector { + /// Called before the interpreter is initialized. + /// + /// If `interp.instruction_result` is set to anything other than [interpreter::InstructionResult::Continue] then the execution of the interpreter + /// is skipped. + #[inline] + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut CTX) { + let _ = interp; + let _ = context; + } + + /// Called on each step of the interpreter. + /// + /// Information about the current execution, including the memory, stack and more is available + /// on `interp` (see [Interpreter]). + /// + /// # Example + /// + /// To get the current opcode, use `interp.current_opcode()`. + #[inline] + fn step(&mut self, interp: &mut Interpreter, context: &mut CTX) { + let _ = interp; + let _ = context; + } + + /// Called after `step` when the instruction has been executed. + /// + /// Setting `interp.instruction_result` to anything other than [interpreter::InstructionResult::Continue] alters the execution + /// of the interpreter. + #[inline] + fn step_end(&mut self, interp: &mut Interpreter, context: &mut CTX) { + let _ = interp; + let _ = context; + } + + /// Called when a log is emitted. + #[inline] + fn log(&mut self, interp: &mut Interpreter, context: &mut CTX, log: Log) { + let _ = interp; + let _ = context; + let _ = log; + } + + /// Called whenever a call to a contract is about to start. + /// + /// InstructionResulting anything other than [interpreter::InstructionResult::Continue] overrides the result of the call. + #[inline] + fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option { + let _ = context; + let _ = inputs; + None + } + + /// Called when a call to a contract has concluded. + /// + /// The returned [CallOutcome] is used as the result of the call. + /// + /// This allows the inspector to modify the given `result` before returning it. + #[inline] + fn call_end(&mut self, context: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) { + let _ = context; + let _ = inputs; + let _ = outcome; + } + + /// Called when a contract is about to be created. + /// + /// If this returns `Some` then the [CreateOutcome] is used to override the result of the creation. + /// + /// If this returns `None` then the creation proceeds as normal. + #[inline] + fn create(&mut self, context: &mut CTX, inputs: &mut CreateInputs) -> Option { + let _ = context; + let _ = inputs; + None + } + + /// Called when a contract has been created. + /// + /// InstructionResulting anything other than the values passed to this function (`(ret, remaining_gas, + /// address, out)`) will alter the result of the create. + #[inline] + fn create_end( + &mut self, + context: &mut CTX, + inputs: &CreateInputs, + outcome: &mut CreateOutcome, + ) { + let _ = context; + let _ = inputs; + let _ = outcome; + } + + /// Called when EOF creating is called. + /// + /// This can happen from create TX or from EOFCREATE opcode. + fn eofcreate( + &mut self, + context: &mut CTX, + inputs: &mut EOFCreateInputs, + ) -> Option { + let _ = context; + let _ = inputs; + None + } + + /// Called when eof creating has ended. + fn eofcreate_end( + &mut self, + context: &mut CTX, + inputs: &EOFCreateInputs, + outcome: &mut CreateOutcome, + ) { + let _ = context; + let _ = inputs; + let _ = outcome; + } + + /// Called when a contract has been self-destructed with funds transferred to target. + #[inline] + fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { + let _ = contract; + let _ = target; + let _ = value; + } +} + +#[auto_impl(&mut, Box)] +pub trait JournalExt { + fn logs(&self) -> &[Log]; + + fn last_journal(&self) -> &[JournalEntry]; + + fn evm_state(&self) -> &EvmState; + + fn evm_state_mut(&mut self) -> &mut EvmState; +} + +impl JournalExt for JournaledState { + #[inline] + fn logs(&self) -> &[Log] { + &self.logs + } + + #[inline] + fn last_journal(&self) -> &[JournalEntry] { + self.journal.last().expect("Journal is never empty") + } + + #[inline] + fn evm_state(&self) -> &EvmState { + &self.state + } + + #[inline] + fn evm_state_mut(&mut self) -> &mut EvmState { + &mut self.state + } +} + +pub trait InspectorFrame { + type IT: InterpreterTypes; + type FrameInput; + + fn interpreter(&mut self) -> &mut Interpreter; + + fn frame_input(&self) -> &FrameInput; +} + +impl InspectorFrame for EthFrame +where + IT: InterpreterTypes, +{ + type IT = IT; + type FrameInput = FrameInput; + + fn interpreter(&mut self) -> &mut Interpreter { + &mut self.interpreter + } + + fn frame_input(&self) -> &FrameInput { + &self.input + } +} + +pub trait EthInspectorHandler: EthHandler +where + Self::Evm: + EvmTrait::Evm as EvmTrait>::Context, Self::IT>>, + Self::Frame: InspectorFrame, +{ + type IT: InterpreterTypes; + + fn inspect_run( + &mut self, + evm: &mut Self::Evm, + ) -> Result, Self::Error> { + let init_and_floor_gas = self.validate(evm)?; + let eip7702_refund = self.pre_execution(evm)? as i64; + // enable instruction inspection + evm.enable_inspection(true); + let exec_result = self.inspect_execution(evm, &init_and_floor_gas); + // disable instruction inspection + evm.enable_inspection(false); + self.post_execution(evm, exec_result?, init_and_floor_gas, eip7702_refund) + } + + fn inspect_execution( + &mut self, + evm: &mut Self::Evm, + init_and_floor_gas: &InitialAndFloorGas, + ) -> Result { + let gas_limit = evm.ctx().tx().gas_limit() - init_and_floor_gas.initial_gas; + + // Create first frame action + let first_frame = self.inspect_create_first_frame(evm, gas_limit)?; + let mut frame_result = match first_frame { + ItemOrResult::Item(frame) => self.inspect_run_exec_loop(evm, frame)?, + ItemOrResult::Result(result) => result, + }; + + self.last_frame_result(evm, &mut frame_result)?; + Ok(frame_result) + } + + /* EXECUTION */ + fn inspect_create_first_frame( + &mut self, + evm: &mut Self::Evm, + gas_limit: u64, + ) -> Result, Self::Error> { + let ctx = evm.ctx_ref(); + let init_frame = execution::create_init_frame(ctx.tx(), ctx.cfg().spec().into(), gas_limit); + self.inspect_frame_init_first(evm, init_frame) + } + + /* FRAMES */ + + fn inspect_frame_init_first( + &mut self, + evm: &mut Self::Evm, + mut frame_input: ::FrameInit, + ) -> Result, Self::Error> { + let (ctx, inspector) = evm.ctx_inspector(); + if let Some(output) = frame_start(ctx, inspector, &mut frame_input) { + return Ok(ItemOrResult::Result(output)); + } + let mut ret = self.frame_init_first(evm, frame_input.clone()); + + // only if new frame is created call initialize_interp hook. + if let Ok(ItemOrResult::Item(frame)) = &mut ret { + let (context, inspector) = evm.ctx_inspector(); + inspector.initialize_interp(frame.interpreter(), context); + } else if let Ok(ItemOrResult::Result(result)) = &mut ret { + let (context, inspector) = evm.ctx_inspector(); + frame_end(context, inspector, &frame_input, result); + } + ret + } + + fn inspect_run_exec_loop( + &mut self, + evm: &mut Self::Evm, + frame: Self::Frame, + ) -> Result { + let mut frame_stack: Vec = vec![frame]; + loop { + let frame = frame_stack.last_mut().unwrap(); + let call_or_result = self.frame_call(frame, evm)?; + + let result = match call_or_result { + ItemOrResult::Item(mut init) => { + let (context, inspector) = evm.ctx_inspector(); + if let Some(output) = frame_start(context, inspector, &mut init) { + output + } else { + match self.frame_init(frame, evm, init)? { + ItemOrResult::Item(mut new_frame) => { + // only if new frame is created call initialize_interp hook. + let (context, inspector) = evm.ctx_inspector(); + inspector.initialize_interp(new_frame.interpreter(), context); + frame_stack.push(new_frame); + continue; + } + // Dont pop the frame as new frame was not created. + ItemOrResult::Result(mut result) => { + let (context, inspector) = evm.ctx_inspector(); + frame_end(context, inspector, frame.frame_input(), &mut result); + result + } + } + } + } + ItemOrResult::Result(mut result) => { + let (context, inspector) = evm.ctx_inspector(); + frame_end(context, inspector, frame.frame_input(), &mut result); + + // Pop frame that returned result + frame_stack.pop(); + result + } + }; + + let Some(frame) = frame_stack.last_mut() else { + return Ok(result); + }; + + self.frame_return_result(frame, evm, result)?; + } + } +} + +fn frame_start( + context: &mut CTX, + inspector: &mut impl Inspector, + frame_input: &mut FrameInput, +) -> Option { + match frame_input { + FrameInput::Call(i) => { + if let Some(output) = inspector.call(context, i) { + return Some(FrameResult::Call(output)); + } + } + FrameInput::Create(i) => { + if let Some(output) = inspector.create(context, i) { + return Some(FrameResult::Create(output)); + } + } + FrameInput::EOFCreate(i) => { + if let Some(output) = inspector.eofcreate(context, i) { + return Some(FrameResult::EOFCreate(output)); + } + } + } + None +} + +fn frame_end( + context: &mut CTX, + inspector: &mut impl Inspector, + frame_input: &FrameInput, + frame_output: &mut FrameResult, +) { + match frame_output { + FrameResult::Call(outcome) => { + let FrameInput::Call(i) = frame_input else { + panic!("FrameInput::Call expected"); + }; + inspector.call_end(context, i, outcome); + } + FrameResult::Create(outcome) => { + let FrameInput::Create(i) = frame_input else { + panic!("FrameInput::Create expected"); + }; + inspector.create_end(context, i, outcome); + } + FrameResult::EOFCreate(outcome) => { + let FrameInput::EOFCreate(i) = frame_input else { + panic!("FrameInput::EofCreate expected"); + }; + inspector.eofcreate_end(context, i, outcome); + } + } +} + +// INSTRUCTIONS FOR INSPECTOR + +// pub struct InspectorInstructionProvider { +// instruction_table: Rc<[InspectorInstruction; 256]>, +// } + +// impl Clone for InspectorInstructionProvider +// where +// WIRE: InterpreterTypes, +// { +// fn clone(&self) -> Self { +// Self { +// instruction_table: self.instruction_table.clone(), +// } +// } +// } + +// impl InspectorInstructionProvider +// where +// WIRE: InterpreterTypes, +// HOST: Host + JournalExtGetter + JournalGetter + InspectorCtx, +// { +// pub fn (base_table: InstructionTable) -> Self { +// let mut table: [MaybeUninit>; 256] = +// unsafe { MaybeUninit::uninit().assume_init() }; + +// for (i, element) in table.iter_mut().enumerate() { +// let function: InspectorInstruction = InspectorInstruction { +// instruction: base_table[i], +// }; +// *element = MaybeUninit::new(function); +// } + +// let mut table = unsafe { +// core::mem::transmute::< +// [MaybeUninit>; 256], +// [InspectorInstruction; 256], +// >(table) +// }; + +// // Inspector log wrapper +// fn inspector_log( +// interpreter: &mut Interpreter<::IT>, +// context: &mut CTX, +// prev: Instruction<::IT, CTX>, +// ) { +// prev(interpreter, context); + +// if interpreter.control.instruction_result() == InstructionResult::Continue { +// let last_log = context.journal_ext().logs().last().unwrap().clone(); +// context.inspector_log(interpreter, &last_log); +// } +// } + +// /* LOG and Selfdestruct instructions */ +// table[OpCode::LOG0.as_usize()] = InspectorInstruction { +// instruction: |interp, context| { +// inspector_log(interp, context, log::<0, HOST>); +// }, +// }; +// table[OpCode::LOG1.as_usize()] = InspectorInstruction { +// instruction: |interp, context| { +// inspector_log(interp, context, log::<1, HOST>); +// }, +// }; +// table[OpCode::LOG2.as_usize()] = InspectorInstruction { +// instruction: |interp, context| { +// inspector_log(interp, context, log::<2, HOST>); +// }, +// }; +// table[OpCode::LOG3.as_usize()] = InspectorInstruction { +// instruction: |interp, context| { +// inspector_log(interp, context, log::<3, HOST>); +// }, +// }; +// table[OpCode::LOG4.as_usize()] = InspectorInstruction { +// instruction: |interp, context| { +// inspector_log(interp, context, log::<4, HOST>); +// }, +// }; + +// table[OpCode::SELFDESTRUCT.as_usize()] = InspectorInstruction { +// instruction: |interp, context| { +// selfdestruct::(interp, context); +// if interp.control.instruction_result() == InstructionResult::SelfDestruct { +// match context.journal_ext().last_journal().last() { +// Some(JournalEntry::AccountDestroyed { +// address, +// target, +// had_balance, +// .. +// }) => { +// context.inspector_selfdestruct(*address, *target, *had_balance); +// } +// Some(JournalEntry::BalanceTransfer { +// from, to, balance, .. +// }) => { +// context.inspector_selfdestruct(*from, *to, *balance); +// } +// _ => {} +// } +// } +// }, +// }; + +// Self { +// instruction_table: Rc::new(table), +// } +// } +// } diff --git a/crates/handler/src/instructions.rs b/crates/handler/src/instructions.rs index e0de485952..fe1fbc6c58 100644 --- a/crates/handler/src/instructions.rs +++ b/crates/handler/src/instructions.rs @@ -1,32 +1,26 @@ +use auto_impl::auto_impl; +use context_interface::ContextTrait; use interpreter::{ table::{make_instruction_table, InstructionTable}, Host, Interpreter, InterpreterAction, InterpreterTypes, }; use std::rc::Rc; -pub trait InstructionExecutor: Clone + Default { +/// Stores instructions for EVM. +#[auto_impl(&, Arc, Rc)] +pub trait InstructionProvider { + type Context; type InterpreterTypes: InterpreterTypes; - type CTX; type Output; - fn run( - &mut self, - context: &mut Self::CTX, - interpreter: &mut Interpreter, - ) -> Self::Output; -} - -pub struct EthInstructionExecutor { - instruction_table: Rc>, + fn instruction_table(&self) -> &InstructionTable; } -pub trait InstructionExecutorGetter { - type InstructionExecutor: InstructionExecutor; - - fn executor(&mut self) -> &mut Self::InstructionExecutor; +pub struct EthInstructions { + pub instruction_table: Rc>, } -impl Clone for EthInstructionExecutor +impl Clone for EthInstructions where WIRE: InterpreterTypes, { @@ -37,44 +31,55 @@ where } } -impl EthInstructionExecutor +impl EthInstructions where WIRE: InterpreterTypes, HOST: Host, { - pub fn new() -> Self { + pub fn new_mainnet() -> Self { + Self::new(make_instruction_table::()) + } + + pub fn new(base_table: InstructionTable) -> Self { Self { - instruction_table: Rc::new(make_instruction_table::()), + instruction_table: Rc::new(base_table), } } } -impl InstructionExecutor for EthInstructionExecutor +pub trait ContextInspectRun { + type InterpreterTypes: InterpreterTypes; + type Context: ContextTrait + Host; + + fn run_context( + &mut self, + interpretere: Interpreter, + instructions: &InstructionTable, + ); +} + +impl InstructionProvider for EthInstructions where IT: InterpreterTypes, CTX: Host, { type InterpreterTypes = IT; - type CTX = CTX; + type Context = CTX; /// TODO Interpreter action could be tied to InterpreterTypes so we can /// set custom actions from instructions. type Output = InterpreterAction; - fn run( - &mut self, - context: &mut Self::CTX, - interpreter: &mut Interpreter, - ) -> Self::Output { - interpreter.run_plain(self.instruction_table.as_ref(), context) + fn instruction_table(&self) -> &InstructionTable { + &self.instruction_table } } -impl Default for EthInstructionExecutor +impl Default for EthInstructions where WIRE: InterpreterTypes, HOST: Host, { fn default() -> Self { - Self::new() + Self::new_mainnet() } } diff --git a/crates/handler/interface/src/item_or_result.rs b/crates/handler/src/item_or_result.rs similarity index 100% rename from crates/handler/interface/src/item_or_result.rs rename to crates/handler/src/item_or_result.rs diff --git a/crates/handler/src/lib.rs b/crates/handler/src/lib.rs index 355c7c5800..62914d92b9 100644 --- a/crates/handler/src/lib.rs +++ b/crates/handler/src/lib.rs @@ -11,14 +11,22 @@ pub mod execution; mod frame; mod frame_data; pub mod handler; +pub mod inspector; pub mod instructions; +mod item_or_result; +mod mainnet_handler; +pub mod noop; pub mod post_execution; pub mod pre_execution; mod precompile_provider; pub mod validation; // Public exports -pub use frame::{return_create, return_eofcreate, EthFrame, EthFrameContext, FrameContext}; +pub use frame::{return_create, return_eofcreate, CtxTraitDbError, EthFrame, Frame}; pub use frame_data::{FrameData, FrameResult}; -pub use handler::{EthContext, EthError, EthHandler, MainnetHandler}; -pub use precompile_provider::EthPrecompileProvider; +pub use handler::{inspect_instructions, EthHandler, EthTraitError, EvmTrait}; +pub use inspector::{Inspector, JournalExt}; +pub use item_or_result::{FrameInitOrResult, FrameOrResult, ItemOrResult}; +pub use mainnet_handler::MainnetHandler; +pub use noop::NoOpInspector; +pub use precompile_provider::{EthPrecompiles, PrecompileProvider}; diff --git a/crates/handler/src/mainnet_handler.rs b/crates/handler/src/mainnet_handler.rs new file mode 100644 index 0000000000..86153880c0 --- /dev/null +++ b/crates/handler/src/mainnet_handler.rs @@ -0,0 +1,51 @@ +use super::{EthHandler, EthTraitError}; +use crate::{ + inspector::{EthInspectorHandler, Inspector, InspectorFrame}, + EvmTrait, Frame, FrameResult, +}; +use context_interface::{result::HaltReason, ContextTrait, Journal}; + +use interpreter::{interpreter::EthInterpreter, FrameInput}; + +use primitives::Log; +use state::EvmState; +use std::vec::Vec; + +pub struct MainnetHandler { + pub _phantom: core::marker::PhantomData<(CTX, ERROR, FRAME)>, +} + +impl EthHandler for MainnetHandler +where + EVM: EvmTrait)>>>, + ERROR: EthTraitError, + // TODO `FrameResult` should be a generic trait. + // TODO `FrameInit` should be a generic. + FRAME: Frame, +{ + type Evm = EVM; + type Error = ERROR; + type Frame = FRAME; + type HaltReason = HaltReason; +} + +impl Default for MainnetHandler { + fn default() -> Self { + Self { + _phantom: core::marker::PhantomData, + } + } +} + +impl EthInspectorHandler for MainnetHandler +where + CTX: EvmTrait< + Context: ContextTrait)>>, + Inspector: Inspector<<::Evm as EvmTrait>::Context, EthInterpreter>, + >, + ERROR: EthTraitError, + FRAME: Frame + + InspectorFrame, +{ + type IT = EthInterpreter; +} diff --git a/crates/inspector/src/noop.rs b/crates/handler/src/noop.rs similarity index 77% rename from crates/inspector/src/noop.rs rename to crates/handler/src/noop.rs index 8119182bda..776dbe99f6 100644 --- a/crates/inspector/src/noop.rs +++ b/crates/handler/src/noop.rs @@ -1,6 +1,5 @@ -use revm::interpreter::InterpreterTypes; - -use crate::Inspector; +use crate::inspector::Inspector; +use interpreter::InterpreterTypes; /// Dummy [Inspector], helpful as standalone replacement. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/crates/handler/src/post_execution.rs b/crates/handler/src/post_execution.rs index 7701d7b90c..dcfe64dd6f 100644 --- a/crates/handler/src/post_execution.rs +++ b/crates/handler/src/post_execution.rs @@ -1,9 +1,9 @@ use super::frame_data::FrameResult; +use context_interface::ContextTrait; use context_interface::{ journaled_state::Journal, result::{ExecutionResult, HaltReasonTrait, ResultAndState}, - Block, BlockGetter, Cfg, CfgGetter, JournalDBError, JournalGetter, Transaction, - TransactionGetter, + Block, Cfg, Database, Transaction, }; use interpreter::{Gas, InitialAndFloorGas, SuccessOrHalt}; use primitives::{Log, U256}; @@ -29,10 +29,10 @@ pub fn refund(spec: SpecId, gas: &mut Gas, eip7702_refund: i64) { gas.set_final_refund(spec.is_enabled_in(SpecId::LONDON)); } -pub fn reimburse_caller( +pub fn reimburse_caller( context: &mut CTX, gas: &mut Gas, -) -> Result<(), JournalDBError> { +) -> Result<(), ::Error> { let basefee = context.block().basefee() as u128; let caller = context.tx().caller(); let effective_gas_price = context.tx().effective_gas_price(basefee); @@ -51,10 +51,10 @@ pub fn reimburse_caller( Ok(()) } -pub fn reward_beneficiary( +pub fn reward_beneficiary( context: &mut CTX, gas: &mut Gas, -) -> Result<(), JournalDBError> { +) -> Result<(), ::Error> { let block = context.block(); let tx = context.tx(); let beneficiary = block.beneficiary(); @@ -88,7 +88,7 @@ pub fn reward_beneficiary)>>, + CTX: ContextTrait)>>, HALTREASON: HaltReasonTrait, >( context: &mut CTX, diff --git a/crates/handler/src/pre_execution.rs b/crates/handler/src/pre_execution.rs index fe6bde7538..efd230b48d 100644 --- a/crates/handler/src/pre_execution.rs +++ b/crates/handler/src/pre_execution.rs @@ -3,21 +3,18 @@ //! They handle initial setup of the EVM, call loop and the final return of the EVM use bytecode::Bytecode; +use context_interface::ContextTrait; use context_interface::{ journaled_state::Journal, result::InvalidTransaction, transaction::{Transaction, TransactionType}, - Block, BlockGetter, Cfg, CfgGetter, Database, DatabaseGetter, JournalDBError, JournalGetter, - PerformantContextAccess, TransactionGetter, + Block, Cfg, Database, }; use primitives::{Address, BLOCKHASH_STORAGE_ADDRESS, KECCAK_EMPTY, U256}; use specification::{eip7702, hardfork::SpecId}; use std::vec::Vec; -pub fn load_accounts< - CTX: PerformantContextAccess + BlockGetter + JournalGetter + CfgGetter, - ERROR: From> + From<::Error>, ->( +pub fn load_accounts::Error>>( context: &mut CTX, ) -> Result<(), ERROR> { let spec = context.cfg().spec().into(); @@ -38,15 +35,23 @@ pub fn load_accounts< } // Load access list - context.load_access_list()?; + let (tx, journal) = context.tx_journal(); + if let Some(access_list) = tx.access_list() { + for access_list in access_list { + journal.warm_account_and_storage( + *access_list.0, + access_list.1.iter().map(|i| U256::from_be_bytes(i.0)), + )?; + } + } Ok(()) } #[inline] -pub fn deduct_caller( +pub fn deduct_caller( context: &mut CTX, -) -> Result<(), JournalDBError> { +) -> Result<(), ::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); @@ -85,8 +90,8 @@ pub fn deduct_caller( /// Apply EIP-7702 auth list and return number gas refund on already created accounts. #[inline] pub fn apply_eip7702_auth_list< - CTX: TransactionGetter + JournalGetter + CfgGetter, - ERROR: From + From>, + CTX: ContextTrait, + ERROR: From + From<::Error>, >( context: &mut CTX, ) -> Result { @@ -182,22 +187,3 @@ pub fn apply_eip7702_auth_list< Ok(refunded_gas) } - -pub trait EthPreExecutionContext: - TransactionGetter - + BlockGetter - + JournalGetter - + CfgGetter - + PerformantContextAccess::Database as Database>::Error> -{ -} - -impl< - CTX: TransactionGetter - + BlockGetter - + JournalGetter - + CfgGetter - + PerformantContextAccess::Database as Database>::Error>, - > EthPreExecutionContext for CTX -{ -} diff --git a/crates/handler/src/precompile_provider.rs b/crates/handler/src/precompile_provider.rs index 95169573b3..54f6b9387e 100644 --- a/crates/handler/src/precompile_provider.rs +++ b/crates/handler/src/precompile_provider.rs @@ -1,6 +1,6 @@ +use auto_impl::auto_impl; use context::Cfg; -use context_interface::CfgGetter; -use handler_interface::PrecompileProvider; +use context_interface::ContextTrait; use interpreter::{Gas, InstructionResult, InterpreterResult}; use precompile::PrecompileErrors; use precompile::{PrecompileSpecId, Precompiles}; @@ -8,12 +8,35 @@ use primitives::{Address, Bytes}; use specification::hardfork::SpecId; use std::boxed::Box; -pub struct EthPrecompileProvider { +#[auto_impl(&mut, Box)] +pub trait PrecompileProvider: Clone { + type Context: ContextTrait; + type Output; + + fn set_spec(&mut self, spec: <::Cfg as Cfg>::Spec); + + /// Run the precompile. + fn run( + &mut self, + context: &mut Self::Context, + address: &Address, + bytes: &Bytes, + gas_limit: u64, + ) -> Result, PrecompileErrors>; + + /// Get the warm addresses. + fn warm_addresses(&self) -> Box + '_>; + + /// Check if the address is a precompile. + fn contains(&self, address: &Address) -> bool; +} + +pub struct EthPrecompiles { pub precompiles: &'static Precompiles, - pub _phantom: core::marker::PhantomData<(CTX, ERROR)>, + pub _phantom: core::marker::PhantomData, } -impl Clone for EthPrecompileProvider { +impl Clone for EthPrecompiles { fn clone(&self) -> Self { Self { precompiles: self.precompiles, @@ -22,32 +45,25 @@ impl Clone for EthPrecompileProvider { } } -impl Default for EthPrecompileProvider { +impl Default for EthPrecompiles +where + CTX: ContextTrait, +{ fn default() -> Self { - Self::new(SpecId::LATEST) - } -} - -impl EthPrecompileProvider { - pub fn new(spec: SpecId) -> Self { Self { - precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)), + precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(SpecId::LATEST)), _phantom: core::marker::PhantomData, } } } -impl PrecompileProvider for EthPrecompileProvider +impl PrecompileProvider for EthPrecompiles where - CTX: CfgGetter, - ERROR: From, + CTX: ContextTrait, { type Context = CTX; - type Error = ERROR; type Output = InterpreterResult; - type Spec = <::Cfg as Cfg>::Spec; - - fn set_spec(&mut self, spec: Self::Spec) { + fn set_spec(&mut self, spec: <::Cfg as Cfg>::Spec) { self.precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec.into())); } @@ -57,7 +73,7 @@ where address: &Address, bytes: &Bytes, gas_limit: u64, - ) -> Result, Self::Error> { + ) -> Result, PrecompileErrors> { let Some(precompile) = self.precompiles.get(address) else { return Ok(None); }; @@ -82,7 +98,7 @@ where InstructionResult::PrecompileError }; } - Err(err @ PrecompileErrors::Fatal { .. }) => return Err(err.into()), + Err(err @ PrecompileErrors::Fatal { .. }) => return Err(err), } Ok(Some(result)) } diff --git a/crates/handler/src/validation.rs b/crates/handler/src/validation.rs index 420528cc3a..15e01019a7 100644 --- a/crates/handler/src/validation.rs +++ b/crates/handler/src/validation.rs @@ -1,8 +1,9 @@ +use context_interface::ContextTrait; use context_interface::{ journaled_state::Journal, result::{InvalidHeader, InvalidTransaction}, transaction::{Transaction, TransactionType}, - Block, BlockGetter, Cfg, CfgGetter, JournalDBError, JournalGetter, TransactionGetter, + Block, Cfg, Database, }; use core::cmp::{self, Ordering}; use interpreter::gas::{self, InitialAndFloorGas}; @@ -11,10 +12,7 @@ use specification::{eip4844, hardfork::SpecId}; use state::AccountInfo; use std::boxed::Box; -pub fn validate_env< - CTX: CfgGetter + BlockGetter + TransactionGetter, - ERROR: From + From, ->( +pub fn validate_env + From>( context: CTX, ) -> Result<(), ERROR> { let spec = context.cfg().spec().into(); @@ -30,8 +28,8 @@ pub fn validate_env< } pub fn validate_tx_against_state< - CTX: TransactionGetter + JournalGetter + CfgGetter, - ERROR: From + From>, + CTX: ContextTrait, + ERROR: From + From<::Error>, >( mut context: CTX, ) -> Result<(), ERROR> { @@ -103,7 +101,7 @@ pub fn validate_eip4844_tx( } /// Validate transaction against block and configuration for mainnet. -pub fn validate_tx_env( +pub fn validate_tx_env( context: CTX, spec_id: SpecId, ) -> Result<(), InvalidTransaction> { @@ -233,7 +231,7 @@ pub fn validate_tx_env( /// Validate account against the transaction. #[inline] -pub fn validate_tx_against_account( +pub fn validate_tx_against_account( account: &AccountInfo, context: CTX, ) -> Result<(), InvalidTransaction> { @@ -320,25 +318,3 @@ pub fn validate_initial_tx_gas( Ok(gas) } - -/// Helper trait that summarizes ValidationHandler requirements from Context. -/// -pub trait EthValidationContext: - TransactionGetter + BlockGetter + JournalGetter + CfgGetter -{ -} - -impl EthValidationContext for T {} - -/// Helper trait that summarizes all possible requirements by EthValidation. -pub trait EthValidationError: - From + From + From> -{ -} - -impl< - CTX: JournalGetter, - T: From + From + From>, - > EthValidationError for T -{ -} diff --git a/crates/inspector/Cargo.toml b/crates/inspector/Cargo.toml index c3da09b56b..a4186c6c82 100644 --- a/crates/inspector/Cargo.toml +++ b/crates/inspector/Cargo.toml @@ -25,9 +25,6 @@ all = "warn" # revm revm.workspace = true -# mics -auto_impl.workspace = true - # Optional serde = { version = "1.0", default-features = false, features = [ "derive", diff --git a/crates/inspector/src/eip3155.rs b/crates/inspector/src/eip3155.rs index 168568e92c..ab6b39b86e 100644 --- a/crates/inspector/src/eip3155.rs +++ b/crates/inspector/src/eip3155.rs @@ -1,9 +1,10 @@ -use crate::{inspectors::GasInspector, Inspector}; +use crate::inspectors::GasInspector; use revm::interpreter::interpreter_types::{RuntimeFlag, SubRoutineStack}; use revm::{ bytecode::opcode::OpCode, context::Cfg, - context_interface::{CfgGetter, Journal, JournalGetter, Transaction, TransactionGetter}, + context_interface::{ContextTrait, Journal, Transaction}, + handler::inspector::Inspector, interpreter::{ interpreter_types::{Jumps, LoopControl, MemoryTrait, StackTrait}, CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterResult, @@ -31,7 +32,7 @@ pub struct TracerEip3155 { skip: bool, include_memory: bool, memory: Option, - _phantom: std::marker::PhantomData<(CTX, INTR)>, + _phantom: core::marker::PhantomData<(CTX, INTR)>, } // # Output @@ -113,7 +114,7 @@ struct Summary { impl TracerEip3155 where - CTX: CfgGetter + TransactionGetter, + CTX: ContextTrait, INTR:, { /// Creates a new EIP-3155 tracer with the given output writer, by first wrapping it in a @@ -218,7 +219,7 @@ impl CloneStack for Stack { impl Inspector for TracerEip3155 where - CTX: CfgGetter + TransactionGetter + JournalGetter, + CTX: ContextTrait, INTR: InterpreterTypes, { fn initialize_interp(&mut self, interp: &mut Interpreter, _: &mut CTX) { diff --git a/crates/inspector/src/exec.rs b/crates/inspector/src/exec.rs deleted file mode 100644 index d84085f056..0000000000 --- a/crates/inspector/src/exec.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::{ - inspector_context::InspectorContext, - inspector_instruction::InspectorInstructionExecutor, - journal::{JournalExt, JournalExtGetter}, - Inspector, InspectorCtx, InspectorHandlerImpl, -}; -use revm::{ - context::Cfg, - context_interface::{ - result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, ResultAndState}, - Block, DatabaseGetter, Journal, Transaction, - }, - database_interface::Database, - handler::{ - handler::{EthContext, EthHandler, MainnetHandler}, - EthFrame, - }, - interpreter::{interpreter::EthInterpreter, table::make_instruction_table, InterpreterTypes}, - primitives::Log, - state::EvmState, - Context, DatabaseCommit, ExecuteCommitEvm, ExecuteEvm, -}; -use std::vec::Vec; - -pub trait InspectEvm: ExecuteEvm { - fn inspect<'a, 'b, INSP>(&'a mut self, tx: Self::Transaction, inspector: INSP) -> Self::Output - where - INSP: Inspector<&'a mut Self, INTR> + 'b, - { - self.set_tx(tx); - self.inspect_previous(inspector) - } - - /// Drawback if inspector overlives the context it will take the mutable reference - /// of it and inspector needs to be dropped to release the mutable reference. - fn inspect_previous<'a, 'b, INSP>(&'a mut self, inspector: INSP) -> Self::Output - where - INSP: Inspector<&'a mut Self, INTR> + 'b; -} - -impl< - BLOCK: Block, - TX: Transaction, - CFG: Cfg, - DB: Database, - JOURNAL: Journal)> + JournalExt, - CHAIN, - > InspectEvm for Context -{ - fn inspect_previous<'a, 'b, INSP>(&'a mut self, inspector: INSP) -> Self::Output - where - INSP: Inspector<&'a mut Self, EthInterpreter> + 'b, - { - let mut insp = InspectorContext::new(self, inspector); - inspect_main(&mut insp) - } -} - -pub trait InspectCommitEvm: InspectEvm + ExecuteCommitEvm { - fn inspect_commit<'a, 'b, INSP>( - &'a mut self, - tx: Self::Transaction, - inspector: INSP, - ) -> Self::CommitOutput - where - INSP: Inspector<&'a mut Self, INTR> + 'b, - { - self.set_tx(tx); - self.inspect_commit_previous(inspector) - } - - fn inspect_commit_previous<'a, 'b, INSP>(&'a mut self, inspector: INSP) -> Self::CommitOutput - where - INSP: Inspector<&'a mut Self, INTR> + 'b; -} - -impl< - BLOCK: Block, - TX: Transaction, - CFG: Cfg, - DB: Database + DatabaseCommit, - JOURNAL: Journal)> + JournalExt, - CHAIN, - > InspectCommitEvm for Context -{ - fn inspect_commit_previous<'a, 'b, INSP>(&'a mut self, inspector: INSP) -> Self::CommitOutput - where - INSP: Inspector<&'a mut Self, EthInterpreter> + 'b, - { - let mut insp = InspectorContext::new(self, inspector); - inspect_main_commit(&mut insp) - } -} - -pub fn inspect_main< - DB: Database, - CTX: EthContext - + JournalExtGetter - + DatabaseGetter - + InspectorCtx, ->( - ctx: &mut CTX, -) -> Result, EVMError<::Error, InvalidTransaction>> { - InspectorHandlerImpl::<_, _, EthFrame<_, _, _, _>, _, _, EthInterpreter>::new( - MainnetHandler::<_, _, _, _, InspectorInstructionExecutor>::default(), - make_instruction_table(), - ) - .run(ctx) -} - -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/gas.rs b/crates/inspector/src/gas.rs index c64d4189e1..84fadecd5d 100644 --- a/crates/inspector/src/gas.rs +++ b/crates/inspector/src/gas.rs @@ -65,145 +65,105 @@ impl GasInspector { } } -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::inspector_handle_register; -// use database::BenchmarkDB; -// use revm::{ -// bytecode::{opcode, Bytecode}, -// context_interface::EvmWiring as PrimitiveEvmWiring, -// context_interface::{DefaultEthereumWiring, EthereumWiring}, -// interpreter::Interpreter, -// primitives::{address, Bytes, Log, TxKind}, -// Evm, EvmWiring, -// }; - -// type TestEvmWiring = DefaultEthereumWiring; - -// #[derive(Default, Debug)] -// struct StackInspector { -// pc: usize, -// gas_inspector: GasInspector, -// gas_remaining_steps: Vec<(usize, u64)>, -// } - -// impl Inspector for StackInspector { -// fn initialize_interp( -// &mut self, -// interp: &mut Interpreter, -// context: &mut EvmContext, -// ) { -// self.gas_inspector.initialize_interp(interp, context); -// } - -// fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { -// self.pc = interp.program_counter(); -// self.gas_inspector.step(interp, context); -// } - -// fn log( -// &mut self, -// interp: &mut Interpreter, -// context: &mut EvmContext, -// log: &Log, -// ) { -// self.gas_inspector.log(interp, context, log); -// } - -// fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { -// self.gas_inspector.step_end(interp, context); -// self.gas_remaining_steps -// .push((self.pc, self.gas_inspector.gas_remaining())); -// } - -// fn call( -// &mut self, -// context: &mut EvmContext, -// call: &mut CallInputs, -// ) -> Option { -// self.gas_inspector.call(context, call) -// } - -// fn call_end( -// &mut self, -// context: &mut EvmContext, -// inputs: &CallInputs, -// outcome: CallOutcome, -// ) -> CallOutcome { -// self.gas_inspector.call_end(context, inputs, outcome) -// } - -// fn create( -// &mut self, -// context: &mut EvmContext, -// call: &mut CreateInputs, -// ) -> Option { -// self.gas_inspector.create(context, call); -// None -// } - -// fn create_end( -// &mut self, -// context: &mut EvmContext, -// inputs: &CreateInputs, -// outcome: CreateOutcome, -// ) -> CreateOutcome { -// self.gas_inspector.create_end(context, inputs, outcome) -// } -// } - -// #[test] -// fn test_gas_inspector() { -// let contract_data: Bytes = Bytes::from(vec![ -// opcode::PUSH1, -// 0x1, -// opcode::PUSH1, -// 0xb, -// opcode::JUMPI, -// opcode::PUSH1, -// 0x1, -// opcode::PUSH1, -// 0x1, -// opcode::PUSH1, -// 0x1, -// opcode::JUMPDEST, -// opcode::STOP, -// ]); -// let bytecode = Bytecode::new_raw(contract_data); - -// let mut evm = Evm::>::builder() -// .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) -// .with_default_ext_context() -// .modify_tx_env(|tx| { -// *tx = ::Transaction::default(); - -// tx.caller = address!("1000000000000000000000000000000000000000"); -// tx.kind = TxKind::Call(address!("0000000000000000000000000000000000000000")); -// tx.gas_limit = 21100; -// }) -// .append_handler_register(inspector_handle_register) -// .build(); - -// // Run evm. -// evm.transact().unwrap(); - -// let inspector = evm.into_context().external; - -// // Starting from 100gas -// let steps = vec![ -// // push1 -3 -// (0, 97), -// // push1 -3 -// (2, 94), -// // jumpi -10 -// (4, 84), -// // jumpdest 1 -// (11, 83), -// // stop 0 -// (12, 83), -// ]; - -// assert_eq!(inspector.gas_remaining_steps, steps); -// } -// } +#[cfg(test)] +mod tests { + use super::*; + use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; + use revm::{ + bytecode::{opcode, Bytecode}, + handler::inspector::Inspector, + interpreter::{ + interpreter_types::{Jumps, LoopControl}, + CallInputs, CreateInputs, Interpreter, InterpreterTypes, + }, + primitives::{Bytes, TxKind}, + Context, + InspectEvm, + MainBuilder, + MainContext, //Evm, + //EvmWiring, + }; + + #[derive(Default, Debug)] + struct StackInspector { + pc: usize, + gas_inspector: GasInspector, + gas_remaining_steps: Vec<(usize, u64)>, + } + + impl Inspector for StackInspector { + fn initialize_interp(&mut self, interp: &mut Interpreter, _context: &mut CTX) { + self.gas_inspector.initialize_interp(interp.control.gas()); + } + + fn step(&mut self, interp: &mut Interpreter, _context: &mut CTX) { + self.pc = interp.bytecode.pc(); + self.gas_inspector.step(interp.control.gas()); + } + + fn step_end(&mut self, interp: &mut Interpreter, _context: &mut CTX) { + self.gas_inspector.step_end(interp.control.gas()); + self.gas_remaining_steps + .push((self.pc, self.gas_inspector.gas_remaining())); + } + + fn call_end(&mut self, _c: &mut CTX, _i: &CallInputs, outcome: &mut CallOutcome) { + self.gas_inspector.call_end(outcome) + } + + fn create_end(&mut self, _c: &mut CTX, _i: &CreateInputs, outcome: &mut CreateOutcome) { + self.gas_inspector.create_end(outcome) + } + } + + #[test] + fn test_gas_inspector() { + let contract_data: Bytes = Bytes::from(vec![ + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0xb, + opcode::JUMPI, + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0x1, + opcode::JUMPDEST, + opcode::STOP, + ]); + let bytecode = Bytecode::new_raw(contract_data); + + let ctx = Context::mainnet() + .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) + .modify_tx_chained(|tx| { + tx.caller = BENCH_CALLER; + tx.kind = TxKind::Call(BENCH_TARGET); + tx.gas_limit = 21100; + }); + + let mut evm = ctx.build_mainnet_with_inspector(StackInspector::default()); + + // Run evm. + evm.inspect_previous().unwrap(); + + let inspector = &evm.data.inspector; + + // Starting from 100gas + let steps = vec![ + // push1 -3 + (0, 97), + // push1 -3 + (2, 94), + // jumpi -10 + (4, 84), + // jumpdest 1 + (11, 83), + // stop 0 + (12, 83), + ]; + + assert_eq!(inspector.gas_remaining_steps, steps); + } +} diff --git a/crates/inspector/src/inspector.rs b/crates/inspector/src/inspector.rs deleted file mode 100644 index 7cf098e0f8..0000000000 --- a/crates/inspector/src/inspector.rs +++ /dev/null @@ -1,325 +0,0 @@ -use crate::{ - inspector_instruction::InspectorInstructionExecutor, - journal::{JournalExt, JournalExtGetter}, -}; -use auto_impl::auto_impl; -use revm::{ - context_interface::Journal, - database_interface::Database, - handler::{ - handler::{EthContext, EthError, EthHandler}, - EthFrame, FrameContext, FrameResult, - }, - handler_interface::{Frame, ItemOrResult, PrecompileProvider}, - interpreter::{ - table::InstructionTable, CallInputs, CallOutcome, CreateInputs, CreateOutcome, - EOFCreateInputs, FrameInput, Interpreter, InterpreterTypes, - }, - primitives::{Address, Log, U256}, - Context, -}; - -/// EVM [Interpreter] callbacks. -#[auto_impl(&mut, Box)] -pub trait Inspector { - /// Called before the interpreter is initialized. - /// - /// If `interp.instruction_result` is set to anything other than [revm::interpreter::InstructionResult::Continue] then the execution of the interpreter - /// is skipped. - #[inline] - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut CTX) { - let _ = interp; - let _ = context; - } - - /// Called on each step of the interpreter. - /// - /// Information about the current execution, including the memory, stack and more is available - /// on `interp` (see [Interpreter]). - /// - /// # Example - /// - /// To get the current opcode, use `interp.current_opcode()`. - #[inline] - fn step(&mut self, interp: &mut Interpreter, context: &mut CTX) { - let _ = interp; - let _ = context; - } - - /// Called after `step` when the instruction has been executed. - /// - /// Setting `interp.instruction_result` to anything other than [revm::interpreter::InstructionResult::Continue] alters the execution - /// of the interpreter. - #[inline] - fn step_end(&mut self, interp: &mut Interpreter, context: &mut CTX) { - let _ = interp; - let _ = context; - } - - /// Called when a log is emitted. - #[inline] - fn log(&mut self, interp: &mut Interpreter, context: &mut CTX, log: &Log) { - let _ = interp; - let _ = context; - let _ = log; - } - - /// Called whenever a call to a contract is about to start. - /// - /// InstructionResulting anything other than [revm::interpreter::InstructionResult::Continue] overrides the result of the call. - #[inline] - fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option { - let _ = context; - let _ = inputs; - None - } - - /// Called when a call to a contract has concluded. - /// - /// The returned [CallOutcome] is used as the result of the call. - /// - /// This allows the inspector to modify the given `result` before returning it. - #[inline] - fn call_end(&mut self, context: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) { - let _ = context; - let _ = inputs; - let _ = outcome; - } - - /// Called when a contract is about to be created. - /// - /// If this returns `Some` then the [CreateOutcome] is used to override the result of the creation. - /// - /// If this returns `None` then the creation proceeds as normal. - #[inline] - fn create(&mut self, context: &mut CTX, inputs: &mut CreateInputs) -> Option { - let _ = context; - let _ = inputs; - None - } - - /// Called when a contract has been created. - /// - /// InstructionResulting anything other than the values passed to this function (`(ret, remaining_gas, - /// address, out)`) will alter the result of the create. - #[inline] - fn create_end( - &mut self, - context: &mut CTX, - inputs: &CreateInputs, - outcome: &mut CreateOutcome, - ) { - let _ = context; - let _ = inputs; - let _ = outcome; - } - - /// Called when EOF creating is called. - /// - /// This can happen from create TX or from EOFCREATE opcode. - fn eofcreate( - &mut self, - context: &mut CTX, - inputs: &mut EOFCreateInputs, - ) -> Option { - let _ = context; - let _ = inputs; - None - } - - /// Called when eof creating has ended. - fn eofcreate_end( - &mut self, - context: &mut CTX, - inputs: &EOFCreateInputs, - outcome: &mut CreateOutcome, - ) { - let _ = context; - let _ = inputs; - let _ = outcome; - } - - /// Called when a contract has been self-destructed with funds transferred to target. - #[inline] - fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { - let _ = contract; - let _ = target; - let _ = value; - } -} - -/// Provides access to an `Inspector` instance. -pub trait GetInspector { - /// Returns the associated `Inspector`. - fn get_inspector(&mut self) -> &mut impl Inspector; -} - -#[auto_impl(&mut, Box)] -pub trait InspectorCtx { - type IT: InterpreterTypes; - - fn step(&mut self, interp: &mut Interpreter); - fn step_end(&mut self, interp: &mut Interpreter); - fn initialize_interp(&mut self, interp: &mut Interpreter); - fn frame_start(&mut self, frame_input: &mut FrameInput) -> Option; - fn frame_end(&mut self, frame_output: &mut FrameResult); - fn inspector_selfdestruct(&mut self, contract: Address, target: Address, value: U256); - fn inspector_log(&mut self, interp: &mut Interpreter, log: &Log); -} - -impl> GetInspector for INSP { - #[inline] - fn get_inspector(&mut self) -> &mut impl Inspector { - self - } -} - -impl + JournalExt, CHAIN> - JournalExtGetter for Context -{ - type JournalExt = JOURNAL; - - fn journal_ext(&self) -> &Self::JournalExt { - &self.journaled_state - } -} - -pub struct InspectorHandlerImpl { - pub handler: HANDLER, - pub base_instructions: InstructionTable, - _phantom: core::marker::PhantomData<(CTX, ERROR, FRAME, PRECOMPILES)>, -} - -impl - InspectorHandlerImpl -{ - pub fn new(handler: HANDLER, base_instructions: InstructionTable) -> Self { - Self { - handler, - base_instructions, - _phantom: core::marker::PhantomData, - } - } -} - -pub trait FrameInterpreterGetter { - type IT: InterpreterTypes; - - fn interpreter(&mut self) -> &mut Interpreter; -} - -impl FrameInterpreterGetter - for EthFrame -{ - type IT = IW; - - fn interpreter(&mut self) -> &mut Interpreter { - &mut self.interpreter - } -} - -impl EthHandler - for InspectorHandlerImpl -where - 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: EthHandler, -{ - type Context = CTX; - type Error = ERROR; - type Frame = FRAME; - type Precompiles = PRECOMPILES; - type Instructions = InspectorInstructionExecutor; - type HaltReason = ::HaltReason; - - fn instructions(&self, _context: &mut Self::Context) -> Self::Instructions { - InspectorInstructionExecutor::new(self.base_instructions) - } - - 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(ItemOrResult::Result(output)); - } - let mut ret = self - .handler - .frame_init_first(context, frame_context, frame_input); - - // only if new frame is created call initialize_interp hook. - if let Ok(ItemOrResult::Item(frame)) = &mut ret { - context.initialize_interp(frame.interpreter()); - } - ret - } - - fn frame_init( - &mut self, - 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(ItemOrResult::Result(output)); - } - let mut ret = self - .handler - .frame_init(frame, context, frame_context, frame_input); - - // only if new frame is created call initialize_interp hook. - if let Ok(ItemOrResult::Item(frame)) = &mut ret { - context.initialize_interp(frame.interpreter()); - } - ret - } - - fn frame_return_result( - &mut self, - 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 last_frame_result( - &self, - context: &mut Self::Context, - frame_context: &mut ::FrameContext, - frame_result: &mut ::FrameResult, - ) -> Result<(), Self::Error> { - context.frame_end(frame_result); - self.handler - .last_frame_result(context, frame_context, frame_result) - } -} diff --git a/crates/inspector/src/inspector_context.rs b/crates/inspector/src/inspector_context.rs deleted file mode 100644 index f5d696eb4d..0000000000 --- a/crates/inspector/src/inspector_context.rs +++ /dev/null @@ -1,261 +0,0 @@ -use revm::{ - context_interface::{ - block::BlockSetter, transaction::TransactionSetter, BlockGetter, CfgGetter, DatabaseGetter, - ErrorGetter, JournalGetter, PerformantContextAccess, TransactionGetter, - }, - database_interface::Database, - handler::{handler::EthContext, FrameResult}, - interpreter::{interpreter::EthInterpreter, FrameInput, Host, Interpreter}, - primitives::{Address, Log, U256}, -}; -use std::vec::Vec; - -use crate::{journal::JournalExtGetter, GetInspector, Inspector, InspectorCtx}; - -/// EVM context contains data that EVM needs for execution. -#[derive(Clone, Debug)] -pub struct InspectorContext { - pub inspector: INSP, - pub inner: CTX, - pub frame_input_stack: Vec, -} - -impl, CTX: EthContext + DatabaseGetter> EthContext - for InspectorContext -{ -} - -impl, CTX: EthContext + DatabaseGetter> EthContext - for &mut InspectorContext -{ -} - -impl InspectorContext -where - CTX: BlockGetter - + TransactionGetter - + CfgGetter - + DatabaseGetter - + JournalGetter - + ErrorGetter - + Host, -{ - pub fn new(inner: CTX, inspector: INSP) -> Self { - Self { - inner, - inspector, - frame_input_stack: Vec::new(), - } - } -} - -impl InspectorCtx for InspectorContext -where - INSP: GetInspector, - CTX: DatabaseGetter, -{ - type IT = EthInterpreter; - - fn step(&mut self, interp: &mut Interpreter) { - self.inspector.get_inspector().step(interp, &mut self.inner); - } - - fn step_end(&mut self, interp: &mut Interpreter) { - self.inspector - .get_inspector() - .step_end(interp, &mut self.inner); - } - - fn initialize_interp(&mut self, interp: &mut Interpreter) { - self.inspector - .get_inspector() - .initialize_interp(interp, &mut self.inner); - } - fn inspector_log(&mut self, interp: &mut Interpreter, log: &Log) { - self.inspector - .get_inspector() - .log(interp, &mut self.inner, log); - } - - fn frame_start(&mut self, frame_input: &mut FrameInput) -> Option { - let insp = self.inspector.get_inspector(); - let context = &mut self.inner; - match frame_input { - FrameInput::Call(i) => { - if let Some(output) = insp.call(context, i) { - return Some(FrameResult::Call(output)); - } - } - FrameInput::Create(i) => { - if let Some(output) = insp.create(context, i) { - return Some(FrameResult::Create(output)); - } - } - FrameInput::EOFCreate(i) => { - if let Some(output) = insp.eofcreate(context, i) { - return Some(FrameResult::EOFCreate(output)); - } - } - } - self.frame_input_stack.push(frame_input.clone()); - None - } - - fn frame_end(&mut self, frame_output: &mut FrameResult) { - let insp = self.inspector.get_inspector(); - let context = &mut self.inner; - let Some(frame_input) = self.frame_input_stack.pop() else { - // case where call returns immediately will not push to call stack. - return; - }; - match frame_output { - FrameResult::Call(outcome) => { - let FrameInput::Call(i) = frame_input else { - panic!("FrameInput::Call expected"); - }; - insp.call_end(context, &i, outcome); - } - FrameResult::Create(outcome) => { - let FrameInput::Create(i) = frame_input else { - panic!("FrameInput::Create expected"); - }; - insp.create_end(context, &i, outcome); - } - FrameResult::EOFCreate(outcome) => { - let FrameInput::EOFCreate(i) = frame_input else { - panic!("FrameInput::EofCreate expected"); - }; - insp.eofcreate_end(context, &i, outcome); - } - } - } - - fn inspector_selfdestruct(&mut self, contract: Address, target: Address, value: U256) { - self.inspector - .get_inspector() - .selfdestruct(contract, target, value) - } -} - -impl CfgGetter for InspectorContext -where - CTX: CfgGetter + DatabaseGetter, -{ - type Cfg = ::Cfg; - - fn cfg(&self) -> &Self::Cfg { - self.inner.cfg() - } -} - -impl JournalGetter for InspectorContext -where - CTX: JournalGetter + DatabaseGetter, -{ - type Journal = ::Journal; - - fn journal(&mut self) -> &mut Self::Journal { - self.inner.journal() - } - - fn journal_ref(&self) -> &Self::Journal { - self.inner.journal_ref() - } -} - -impl, CTX> Host for InspectorContext -where - CTX: Host + DatabaseGetter, -{ - fn set_error(&mut self, error: ::Error) { - self.inner.set_error(error); - } -} - -impl DatabaseGetter for InspectorContext -where - CTX: DatabaseGetter, -{ - type Database = ::Database; - - fn db(&mut self) -> &mut Self::Database { - self.inner.db() - } - - fn db_ref(&self) -> &Self::Database { - self.inner.db_ref() - } -} - -impl ErrorGetter for InspectorContext -where - CTX: ErrorGetter + JournalGetter, -{ - type Error = ::Error; - - fn take_error(&mut self) -> Result<(), Self::Error> { - self.inner.take_error() - } -} - -impl TransactionGetter for InspectorContext -where - CTX: TransactionGetter + DatabaseGetter, -{ - type Transaction = ::Transaction; - - fn tx(&self) -> &Self::Transaction { - self.inner.tx() - } -} - -impl TransactionSetter for InspectorContext -where - CTX: TransactionSetter + DatabaseGetter, -{ - fn set_tx(&mut self, tx: ::Transaction) { - self.inner.set_tx(tx); - } -} - -impl BlockGetter for InspectorContext -where - CTX: BlockGetter + DatabaseGetter, -{ - type Block = ::Block; - - fn block(&self) -> &Self::Block { - self.inner.block() - } -} - -impl BlockSetter for InspectorContext -where - CTX: BlockSetter + DatabaseGetter, -{ - fn set_block(&mut self, block: ::Block) { - self.inner.set_block(block); - } -} - -impl JournalExtGetter for InspectorContext -where - CTX: JournalExtGetter + DatabaseGetter, -{ - type JournalExt = ::JournalExt; - - fn journal_ext(&self) -> &Self::JournalExt { - self.inner.journal_ext() - } -} - -impl PerformantContextAccess for InspectorContext -where - CTX: PerformantContextAccess + DatabaseGetter, -{ - type Error = ::Error; - - fn load_access_list(&mut self) -> Result<(), Self::Error> { - self.inner.load_access_list() - } -} diff --git a/crates/inspector/src/inspector_instruction.rs b/crates/inspector/src/inspector_instruction.rs deleted file mode 100644 index b852bd6714..0000000000 --- a/crates/inspector/src/inspector_instruction.rs +++ /dev/null @@ -1,202 +0,0 @@ -use core::mem::MaybeUninit; -use revm::{ - bytecode::opcode::OpCode, - context_interface::JournalGetter, - handler::instructions::InstructionExecutor, - interpreter::{ - instructions::host::{log, selfdestruct}, - interpreter_types::{Jumps, LoopControl}, - table::{make_instruction_table, CustomInstruction, InstructionTable}, - Host, Instruction, InstructionResult, Interpreter, InterpreterAction, InterpreterTypes, - }, - JournalEntry, -}; -use std::rc::Rc; - -use crate::{ - journal::{JournalExt, JournalExtGetter}, - 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) { - (self.instruction)(interpreter, host); - } - - fn from_base(instruction: Instruction) -> Self { - Self { instruction } - } -} - -pub struct InspectorInstructionExecutor { - instruction_table: Rc<[InspectorInstruction; 256]>, -} - -impl Clone for InspectorInstructionExecutor -where - WIRE: InterpreterTypes, -{ - fn clone(&self) -> Self { - Self { - instruction_table: self.instruction_table.clone(), - } - } -} - -impl InspectorInstructionExecutor -where - WIRE: InterpreterTypes, - HOST: Host + JournalExtGetter + JournalGetter + InspectorCtx, -{ - pub fn new(base_table: InstructionTable) -> Self { - let mut table: [MaybeUninit>; 256] = - unsafe { MaybeUninit::uninit().assume_init() }; - - for (i, element) in table.iter_mut().enumerate() { - let function: InspectorInstruction = InspectorInstruction { - instruction: base_table[i], - }; - *element = MaybeUninit::new(function); - } - - let mut table = unsafe { - core::mem::transmute::< - [MaybeUninit>; 256], - [InspectorInstruction; 256], - >(table) - }; - - // Inspector log wrapper - fn inspector_log( - interpreter: &mut Interpreter<::IT>, - context: &mut CTX, - prev: Instruction<::IT, CTX>, - ) { - prev(interpreter, context); - - if interpreter.control.instruction_result() == InstructionResult::Continue { - let last_log = context.journal_ext().logs().last().unwrap().clone(); - context.inspector_log(interpreter, &last_log); - } - } - - /* LOG and Selfdestruct instructions */ - table[OpCode::LOG0.as_usize()] = InspectorInstruction { - instruction: |interp, context| { - inspector_log(interp, context, log::<0, HOST>); - }, - }; - table[OpCode::LOG1.as_usize()] = InspectorInstruction { - instruction: |interp, context| { - inspector_log(interp, context, log::<1, HOST>); - }, - }; - table[OpCode::LOG2.as_usize()] = InspectorInstruction { - instruction: |interp, context| { - inspector_log(interp, context, log::<2, HOST>); - }, - }; - table[OpCode::LOG3.as_usize()] = InspectorInstruction { - instruction: |interp, context| { - inspector_log(interp, context, log::<3, HOST>); - }, - }; - table[OpCode::LOG4.as_usize()] = InspectorInstruction { - instruction: |interp, context| { - inspector_log(interp, context, log::<4, HOST>); - }, - }; - - table[OpCode::SELFDESTRUCT.as_usize()] = InspectorInstruction { - instruction: |interp, context| { - selfdestruct::(interp, context); - if interp.control.instruction_result() == InstructionResult::SelfDestruct { - match context.journal_ext().last_journal().last() { - Some(JournalEntry::AccountDestroyed { - address, - target, - had_balance, - .. - }) => { - context.inspector_selfdestruct(*address, *target, *had_balance); - } - Some(JournalEntry::BalanceTransfer { - from, to, balance, .. - }) => { - context.inspector_selfdestruct(*from, *to, *balance); - } - _ => {} - } - } - }, - }; - - Self { - instruction_table: Rc::new(table), - } - } -} - -impl InstructionExecutor for InspectorInstructionExecutor -where - IT: InterpreterTypes, - CTX: Host + JournalExtGetter + JournalGetter + InspectorCtx, -{ - type InterpreterTypes = IT; - type CTX = CTX; - type Output = InterpreterAction; - - fn run( - &mut self, - context: &mut Self::CTX, - interpreter: &mut Interpreter, - ) -> Self::Output { - interpreter.reset_control(); - - // Main loop - while interpreter.control.instruction_result().is_continue() { - // Get current opcode. - let opcode = interpreter.bytecode.opcode(); - - // Call Inspector step. - context.step(interpreter); - if interpreter.control.instruction_result() != InstructionResult::Continue { - break; - } - - // SAFETY: In analysis we are doing padding of bytecode so that we are sure that last - // byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction - // it will do noop and just stop execution of this contract - interpreter.bytecode.relative_jump(1); - - // Execute instruction. - self.instruction_table[opcode as usize].exec(interpreter, context); - - // Call step_end. - context.step_end(interpreter); - } - - interpreter.take_next_action() - } -} - -impl Default for InspectorInstructionExecutor -where - WIRE: InterpreterTypes, - HOST: Host + JournalExtGetter + JournalGetter + InspectorCtx, -{ - fn default() -> Self { - Self::new(make_instruction_table()) - } -} diff --git a/crates/inspector/src/journal.rs b/crates/inspector/src/journal.rs deleted file mode 100644 index 7d80678fd6..0000000000 --- a/crates/inspector/src/journal.rs +++ /dev/null @@ -1,41 +0,0 @@ -use auto_impl::auto_impl; -use revm::{ - context::JournaledState, database_interface::Database, primitives::Log, state::EvmState, - JournalEntry, -}; - -#[auto_impl(&mut, Box)] -pub trait JournalExt { - fn logs(&self) -> &[Log]; - - fn last_journal(&self) -> &[JournalEntry]; - - fn evm_state(&self) -> &EvmState; - - fn evm_state_mut(&mut self) -> &mut EvmState; -} - -impl JournalExt for JournaledState { - fn logs(&self) -> &[Log] { - &self.logs - } - - fn last_journal(&self) -> &[JournalEntry] { - self.journal.last().expect("Journal is never empty") - } - - fn evm_state(&self) -> &EvmState { - &self.state - } - - fn evm_state_mut(&mut self) -> &mut EvmState { - &mut self.state - } -} - -#[auto_impl(&, &mut, Box, Arc)] -pub trait JournalExtGetter { - type JournalExt: JournalExt; - - fn journal_ext(&self) -> &Self::JournalExt; -} diff --git a/crates/inspector/src/lib.rs b/crates/inspector/src/lib.rs index 22b32b10fb..3f8911b6ad 100644 --- a/crates/inspector/src/lib.rs +++ b/crates/inspector/src/lib.rs @@ -2,25 +2,16 @@ #![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; #[cfg(all(feature = "std", feature = "serde-json"))] mod eip3155; -pub mod exec; mod gas; -mod inspector; -pub mod inspector_context; -pub mod inspector_instruction; -pub mod journal; -mod noop; -pub use inspector::*; - -/// [Inspector] implementations. +/// Inspector implementations. pub mod inspectors { #[cfg(all(feature = "std", feature = "serde-json"))] pub use super::eip3155::TracerEip3155; pub use super::gas::GasInspector; - pub use super::noop::NoOpInspector; } diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index f1016f7ab7..10d30150a6 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -3,8 +3,6 @@ mod input; mod loop_control; mod return_data; mod runtime_flags; -#[cfg(feature = "serde")] -pub mod serde; mod shared_memory; mod stack; mod subroutine_stack; @@ -209,29 +207,6 @@ impl InterpreterResult { #[cfg(test)] mod tests { - // use super::*; - // use crate::{table::InstructionTable, DummyHost}; - - // #[test] - // fn object_safety() { - // let mut interp = Interpreter::new(Contract::default(), u64::MAX, false); - // interp.spec_id = SpecId::CANCUN; - // let mut host = crate::DummyHost::::default(); - // let table: &InstructionTable> = - // &crate::table::make_instruction_table::>( - // ); - // let _ = interp.run(EMPTY_SHARED_MEMORY, table, &mut host); - - // let host: &mut dyn Host = - // &mut host as &mut dyn Host; - // let table: &InstructionTable> = - // &crate::table::make_instruction_table::< - // Interpreter, - // dyn Host, - // >(); - // let _ = interp.run(EMPTY_SHARED_MEMORY, table, host); - // } - use super::*; use bytecode::Bytecode; use primitives::{Address, Bytes, U256}; diff --git a/crates/interpreter/src/interpreter/serde.rs b/crates/interpreter/src/interpreter/serde.rs deleted file mode 100644 index ebbfd19956..0000000000 --- a/crates/interpreter/src/interpreter/serde.rs +++ /dev/null @@ -1,130 +0,0 @@ -// use super::{subroutine_stack::SubRoutineImpl, Interpreter}; -// use crate::{Contract, Gas, InstructionResult, InterpreterAction, SharedMemory, Stack}; -// use primitives::Bytes; -// use serde::{Deserialize, Deserializer, Serialize, Serializer}; -// use specification::hardfork::SpecId; - -// #[derive(Serialize)] -// struct InterpreterSerde<'a> { -// program_counter: usize, - -// gas: &'a Gas, -// contract: &'a Contract, -// instruction_result: InstructionResult, -// bytecode: &'a Bytes, -// is_eof: bool, -// is_eof_init: bool, -// shared_memory: &'a SharedMemory, -// stack: &'a Stack, -// function_stack: &'a SubRoutineImpl, -// return_data_buffer: &'a Bytes, -// is_static: bool, -// next_action: &'a InterpreterAction, -// spec_id: SpecId, -// } - -// #[derive(Deserialize)] -// struct InterpreterDe { -// program_counter: usize, - -// gas: Gas, -// contract: Contract, -// instruction_result: InstructionResult, -// bytecode: Bytes, -// is_eof: bool, -// is_eof_init: bool, -// shared_memory: SharedMemory, -// stack: Stack, -// function_stack: SubRoutineImpl, -// return_data_buffer: Bytes, -// is_static: bool, -// next_action: InterpreterAction, -// spec_id: SpecId, -// } - -// impl Serialize for Interpreter { -// fn serialize(&self, serializer: S) -> Result -// where -// S: Serializer, -// { -// InterpreterSerde { -// program_counter: self.bytecode().pc(), -// gas: &self.gas, -// contract: &self.contract, -// instruction_result: self.instruction_result, -// bytecode: &self.bytecode, -// is_eof: self.is_eof, -// is_eof_init: self.is_eof_init, -// shared_memory: &self.shared_memory, -// stack: &self.stack, -// function_stack: &self.function_stack, -// return_data_buffer: &self.return_data_buffer, -// is_static: self.is_static, -// next_action: &self.next_action, -// spec_id: self.spec_id, -// } -// .serialize(serializer) -// } -// } - -// impl<'de> Deserialize<'de> for Interpreter { -// fn deserialize(deserializer: D) -> Result -// where -// D: Deserializer<'de>, -// { -// let InterpreterDe { -// program_counter, -// gas, -// contract, -// instruction_result, -// bytecode, -// is_eof, -// is_eof_init, -// shared_memory, -// stack, -// function_stack, -// return_data_buffer, -// is_static, -// next_action, -// spec_id, -// } = InterpreterDe::deserialize(deserializer)?; - -// // Reconstruct the instruction pointer from usize -// if program_counter >= bytecode.len() { -// return Err(serde::de::Error::custom("program_counter out of bounds")); -// } - -// // SAFETY: Range of program_counter checked above -// let instruction_pointer = unsafe { bytecode.as_ptr().add(program_counter) }; - -// Ok(Interpreter { -// instruction_pointer, -// gas, -// contract, -// instruction_result, -// bytecode, -// is_eof, -// is_eof_init, -// shared_memory, -// stack, -// function_stack, -// return_data_buffer, -// is_static, -// next_action, -// spec_id, -// }) -// } -// } - -// #[cfg(test)] -// mod tests { -// use super::*; - -// #[test] -// fn test_serde() { -// let interp = Interpreter::new(Contract::default(), u64::MAX, false); -// let serialized = bincode::serialize(&interp).unwrap(); -// let de: Interpreter = bincode::deserialize(&serialized).unwrap(); -// assert_eq!(interp.program_counter(), de.program_counter()); -// } -// } diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index 05bec1a828..c362ec28e8 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -27,7 +27,7 @@ pub mod table; // Reexport primary types. pub use context_interface::{ - host::{DummyHost, Host, SStoreResult, SelfDestructResult, StateLoad}, + host::{Host, SStoreResult, SelfDestructResult, StateLoad}, CreateScheme, }; pub use gas::{Gas, InitialAndFloorGas}; diff --git a/crates/interpreter/src/table.rs b/crates/interpreter/src/table.rs index 3e4f4833be..2f578669c6 100644 --- a/crates/interpreter/src/table.rs +++ b/crates/interpreter/src/table.rs @@ -14,9 +14,6 @@ pub type Instruction = for<'a> fn(&'a mut Interpreter, &'a mut H); /// Instruction table is list of instruction function pointers mapped to 256 EVM opcodes. pub type InstructionTable = [Instruction; 256]; -/// EVM dynamic opcode function signature. -pub type DynInstruction = dyn Fn(&mut Interpreter, &mut H); - /// A table of boxed instructions. pub type CustomInstructionTable = [IT; 256]; diff --git a/crates/optimism/Cargo.toml b/crates/optimism/Cargo.toml index 78e0250458..7666f6fc11 100644 --- a/crates/optimism/Cargo.toml +++ b/crates/optimism/Cargo.toml @@ -25,8 +25,6 @@ all = "warn" # revm revm.workspace = true precompile = { workspace = true, features = ["secp256r1"] } -inspector.workspace = true -derive_more.workspace = true auto_impl.workspace = true # static precompile sets. diff --git a/crates/optimism/src/api.rs b/crates/optimism/src/api.rs index f129f18c03..101ee66c47 100644 --- a/crates/optimism/src/api.rs +++ b/crates/optimism/src/api.rs @@ -1,4 +1,3 @@ +pub mod builder; +pub mod default_ctx; pub mod exec; -pub mod exec_op; -pub mod inspect; -pub mod into_optimism; diff --git a/crates/optimism/src/api/builder.rs b/crates/optimism/src/api/builder.rs new file mode 100644 index 0000000000..def7cc45e6 --- /dev/null +++ b/crates/optimism/src/api/builder.rs @@ -0,0 +1,51 @@ +use crate::{evm::OpEvm, transaction::OpTxTrait, L1BlockInfo, OpSpec, OpTransaction}; +use precompile::Log; +use revm::{ + context::{BlockEnv, Cfg, CfgEnv, TxEnv}, + context_interface::{Block, Journal}, + handler::{instructions::EthInstructions, noop::NoOpInspector}, + interpreter::interpreter::EthInterpreter, + state::EvmState, + Context, Database, JournaledState, +}; +use std::vec::Vec; + +pub trait OpBuilder: Sized { + type Context; + + fn build_op( + self, + ) -> OpEvm>; + + fn build_op_with_inspector( + self, + inspector: INSP, + ) -> OpEvm>; +} + +impl OpBuilder for Context +where + BLOCK: Block, + TX: OpTxTrait, + CFG: Cfg, + DB: Database, + JOURNAL: Journal)>, +{ + type Context = Self; + + fn build_op( + self, + ) -> OpEvm> { + OpEvm::new(self, NoOpInspector {}) + } + + fn build_op_with_inspector( + self, + inspector: INSP, + ) -> OpEvm> { + OpEvm::new(self, inspector) + } +} + +pub type OpContext = + Context, CfgEnv, DB, JournaledState, L1BlockInfo>; diff --git a/crates/optimism/src/api/default_ctx.rs b/crates/optimism/src/api/default_ctx.rs new file mode 100644 index 0000000000..a71f599f49 --- /dev/null +++ b/crates/optimism/src/api/default_ctx.rs @@ -0,0 +1,53 @@ +use crate::{L1BlockInfo, OpSpec, OpSpecId, OpTransaction}; +use revm::{ + context::{BlockEnv, CfgEnv, TxEnv}, + database_interface::EmptyDB, + Context, JournaledState, MainContext, +}; + +pub trait DefaultOp { + fn op() -> Context< + BlockEnv, + OpTransaction, + CfgEnv, + EmptyDB, + JournaledState, + L1BlockInfo, + >; +} + +impl DefaultOp + for Context< + BlockEnv, + OpTransaction, + CfgEnv, + EmptyDB, + JournaledState, + L1BlockInfo, + > +{ + fn op() -> Self { + Context::mainnet() + .with_tx(OpTransaction::default()) + .with_cfg(CfgEnv::new().with_spec(OpSpec::Op(OpSpecId::BEDROCK))) + .with_chain(L1BlockInfo::default()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::api::builder::OpBuilder; + use revm::{ExecuteEvm, InspectEvm}; + + #[test] + fn default_run_op() { + let ctx = Context::op(); + // convert to optimism context + let mut evm = ctx.build_op(); + // execute + let _ = evm.transact_previous(); + // inspect + let _ = evm.inspect_previous(); + } +} diff --git a/crates/optimism/src/api/exec.rs b/crates/optimism/src/api/exec.rs index 3084bd1bf8..ef43cfd4ac 100644 --- a/crates/optimism/src/api/exec.rs +++ b/crates/optimism/src/api/exec.rs @@ -1,27 +1,119 @@ -use revm::context_interface::{ - block::BlockSetter, transaction::TransactionSetter, TransactionGetter, +use crate::{ + evm::OpEvm, handler::OpHandler, transaction::OpTxTrait, L1BlockInfo, OpHaltReason, OpSpec, + OpTransactionError, }; +use precompile::Log; +use revm::{ + context_interface::{ + result::{EVMError, ExecutionResult, ResultAndState}, + Block, Cfg, ContextTrait, Database, Journal, + }, + handler::{ + handler::EvmTrait, + inspector::{Inspector, JournalExt}, + instructions::EthInstructions, + EthFrame, EthHandler, + }, + interpreter::interpreter::EthInterpreter, + state::EvmState, + Context, DatabaseCommit, ExecuteCommitEvm, ExecuteEvm, InspectCommitEvm, InspectEvm, +}; +use std::vec::Vec; + +impl ExecuteEvm + for OpEvm< + Context, + INSP, + EthInstructions>, + > +where + BLOCK: Block, + TX: OpTxTrait, + CFG: Cfg, + DB: Database, + JOURNAL: Journal)> + JournalExt, + INSP: Inspector, EthInterpreter>, +{ + type Output = + Result, EVMError<::Error, OpTransactionError>>; -/// Execute EVM transactions. -pub trait ExecuteOpEvm: BlockSetter + TransactionSetter { - type Output; + fn transact_previous(&mut self) -> Self::Output { + let mut h = OpHandler::<_, _, EthFrame<_, _, _>>::new(); + h.run(self) + } +} - fn op_exec_previous(&mut self) -> Self::Output; +impl ExecuteCommitEvm + for OpEvm< + Context, + INSP, + EthInstructions>, + > +where + BLOCK: Block, + TX: OpTxTrait, + CFG: Cfg, + DB: Database + DatabaseCommit, + JOURNAL: Journal)> + JournalExt, + INSP: Inspector, EthInterpreter>, +{ + type CommitOutput = Result< + ExecutionResult, + EVMError<::Error, OpTransactionError>, + >; - fn op_exec(&mut self, tx: ::Transaction) -> Self::Output { - self.set_tx(tx); - self.op_exec_previous() + fn transact_commit_previous(&mut self) -> Self::CommitOutput { + self.transact_previous().map(|r| { + self.ctx().db().commit(r.state); + r.result + }) } } -/// Execute EVM transactions and commit to the state. -pub trait ExecuteCommitOpEvm: ExecuteOpEvm { - type CommitOutput; +impl InspectEvm + for OpEvm< + Context, + INSP, + EthInstructions>, + > +where + BLOCK: Block, + TX: OpTxTrait, + CFG: Cfg, + DB: Database, + JOURNAL: Journal)> + JournalExt, + INSP: Inspector, EthInterpreter>, +{ + type Inspector = INSP; - fn op_exec_commit_previous(&mut self) -> Self::CommitOutput; + fn set_inspector(&mut self, inspector: Self::Inspector) { + self.data.inspector = inspector; + } + + fn inspect_previous(&mut self) -> Self::Output { + let mut h = OpHandler::<_, _, EthFrame<_, _, _>>::new(); + h.run(self) + } +} - fn op_exec_commit(&mut self, tx: Self::Transaction) -> Self::CommitOutput { - self.set_tx(tx); - self.op_exec_commit_previous() +impl InspectCommitEvm + for OpEvm< + Context, + INSP, + EthInstructions>, + > +where + BLOCK: Block, + TX: OpTxTrait, + CFG: Cfg, + DB: Database + DatabaseCommit, + JOURNAL: Journal)> + JournalExt, + INSP: Inspector, EthInterpreter>, +{ + fn inspect_commit_previous(&mut self) -> Self::CommitOutput { + self.inspect_previous().map(|r| { + self.ctx().db().commit(r.state); + r.result + }) } } diff --git a/crates/optimism/src/api/exec_op.rs b/crates/optimism/src/api/exec_op.rs deleted file mode 100644 index 536cf91c24..0000000000 --- a/crates/optimism/src/api/exec_op.rs +++ /dev/null @@ -1,51 +0,0 @@ -use revm::{ - context_interface::{ - result::{EVMError, ExecutionResult, ResultAndState}, - Cfg, CfgGetter, DatabaseGetter, - }, - handler::{instructions::EthInstructionExecutor, EthContext, EthFrame, EthHandler}, - interpreter::interpreter::EthInterpreter, - Database, DatabaseCommit, -}; - -use crate::{ - handler::{precompiles::OpPrecompileProvider, OpHandler}, - transaction::abstraction::OpTxGetter, - L1BlockInfoGetter, OpHaltReason, OpSpec, OpTransactionError, -}; - -/// Helper function that executed a transaction and commits the state. -pub fn transact_op( - ctx: &mut CTX, -) -> Result< - ResultAndState, - EVMError<<::Database as Database>::Error, OpTransactionError>, -> -where - ::Cfg: Cfg, -{ - let mut op = OpHandler::< - CTX, - _, - EthFrame, - OpPrecompileProvider, - EthInstructionExecutor, - >::new(); - op.run(ctx) -} - -pub fn transact_op_commit( - ctx: &mut CTX, -) -> Result< - ExecutionResult, - EVMError<<::Database as Database>::Error, OpTransactionError>, -> -where - ::Database: DatabaseCommit, - ::Cfg: Cfg, -{ - transact_op(ctx).map(|r| { - ctx.db().commit(r.state); - r.result - }) -} diff --git a/crates/optimism/src/api/inspect.rs b/crates/optimism/src/api/inspect.rs deleted file mode 100644 index 7b60db8f65..0000000000 --- a/crates/optimism/src/api/inspect.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::{ - context::OpContext, - handler::{precompiles::OpPrecompileProvider, OpHandler}, - transaction::{abstraction::OpTxGetter, OpTxTrait}, - L1BlockInfoGetter, OpHaltReason, OpSpec, OpTransactionError, -}; -use inspector::{ - exec::InspectEvm, - inspector_context::InspectorContext, - inspector_instruction::InspectorInstructionExecutor, - journal::{JournalExt, JournalExtGetter}, - Inspector, InspectorCtx, InspectorHandlerImpl, -}; -use revm::{ - context::Cfg, - context_interface::{ - result::{EVMError, ExecutionResult, ResultAndState}, - Block, CfgGetter, DatabaseGetter, Journal, - }, - database_interface::Database, - handler::{handler::EthContext, EthFrame, EthHandler, FrameContext}, - interpreter::{interpreter::EthInterpreter, table::make_instruction_table, InterpreterTypes}, - primitives::Log, - state::EvmState, - DatabaseCommit, ExecuteCommitEvm, -}; -use std::vec::Vec; - -impl< - BLOCK: Block, - TX: OpTxTrait, - CFG: Cfg, - DB: Database, - JOURNAL: Journal)> + JournalExt, - > InspectEvm for OpContext -{ - fn inspect_previous<'a, 'b, INSP>(&'a mut self, inspector: INSP) -> Self::Output - where - INSP: Inspector<&'a mut Self, EthInterpreter> + 'b, - { - let mut insp = InspectorContext::new(self, inspector); - inspect_op(&mut insp) - } -} - -pub trait InspectCommitEvm: InspectEvm + ExecuteCommitEvm { - fn inspect_commit<'a, 'b, INSP>( - &'a mut self, - tx: Self::Transaction, - inspector: INSP, - ) -> Self::CommitOutput - where - INSP: Inspector<&'a mut Self, INTR> + 'b, - { - self.set_tx(tx); - self.inspect_commit_previous(inspector) - } - - fn inspect_commit_previous<'a, 'b, INSP>(&'a mut self, inspector: INSP) -> Self::CommitOutput - where - INSP: Inspector<&'a mut Self, INTR> + 'b; -} - -impl< - BLOCK: Block, - TX: OpTxTrait, - CFG: Cfg, - DB: Database + DatabaseCommit, - JOURNAL: Journal)> + JournalExt, - > InspectCommitEvm for OpContext -{ - fn inspect_commit_previous<'a, 'b, INSP>(&'a mut self, inspector: INSP) -> Self::CommitOutput - where - INSP: Inspector<&'a mut Self, EthInterpreter> + 'b, - { - let mut insp = InspectorContext::new(self, inspector); - inspect_op(&mut insp).map(|res| { - insp.db().commit(res.state); - res.result - }) - } -} - -pub fn inspect_op( - ctx: &mut CTX, -) -> Result, EVMError<::Error, OpTransactionError>> -where - DB: Database, - CTX: EthContext - + OpTxGetter - + L1BlockInfoGetter - + JournalExtGetter - + DatabaseGetter - + InspectorCtx, - // Have Cfg with OpSpec - ::Cfg: Cfg, -{ - InspectorHandlerImpl::<_, _, EthFrame<_, _, _, _>, _, _, EthInterpreter>::new( - OpHandler::< - CTX, - _, - EthFrame< - CTX, - _, - _, - FrameContext< - OpPrecompileProvider, - InspectorInstructionExecutor, - >, - >, - //+ FrameInterpreterGetter, - OpPrecompileProvider::Error, OpTransactionError>>, - InspectorInstructionExecutor, - >::default(), - make_instruction_table(), - ) - .run(ctx) -} - -pub fn inspect_op_commit( - ctx: &mut CTX, -) -> Result, EVMError<::Error, OpTransactionError>> -where - CTX: EthContext - + OpTxGetter - + JournalExtGetter - + DatabaseGetter - + InspectorCtx - + L1BlockInfoGetter, - // Have Cfg with OpSpec - ::Cfg: Cfg, -{ - inspect_op(ctx).map(|res| { - ctx.db().commit(res.state); - res.result - }) -} diff --git a/crates/optimism/src/api/into_optimism.rs b/crates/optimism/src/api/into_optimism.rs deleted file mode 100644 index 53735ca18d..0000000000 --- a/crates/optimism/src/api/into_optimism.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::{ - context::OpContext, transaction::OpTxTrait, L1BlockInfo, OpSpec, OpSpecId, OpTransaction, -}; -use revm::{ - context::{BlockEnv, CfgEnv, TxEnv}, - context_interface::{Block, Cfg, Journal, Transaction}, - database_interface::EmptyDB, - Context, Database, JournaledState, -}; - -pub trait IntoOptimism< - BLOCK: Block, - TX: OpTxTrait = OpTransaction, - CFG: Cfg = CfgEnv, - DB: Database = EmptyDB, - JOURNAL: Journal = JournaledState, -> -{ - fn into_optimism(self) -> OpContext; -} - -impl> - IntoOptimism, CfgEnv, DB, JOURNAL> - for Context, CfgEnv, DB, JOURNAL, L1BlockInfo> -{ - fn into_optimism(self) -> OpContext, CfgEnv, DB, JOURNAL> { - OpContext(self) - } -} - -pub trait DefaultOp { - fn default_op() -> Context< - BlockEnv, - OpTransaction, - CfgEnv, - EmptyDB, - JournaledState, - L1BlockInfo, - >; -} - -impl DefaultOp - for Context< - BlockEnv, - OpTransaction, - CfgEnv, - EmptyDB, - JournaledState, - L1BlockInfo, - > -{ - fn default_op() -> Self { - Context::default() - .with_tx(OpTransaction::default()) - .with_cfg(CfgEnv::new().with_spec(OpSpec::Op(OpSpecId::BEDROCK))) - .with_chain(L1BlockInfo::default()) - } -} - -#[cfg(test)] -mod test { - use super::*; - use revm::ExecuteEvm; - - #[test] - fn default_than_into() { - let ctx = Context::default_op(); - // convert to optimism context - let mut op_ctx = ctx.into_optimism(); - let _ = op_ctx.exec_previous(); - } -} diff --git a/crates/optimism/src/context.rs b/crates/optimism/src/context.rs deleted file mode 100644 index f13ccb3ae6..0000000000 --- a/crates/optimism/src/context.rs +++ /dev/null @@ -1,295 +0,0 @@ -use crate::{ - api::exec_op::transact_op, - transaction::{abstraction::OpTxGetter, OpTxTrait}, - L1BlockInfo, L1BlockInfoGetter, OpHaltReason, OpSpec, OpSpecId, OpTransaction, - OpTransactionError, -}; -use derive_more::derive::{AsMut, AsRef, Deref, DerefMut}; -use inspector::journal::{JournalExt, JournalExtGetter}; -use precompile::Log; -use revm::{ - context::{BlockEnv, CfgEnv, TxEnv}, - context_interface::{ - block::BlockSetter, - result::{EVMError, ExecutionResult, ResultAndState}, - transaction::TransactionSetter, - Block, BlockGetter, Cfg, CfgGetter, DatabaseGetter, ErrorGetter, Journal, JournalDBError, - JournalGetter, PerformantContextAccess, Transaction, TransactionGetter, - }, - database_interface::EmptyDB, - handler::EthContext, - interpreter::Host, - state::EvmState, - Context, Database, DatabaseCommit, ExecuteCommitEvm, ExecuteEvm, JournaledState, -}; -use std::vec::Vec; - -#[derive(AsRef, AsMut, Deref, DerefMut)] -pub struct OpContext< - BLOCK = BlockEnv, - TX = OpTransaction, - CFG = CfgEnv, - DB: Database = EmptyDB, - JOURNAL: Journal = JournaledState, ->(pub Context); - -impl Default for OpContext { - fn default() -> Self { - Self( - Context::default() - .with_tx(OpTransaction::default()) - .with_cfg(CfgEnv::new().with_spec(OpSpec::Op(OpSpecId::BEDROCK))) - .with_chain(L1BlockInfo::default()), - ) - } -} - -impl OpContext { - pub fn default_ctx() -> Context< - BlockEnv, - OpTransaction, - CfgEnv, - EmptyDB, - JournaledState, - L1BlockInfo, - > { - Context::default() - .with_tx(OpTransaction::default()) - .with_cfg(CfgEnv::new().with_spec(OpSpec::Op(OpSpecId::BEDROCK))) - .with_chain(L1BlockInfo::default()) - } -} - -impl> BlockGetter - for OpContext -{ - type Block = BLOCK; - - fn block(&self) -> &Self::Block { - self.0.block() - } -} - -impl< - BLOCK: Block, - TX: OpTxTrait, - CFG: Cfg, - DB: Database, - JOURNAL: Journal)>, - > EthContext for OpContext -{ -} - -impl< - BLOCK: Block, - TX: OpTxTrait, - CFG: Cfg, - DB: Database, - JOURNAL: Journal)>, - > EthContext for &mut OpContext -{ -} - -impl> ErrorGetter - for OpContext -{ - type Error = JournalDBError; - - fn take_error(&mut self) -> Result<(), Self::Error> { - self.0.take_error() - } -} - -impl> BlockSetter - for OpContext -{ - fn set_block(&mut self, block: Self::Block) { - self.0.set_block(block) - } -} - -impl> OpTxGetter - for OpContext -{ - type OpTransaction = TX; - - fn op_tx(&self) -> &Self::OpTransaction { - self.0.tx() - } -} - -impl> TransactionGetter - for OpContext -{ - type Transaction = TX; - - fn tx(&self) -> &Self::Transaction { - self.0.tx() - } -} - -impl> TransactionSetter - for OpContext -{ - fn set_tx(&mut self, tx: Self::Transaction) { - self.0.set_tx(tx) - } -} - -impl + JournalExt> - JournalExtGetter for OpContext -{ - type JournalExt = JOURNAL; - - fn journal_ext(&self) -> &Self::JournalExt { - self.0.journal_ref() - } -} - -impl> CfgGetter - for OpContext -{ - type Cfg = CFG; - - fn cfg(&self) -> &Self::Cfg { - self.0.cfg() - } -} - -impl> L1BlockInfoGetter - for OpContext -{ - fn l1_block_info(&self) -> &L1BlockInfo { - self.0.l1_block_info() - } - - fn l1_block_info_mut(&mut self) -> &mut L1BlockInfo { - self.0.l1_block_info_mut() - } -} - -impl> DatabaseGetter - for OpContext -{ - type Database = DB; - - fn db(&mut self) -> &mut Self::Database { - self.0.db() - } - - fn db_ref(&self) -> &Self::Database { - self.0.db_ref() - } -} - -impl> JournalGetter - for OpContext -{ - type Journal = JOURNAL; - - fn journal(&mut self) -> &mut Self::Journal { - self.0.journal() - } - - fn journal_ref(&self) -> &Self::Journal { - self.0.journal_ref() - } -} - -impl> - PerformantContextAccess for OpContext -{ - type Error = JournalDBError; - - fn load_access_list(&mut self) -> Result<(), Self::Error> { - self.0.load_access_list() - } -} - -impl Host for OpContext -where - BLOCK: Block, - TX: Transaction, - CFG: Cfg, - DB: Database, - JOURNAL: Journal, -{ - fn set_error(&mut self, error: DB::Error) { - self.0.set_error(error) - } -} - -impl OpContext -where - BLOCK: Block, - TX: Transaction, - CFG: Cfg, - DB: Database, - JOURNAL: Journal, -{ - pub fn new(context: Context) -> Self { - Self(context) - } -} - -impl ExecuteEvm for OpContext -where - BLOCK: Block, - TX: OpTxTrait, - CFG: Cfg, - DB: Database, - JOURNAL: Journal)>, -{ - type Output = - Result, EVMError<::Error, OpTransactionError>>; - - fn exec_previous(&mut self) -> Self::Output { - transact_op(self) - } -} - -impl ExecuteCommitEvm for OpContext -where - BLOCK: Block, - TX: OpTxTrait, - CFG: Cfg, - DB: Database + DatabaseCommit, - JOURNAL: Journal)>, -{ - type CommitOutput = Result< - ExecutionResult, - EVMError<::Error, OpTransactionError>, - >; - - fn exec_commit_previous(&mut self) -> Self::CommitOutput { - transact_op(self).map(|r| { - self.db().commit(r.state); - r.result - }) - } -} - -#[cfg(test)] -mod test { - - use crate::api::into_optimism::{DefaultOp, IntoOptimism}; - - use super::*; - - #[test] - fn test_run() { - let mut ctx = Context::default(); - // run default tx for mainnet; - let _ = ctx.exec_previous().unwrap(); - - let ctx = Context::default_op(); - // convert to optimism context - let mut op_ctx = ctx.into_optimism(); - // modify gas limit. - op_ctx.modify_tx(|tx| { - tx.base.gas_limit = 1000; - }); - // run default tx for optimism; - let _ = op_ctx.exec_previous(); - } -} diff --git a/crates/optimism/src/evm.rs b/crates/optimism/src/evm.rs new file mode 100644 index 0000000000..110cfedf27 --- /dev/null +++ b/crates/optimism/src/evm.rs @@ -0,0 +1,101 @@ +use revm::{ + context::{setters::ContextSetters, EvmData}, + context_interface::ContextTrait, + handler::{ + handler::EvmTrait, + inspect_instructions, + inspector::{Inspector, JournalExt}, + instructions::{EthInstructions, InstructionProvider}, + }, + interpreter::{interpreter::EthInterpreter, Host, Interpreter, InterpreterAction}, +}; + +use crate::handler::precompiles::OpPrecompileProvider; + +pub struct OpEvm { + pub data: EvmData, + pub enabled_inspection: bool, + pub instruction: I, + pub precompiles: OpPrecompileProvider, +} + +impl OpEvm> { + pub fn new(ctx: CTX, inspector: INSP) -> Self { + Self { + data: EvmData { ctx, inspector }, + enabled_inspection: false, + instruction: EthInstructions::new_mainnet(), + precompiles: OpPrecompileProvider::default(), + } + } +} + +impl ContextSetters for OpEvm { + type Tx = ::Tx; + type Block = ::Block; + + fn set_tx(&mut self, tx: Self::Tx) { + self.data.ctx.set_tx(tx); + } + + fn set_block(&mut self, block: Self::Block) { + self.data.ctx.set_block(block); + } +} + +impl EvmTrait for OpEvm +where + CTX: ContextTrait, + I: InstructionProvider, + INSP: Inspector, +{ + type Context = CTX; + type Inspector = INSP; + type Instructions = I; + type Precompiles = OpPrecompileProvider; + + fn run_interpreter( + &mut self, + interpreter: &mut Interpreter< + ::InterpreterTypes, + >, + ) -> ::Output { + let context = &mut self.data.ctx; + let instructions = &mut self.instruction; + let inspector = &mut self.data.inspector; + if self.enabled_inspection { + inspect_instructions( + context, + interpreter, + inspector, + instructions.instruction_table(), + ) + } else { + interpreter.run_plain(instructions.instruction_table(), context) + } + } + + fn enable_inspection(&mut self, enable: bool) { + self.enabled_inspection = enable; + } + + fn ctx(&mut self) -> &mut Self::Context { + &mut self.data.ctx + } + + fn ctx_ref(&self) -> &Self::Context { + &self.data.ctx + } + + fn ctx_inspector(&mut self) -> (&mut Self::Context, &mut Self::Inspector) { + (&mut self.data.ctx, &mut self.data.inspector) + } + + fn ctx_instructions(&mut self) -> (&mut Self::Context, &mut Self::Instructions) { + (&mut self.data.ctx, &mut self.instruction) + } + + fn ctx_precompiles(&mut self) -> (&mut Self::Context, &mut Self::Precompiles) { + (&mut self.data.ctx, &mut self.precompiles) + } +} diff --git a/crates/optimism/src/fast_lz.rs b/crates/optimism/src/fast_lz.rs index af03c9c6e6..067c19f98b 100644 --- a/crates/optimism/src/fast_lz.rs +++ b/crates/optimism/src/fast_lz.rs @@ -108,7 +108,7 @@ fn u24(input: &[u8], idx: u32) -> u32 { #[cfg(test)] mod tests { - use crate::context::OpContext; + use crate::api::builder::OpBuilder; use super::*; use alloy_sol_types::sol; @@ -160,7 +160,9 @@ mod tests { // The source of this contract is here: https://github.com/danyalprout/fastlz/blob/main/src/FastLz.sol#L6-L10 use database::FFADDRESS; - use revm::ExecuteEvm; + use revm::{Context, ExecuteEvm}; + + use crate::api::default_ctx::DefaultOp; sol! { interface FastLz { function fastLz(bytes input) external view returns (uint256); @@ -171,18 +173,18 @@ mod tests { let native_val = flz_compress_len(&input); - let mut ctx = OpContext( - OpContext::default_ctx().with_db(BenchmarkDB::new_bytecode(contract_bytecode.clone())), - ); - ctx.modify_tx(|tx| { - tx.base.caller = EEADDRESS; - tx.base.kind = TxKind::Call(FFADDRESS); - tx.base.data = FastLz::fastLzCall::new((input,)).abi_encode().into(); - tx.base.gas_limit = 3_000_000; - tx.enveloped_tx = Some(Bytes::default()); - }); - - let result_and_state = ctx.exec_previous().unwrap(); + let mut evm = Context::op() + .with_db(BenchmarkDB::new_bytecode(contract_bytecode.clone())) + .modify_tx_chained(|tx| { + tx.base.caller = EEADDRESS; + tx.base.kind = TxKind::Call(FFADDRESS); + tx.base.data = FastLz::fastLzCall::new((input,)).abi_encode().into(); + tx.base.gas_limit = 3_000_000; + tx.enveloped_tx = Some(Bytes::default()); + }) + .build_op(); + + let result_and_state = evm.transact_previous().unwrap(); let output = result_and_state.result.output().unwrap(); let evm_val = FastLz::fastLzCall::abi_decode_returns(output, true) diff --git a/crates/optimism/src/handler.rs b/crates/optimism/src/handler.rs index 0301a189a7..5a78f05e6a 100644 --- a/crates/optimism/src/handler.rs +++ b/crates/optimism/src/handler.rs @@ -4,53 +4,46 @@ pub mod precompiles; use crate::{ transaction::{ - abstraction::OpTxGetter, deposit::{DepositTransaction, DEPOSIT_TRANSACTION_TYPE}, OpTransactionError, OpTxTrait, }, - L1BlockInfoGetter, OpHaltReason, OpSpec, OpSpecId, BASE_FEE_RECIPIENT, L1_FEE_RECIPIENT, + L1BlockInfo, OpHaltReason, OpSpec, OpSpecId, BASE_FEE_RECIPIENT, L1_FEE_RECIPIENT, }; -use precompiles::OpPrecompileProvider; +use precompile::Log; use revm::{ + context_interface::ContextTrait, context_interface::{ result::{EVMError, ExecutionResult, FromStringError, InvalidTransaction, ResultAndState}, - Block, Cfg, CfgGetter, Journal, Transaction, TransactionGetter, + Block, Cfg, Journal, Transaction, }, handler::{ - instructions::InstructionExecutor, EthContext, EthError, EthHandler, FrameContext, - FrameResult, MainnetHandler, + handler::{EthTraitError, EvmTrait}, + inspector::{EthInspectorHandler, Inspector, InspectorFrame}, + EthHandler, Frame, FrameResult, MainnetHandler, }, - handler_interface::Frame, - interpreter::{interpreter::EthInterpreter, FrameInput, Gas, Host}, + interpreter::{interpreter::EthInterpreter, FrameInput, Gas}, primitives::{hash_map::HashMap, U256}, specification::hardfork::SpecId, - state::Account, + state::{Account, EvmState}, Database, }; +use std::vec::Vec; -pub struct OpHandler { - pub main: MainnetHandler, +pub struct OpHandler { + pub mainnet: MainnetHandler, + pub _phantom: core::marker::PhantomData<(EVM, ERROR, FRAME)>, } -impl - OpHandler, INSTRUCTIONS> -where - INSTRUCTIONS: InstructionExecutor, -{ +impl OpHandler { pub fn new() -> Self { Self { - main: MainnetHandler { - _phantom: Default::default(), - }, + mainnet: MainnetHandler::default(), + _phantom: core::marker::PhantomData, } } } -impl Default - for OpHandler, INSTRUCTIONS> -where - INSTRUCTIONS: InstructionExecutor, -{ +impl Default for OpHandler { fn default() -> Self { Self::new() } @@ -66,104 +59,97 @@ impl IsTxError for EVMError { } } -impl EthHandler - for OpHandler, INSTRUCTIONS> +impl EthHandler for OpHandler where - CTX: EthContext + OpTxGetter + L1BlockInfoGetter, - // Have Cfg with OpSpec - ::Cfg: Cfg, - ERROR: EthError + From + IsTxError + FromStringError, - INSTRUCTIONS: InstructionExecutor, + EVM: EvmTrait< + Context: ContextTrait< + Journal: Journal)>, + Tx: OpTxTrait, + Cfg: Cfg, + Chain = L1BlockInfo, + >, + >, + ERROR: EthTraitError + From + FromStringError + IsTxError, // 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, INSTRUCTIONS>, - >, + FRAME: Frame, { - type Context = CTX; + type Evm = EVM; type Error = ERROR; type Frame = FRAME; - type Precompiles = OpPrecompileProvider; - type Instructions = INSTRUCTIONS; type HaltReason = OpHaltReason; - fn precompile(&self, _context: &mut Self::Context) -> Self::Precompiles { - OpPrecompileProvider::default() - } - - fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { + fn validate_env(&self, evm: &mut Self::Evm) -> 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(); + let ctx = evm.ctx(); + let tx = ctx.tx(); + let tx_type = tx.tx_type(); if tx_type == DEPOSIT_TRANSACTION_TYPE { - let tx = context.op_tx(); // Do not allow for a system transaction to be processed if Regolith is enabled. - if tx.is_system_transaction() && context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH) + if tx.is_system_transaction() + && evm.ctx().cfg().spec().is_enabled_in(OpSpecId::REGOLITH) { return Err(OpTransactionError::DepositSystemTxPostRegolith.into()); } return Ok(()); } - self.main.validate_env(context) + self.mainnet.validate_env(evm) } - fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - if context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE { + fn validate_tx_against_state(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { + if evm.ctx().tx().tx_type() == DEPOSIT_TRANSACTION_TYPE { return Ok(()); } - self.main.validate_tx_against_state(context) + self.mainnet.validate_tx_against_state(evm) } - fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + fn load_accounts(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { // The L1-cost fee is only computed for Optimism non-deposit transactions. - let spec = context.cfg().spec(); - if context.tx().tx_type() != DEPOSIT_TRANSACTION_TYPE { + let spec = evm.ctx().cfg().spec(); + if evm.ctx().tx().tx_type() != DEPOSIT_TRANSACTION_TYPE { let l1_block_info: crate::L1BlockInfo = - super::L1BlockInfo::try_fetch(context.db(), spec)?; + super::L1BlockInfo::try_fetch(evm.ctx().db(), spec)?; // Storage L1 block info for later use. - *context.l1_block_info_mut() = l1_block_info; + *evm.ctx().chain() = l1_block_info; } - self.main.load_accounts(context) + self.mainnet.load_accounts(evm) } - 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; + fn deduct_caller(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { + let ctx = evm.ctx(); + let caller = ctx.tx().caller(); + let is_deposit = ctx.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; // If the transaction is a deposit with a `mint` value, add the mint value // in wei to the caller's balance. This should be persisted to the database // prior to the rest of execution. let mut tx_l1_cost = U256::ZERO; if is_deposit { - let tx = context.op_tx(); + let tx = ctx.tx(); if let Some(mint) = tx.mint() { - let mut caller_account = context.journal().load_account(caller)?; + let mut caller_account = ctx.journal().load_account(caller)?; caller_account.info.balance += U256::from(mint); } } else { - let enveloped_tx = context - .op_tx() + let enveloped_tx = ctx + .tx() .enveloped_tx() .expect("all not deposit tx have enveloped tx") .clone(); - tx_l1_cost = context - .l1_block_info() - .calculate_tx_l1_cost(&enveloped_tx, context.cfg().spec()); + let spec = ctx.cfg().spec(); + tx_l1_cost = ctx.chain().calculate_tx_l1_cost(&enveloped_tx, spec); } // We deduct caller max balance after minting and before deducing the // L1 cost, max values is already checked in pre_validate but L1 cost wasn't. - self.main.deduct_caller(context)?; + self.mainnet.deduct_caller(evm)?; // If the transaction is not a deposit transaction, subtract the L1 data fee from the // caller's balance directly after minting the requested amount of ETH. if !is_deposit { - let mut caller_account = context.journal().load_account(caller)?; + let mut caller_account = evm.ctx().journal().load_account(caller)?; if tx_l1_cost > caller_account.info.balance { return Err(InvalidTransaction::LackOfFundForMaxFee { @@ -179,14 +165,14 @@ where fn last_frame_result( &self, - context: &mut Self::Context, - _frame_context: &mut ::FrameContext, + evm: &mut Self::Evm, frame_result: &mut ::FrameResult, ) -> Result<(), Self::Error> { - let tx = context.tx(); + let ctx = evm.ctx(); + let tx = ctx.tx(); let is_deposit = tx.tx_type() == DEPOSIT_TRANSACTION_TYPE; let tx_gas_limit = tx.gas_limit(); - let is_regolith = context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH); + let is_regolith = ctx.cfg().spec().is_enabled_in(OpSpecId::REGOLITH); let instruction_result = frame_result.interpreter_result().result; let gas = frame_result.gas_mut(); @@ -216,7 +202,7 @@ where gas.erase_cost(remaining); gas.record_refund(refunded); } else if is_deposit { - let tx = context.op_tx(); + let tx = ctx.tx(); if tx.is_system_transaction() { // System transactions were a special type of deposit transaction in // the Bedrock hardfork that did not incur any gas costs. @@ -245,57 +231,59 @@ where fn refund( &self, - context: &mut Self::Context, + evm: &mut Self::Evm, exec_result: &mut ::FrameResult, eip7702_refund: i64, ) { exec_result.gas_mut().record_refund(eip7702_refund); - let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; - let is_regolith = context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH); + let is_deposit = evm.ctx().tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; + let is_regolith = evm.ctx().cfg().spec().is_enabled_in(OpSpecId::REGOLITH); // Prior to Regolith, deposit transactions did not receive gas refunds. let is_gas_refund_disabled = is_deposit && !is_regolith; if !is_gas_refund_disabled { exec_result .gas_mut() - .set_final_refund(context.cfg().spec().is_enabled_in(SpecId::LONDON)); + .set_final_refund(evm.ctx().cfg().spec().is_enabled_in(SpecId::LONDON)); } } fn reward_beneficiary( &self, - context: &mut Self::Context, + evm: &mut Self::Evm, exec_result: &mut ::FrameResult, ) -> Result<(), Self::Error> { - self.main.reward_beneficiary(context, exec_result)?; - - let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; + let is_deposit = evm.ctx().tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; // Transfer fee to coinbase/beneficiary. if !is_deposit { - self.main.reward_beneficiary(context, exec_result)?; - let basefee = context.block().basefee() as u128; + self.mainnet.reward_beneficiary(evm, exec_result)?; + let basefee = evm.ctx().block().basefee() as u128; // If the transaction is not a deposit transaction, fees are paid out // to both the Base Fee Vault as well as the L1 Fee Vault. - let l1_block_info = context.l1_block_info(); + let ctx = evm.ctx(); + let envolepo = ctx.tx().enveloped_tx().cloned(); + let spec = ctx.cfg().spec(); + let l1_block_info = ctx.chain(); - let Some(enveloped_tx) = &context.op_tx().enveloped_tx() else { + let Some(enveloped_tx) = &envolepo else { return Err(ERROR::from_string( "[OPTIMISM] Failed to load enveloped transaction.".into(), )); }; - let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, context.cfg().spec()); + let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, spec); // Send the L1 cost of the transaction to the L1 Fee Vault. - let mut l1_fee_vault_account = context.journal().load_account(L1_FEE_RECIPIENT)?; + let mut l1_fee_vault_account = ctx.journal().load_account(L1_FEE_RECIPIENT)?; l1_fee_vault_account.mark_touch(); l1_fee_vault_account.info.balance += l1_cost; // Send the base fee of the transaction to the Base Fee Vault. - let mut base_fee_vault_account = context.journal().load_account(BASE_FEE_RECIPIENT)?; + let mut base_fee_vault_account = + evm.ctx().journal().load_account(BASE_FEE_RECIPIENT)?; base_fee_vault_account.mark_touch(); base_fee_vault_account.info.balance += U256::from(basefee.saturating_mul( (exec_result.gas().spent() - exec_result.gas().refunded() as u64) as u128, @@ -306,17 +294,17 @@ where fn output( &self, - context: &mut Self::Context, + evm: &mut Self::Evm, result: ::FrameResult, ) -> Result, Self::Error> { - let result = self.main.output(context, result)?; + let result = self.mainnet.output(evm, result)?; let result = result.map_haltreason(OpHaltReason::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 // and the caller nonce will be incremented there. - let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; - if is_deposit && context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH) { + let is_deposit = evm.ctx().tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; + if is_deposit && evm.ctx().cfg().spec().is_enabled_in(OpSpecId::REGOLITH) { return Err(ERROR::from(OpTransactionError::HaltedDepositPostRegolith)); } } @@ -325,16 +313,17 @@ where fn end( &self, - context: &mut Self::Context, + evm: &mut Self::Evm, end_output: Result, Self::Error>, ) -> Result, Self::Error> { //end_output - let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; + let is_deposit = evm.ctx().tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; end_output.or_else(|err| { if err.is_tx_error() && is_deposit { - let spec = context.cfg().spec(); - let tx = context.op_tx(); + let ctx = evm.ctx(); + let spec = ctx.cfg().spec(); + let tx = ctx.tx(); let caller = tx.caller(); let mint = tx.mint(); let is_system_tx = tx.is_system_transaction(); @@ -350,7 +339,7 @@ where // always persist the mint amount, even if the transaction fails. let account = { let mut acc = Account::from( - context + evm.ctx() .db() .basic(caller) .unwrap_or_default() @@ -390,346 +379,348 @@ where } } -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::{ -// context_interface::OptimismEvmWiring, transaction::deposit::TxDeposit, BedrockSpec, -// L1BlockInfo, LatestSpec, OpTransaction, RegolithSpec, -// }; -// use database::InMemoryDB; -// use revm::{ -// context_interface::default::{block::BlockEnv, Env, TxEnv}, -// database_interface::EmptyDB, -// interpreter::{CallOutcome, InstructionResult, InterpreterResult}, -// primitives::{bytes, Address, Bytes, B256}, -// state::AccountInfo, -// }; -// use std::boxed::Box; - -// type TestEmptyOpWiring = OptimismEvmWiring; -// type TestMemOpWiring = OptimismEvmWiring; - -// /// Creates frame result. -// fn call_last_frame_return( -// env: EnvWiring, -// instruction_result: InstructionResult, -// gas: Gas, -// ) -> Gas -// where -// SPEC: OptimismSpec, -// { -// let mut context = Context::::new_with_db(EmptyDB::default()); -// 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_revert_gas() { -// let mut env = Envcontext_interface::::default(); -// let tx = TxEnv { -// gas_limit: 100, -// ..Default::default() -// }; -// env.tx = OpTransaction::Base { -// tx, -// enveloped_tx: None, -// }; - -// let gas = -// call_last_frame_return::(env, InstructionResult::Revert, Gas::new(90)); -// assert_eq!(gas.remaining(), 90); -// assert_eq!(gas.spent(), 10); -// assert_eq!(gas.refunded(), 0); -// } - -// #[test] -// fn test_consume_gas() { -// let mut env = Envcontext_interface::::default(); -// //env.tx.base.gas_limit = 100; -// //env.tx.source_hash = Some(B256::ZERO); - -// let deposit = TxDeposit { -// gas_limit: 100, -// source_hash: B256::ZERO, -// ..Default::default() -// }; -// env.tx = OpTransaction::Deposit(deposit); - -// let gas = -// call_last_frame_return::(env, 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 env = Envcontext_interface::::default(); -// //env.tx.base.gas_limit = 100; -// //env.tx.source_hash = Some(B256::ZERO); -// let deposit = TxDeposit { -// gas_limit: 100, -// source_hash: B256::ZERO, -// ..Default::default() -// }; -// env.tx = OpTransaction::Deposit(deposit); - -// let mut ret_gas = Gas::new(90); -// ret_gas.record_refund(20); - -// let gas = -// call_last_frame_return::(env.clone(), InstructionResult::Stop, ret_gas); -// assert_eq!(gas.remaining(), 90); -// assert_eq!(gas.spent(), 10); -// assert_eq!(gas.refunded(), 2); // min(20, 10/5) - -// let gas = call_last_frame_return::(env, InstructionResult::Revert, ret_gas); -// assert_eq!(gas.remaining(), 90); -// assert_eq!(gas.spent(), 10); -// assert_eq!(gas.refunded(), 0); -// } - -// #[test] -// fn test_consume_gas_sys_deposit_tx() { -// let mut env = Envcontext_interface::::default(); -// //env.tx.base.gas_limit = 100; -// //env.tx.source_hash = Some(B256::ZERO); - -// let deposit = TxDeposit { -// gas_limit: 100, -// source_hash: B256::ZERO, -// ..Default::default() -// }; -// env.tx = OpTransaction::Deposit(deposit); - -// let gas = call_last_frame_return::(env, InstructionResult::Stop, Gas::new(90)); -// assert_eq!(gas.remaining(), 0); -// assert_eq!(gas.spent(), 100); -// assert_eq!(gas.refunded(), 0); -// } - -// #[test] -// fn test_commit_mint_value() { -// let caller = Address::ZERO; -// let mut db = InMemoryDB::default(); -// db.insert_account_info( -// caller, -// AccountInfo { -// balance: U256::from(1000), -// ..Default::default() -// }, -// ); - -// let mut context = Context::::new_with_db(db); -// *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { -// l1_base_fee: U256::from(1_000), -// l1_fee_overhead: Some(U256::from(1_000)), -// l1_base_fee_scalar: U256::from(1_000), -// ..Default::default() -// }); -// // // Enveloped needs to be some but it will deduce zero fee. -// // context.evm.inner.env.tx.enveloped_tx = Some(bytes!("")); -// // // added mint value is 10. -// // context.evm.inner.env.tx.mint = Some(10); - -// let deposit = TxDeposit { -// gas_limit: 100, -// mint: Some(10), -// source_hash: B256::ZERO, -// ..Default::default() -// }; -// context.evm.inner.env.tx = OpTransaction::Deposit(deposit); - -// deduct_caller::(&mut context).unwrap(); - -// // Check the account balance is updated. -// let account = context -// .evm -// .inner -// .journaled_state -// .load_account(caller, &mut context.evm.inner.db) -// .unwrap(); -// assert_eq!(account.info.balance, U256::from(1010)); -// } - -// #[test] -// fn test_remove_l1_cost_non_deposit() { -// let caller = Address::ZERO; -// let mut db = InMemoryDB::default(); -// db.insert_account_info( -// caller, -// AccountInfo { -// balance: U256::from(1000), -// ..Default::default() -// }, -// ); -// let mut context = Context::::new_with_db(db); -// *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { -// l1_base_fee: U256::from(1_000), -// l1_fee_overhead: Some(U256::from(1_000)), -// l1_base_fee_scalar: U256::from(1_000), -// ..Default::default() -// }); -// // // l1block cost is 1048 fee. -// // context.evm.inner.env.tx.enveloped_tx = Some(bytes!("FACADE")); -// // // added mint value is 10. -// // context.evm.inner.env.tx.mint = Some(10); -// // // Putting source_hash to some makes it a deposit transaction. -// // // so enveloped_tx gas cost is ignored. -// // context.evm.inner.env.tx.source_hash = Some(B256::ZERO); - -// let deposit = TxDeposit { -// mint: Some(10), -// source_hash: B256::ZERO, -// ..Default::default() -// }; -// context.evm.inner.env.tx = OpTransaction::Deposit(deposit); - -// deduct_caller::(&mut context).unwrap(); - -// // Check the account balance is updated. -// let account = context -// .evm -// .inner -// .journaled_state -// .load_account(caller, &mut context.evm.inner.db) -// .unwrap(); -// assert_eq!(account.info.balance, U256::from(1010)); -// } - -// #[test] -// fn test_remove_l1_cost() { -// let caller = Address::ZERO; -// let mut db = InMemoryDB::default(); -// db.insert_account_info( -// caller, -// AccountInfo { -// balance: U256::from(1049), -// ..Default::default() -// }, -// ); -// let mut context = Context::::new_with_db(db); -// *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { -// l1_base_fee: U256::from(1_000), -// l1_fee_overhead: Some(U256::from(1_000)), -// l1_base_fee_scalar: U256::from(1_000), -// ..Default::default() -// }); -// // l1block cost is 1048 fee. -// context.evm.inner.env.tx = OpTransaction::Base { -// tx: TxEnv::default(), -// enveloped_tx: Some(bytes!("FACADE")), -// }; -// deduct_caller::(&mut context).unwrap(); - -// // Check the account balance is updated. -// let account = context -// .evm -// .inner -// .journaled_state -// .load_account(caller, &mut context.evm.inner.db) -// .unwrap(); -// assert_eq!(account.info.balance, U256::from(1)); -// } - -// #[test] -// fn test_remove_l1_cost_lack_of_funds() { -// let caller = Address::ZERO; -// let mut db = InMemoryDB::default(); -// db.insert_account_info( -// caller, -// AccountInfo { -// balance: U256::from(48), -// ..Default::default() -// }, -// ); -// let mut context = Context::::new_with_db(db); -// *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { -// l1_base_fee: U256::from(1_000), -// l1_fee_overhead: Some(U256::from(1_000)), -// l1_base_fee_scalar: U256::from(1_000), -// ..Default::default() -// }); -// // l1block cost is 1048 fee. -// context.evm.inner.env.tx = OpTransaction::Base { -// tx: TxEnv::default(), -// enveloped_tx: Some(bytes!("FACADE")), -// }; - -// assert_eq!( -// deduct_caller::(&mut context), -// Err(EVMError::Transaction( -// InvalidTransaction::LackOfFundForMaxFee { -// fee: Box::new(U256::from(1048)), -// balance: Box::new(U256::from(48)), -// } -// .into(), -// )) -// ); -// } - -// #[test] -// fn test_validate_sys_tx() { -// // mark the tx as a system transaction. -// // Set source hash. -// let tx = TxDeposit { -// is_system_transaction: true, -// ..Default::default() -// }; -// let env = Env::> { -// tx: OpTransaction::Deposit(tx), -// ..Default::default() -// }; - -// assert_eq!( -// validate_env::(&env), -// Err(EVMError::Transaction( -// OpTransactionError::DepositSystemTxPostRegolith -// )) -// ); - -// // Pre-regolith system transactions should be allowed. -// assert!(validate_env::(&env).is_ok()); -// } - -// #[test] -// fn test_validate_deposit_tx() { -// // Set source hash. -// let tx = TxDeposit { -// source_hash: B256::ZERO, -// ..Default::default() -// }; -// let env = Env::> { -// tx: OpTransaction::Deposit(tx), -// ..Default::default() -// }; -// assert!(validate_env::(&env).is_ok()); -// } - -// #[test] -// fn test_validate_tx_against_state_deposit_tx() { -// // Set source hash. -// let tx = TxDeposit { -// source_hash: B256::ZERO, -// ..Default::default() -// }; -// let env = Env::> { -// tx: OpTransaction::Deposit(tx), -// ..Default::default() -// }; - -// // Nonce and balance checks should be skipped for deposit transactions. -// assert!(validate_env::(&env).is_ok()); -// } -// } +impl EthInspectorHandler for OpHandler +where + EVM: EvmTrait< + Context: ContextTrait< + Journal: Journal)>, + Tx: OpTxTrait, + Cfg: Cfg, + Chain = L1BlockInfo, + >, + Inspector: Inspector<<::Evm as EvmTrait>::Context, EthInterpreter>, + >, + ERROR: EthTraitError + From + FromStringError + IsTxError, + // TODO `FrameResult` should be a generic trait. + // TODO `FrameInit` should be a generic. + FRAME: Frame + + InspectorFrame, +{ + type IT = EthInterpreter; +} + +#[cfg(test)] +mod tests { + use crate::{DefaultOp, OpBuilder, OpContext}; + + use super::*; + use database::InMemoryDB; + use revm::{ + context::Context, + database_interface::EmptyDB, + handler::EthFrame, + interpreter::{CallOutcome, InstructionResult, InterpreterResult}, + primitives::{bytes, Address, Bytes, B256}, + state::AccountInfo, + }; + use std::boxed::Box; + + /// Creates frame result. + fn call_last_frame_return( + ctx: OpContext, + instruction_result: InstructionResult, + gas: Gas, + ) -> Gas { + let mut evm = ctx.build_op(); + + let mut exec_result = FrameResult::Call(CallOutcome::new( + InterpreterResult { + result: instruction_result, + output: Bytes::new(), + gas, + }, + 0..0, + )); + + let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new(); + + handler + .last_frame_result(&mut evm, &mut exec_result) + .unwrap(); + handler.refund(&mut evm, &mut exec_result, 0); + *exec_result.gas() + } + + #[test] + fn test_revert_gas() { + let ctx = Context::op() + .modify_tx_chained(|tx| { + tx.base.gas_limit = 100; + tx.enveloped_tx = None; + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::BEDROCK.into()); + + let gas = call_last_frame_return(ctx, InstructionResult::Revert, Gas::new(90)); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spent(), 10); + assert_eq!(gas.refunded(), 0); + } + + #[test] + fn test_consume_gas() { + let ctx = Context::op() + .modify_tx_chained(|tx| { + tx.base.gas_limit = 100; + tx.deposit.source_hash = B256::ZERO; + tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE; + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH.into()); + + let gas = call_last_frame_return(ctx, 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 ctx = Context::op() + .modify_tx_chained(|tx| { + tx.base.gas_limit = 100; + tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE; + tx.deposit.source_hash = B256::ZERO; + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH.into()); + + let mut ret_gas = Gas::new(90); + ret_gas.record_refund(20); + + let gas = call_last_frame_return(ctx.clone(), InstructionResult::Stop, ret_gas); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spent(), 10); + assert_eq!(gas.refunded(), 2); // min(20, 10/5) + + let gas = call_last_frame_return(ctx, InstructionResult::Revert, ret_gas); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spent(), 10); + assert_eq!(gas.refunded(), 0); + } + + #[test] + fn test_consume_gas_sys_deposit_tx() { + let ctx = Context::op() + .modify_tx_chained(|tx| { + tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE; + tx.base.gas_limit = 100; + tx.deposit.source_hash = B256::ZERO; + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::BEDROCK.into()); + let gas = call_last_frame_return(ctx, InstructionResult::Stop, Gas::new(90)); + assert_eq!(gas.remaining(), 0); + assert_eq!(gas.spent(), 100); + assert_eq!(gas.refunded(), 0); + } + + #[test] + fn test_commit_mint_value() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + balance: U256::from(1000), + ..Default::default() + }, + ); + + let mut ctx = Context::op() + .with_db(db) + .with_chain(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: Some(U256::from(1_000)), + l1_base_fee_scalar: U256::from(1_000), + ..Default::default() + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH.into()); + ctx.modify_tx(|tx| { + tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE; + tx.deposit.source_hash = B256::ZERO; + tx.deposit.mint = Some(10); + }); + + let mut evm = ctx.build_op(); + + let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new(); + handler.deduct_caller(&mut evm).unwrap(); + + // Check the account balance is updated. + let account = evm.ctx().journal().load_account(caller).unwrap(); + assert_eq!(account.info.balance, U256::from(1010)); + } + + #[test] + fn test_remove_l1_cost_non_deposit() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + balance: U256::from(1000), + ..Default::default() + }, + ); + let ctx = Context::op() + .with_db(db) + .with_chain(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: Some(U256::from(1_000)), + l1_base_fee_scalar: U256::from(1_000), + ..Default::default() + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH.into()) + .modify_tx_chained(|tx| { + tx.base.gas_limit = 100; + tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE; + tx.deposit.mint = Some(10); + tx.enveloped_tx = Some(bytes!("FACADE")); + tx.deposit.source_hash = B256::ZERO; + }); + + let mut evm = ctx.build_op(); + + let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new(); + handler.deduct_caller(&mut evm).unwrap(); + + // Check the account balance is updated. + let account = evm.ctx().journal().load_account(caller).unwrap(); + assert_eq!(account.info.balance, U256::from(1010)); + } + + #[test] + fn test_remove_l1_cost() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + balance: U256::from(1049), + ..Default::default() + }, + ); + let ctx = Context::op() + .with_db(db) + .with_chain(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: Some(U256::from(1_000)), + l1_base_fee_scalar: U256::from(1_000), + ..Default::default() + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH.into()) + .modify_tx_chained(|tx| { + tx.base.gas_limit = 100; + tx.deposit.source_hash = B256::ZERO; + tx.enveloped_tx = Some(bytes!("FACADE")); + }); + + let mut evm = ctx.build_op(); + let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new(); + + // l1block cost is 1048 fee. + handler.deduct_caller(&mut evm).unwrap(); + + // Check the account balance is updated. + let account = evm.ctx().journal().load_account(caller).unwrap(); + assert_eq!(account.info.balance, U256::from(1)); + } + + #[test] + fn test_remove_l1_cost_lack_of_funds() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + balance: U256::from(48), + ..Default::default() + }, + ); + let ctx = Context::op() + .with_db(db) + .with_chain(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: Some(U256::from(1_000)), + l1_base_fee_scalar: U256::from(1_000), + ..Default::default() + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH.into()) + .modify_tx_chained(|tx| { + tx.enveloped_tx = Some(bytes!("FACADE")); + }); + + // l1block cost is 1048 fee. + let mut evm = ctx.build_op(); + let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new(); + + // l1block cost is 1048 fee. + assert_eq!( + handler.deduct_caller(&mut evm), + Err(EVMError::Transaction( + InvalidTransaction::LackOfFundForMaxFee { + fee: Box::new(U256::from(1048)), + balance: Box::new(U256::from(48)), + } + .into(), + )) + ); + } + + #[test] + fn test_validate_sys_tx() { + // mark the tx as a system transaction. + let ctx = Context::op() + .modify_tx_chained(|tx| { + tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE; + tx.deposit.is_system_transaction = true; + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH.into()); + + let mut evm = ctx.build_op(); + let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new(); + + assert_eq!( + handler.validate_env(&mut evm), + Err(EVMError::Transaction( + OpTransactionError::DepositSystemTxPostRegolith + )) + ); + + evm.ctx() + .modify_cfg(|cfg| cfg.spec = OpSpecId::BEDROCK.into()); + + // Pre-regolith system transactions should be allowed. + assert!(handler.validate_env(&mut evm).is_ok()); + } + + #[test] + fn test_validate_deposit_tx() { + // Set source hash. + let ctx = Context::op() + .modify_tx_chained(|tx| { + tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE; + tx.deposit.source_hash = B256::ZERO; + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH.into()); + + let mut evm = ctx.build_op(); + let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new(); + + assert!(handler.validate_env(&mut evm).is_ok()); + } + + #[test] + fn test_validate_tx_against_state_deposit_tx() { + // Set source hash. + let ctx = Context::op() + .modify_tx_chained(|tx| { + tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE; + tx.deposit.source_hash = B256::ZERO; + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH.into()); + + let mut evm = ctx.build_op(); + let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new(); + + // Nonce and balance checks should be skipped for deposit transactions. + assert!(handler.validate_env(&mut evm).is_ok()); + } +} diff --git a/crates/optimism/src/handler/precompiles.rs b/crates/optimism/src/handler/precompiles.rs index 17e96c8695..c85baacb32 100644 --- a/crates/optimism/src/handler/precompiles.rs +++ b/crates/optimism/src/handler/precompiles.rs @@ -2,17 +2,19 @@ use crate::{OpSpec, OpSpecId}; use once_cell::race::OnceBox; use precompile::{secp256r1, PrecompileErrors, Precompiles}; use revm::{ - context::Cfg, context_interface::CfgGetter, handler::EthPrecompileProvider, - handler_interface::PrecompileProvider, interpreter::InterpreterResult, + context::Cfg, + context_interface::ContextTrait, + handler::{EthPrecompiles, PrecompileProvider}, + interpreter::InterpreterResult, specification::hardfork::SpecId, }; use std::boxed::Box; -pub struct OpPrecompileProvider { - precompile_provider: EthPrecompileProvider, +pub struct OpPrecompileProvider { + precompile_provider: EthPrecompiles, } -impl Clone for OpPrecompileProvider { +impl Clone for OpPrecompileProvider { fn clone(&self) -> Self { Self { precompile_provider: self.precompile_provider.clone(), @@ -20,10 +22,10 @@ impl Clone for OpPrecompileProvider { } } -impl OpPrecompileProvider { +impl OpPrecompileProvider { pub fn new(precompiles: &'static Precompiles) -> Self { Self { - precompile_provider: EthPrecompileProvider { + precompile_provider: EthPrecompiles { precompiles, _phantom: core::marker::PhantomData, }, @@ -91,19 +93,15 @@ pub fn granite() -> &'static Precompiles { }) } -impl PrecompileProvider for OpPrecompileProvider +impl PrecompileProvider for OpPrecompileProvider where - CTX: CfgGetter, - ::Cfg: Cfg, - ERROR: From, + CTX: ContextTrait>, { type Context = CTX; - type Error = ERROR; type Output = InterpreterResult; - type Spec = OpSpec; #[inline] - fn set_spec(&mut self, spec: Self::Spec) { + fn set_spec(&mut self, spec: <::Cfg as Cfg>::Spec) { *self = Self::new_with_spec(spec); } @@ -114,7 +112,7 @@ where address: &precompile::Address, bytes: &precompile::Bytes, gas_limit: u64, - ) -> Result, Self::Error> { + ) -> Result, PrecompileErrors> { self.precompile_provider .run(context, address, bytes, gas_limit) } @@ -130,7 +128,7 @@ where } } -impl Default for OpPrecompileProvider { +impl Default for OpPrecompileProvider { fn default() -> Self { Self::new_with_spec(OpSpec::Op(OpSpecId::ISTHMUS)) } diff --git a/crates/optimism/src/l1block.rs b/crates/optimism/src/l1block.rs index af7220f9ff..0457b058ea 100644 --- a/crates/optimism/src/l1block.rs +++ b/crates/optimism/src/l1block.rs @@ -1,13 +1,9 @@ use crate::{transaction::estimate_tx_compressed_size, OpSpecId}; -use auto_impl::auto_impl; use core::ops::Mul; -use inspector::inspector_context::InspectorContext; use revm::{ - context_interface::{DatabaseGetter, Journal}, database_interface::Database, primitives::{address, Address, U256}, specification::hardfork::SpecId, - Context, }; use super::OpSpec; @@ -246,36 +242,6 @@ impl L1BlockInfo { } } -#[auto_impl(&mut, Box)] -pub trait L1BlockInfoGetter { - fn l1_block_info(&self) -> &L1BlockInfo; - fn l1_block_info_mut(&mut self) -> &mut L1BlockInfo; -} - -impl> L1BlockInfoGetter - for Context -{ - fn l1_block_info(&self) -> &L1BlockInfo { - &self.chain - } - - fn l1_block_info_mut(&mut self) -> &mut L1BlockInfo { - &mut self.chain - } -} - -impl L1BlockInfoGetter - for InspectorContext -{ - fn l1_block_info(&self) -> &L1BlockInfo { - self.inner.l1_block_info() - } - - fn l1_block_info_mut(&mut self) -> &mut L1BlockInfo { - self.inner.l1_block_info_mut() - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/optimism/src/lib.rs b/crates/optimism/src/lib.rs index 72f74867f8..dd2d917cca 100644 --- a/crates/optimism/src/lib.rs +++ b/crates/optimism/src/lib.rs @@ -7,7 +7,7 @@ extern crate alloc as std; pub mod api; pub mod bn128; -pub mod context; +pub mod evm; pub mod fast_lz; pub mod handler; pub mod l1block; @@ -15,9 +15,12 @@ pub mod result; pub mod spec; pub mod transaction; -pub use l1block::{ - L1BlockInfo, L1BlockInfoGetter, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT, +pub use api::{ + builder::{OpBuilder, OpContext}, + default_ctx::DefaultOp, }; +pub use evm::OpEvm; +pub use l1block::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; pub use result::OpHaltReason; pub use spec::*; pub use transaction::{error::OpTransactionError, estimate_tx_compressed_size, OpTransaction}; diff --git a/crates/optimism/src/transaction/abstraction.rs b/crates/optimism/src/transaction/abstraction.rs index 167b8c515d..d5901e31d2 100644 --- a/crates/optimism/src/transaction/abstraction.rs +++ b/crates/optimism/src/transaction/abstraction.rs @@ -1,14 +1,9 @@ use super::deposit::{DepositTransaction, DepositTransactionParts}; use auto_impl::auto_impl; -use inspector::inspector_context::InspectorContext; use revm::{ context::TxEnv, - context_interface::{ - transaction::{AuthorizationItem, Transaction}, - DatabaseGetter, Journal, TransactionGetter, - }, + context_interface::transaction::{AuthorizationItem, Transaction}, primitives::{Address, Bytes, TxKind, B256, U256}, - Context, Database, }; use std::vec; @@ -17,33 +12,6 @@ pub trait OpTxTrait: Transaction + DepositTransaction { fn enveloped_tx(&self) -> Option<&Bytes>; } -#[auto_impl(&, &mut, Box, Arc)] -pub trait OpTxGetter: TransactionGetter { - type OpTransaction: OpTxTrait; - - fn op_tx(&self) -> &Self::OpTransaction; -} - -impl, CHAIN> OpTxGetter - for Context, CFG, DB, JOURNAL, CHAIN> -{ - type OpTransaction = OpTransaction; - - fn op_tx(&self) -> &Self::OpTransaction { - &self.tx - } -} - -impl OpTxGetter - for InspectorContext -{ - type OpTransaction = ::OpTransaction; - - fn op_tx(&self) -> &Self::OpTransaction { - self.inner.op_tx() - } -} - #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct OpTransaction { diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 994fe73e8b..02e7904846 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -33,7 +33,6 @@ bytecode.workspace = true context.workspace = true context-interface.workspace = true handler.workspace = true -handler-interface.workspace = true [dev-dependencies] database.workspace = true @@ -54,7 +53,6 @@ std = [ "interpreter/std", "precompile/std", "handler/std", - "handler-interface/std", "context/std", "context-interface/std", ] diff --git a/crates/revm/src/exec.rs b/crates/revm/src/exec.rs deleted file mode 100644 index b3f37986c1..0000000000 --- a/crates/revm/src/exec.rs +++ /dev/null @@ -1,25 +0,0 @@ -use context_interface::{block::BlockSetter, transaction::TransactionSetter}; - -/// Execute EVM transactions. -pub trait ExecuteEvm: BlockSetter + TransactionSetter { - type Output; - - fn exec_previous(&mut self) -> Self::Output; - - fn exec(&mut self, tx: Self::Transaction) -> Self::Output { - self.set_tx(tx); - self.exec_previous() - } -} - -/// Execute EVM transactions and commit to the state. -pub trait ExecuteCommitEvm: ExecuteEvm { - type CommitOutput; - - fn exec_commit_previous(&mut self) -> Self::CommitOutput; - - fn exec_commit(&mut self, tx: Self::Transaction) -> Self::CommitOutput { - self.set_tx(tx); - self.exec_commit_previous() - } -} diff --git a/crates/revm/src/exec_eth.rs b/crates/revm/src/exec_eth.rs deleted file mode 100644 index 735663164f..0000000000 --- a/crates/revm/src/exec_eth.rs +++ /dev/null @@ -1,109 +0,0 @@ -use crate::{ExecuteCommitEvm, ExecuteEvm}; -use context::{Cfg, Context}; -use context_interface::{ - result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, ResultAndState}, - Block, Database, DatabaseGetter, Journal, Transaction, -}; -use database_interface::DatabaseCommit; -use handler::{ - instructions::EthInstructionExecutor, EthContext, EthFrame, EthHandler, EthPrecompileProvider, - MainnetHandler, -}; -use interpreter::interpreter::EthInterpreter; -use primitives::Log; -use state::EvmState; -use std::vec::Vec; - -impl ExecuteEvm for Context -where - BLOCK: Block, - TX: Transaction, - CFG: Cfg, - DB: Database, - JOURNAL: Journal)>, -{ - type Output = - Result, EVMError<::Error, InvalidTransaction>>; - - fn exec_previous(&mut self) -> Self::Output { - transact_main(self) - } -} - -impl ExecuteCommitEvm - for Context -where - BLOCK: Block, - TX: Transaction, - CFG: Cfg, - DB: Database + DatabaseCommit, - JOURNAL: Journal)>, -{ - type CommitOutput = - Result, EVMError<::Error, InvalidTransaction>>; - - fn exec_commit_previous(&mut self) -> Self::CommitOutput { - transact_main(self).map(|r| { - self.db().commit(r.state); - r.result - }) - } -} - -/// Helper function that executed a transaction and commits the state. -pub fn transact_main( - ctx: &mut CTX, -) -> Result< - ResultAndState, - EVMError<<::Database as Database>::Error, InvalidTransaction>, -> { - MainnetHandler::< - CTX, - _, - EthFrame, - EthPrecompileProvider, - EthInstructionExecutor, - >::default() - .run(ctx) -} - -#[cfg(test)] -mod test { - use super::*; - use bytecode::{ - opcode::{PUSH1, SSTORE}, - Bytecode, - }; - use context_interface::TransactionType; - use database::{BenchmarkDB, EEADDRESS, FFADDRESS}; - use primitives::{address, TxKind, U256}; - use specification::hardfork::SpecId; - - #[test] - fn sanity_eip7702_tx() { - let auth = address!("0000000000000000000000000000000000000100"); - - let bytecode = Bytecode::new_legacy([PUSH1, 0x01, PUSH1, 0x01, SSTORE].into()); - - let mut ctx = Context::default() - .modify_cfg_chained(|cfg| cfg.spec = SpecId::PRAGUE) - .with_db(BenchmarkDB::new_bytecode(bytecode)) - .modify_tx_chained(|tx| { - tx.tx_type = TransactionType::Eip7702.into(); - tx.gas_limit = 100_000; - tx.authorization_list = vec![(Some(auth), U256::from(0), 0, FFADDRESS)]; - tx.caller = EEADDRESS; - tx.kind = TxKind::Call(auth); - }); - - let ok = ctx.exec_previous().unwrap(); - - let auth_acc = ok.state.get(&auth).unwrap(); - assert_eq!(auth_acc.info.code, Some(Bytecode::new_eip7702(FFADDRESS))); - assert_eq!(auth_acc.info.nonce, 1); - assert_eq!( - auth_acc.storage.get(&U256::from(1)).unwrap().present_value, - U256::from(1) - ); - } -} diff --git a/crates/revm/src/exec_inspect.rs b/crates/revm/src/exec_inspect.rs new file mode 100644 index 0000000000..bc636e656e --- /dev/null +++ b/crates/revm/src/exec_inspect.rs @@ -0,0 +1,69 @@ +use context::setters::ContextSetters; + +/// Execute EVM transactions. +pub trait ExecuteEvm: ContextSetters { + type Output; + + fn transact_previous(&mut self) -> Self::Output; + + fn transact(&mut self, tx: Self::Tx) -> Self::Output { + self.set_tx(tx); + self.transact_previous() + } +} + +/// Execute EVM transactions and commit to the state. +/// TODO this trait can be implemented for all ExecuteEvm for specific Output/CommitOutput +pub trait ExecuteCommitEvm: ExecuteEvm { + type CommitOutput; + + fn transact_commit_previous(&mut self) -> Self::CommitOutput; + + fn transact_commit(&mut self, tx: Self::Tx) -> Self::CommitOutput { + self.set_tx(tx); + self.transact_commit_previous() + } +} + +pub trait InspectEvm: ExecuteEvm { + type Inspector; + + fn set_inspector(&mut self, inspector: Self::Inspector); + + fn inspect_previous(&mut self) -> Self::Output; + + fn inspect_previous_with_inspector(&mut self, inspector: Self::Inspector) -> Self::Output { + self.set_inspector(inspector); + self.inspect_previous() + } + + fn inspect( + &mut self, + tx: ::Tx, + inspector: Self::Inspector, + ) -> Self::Output { + self.set_tx(tx); + self.inspect_previous_with_inspector(inspector) + } +} + +pub trait InspectCommitEvm: InspectEvm + ExecuteCommitEvm { + fn inspect_commit_previous(&mut self) -> Self::CommitOutput; + + fn inspect_commit_previous_with_inspector( + &mut self, + inspector: Self::Inspector, + ) -> Self::CommitOutput { + self.set_inspector(inspector); + self.inspect_commit_previous() + } + + fn inspect_commit( + &mut self, + tx: ::Tx, + inspector: Self::Inspector, + ) -> Self::CommitOutput { + self.set_tx(tx); + self.inspect_commit_previous_with_inspector(inspector) + } +} diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index aed09f5041..46335ef25f 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -11,7 +11,6 @@ pub use context; pub use context_interface; pub use database_interface; pub use handler; -pub use handler_interface; pub use interpreter; pub use precompile; pub use primitives; @@ -20,13 +19,14 @@ pub use state; // Modules. -mod exec; -mod exec_eth; +mod exec_inspect; +mod mainnet_builder; +mod mainnet_exec_inspect; // Export items. pub use context::journaled_state::{JournalEntry, JournaledState}; pub use context::Context; pub use database_interface::{Database, DatabaseCommit, DatabaseRef}; -pub use exec::{ExecuteCommitEvm, ExecuteEvm}; -pub use exec_eth::transact_main; +pub use exec_inspect::{ExecuteCommitEvm, ExecuteEvm, InspectCommitEvm, InspectEvm}; +pub use mainnet_builder::{MainBuilder, MainContext, MainnetEvm}; diff --git a/crates/revm/src/mainnet_builder.rs b/crates/revm/src/mainnet_builder.rs new file mode 100644 index 0000000000..56c6cdd1b8 --- /dev/null +++ b/crates/revm/src/mainnet_builder.rs @@ -0,0 +1,70 @@ +use context::{BlockEnv, Cfg, CfgEnv, Context, Evm, EvmData, JournaledState, TxEnv}; +use context_interface::{Block, Database, Journal, Transaction}; +use database_interface::EmptyDB; +use handler::{instructions::EthInstructions, noop::NoOpInspector, EthPrecompiles}; +use interpreter::interpreter::EthInterpreter; +use primitives::Log; +use specification::hardfork::SpecId; +use state::EvmState; +use std::vec::Vec; + +pub type MainnetEvm = + Evm, EthPrecompiles>; + +pub trait MainBuilder: Sized { + type Context; + + fn build_mainnet(self) -> MainnetEvm; + + fn build_mainnet_with_inspector(self, inspector: INSP) + -> MainnetEvm; +} + +impl MainBuilder for Context +where + BLOCK: Block, + TX: Transaction, + CFG: Cfg, + DB: Database, + JOURNAL: Journal)>, +{ + type Context = Self; + + fn build_mainnet(self) -> MainnetEvm { + Evm { + data: EvmData { + ctx: self, + inspector: NoOpInspector {}, + }, + enabled_inspection: false, + instruction: EthInstructions::default(), + precompiles: EthPrecompiles::default(), + } + } + + fn build_mainnet_with_inspector( + self, + inspector: INSP, + ) -> MainnetEvm { + Evm { + data: EvmData { + ctx: self, + inspector, + }, + enabled_inspection: true, + instruction: EthInstructions::default(), + precompiles: EthPrecompiles::default(), + } + } +} + +/// Trait used to initialize Context with default mainnet types. +pub trait MainContext { + fn mainnet() -> Self; +} + +impl MainContext for Context, ()> { + fn mainnet() -> Self { + Context::new(EmptyDB::new(), SpecId::LATEST) + } +} diff --git a/crates/revm/src/mainnet_exec_inspect.rs b/crates/revm/src/mainnet_exec_inspect.rs new file mode 100644 index 0000000000..2d434f7106 --- /dev/null +++ b/crates/revm/src/mainnet_exec_inspect.rs @@ -0,0 +1,146 @@ +use crate::{exec_inspect::ExecuteCommitEvm, ExecuteEvm}; +use crate::{InspectCommitEvm, InspectEvm}; +use context::setters::ContextSetters; +use context::Evm; +use context_interface::{ + result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, ResultAndState}, + ContextTrait, Database, Journal, +}; +use database_interface::DatabaseCommit; +use handler::inspector::JournalExt; +use handler::{handler::EvmTrait, inspector::EthInspectorHandler}; +use handler::{ + inspector::Inspector, instructions::EthInstructions, EthFrame, EthHandler, EthPrecompiles, + MainnetHandler, +}; +use interpreter::interpreter::EthInterpreter; + +use primitives::Log; +use state::EvmState; +use std::vec::Vec; + +impl ExecuteEvm + for Evm, EthPrecompiles> +where + CTX: ContextSetters + + ContextTrait)> + JournalExt>, + INSP: Inspector, +{ + type Output = Result< + ResultAndState, + EVMError<::Error, InvalidTransaction>, + >; + + fn transact_previous(&mut self) -> Self::Output { + let mut t = MainnetHandler::<_, _, EthFrame<_, _, _>>::default(); + t.run(self) + } +} + +impl ExecuteCommitEvm + for Evm, EthPrecompiles> +where + CTX: ContextSetters + + ContextTrait< + Journal: Journal)> + JournalExt, + Db: DatabaseCommit, + >, + INSP: Inspector, +{ + type CommitOutput = Result< + ExecutionResult, + EVMError<::Error, InvalidTransaction>, + >; + + fn transact_commit_previous(&mut self) -> Self::CommitOutput { + self.transact_previous().map(|r| { + self.db().commit(r.state); + r.result + }) + } +} + +impl InspectEvm + for Evm, EthPrecompiles> +where + CTX: ContextSetters + + ContextTrait)> + JournalExt>, + INSP: Inspector, +{ + type Inspector = INSP; + + fn set_inspector(&mut self, inspector: Self::Inspector) { + self.data.inspector = inspector; + } + + fn inspect_previous(&mut self) -> Self::Output { + let mut t = MainnetHandler::<_, _, EthFrame<_, _, _>> { + _phantom: core::marker::PhantomData, + }; + + t.inspect_run(self) + } +} + +impl InspectCommitEvm + for Evm, EthPrecompiles> +where + CTX: ContextSetters + + ContextTrait< + Journal: Journal)> + JournalExt, + Db: DatabaseCommit, + >, + INSP: Inspector, +{ + fn inspect_commit_previous(&mut self) -> Self::CommitOutput { + self.inspect_previous().map(|r| { + self.ctx().db().commit(r.state); + r.result + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{MainBuilder, MainContext}; + use bytecode::{ + opcode::{PUSH1, SSTORE}, + Bytecode, + }; + use context::Context; + use context_interface::TransactionType; + use database::{BenchmarkDB, EEADDRESS, FFADDRESS}; + use primitives::{address, TxKind, U256}; + use specification::hardfork::SpecId; + + #[test] + fn sanity_eip7702_tx() { + let auth = address!("0000000000000000000000000000000000000100"); + + let bytecode = Bytecode::new_legacy([PUSH1, 0x01, PUSH1, 0x01, SSTORE].into()); + + let ctx = Context::mainnet() + .modify_cfg_chained(|cfg| cfg.spec = SpecId::PRAGUE) + .with_db(BenchmarkDB::new_bytecode(bytecode)) + .modify_tx_chained(|tx| { + tx.tx_type = TransactionType::Eip7702.into(); + tx.gas_limit = 100_000; + tx.authorization_list = vec![(Some(auth), U256::from(0), 0, FFADDRESS)]; + tx.caller = EEADDRESS; + tx.kind = TxKind::Call(auth); + }); + + let mut evm = ctx.build_mainnet(); + + let ok = evm.transact_previous().unwrap(); + + let auth_acc = ok.state.get(&auth).unwrap(); + assert_eq!(auth_acc.info.code, Some(Bytecode::new_eip7702(FFADDRESS))); + assert_eq!(auth_acc.info.nonce, 1); + assert_eq!( + auth_acc.storage.get(&U256::from(1)).unwrap().present_value, + U256::from(1) + ); + } +} diff --git a/examples/block_traces/src/main.rs b/examples/block_traces/src/main.rs index 72e8c92563..ed62a5e42b 100644 --- a/examples/block_traces/src/main.rs +++ b/examples/block_traces/src/main.rs @@ -9,8 +9,11 @@ use alloy_provider::{ }; use database::{AlloyDB, CacheDB, StateBuilder}; use indicatif::ProgressBar; -use inspector::{exec::InspectCommitEvm, inspectors::TracerEip3155}; -use revm::{database_interface::WrapDatabaseAsync, primitives::TxKind, Context}; +use inspector::inspectors::TracerEip3155; +use revm::{ + database_interface::WrapDatabaseAsync, primitives::TxKind, Context, InspectEvm, MainBuilder, + MainContext, +}; use std::fs::OpenOptions; use std::io::BufWriter; use std::io::Write; @@ -72,7 +75,7 @@ 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 ctx = Context::builder() + let ctx = Context::mainnet() .with_db(&mut state) .modify_block_chained(|b| { b.number = block.header.number; @@ -87,6 +90,17 @@ async fn main() -> anyhow::Result<()> { c.chain_id = chain_id; }); + let write = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open("traces/0.json"); + let inner = Arc::new(Mutex::new(BufWriter::new( + write.expect("Failed to open file"), + ))); + let writer = FlushWriter::new(Arc::clone(&inner)); + let mut evm = ctx.build_mainnet_with_inspector(TracerEip3155::new(Box::new(writer))); + let txs = block.transactions.len(); println!("Found {txs} transactions."); @@ -102,7 +116,7 @@ async fn main() -> anyhow::Result<()> { }; for tx in transactions { - ctx.modify_tx(|etx| { + evm.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()); @@ -111,12 +125,15 @@ async fn main() -> anyhow::Result<()> { etx.gas_priority_fee = tx.max_priority_fee_per_gas(); etx.chain_id = Some(chain_id); etx.nonce = tx.nonce(); - // TODO rakita - // if let Some(access_list) = tx.access_list() { - // etx.access_list = access_list.to_owned(); - // } else { - // etx.access_list = Default::default(); - // } + if let Some(access_list) = tx.access_list() { + etx.access_list = access_list + .0 + .iter() + .map(|item| (item.address, item.storage_keys.clone())) + .collect(); + } else { + etx.access_list = Default::default(); + } etx.kind = match tx.to() { Some(to_address) => TxKind::Call(to_address), @@ -138,8 +155,7 @@ async fn main() -> anyhow::Result<()> { let writer = FlushWriter::new(Arc::clone(&inner)); // Inspect and commit the transaction to the EVM - - let res = ctx.inspect_commit_previous(TracerEip3155::new(Box::new(writer))); + let res = evm.inspect_previous_with_inspector(TracerEip3155::new(Box::new(writer))); 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 6af133a6f1..eed7fc61c9 100644 --- a/examples/cheatcode_inspector/src/main.rs +++ b/examples/cheatcode_inspector/src/main.rs @@ -9,8 +9,11 @@ use std::{convert::Infallible, fmt::Debug}; use database::InMemoryDB; use inspector::{ - exec::inspect_main, inspector_context::InspectorContext, inspectors::TracerEip3155, - journal::JournalExt, GetInspector, Inspector, + exec::{inspect_main, InspectEvm}, + inspector_context::InspectorContext, + inspectors::TracerEip3155, + journal::JournalExt, + GetInspector, Inspector, }; use revm::{ bytecode::Bytecode, @@ -21,14 +24,13 @@ use revm::{ result::{EVMError, InvalidTransaction}, Block, Journal, JournalGetter, Transaction, }, - handler::EthPrecompileProvider, - handler_interface::PrecompileProvider, + handler::{EthPrecompiles, PrecompileProvider}, interpreter::{interpreter::EthInterpreter, CallInputs, CallOutcome, InterpreterResult}, precompile::{Address, HashSet, B256}, primitives::{Log, U256}, specification::hardfork::SpecId, state::{Account, EvmState, TransientStorage}, - Context, Database, DatabaseCommit, JournalEntry, JournaledState, + Context, Database, DatabaseCommit, JournalEntry, JournaledState, MainBuilder, }; /// Backend for cheatcodes. @@ -289,7 +291,6 @@ trait DatabaseExt: Journal { PrecompileT: PrecompileProvider< Context = InspectorContext>, Output = InterpreterResult, - Error = EVMError, >; /// Mimics `DatabaseExt::roll_fork_to_transaction` @@ -382,9 +383,8 @@ where // `transact` cheatcode would do this context .journal() - .method_that_takes_inspector_as_argument::<&mut Self, BlockT, TxT, CfgT, EthPrecompileProvider< - InspectorContext<&mut Self, Context>, - EVMError, + .method_that_takes_inspector_as_argument::<&mut Self, BlockT, TxT, CfgT, EthPrecompiles< + InspectorContext<&mut Self, Context> >>( Env { block: block.clone(), @@ -479,13 +479,14 @@ where chain: (), error: Ok(()), }; + let mut evm = context.build_mainnet(); - let mut inspector_context = InspectorContext::< - InspectorT, - Context, - >::new(context, inspector); - - let result = inspect_main(&mut inspector_context)?; + // let mut inspector_context = InspectorContext::< + // InspectorT, + // Context, + // >::new(context, inspector); + let result = evm.inspect_previous(inspector)?; + //let result = inspect_main(&mut inspector_context)?; // Persist the changes to the original backend. backend.journaled_state.database.commit(result.state); @@ -515,31 +516,22 @@ fn main() -> anyhow::Result<()> { let mut inspector = Cheatcodes::::default(); let env = Env::mainnet(); - let context = Context { + let mut evm = Context { tx: env.tx, block: env.block, cfg: env.cfg, journaled_state: backend, chain: (), error: Ok(()), - }; - let mut context = InspectorContext::new(context, &mut inspector); + } + .build_mainnet(); - inspect_main(&mut context)?; + evm.inspect_previous(&mut inspector)?; // Sanity check - assert_eq!(context.inspector.call_count, 2); - assert_eq!( - context.inner.journaled_state.method_with_inspector_counter, - 1 - ); - assert_eq!( - context - .inner - .journaled_state - .method_without_inspector_counter, - 1 - ); + assert_eq!(inspector.call_count, 2); + assert_eq!(evm.ctx.journaled_state.method_with_inspector_counter, 1); + assert_eq!(evm.ctx.journaled_state.method_without_inspector_counter, 1); Ok(()) } diff --git a/examples/contract_deployment/src/main.rs b/examples/contract_deployment/src/main.rs index ccc8523166..1bb18c4b98 100644 --- a/examples/contract_deployment/src/main.rs +++ b/examples/contract_deployment/src/main.rs @@ -8,8 +8,9 @@ use revm::{ context::Context, context_interface::result::{ExecutionResult, Output}, database_interface::EmptyDB, + handler::handler::EvmTrait, primitives::{hex, Bytes, TxKind, U256}, - transact_main, ExecuteCommitEvm, + ExecuteCommitEvm, ExecuteEvm, MainBuilder, MainContext, }; /// Load number parameter and set to storage with slot 0 @@ -47,15 +48,17 @@ 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 ctx = Context::builder() + let ctx = Context::mainnet() .modify_tx_chained(|tx| { tx.kind = TxKind::Create; tx.data = bytecode.clone(); }) .with_db(CacheDB::::default()); + let mut evm = ctx.build_mainnet(); + println!("bytecode: {}", hex::encode(bytecode)); - let ref_tx = ctx.exec_commit_previous()?; + let ref_tx = evm.transact_commit_previous()?; let ExecutionResult::Success { output: Output::Create(_, Some(address)), .. @@ -65,13 +68,13 @@ fn main() -> anyhow::Result<()> { }; println!("Created contract at {address}"); - ctx.modify_tx(|tx| { + evm.ctx().modify_tx(|tx| { tx.kind = TxKind::Call(address); tx.data = Default::default(); tx.nonce += 1; }); - let result = transact_main(&mut ctx)?; + let result = evm.transact_previous()?; let Some(storage0) = result .state .get(&address) diff --git a/examples/erc20_gas/src/exec.rs b/examples/erc20_gas/src/exec.rs index 1d8fdc4655..f809e484b9 100644 --- a/examples/erc20_gas/src/exec.rs +++ b/examples/erc20_gas/src/exec.rs @@ -2,26 +2,54 @@ use crate::handler::Erc20MainetHandler; use revm::{ context_interface::{ result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, ResultAndState}, - DatabaseGetter, + ContextTrait, Journal, }, - database_interface::{Database, DatabaseCommit}, - handler::handler::{EthContext, EthHandler}, + database_interface::DatabaseCommit, + handler::{ + instructions::InstructionProvider, CtxTraitDbError, EthFrame, EthHandler, EvmTrait, + PrecompileProvider, + }, + interpreter::{interpreter::EthInterpreter, InterpreterAction, InterpreterResult}, + primitives::Log, + state::EvmState, }; -pub fn transact_erc20evm>( - ctx: &mut CTX, -) -> Result, EVMError<::Error, InvalidTransaction>> { - Erc20MainetHandler::::new().run(ctx) +pub fn transact_erc20evm( + evm: &mut EVM, +) -> Result, EVMError, InvalidTransaction>> +where + EVM: EvmTrait< + Context: ContextTrait)>>, + Precompiles: PrecompileProvider, + Instructions: InstructionProvider< + Context = EVM::Context, + InterpreterTypes = EthInterpreter<()>, + Output = InterpreterAction, + >, + >, +{ + Erc20MainetHandler::>::new().run(evm) } -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); +pub fn transact_erc20evm_commit( + evm: &mut EVM, +) -> Result, EVMError, InvalidTransaction>> +where + EVM: EvmTrait< + Context: ContextTrait< + Journal: Journal)>, + Db: DatabaseCommit, + >, + Precompiles: PrecompileProvider, + Instructions: InstructionProvider< + Context = EVM::Context, + InterpreterTypes = EthInterpreter<()>, + Output = InterpreterAction, + >, + >, +{ + transact_erc20evm(evm).map(|r| { + evm.ctx().db().commit(r.state); r.result }) } diff --git a/examples/erc20_gas/src/handler.rs b/examples/erc20_gas/src/handler.rs index 6570c2f5fb..58e3968efc 100644 --- a/examples/erc20_gas/src/handler.rs +++ b/examples/erc20_gas/src/handler.rs @@ -2,56 +2,49 @@ use revm::{ context::Cfg, context_interface::{ result::{HaltReason, InvalidTransaction}, - Block, CfgGetter, Journal, Transaction, TransactionType, + Block, ContextTrait, Journal, Transaction, TransactionType, }, - handler::{ - instructions::EthInstructionExecutor, EthContext, EthError, EthFrame, EthHandler, - EthPrecompileProvider, FrameContext, - }, - handler_interface::Frame, - interpreter::{interpreter::EthInterpreter, Host}, - precompile::PrecompileErrors, - primitives::U256, + handler::{EthHandler, EthTraitError, EvmTrait, Frame, FrameResult}, + interpreter::FrameInput, + primitives::{Log, U256}, specification::hardfork::SpecId, + state::EvmState, }; use std::cmp::Ordering; use crate::{erc_address_storage, token_operation, TOKEN, TREASURY}; -pub struct Erc20MainetHandler { - _phantom: std::marker::PhantomData<(CTX, ERROR)>, +pub struct Erc20MainetHandler { + _phantom: core::marker::PhantomData<(EVM, ERROR, FRAME)>, } -impl Erc20MainetHandler { +impl Erc20MainetHandler { pub fn new() -> Self { Self { - _phantom: std::marker::PhantomData, + _phantom: core::marker::PhantomData, } } } -impl> Default - for Erc20MainetHandler -{ +impl Default for Erc20MainetHandler { fn default() -> Self { Self::new() } } -impl EthHandler for Erc20MainetHandler +impl EthHandler for Erc20MainetHandler where - CTX: EthContext, - ERROR: EthError, + EVM: EvmTrait)>>>, + FRAME: Frame, + ERROR: EthTraitError, { - type Context = CTX; + type Evm = EVM; type Error = ERROR; - type Precompiles = EthPrecompileProvider; - type Instructions = EthInstructionExecutor; - type Frame = - EthFrame>; + type Frame = FRAME; type HaltReason = HaltReason; - fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + fn validate_tx_against_state(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { + let context = evm.ctx(); let caller = context.tx().caller(); let caller_nonce = context.journal().load_account(caller)?.data.info.nonce; let _ = context.journal().load_account(TOKEN)?.data.clone(); @@ -107,7 +100,8 @@ where Ok(()) } - fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + fn deduct_caller(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { + let context = evm.ctx(); // load and touch token account let _ = context.journal().load_account(TOKEN)?.data; context.journal().touch_account(TOKEN); @@ -125,16 +119,17 @@ where let caller = context.tx().caller(); println!("Deduct caller: {:?} for amount: {gas_cost:?}", caller); - token_operation::(context, caller, TREASURY, U256::from(gas_cost))?; + token_operation::(context, caller, TREASURY, U256::from(gas_cost))?; Ok(()) } fn reimburse_caller( &self, - context: &mut Self::Context, + evm: &mut Self::Evm, exec_result: &mut ::FrameResult, ) -> Result<(), Self::Error> { + let context = evm.ctx(); let basefee = context.block().basefee() as u128; let caller = context.tx().caller(); let effective_gas_price = context.tx().effective_gas_price(basefee); @@ -142,16 +137,22 @@ where let reimbursement = effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128); - token_operation::(context, TREASURY, caller, U256::from(reimbursement))?; + token_operation::( + context, + TREASURY, + caller, + U256::from(reimbursement), + )?; Ok(()) } fn reward_beneficiary( &self, - context: &mut Self::Context, + evm: &mut Self::Evm, exec_result: &mut ::FrameResult, ) -> Result<(), Self::Error> { + let context = evm.ctx(); let tx = context.tx(); let beneficiary = context.block().beneficiary(); let basefee = context.block().basefee() as u128; @@ -166,7 +167,7 @@ where let reward = coinbase_gas_price.saturating_mul((gas.spent() - gas.refunded() as u64) as u128); - token_operation::(context, TREASURY, beneficiary, U256::from(reward))?; + token_operation::(context, TREASURY, beneficiary, U256::from(reward))?; Ok(()) } diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index a018c6876a..ec9416d925 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -14,14 +14,14 @@ use reqwest::{Client, Url}; use revm::{ context_interface::{ result::{InvalidHeader, InvalidTransaction}, - Journal, JournalDBError, JournalGetter, + ContextTrait, Journal, }, database_interface::WrapDatabaseAsync, precompile::PrecompileErrors, primitives::{address, keccak256, Address, Bytes, TxKind, U256}, specification::hardfork::SpecId, state::AccountInfo, - Context, Database, + Context, Database, MainBuilder, MainContext, }; pub mod exec; @@ -88,10 +88,10 @@ pub fn token_operation( amount: U256, ) -> Result<(), ERROR> where - CTX: JournalGetter, + CTX: ContextTrait, ERROR: From + From - + From> + + From<::Error> + From, { let sender_balance_slot = erc_address_storage(sender); @@ -126,7 +126,7 @@ fn balance_of(address: Address, alloy_db: &mut AlloyCacheDB) -> Result { } fn transfer(from: Address, to: Address, amount: U256, cache_db: &mut AlloyCacheDB) -> Result<()> { - let mut ctx = Context::builder() + let mut ctx = Context::mainnet() .with_db(cache_db) .modify_cfg_chained(|cfg| { cfg.spec = SpecId::CANCUN; @@ -139,7 +139,9 @@ fn transfer(from: Address, to: Address, amount: U256, cache_db: &mut AlloyCacheD }) .modify_block_chained(|b| { b.basefee = 1; - }); + }) + .build_mainnet(); + transact_erc20evm_commit(&mut ctx).unwrap(); Ok(()) diff --git a/examples/uniswap_get_reserves/src/main.rs b/examples/uniswap_get_reserves/src/main.rs index 09c07de66d..c3610ccfe3 100644 --- a/examples/uniswap_get_reserves/src/main.rs +++ b/examples/uniswap_get_reserves/src/main.rs @@ -9,7 +9,7 @@ use revm::{ context_interface::result::{ExecutionResult, Output}, database_interface::{DatabaseRef, EmptyDB, WrapDatabaseAsync}, primitives::{address, TxKind, U256}, - transact_main, Context, + Context, ExecuteEvm, MainBuilder, MainContext, }; #[tokio::main] @@ -65,7 +65,7 @@ async fn main() -> anyhow::Result<()> { .unwrap(); // Initialise an empty (default) EVM - let mut ctx = Context::builder() + let mut evm = Context::mainnet() .with_db(cache_db) .modify_tx_chained(|tx| { // fill in missing bits of env struct @@ -77,10 +77,11 @@ async fn main() -> anyhow::Result<()> { tx.data = encoded.into(); // transaction value in wei tx.value = U256::from(0); - }); + }) + .build_mainnet(); // Execute transaction without writing to the DB - let ref_tx = transact_main(&mut ctx).unwrap(); + let ref_tx = evm.transact_previous().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 92788913a5..ec907427f4 100644 --- a/examples/uniswap_v2_usdc_swap/src/main.rs +++ b/examples/uniswap_v2_usdc_swap/src/main.rs @@ -13,7 +13,7 @@ use revm::{ database_interface::WrapDatabaseAsync, primitives::{address, keccak256, Address, Bytes, TxKind, U256}, state::AccountInfo, - transact_main, Context, ExecuteCommitEvm, ExecuteEvm, + Context, ExecuteCommitEvm, ExecuteEvm, MainBuilder, MainContext, }; use std::ops::Div; @@ -96,7 +96,7 @@ fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> let encoded = balanceOfCall { account: address }.abi_encode(); - let mut ctx = Context::builder() + let mut evm = Context::mainnet() .with_db(alloy_db) .modify_tx_chained(|tx| { // 0x1 because calling USDC proxy from zero address fails @@ -104,9 +104,10 @@ fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> tx.kind = TxKind::Call(token); tx.data = encoded.into(); tx.value = U256::from(0); - }); + }) + .build_mainnet(); - let ref_tx = ctx.exec_previous().unwrap(); + let ref_tx = evm.transact_previous().unwrap(); let result = ref_tx.result; let value = match result { @@ -140,16 +141,17 @@ async fn get_amount_out( } .abi_encode(); - let mut ctx = Context::builder() + let mut evm = Context::mainnet() .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); - }); + }) + .build_mainnet(); - let ref_tx = transact_main(&mut ctx).unwrap(); + let ref_tx = evm.transact_previous().unwrap(); let result = ref_tx.result; let value = match result { @@ -172,16 +174,17 @@ fn get_reserves(pair_address: Address, cache_db: &mut AlloyCacheDB) -> Result<(U let encoded = getReservesCall {}.abi_encode(); - let mut ctx = Context::builder() + let mut evm = Context::mainnet() .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); - }); + }) + .build_mainnet(); - let ref_tx = transact_main(&mut ctx).unwrap(); + let ref_tx = evm.transact_previous().unwrap(); let result = ref_tx.result; let value = match result { @@ -220,7 +223,7 @@ fn swap( } .abi_encode(); - let mut ctx = Context::builder() + let mut evm = Context::mainnet() .with_db(cache_db) .modify_tx_chained(|tx| { tx.caller = from; @@ -228,9 +231,10 @@ fn swap( tx.data = encoded.into(); tx.value = U256::from(0); tx.nonce = 1; - }); + }) + .build_mainnet(); - let ref_tx = ctx.exec_commit_previous().unwrap(); + let ref_tx = evm.transact_commit_previous().unwrap(); match ref_tx { ExecutionResult::Success { .. } => {} @@ -253,16 +257,17 @@ fn transfer( let encoded = transferCall { to, amount }.abi_encode(); - let mut ctx = Context::builder() + let mut evm = Context::mainnet() .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); - }); + }) + .build_mainnet(); - let ref_tx = ctx.exec_commit_previous().unwrap(); + let ref_tx = evm.transact_commit_previous().unwrap(); let success: bool = match ref_tx { ExecutionResult::Success { output: Output::Call(value), diff --git a/publish.sh b/publish.sh index 03feccd58e..68aeda54c2 100755 --- a/publish.sh +++ b/publish.sh @@ -8,8 +8,7 @@ cargo publish --package revm-specification cargo publish --package revm-bytecode cargo publish --package revm-state cargo publish --package revm-database-interface -cargo publish --package revm-context-interface -cargo publish --package revm-handler-interface +cargo publish --package revm-context-interface cargo publish --package revm-interpreter cargo publish --package revm-precompile cargo publish --package revm-database