diff --git a/Cargo.lock b/Cargo.lock index b7ee6e07af..2081056e53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3540,10 +3540,13 @@ version = "1.0.0" 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 a3e5a42bdf..fcfc566312 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ handler-interface = { path = "crates/handler/interface", package = "revm-handler cfg-if = { version = "1.0", default-features = false } auto_impl = { version = "1.2.0" } derive-where = { version = "1.2.7", default-features = false } +derive_more = { version = "1.0.0", default-features = false } criterion = { package = "codspeed-criterion-compat", version = "2.7" } [workspace.package] diff --git a/bins/revme/benches/evm.rs b/bins/revme/benches/evm.rs index a4fc9bfb62..ac03be32b8 100644 --- a/bins/revme/benches/evm.rs +++ b/bins/revme/benches/evm.rs @@ -5,6 +5,9 @@ use revme::cmd::{ }; fn evm(c: &mut Criterion) { + // call analysis to init static data. + revme::cmd::bench::analysis::run(); + for &bench_name in BenchName::ALL { let cmd = MainCmd::Bench(bench::Cmd { name: bench_name }); c.bench_function(bench_name.as_str(), |b| { diff --git a/bins/revme/src/cmd/bench/analysis.rs b/bins/revme/src/cmd/bench/analysis.rs index 88219799c7..ee397d4a64 100644 --- a/bins/revme/src/cmd/bench/analysis.rs +++ b/bins/revme/src/cmd/bench/analysis.rs @@ -1,8 +1,10 @@ -use database::BenchmarkDB; +use std::time::Instant; + +use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; use revm::{ bytecode::Bytecode, - primitives::{address, bytes, hex, Bytes, TxKind}, - transact_main, Context, + primitives::{bytes, hex, Bytes, TxKind}, + Context, ExecuteEvm, }; const BYTES: &str = include_str!("analysis.hex"); @@ -15,10 +17,17 @@ pub fn run() { .with_db(BenchmarkDB::new_bytecode(bytecode)) .modify_tx_chained(|tx| { // Execution globals block hash/gas_limit/coinbase/timestamp.. - tx.caller = address!("1000000000000000000000000000000000000000"); - tx.kind = TxKind::Call(address!("0000000000000000000000000000000000000000")); + 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 _ = transact_main(&mut context); + + let time = Instant::now(); + let _ = context.exec_previous(); + println!("First init: {:?}", time.elapsed()); + + let time = Instant::now(); + let _ = context.exec_previous(); + println!("Run: {:?}", time.elapsed()); } diff --git a/bins/revme/src/cmd/bench/burntpix.rs b/bins/revme/src/cmd/bench/burntpix.rs index b8f922a823..35929f8b9f 100644 --- a/bins/revme/src/cmd/bench/burntpix.rs +++ b/bins/revme/src/cmd/bench/burntpix.rs @@ -8,11 +8,11 @@ use static_data::{ use alloy_sol_macro::sol; use alloy_sol_types::SolCall; -use database::CacheDB; +use database::{CacheDB, BENCH_CALLER}; use revm::{ context_interface::result::{ExecutionResult, Output}, database_interface::EmptyDB, - primitives::{address, hex, keccak256, Address, Bytes, TxKind, B256, U256}, + primitives::{hex, keccak256, Address, Bytes, TxKind, B256, U256}, state::{AccountInfo, Bytecode}, transact_main, Context, }; @@ -37,7 +37,7 @@ pub fn run() { let db = init_db(); let mut context = Context::builder().with_db(db).modify_tx_chained(|tx| { - tx.caller = address!("1000000000000000000000000000000000000000"); + tx.caller = BENCH_CALLER; tx.kind = TxKind::Call(BURNTPIX_MAIN_ADDRESS); tx.data = run_call_data.clone().into(); tx.gas_limit = u64::MAX; diff --git a/bins/revme/src/cmd/bench/snailtracer.rs b/bins/revme/src/cmd/bench/snailtracer.rs index 8fd548614a..828a9d1183 100644 --- a/bins/revme/src/cmd/bench/snailtracer.rs +++ b/bins/revme/src/cmd/bench/snailtracer.rs @@ -1,7 +1,7 @@ -use database::BenchmarkDB; +use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; use revm::{ bytecode::Bytecode, - primitives::{address, bytes, hex, Bytes, TxKind}, + primitives::{bytes, hex, Bytes, TxKind}, transact_main, Context, }; @@ -10,8 +10,8 @@ pub fn simple_example(bytecode: Bytecode) { .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) .modify_tx_chained(|tx| { // Execution globals block hash/gas_limit/coinbase/timestamp.. - tx.caller = address!("1000000000000000000000000000000000000000"); - tx.kind = TxKind::Call(address!("0000000000000000000000000000000000000000")); + tx.caller = BENCH_CALLER; + tx.kind = TxKind::Call(BENCH_TARGET); tx.data = bytes!("30627b7c"); tx.gas_limit = 1_000_000_000; }); diff --git a/bins/revme/src/cmd/bench/transfer.rs b/bins/revme/src/cmd/bench/transfer.rs index 9ffffdc150..d7a60da10a 100644 --- a/bins/revme/src/cmd/bench/transfer.rs +++ b/bins/revme/src/cmd/bench/transfer.rs @@ -1,8 +1,10 @@ -use database::BenchmarkDB; +use std::time::Instant; + +use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; use revm::{ bytecode::Bytecode, primitives::{TxKind, U256}, - transact_main, Context, + Context, ExecuteEvm, }; pub fn run() { @@ -10,15 +12,15 @@ pub fn run() { .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) .modify_tx_chained(|tx| { // Execution globals block hash/gas_limit/coinbase/timestamp.. - tx.caller = "0x0000000000000000000000000000000000000001" - .parse() - .unwrap(); + tx.caller = BENCH_CALLER; + tx.kind = TxKind::Call(BENCH_TARGET); tx.value = U256::from(10); - tx.kind = TxKind::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); }); - let _ = transact_main(&mut context); + let time = Instant::now(); + let _ = context.exec_previous(); + println!("First init: {:?}", time.elapsed()); + + let time = Instant::now(); + let _ = context.exec_previous(); + println!("Run: {:?}", time.elapsed()); } diff --git a/bins/revme/src/cmd/evmrunner.rs b/bins/revme/src/cmd/evmrunner.rs index a379596bb4..35f7650512 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::{inspect_main, inspector_context::InspectorContext, inspectors::TracerEip3155}; +use inspector::{exec::InspectEvm, inspectors::TracerEip3155}; use revm::{ bytecode::{Bytecode, BytecodeDecodeError}, primitives::{address, hex, Address, TxKind}, - transact_main, Context, Database, + transact_main, Context, Database, ExecuteEvm, }; use std::io::Error as IoError; use std::path::PathBuf; @@ -94,18 +94,16 @@ impl Cmd { let bench_options = microbench::Options::default().time(Duration::from_secs(3)); microbench::bench(&bench_options, "Run bytecode", || { - let _ = transact_main(&mut ctx).unwrap(); + let _ = ctx.exec_previous().unwrap(); }); return Ok(()); } let out = if self.trace { - inspect_main(&mut InspectorContext::new( - &mut ctx, - TracerEip3155::new(Box::new(std::io::stdout())), - )) - .map_err(|_| Errors::EVMError)? + let inspector = TracerEip3155::new(Box::new(std::io::stdout())); + ctx.inspect_previous(inspector) + .map_err(|_| Errors::EVMError)? } else { let out = transact_main(&mut ctx).map_err(|_| Errors::EVMError)?; println!("Result: {:#?}", out.result); diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index f02597a60a..c2d310e40c 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -4,9 +4,7 @@ use super::{ }; use database::State; use indicatif::{ProgressBar, ProgressDrawTarget}; -use inspector::{ - inspect_main_commit, inspector_context::InspectorContext, inspectors::TracerEip3155, -}; +use inspector::{exec::InspectCommitEvm, inspectors::TracerEip3155}; use revm::{ bytecode::Bytecode, context::{block::BlockEnv, cfg::CfgEnv, tx::TxEnv}, @@ -18,7 +16,7 @@ use revm::{ database_interface::EmptyDB, primitives::{keccak256, Bytes, TxKind, B256}, specification::{eip4844::TARGET_BLOB_GAS_PER_BLOCK_CANCUN, hardfork::SpecId}, - transact_main_commit, Context, + Context, ExecuteCommitEvm, }; use serde_json::json; use statetest_types::{SpecName, Test, TestSuite}; @@ -421,21 +419,20 @@ pub fn execute_test_suite( // Do the deed let (e, exec_result) = if trace { - let mut ctx = &mut InspectorContext::new( - Context::builder() - .with_block(&block) - .with_tx(&tx) - .with_cfg(&cfg) - .with_db(&mut state), - TracerEip3155::new(Box::new(stderr())).without_summary(), - ); + let mut ctx = Context::builder() + .with_block(&block) + .with_tx(&tx) + .with_cfg(&cfg) + .with_db(&mut state); let timer = Instant::now(); - let res = inspect_main_commit(&mut ctx); + let res = ctx.inspect_commit_previous( + TracerEip3155::new(Box::new(stderr())).without_summary(), + ); *elapsed.lock().unwrap() += timer.elapsed(); let spec = cfg.spec(); - let db = &mut ctx.inner.journaled_state.database; + let db = &mut ctx.journaled_state.database; // Dump state and traces if test failed let output = check_evm_execution( &test, @@ -452,7 +449,7 @@ pub fn execute_test_suite( (e, res) } else { let timer = Instant::now(); - let res = transact_main_commit(&mut ctx); + let res = ctx.exec_commit_previous(); *elapsed.lock().unwrap() += timer.elapsed(); let spec = cfg.spec(); @@ -494,24 +491,20 @@ pub fn execute_test_suite( println!("\nTraces:"); - let mut ctx = InspectorContext::new( - Context::builder() - .with_db(&mut state) - .with_block(&block) - .with_tx(&tx) - .with_cfg(&cfg), + let mut ctx = Context::builder() + .with_db(&mut state) + .with_block(&block) + .with_tx(&tx) + .with_cfg(&cfg); + + let _ = ctx.inspect_commit_previous( TracerEip3155::new(Box::new(stderr())).without_summary(), ); - let _ = inspect_main_commit(&mut ctx); - println!("\nExecution result: {exec_result:#?}"); println!("\nExpected exception: {:?}", test.expect_exception); println!("\nState before: {cache_state:#?}"); - println!( - "\nState after: {:#?}", - ctx.inner.journaled_state.database.cache - ); + println!("\nState after: {:#?}", ctx.journaled_state.database.cache); println!("\nSpecification: {:?}", cfg.spec); println!("\nTx: {tx:#?}"); println!("Block: {block:#?}"); diff --git a/crates/context/interface/src/host.rs b/crates/context/interface/src/host.rs index fce7a768f5..a377210e18 100644 --- a/crates/context/interface/src/host.rs +++ b/crates/context/interface/src/host.rs @@ -1,32 +1,87 @@ mod dummy; pub use crate::journaled_state::StateLoad; +use database_interface::Database; pub use dummy::DummyHost; -use crate::{journaled_state::AccountLoad, BlockGetter, CfgGetter, TransactionGetter}; -use auto_impl::auto_impl; -use primitives::{Address, Bytes, Log, B256, U256}; +use crate::{ + journaled_state::AccountLoad, Block, BlockGetter, CfgGetter, Journal, JournalGetter, + TransactionGetter, +}; +use primitives::{Address, Bytes, Log, B256, BLOCK_HASH_HISTORY, U256}; +use std::boxed::Box; /// EVM context host. -#[auto_impl(&mut, Box)] -pub trait Host: TransactionGetter + BlockGetter + CfgGetter { - /// Load an account code. - fn load_account_delegated(&mut self, address: Address) -> Option>; +pub trait Host: JournalGetter + TransactionGetter + BlockGetter + CfgGetter { + fn set_error( + &mut self, + error: <<::Journal as Journal>::Database as Database>::Error, + ); /// Gets the block hash of the given block `number`. - fn block_hash(&mut self, number: u64) -> Option; + fn block_hash(&mut self, requested_number: u64) -> Option { + let block_number = self.block().number(); + + let Some(diff) = block_number.checked_sub(requested_number) else { + return Some(B256::ZERO); + }; + + // blockhash should push zero if number is same as current block number. + if diff == 0 { + return Some(B256::ZERO); + } + + if diff <= BLOCK_HASH_HISTORY { + return self + .journal() + .db() + .block_hash(requested_number) + .map_err(|e| self.set_error(e)) + .ok(); + } + + Some(B256::ZERO) + } + + fn load_account_delegated(&mut self, address: Address) -> Option> { + self.journal() + .load_account_delegated(address) + .map_err(|e| self.set_error(e)) + .ok() + } /// Gets balance of `address` and if the account is cold. - fn balance(&mut self, address: Address) -> Option>; + fn balance(&mut self, address: Address) -> Option> { + self.journal() + .load_account(address) + .map(|acc| acc.map(|a| a.info.balance)) + .map_err(|e| self.set_error(e)) + .ok() + } /// Gets code of `address` and if the account is cold. - fn code(&mut self, address: Address) -> Option>; + fn code(&mut self, address: Address) -> Option> { + self.journal() + .code(address) + .map_err(|e| self.set_error(e)) + .ok() + } /// Gets code hash of `address` and if the account is cold. - fn code_hash(&mut self, address: Address) -> Option>; + fn code_hash(&mut self, address: Address) -> Option> { + self.journal() + .code_hash(address) + .map_err(|e| self.set_error(e)) + .ok() + } /// Gets storage value of `address` at `index` and if the account is cold. - fn sload(&mut self, address: Address, index: U256) -> Option>; + fn sload(&mut self, address: Address, index: U256) -> Option> { + self.journal() + .sload(address, index) + .map_err(|e| self.set_error(e)) + .ok() + } /// Sets storage value of account address at index. /// @@ -36,23 +91,57 @@ pub trait Host: TransactionGetter + BlockGetter + CfgGetter { address: Address, index: U256, value: U256, - ) -> Option>; + ) -> Option> { + self.journal() + .sstore(address, index, value) + .map_err(|e| self.set_error(e)) + .ok() + } /// Gets the transient storage value of `address` at `index`. - fn tload(&mut self, address: Address, index: U256) -> U256; + fn tload(&mut self, address: Address, index: U256) -> U256 { + self.journal().tload(address, index) + } /// Sets the transient storage value of `address` at `index`. - fn tstore(&mut self, address: Address, index: U256, value: U256); + fn tstore(&mut self, address: Address, index: U256, value: U256) { + self.journal().tstore(address, index, value) + } /// Emits a log owned by `address` with given `LogData`. - fn log(&mut self, log: Log); + fn log(&mut self, log: Log) { + self.journal().log(log); + } /// Marks `address` to be deleted, with funds transferred to `target`. fn selfdestruct( &mut self, address: Address, target: Address, - ) -> Option>; + ) -> Option> { + self.journal() + .selfdestruct(address, target) + .map_err(|e| self.set_error(e)) + .ok() + } +} + +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) + } } /// Represents the result of an `sstore` operation. diff --git a/crates/context/interface/src/host/dummy.rs b/crates/context/interface/src/host/dummy.rs index f803023fc1..3e897397ba 100644 --- a/crates/context/interface/src/host/dummy.rs +++ b/crates/context/interface/src/host/dummy.rs @@ -1,31 +1,30 @@ -use super::{Host, SStoreResult, SelfDestructResult}; -use crate::{Block, BlockGetter, Cfg, CfgGetter, Transaction, TransactionGetter}; -use primitives::{hash_map::Entry, Address, Bytes, HashMap, Log, B256, KECCAK_EMPTY, U256}; -use std::vec::Vec; - -use super::{AccountLoad, StateLoad}; +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 +pub struct DummyHost where BLOCK: Block, TX: Transaction, CFG: Cfg, + JOURNAL: Journal, { pub tx: TX, pub block: BLOCK, pub cfg: CFG, - pub storage: HashMap, - pub transient_storage: HashMap, - pub log: Vec, + pub journal: JOURNAL, } -impl DummyHost +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] @@ -34,21 +33,20 @@ where tx, block, cfg: CFG::default(), - storage: HashMap::default(), - transient_storage: HashMap::default(), - log: Vec::new(), + journal: JOURNAL::default(), } } /// Clears the storage and logs of the dummy host. #[inline] pub fn clear(&mut self) { - self.storage.clear(); - self.log.clear(); + self.journal.clear(); } } -impl BlockGetter for DummyHost { +impl BlockGetter + for DummyHost +{ type Block = BLOCK; fn block(&self) -> &Self::Block { @@ -56,7 +54,9 @@ impl BlockGetter for DummyHost TransactionGetter for DummyHost { +impl TransactionGetter + for DummyHost +{ type Transaction = TX; fn tx(&self) -> &Self::Transaction { @@ -64,7 +64,9 @@ impl TransactionGetter for DummyHost CfgGetter for DummyHost { +impl CfgGetter + for DummyHost +{ type Cfg = CFG; fn cfg(&self) -> &Self::Cfg { @@ -72,85 +74,42 @@ impl CfgGetter for DummyHost Host for DummyHost { - #[inline] - fn load_account_delegated(&mut self, _address: Address) -> Option> { - Some(StateLoad::new(AccountLoad::default(), false)) - } - - #[inline] - fn block_hash(&mut self, _number: u64) -> Option { - Some(B256::ZERO) - } - - #[inline] - fn balance(&mut self, _address: Address) -> Option> { - Some(Default::default()) - } - - #[inline] - fn code(&mut self, _address: Address) -> Option> { - Some(Default::default()) - } - - #[inline] - fn code_hash(&mut self, _address: Address) -> Option> { - Some(StateLoad::new(KECCAK_EMPTY, false)) - } +impl DatabaseGetter + for DummyHost +{ + type Database = ::Database; - #[inline] - fn sload(&mut self, _address: Address, index: U256) -> Option> { - match self.storage.entry(index) { - Entry::Occupied(entry) => Some(StateLoad::new(*entry.get(), false)), - Entry::Vacant(entry) => { - entry.insert(U256::ZERO); - Some(StateLoad::new(U256::ZERO, true)) - } - } + fn db(&mut self) -> &mut Self::Database { + self.journal.db() } - #[inline] - fn sstore( - &mut self, - _address: Address, - index: U256, - value: U256, - ) -> Option> { - let present = self.storage.insert(index, value); - Some(StateLoad { - data: SStoreResult { - original_value: U256::ZERO, - present_value: present.unwrap_or(U256::ZERO), - new_value: value, - }, - is_cold: present.is_none(), - }) + fn db_ref(&self) -> &Self::Database { + self.journal.db_ref() } +} - #[inline] - fn tload(&mut self, _address: Address, index: U256) -> U256 { - self.transient_storage - .get(&index) - .copied() - .unwrap_or_default() - } +impl JournalGetter + for DummyHost +{ + type Journal = JOURNAL; - #[inline] - fn tstore(&mut self, _address: Address, index: U256, value: U256) { - self.transient_storage.insert(index, value); + fn journal(&mut self) -> &mut Self::Journal { + &mut self.journal } - #[inline] - fn log(&mut self, log: Log) { - self.log.push(log) + fn journal_ref(&self) -> &Self::Journal { + &self.journal } +} +impl Host + for DummyHost +{ #[inline] - fn selfdestruct( + fn set_error( &mut self, - _address: Address, - _target: Address, - ) -> Option> { - Some(StateLoad::default()) + 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 9f6734845b..a80639cd49 100644 --- a/crates/context/interface/src/journaled_state.rs +++ b/crates/context/interface/src/journaled_state.rs @@ -1,6 +1,6 @@ use core::ops::{Deref, DerefMut}; use database_interface::{Database, DatabaseGetter}; -use primitives::{Address, HashSet, Log, B256, U256}; +use primitives::{Address, Bytes, HashSet, Log, B256, U256}; use specification::hardfork::SpecId; use state::{Account, Bytecode}; use std::boxed::Box; @@ -110,6 +110,16 @@ pub trait Journal { self.set_code_with_hash(address, code, hash); } + fn code( + &mut self, + address: Address, + ) -> Result, ::Error>; + + fn code_hash( + &mut self, + address: Address, + ) -> Result, ::Error>; + /// Called at the end of the transaction to clean all residue data from journal. fn clear(&mut self); diff --git a/crates/context/interface/src/result.rs b/crates/context/interface/src/result.rs index c8cd35703b..91e230cbdc 100644 --- a/crates/context/interface/src/result.rs +++ b/crates/context/interface/src/result.rs @@ -223,9 +223,9 @@ impl FromStringError for EVMError { } } -impl From for EVMError { +impl> From for EVMError { fn from(value: InvalidTransaction) -> Self { - Self::Transaction(value) + Self::Transaction(TXE::from(value)) } } diff --git a/crates/context/src/cfg.rs b/crates/context/src/cfg.rs index 8e54dcaa65..99f25f6499 100644 --- a/crates/context/src/cfg.rs +++ b/crates/context/src/cfg.rs @@ -77,12 +77,34 @@ pub struct CfgEnv = SpecId> { pub disable_base_fee: bool, } -impl CfgEnv { +impl> CfgEnv { pub fn with_chain_id(mut self, chain_id: u64) -> Self { self.chain_id = chain_id; self } + pub fn with_spec>(self, spec: OSPEC) -> CfgEnv { + CfgEnv { + chain_id: self.chain_id, + limit_contract_code_size: self.limit_contract_code_size, + spec, + disable_nonce_check: self.disable_nonce_check, + blob_target_and_max_count: self.blob_target_and_max_count, + #[cfg(feature = "memory_limit")] + memory_limit: self.memory_limit, + #[cfg(feature = "optional_balance_check")] + disable_balance_check: self.disable_balance_check, + #[cfg(feature = "optional_block_gas_limit")] + disable_block_gas_limit: self.disable_block_gas_limit, + #[cfg(feature = "optional_eip3607")] + disable_eip3607: self.disable_eip3607, + #[cfg(feature = "optional_gas_refund")] + disable_gas_refund: self.disable_gas_refund, + #[cfg(feature = "optional_no_base_fee")] + disable_base_fee: self.disable_base_fee, + } + } + /// Sets the blob target and max count over hardforks. pub fn set_blob_max_and_target_count(&mut self, mut vec: Vec<(SpecId, u8, u8)>) { vec.sort_by_key(|(id, _, _)| *id); diff --git a/crates/context/src/context.rs b/crates/context/src/context.rs index b48969cf6c..3c2917e5da 100644 --- a/crates/context/src/context.rs +++ b/crates/context/src/context.rs @@ -1,19 +1,13 @@ pub mod performant_access; use crate::{block::BlockEnv, cfg::CfgEnv, journaled_state::JournaledState, tx::TxEnv}; -use bytecode::{ - eip7702::{EIP7702_MAGIC_BYTES, EIP7702_MAGIC_HASH}, - EOF_MAGIC_BYTES, EOF_MAGIC_HASH, -}; use context_interface::{ - block::BlockSetter, journaled_state::AccountLoad, transaction::TransactionSetter, Block, - BlockGetter, Cfg, CfgGetter, DatabaseGetter, ErrorGetter, Journal, JournalGetter, Transaction, - TransactionGetter, + block::BlockSetter, transaction::TransactionSetter, Block, BlockGetter, Cfg, CfgGetter, + DatabaseGetter, ErrorGetter, Journal, JournalGetter, Transaction, TransactionGetter, }; use database_interface::{Database, EmptyDB}; use derive_where::derive_where; -use interpreter::{Host, SStoreResult, SelfDestructResult, StateLoad}; -use primitives::{Address, Bytes, Log, B256, BLOCK_HASH_HISTORY, U256}; +use interpreter::Host; use specification::hardfork::SpecId; /// EVM context contains data that EVM needs for execution. @@ -277,57 +271,6 @@ where { f(&mut self.journaled_state); } - - /// Returns account code bytes and if address is cold loaded. - /// - /// In case of EOF account it will return `EOF_MAGIC` (0xEF00) as code. - /// - // TODO : Move this in Journaled state - #[inline] - pub fn code(&mut self, address: Address) -> Result, ::Error> { - let a = self.journaled_state.load_account_code(address)?; - // SAFETY: Safe to unwrap as load_code will insert code if it is empty. - let code = a.info.code.as_ref().unwrap(); - - let code = if code.is_eof() { - EOF_MAGIC_BYTES.clone() - } else if code.is_eip7702() { - EIP7702_MAGIC_BYTES.clone() - } else { - code.original_bytes() - }; - - Ok(StateLoad::new(code, a.is_cold)) - } - - /// Gets code hash of address. - /// - /// In case of EOF account it will return `EOF_MAGIC_HASH` - /// (the hash of `0xEF00`). - /// - // TODO : Move this in Journaled state - #[inline] - pub fn code_hash( - &mut self, - address: Address, - ) -> Result, ::Error> { - let acc = self.journaled_state.load_account_code(address)?; - if acc.is_empty() { - return Ok(StateLoad::new(B256::ZERO, acc.is_cold)); - } - // SAFETY: Safe to unwrap as load_code will insert code if it is empty. - let code = acc.info.code.as_ref().unwrap(); - - let hash = if code.is_eof() { - EOF_MAGIC_HASH - } else if code.is_eip7702() { - EIP7702_MAGIC_HASH - } else { - acc.info.code_hash - }; - - Ok(StateLoad::new(hash, acc.is_cold)) - } } impl Host for Context @@ -338,95 +281,11 @@ where DB: Database, JOURNAL: Journal, { - fn block_hash(&mut self, requested_number: u64) -> Option { - let block_number = self.block().number(); - - let Some(diff) = block_number.checked_sub(requested_number) else { - return Some(B256::ZERO); - }; - - // blockhash should push zero if number is same as current block number. - if diff == 0 { - return Some(B256::ZERO); - } - - if diff <= BLOCK_HASH_HISTORY { - return self - .journaled_state - .db() - .block_hash(requested_number) - .map_err(|e| self.error = Err(e)) - .ok(); - } - - Some(B256::ZERO) - } - - fn load_account_delegated(&mut self, address: Address) -> Option> { - self.journaled_state - .load_account_delegated(address) - .map_err(|e| self.error = Err(e)) - .ok() - } - - fn balance(&mut self, address: Address) -> Option> { - self.journaled_state - .load_account(address) - .map_err(|e| self.error = Err(e)) - .map(|acc| acc.map(|a| a.info.balance)) - .ok() - } - - fn code(&mut self, address: Address) -> Option> { - self.code(address).map_err(|e| self.error = Err(e)).ok() - } - - fn code_hash(&mut self, address: Address) -> Option> { - self.code_hash(address) - .map_err(|e| self.error = Err(e)) - .ok() - } - - fn sload(&mut self, address: Address, index: U256) -> Option> { - self.journaled_state - .sload(address, index) - .map_err(|e| self.error = Err(e)) - .ok() - } - - fn sstore( - &mut self, - address: Address, - index: U256, - value: U256, - ) -> Option> { - self.journaled_state - .sstore(address, index, value) - .map_err(|e| self.error = Err(e)) - .ok() - } - - fn tload(&mut self, address: Address, index: U256) -> U256 { - self.journaled_state.tload(address, index) - } - - fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.journaled_state.tstore(address, index, value) - } - - fn log(&mut self, log: Log) { - self.journaled_state.log(log); - } - - fn selfdestruct( + fn set_error( &mut self, - address: Address, - target: Address, - ) -> Option> { - self.journaled_state - .selfdestruct(address, target) - .map_err(|e| self.error = Err(e)) - .ok() + error: <<::Journal as Journal>::Database as Database>::Error, + ) { + self.error = Err(error); } } diff --git a/crates/context/src/instructions.rs b/crates/context/src/instructions.rs new file mode 100644 index 0000000000..192dc42be1 --- /dev/null +++ b/crates/context/src/instructions.rs @@ -0,0 +1,81 @@ +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/journaled_state.rs b/crates/context/src/journaled_state.rs index 6c8ef8e7ef..408cfd62d9 100644 --- a/crates/context/src/journaled_state.rs +++ b/crates/context/src/journaled_state.rs @@ -1,9 +1,12 @@ -use bytecode::Bytecode; +use bytecode::{ + eip7702::{EIP7702_MAGIC_BYTES, EIP7702_MAGIC_HASH}, + Bytecode, EOF_MAGIC_BYTES, EOF_MAGIC_HASH, +}; use context_interface::journaled_state::{AccountLoad, Journal, JournalCheckpoint, TransferError}; use database_interface::Database; use interpreter::{SStoreResult, SelfDestructResult, StateLoad}; use primitives::{ - hash_map::Entry, Address, HashMap, HashSet, Log, B256, KECCAK_EMPTY, PRECOMPILE3, U256, + hash_map::Entry, Address, Bytes, HashMap, HashSet, Log, B256, KECCAK_EMPTY, PRECOMPILE3, U256, }; use specification::hardfork::{SpecId, SpecId::*}; use state::{Account, EvmState, EvmStorageSlot, TransientStorage}; @@ -144,6 +147,20 @@ impl Journal for JournaledState { self.spec = spec_id; } + fn code( + &mut self, + address: Address, + ) -> Result, ::Error> { + self.code(address) + } + + fn code_hash( + &mut self, + address: Address, + ) -> Result, ::Error> { + self.code_hash(address) + } + fn transfer( &mut self, from: &Address, @@ -403,6 +420,55 @@ impl JournaledState { Ok(None) } + /// Returns account code bytes and if address is cold loaded. + /// + /// In case of EOF account it will return `EOF_MAGIC` (0xEF00) as code. + /// + // TODO : Move this in Journaled state + #[inline] + pub fn code(&mut self, address: Address) -> Result, ::Error> { + let a = self.load_account_code(address)?; + // SAFETY: Safe to unwrap as load_code will insert code if it is empty. + let code = a.info.code.as_ref().unwrap(); + + let code = if code.is_eof() { + EOF_MAGIC_BYTES.clone() + } else if code.is_eip7702() { + EIP7702_MAGIC_BYTES.clone() + } else { + code.original_bytes() + }; + + Ok(StateLoad::new(code, a.is_cold)) + } + + /// Gets code hash of address. + /// + /// In case of EOF account it will return `EOF_MAGIC_HASH` + /// (the hash of `0xEF00`). + #[inline] + pub fn code_hash( + &mut self, + address: Address, + ) -> Result, ::Error> { + let acc = self.load_account_code(address)?; + if acc.is_empty() { + return Ok(StateLoad::new(B256::ZERO, acc.is_cold)); + } + // SAFETY: Safe to unwrap as load_code will insert code if it is empty. + let code = acc.info.code.as_ref().unwrap(); + + let hash = if code.is_eof() { + EOF_MAGIC_HASH + } else if code.is_eip7702() { + EIP7702_MAGIC_HASH + } else { + acc.info.code_hash + }; + + Ok(StateLoad::new(hash, acc.is_cold)) + } + /// Creates account or returns false if collision is detected. /// /// There are few steps done: diff --git a/crates/database/src/in_memory_db.rs b/crates/database/src/in_memory_db.rs index 447498a75f..1b3f55f98b 100644 --- a/crates/database/src/in_memory_db.rs +++ b/crates/database/src/in_memory_db.rs @@ -1,6 +1,6 @@ use core::convert::Infallible; use database_interface::{Database, DatabaseCommit, DatabaseRef, EmptyDB}; -use primitives::{hash_map::Entry, Address, HashMap, Log, B256, KECCAK_EMPTY, U256}; +use primitives::{address, hash_map::Entry, Address, HashMap, Log, B256, KECCAK_EMPTY, U256}; use state::{Account, AccountInfo, Bytecode}; use std::vec::Vec; @@ -402,11 +402,18 @@ impl BenchmarkDB { } } +/// BYTECODE address +pub const FFADDRESS: Address = address!("ffffffffffffffffffffffffffffffffffffffff"); +pub const BENCH_TARGET: Address = FFADDRESS; +/// CALLER address +pub const EEADDRESS: Address = address!("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); +pub const BENCH_CALLER: Address = EEADDRESS; + impl Database for BenchmarkDB { type Error = Infallible; /// Get basic account information. fn basic(&mut self, address: Address) -> Result, Self::Error> { - if address == Address::ZERO { + if address == FFADDRESS { return Ok(Some(AccountInfo { nonce: 1, balance: U256::from(10000000), @@ -414,7 +421,7 @@ impl Database for BenchmarkDB { code_hash: self.1, })); } - if address == Address::with_last_byte(1) { + if address == EEADDRESS { return Ok(Some(AccountInfo { nonce: 0, balance: U256::from(10000000), diff --git a/crates/handler/interface/src/precompile_provider.rs b/crates/handler/interface/src/precompile_provider.rs index e7a15f5885..45f347698f 100644 --- a/crates/handler/interface/src/precompile_provider.rs +++ b/crates/handler/interface/src/precompile_provider.rs @@ -4,7 +4,7 @@ use specification::hardfork::SpecId; use std::boxed::Box; #[auto_impl(&mut, Box)] -pub trait PrecompileProvider: Clone { +pub trait PrecompileProvider: Clone + Default { type Context; type Output; type Error; diff --git a/crates/handler/src/frame.rs b/crates/handler/src/frame.rs index 3d15e7ba21..e9a206359f 100644 --- a/crates/handler/src/frame.rs +++ b/crates/handler/src/frame.rs @@ -1,3 +1,5 @@ +use crate::instructions::{InstructionExecutor, InstructionExecutorGetter}; + use super::frame_data::*; use bytecode::{Eof, EOF_MAGIC_BYTES}; use context_interface::{ @@ -6,10 +8,12 @@ use context_interface::{ TransactionGetter, }; use core::{cell::RefCell, cmp::min}; -use handler_interface::{Frame, ItemOrResult, PrecompileProvider, PrecompileProviderGetter}; +use handler_interface::{ + Frame, FrameInitOrResult, ItemOrResult, PrecompileProvider, PrecompileProviderGetter, +}; use interpreter::{ gas, - interpreter::{EthInterpreter, ExtBytecode, InstructionProvider, InstructionProviderGetter}, + interpreter::{EthInterpreter, ExtBytecode}, interpreter_types::{LoopControl, ReturnData, RuntimeFlag}, return_ok, return_revert, CallInputs, CallOutcome, CallValue, CreateInputs, CreateOutcome, CreateScheme, EOFCreateInputs, EOFCreateKind, FrameInput, Gas, Host, InputsImpl, @@ -62,7 +66,7 @@ where } } -impl EthFrame, FRAMECTX> +impl EthFrame where CTX: EthFrameContext, ERROR: From> + From, @@ -72,8 +76,12 @@ where Error = ERROR, Output = InterpreterResult, >, - > + InstructionProviderGetter< - InstructionProvider: InstructionProvider, Host = CTX>, + > + InstructionExecutorGetter< + InstructionExecutor: InstructionExecutor< + InterpreterTypes = EthInterpreter, + CTX = CTX, + Output = InterpreterAction, + >, >, { /// Make call frame @@ -431,12 +439,12 @@ where } } -pub struct FrameContext { +pub struct FrameContext { pub precompiles: PRECOMPILE, pub instructions: INSTRUCTION, } -impl +impl FrameContext { pub fn new(precompiles: PRECOMPILE, instructions: INSTRUCTION) -> Self { @@ -447,7 +455,7 @@ impl } } -impl PrecompileProviderGetter +impl PrecompileProviderGetter for FrameContext { type PrecompileProvider = PRECOMPILES; @@ -457,12 +465,12 @@ impl Precomp } } -impl InstructionProviderGetter +impl InstructionExecutorGetter for FrameContext { - type InstructionProvider = INSTRUCTIONS; + type InstructionExecutor = INSTRUCTIONS; - fn instructions(&mut self) -> &mut Self::InstructionProvider { + fn executor(&mut self) -> &mut Self::InstructionExecutor { &mut self.instructions } } @@ -476,9 +484,14 @@ where Context = CTX, Error = ERROR, Output = InterpreterResult, + Spec = <::Cfg as Cfg>::Spec, + >, + > + InstructionExecutorGetter< + InstructionExecutor: InstructionExecutor< + InterpreterTypes = EthInterpreter<()>, + CTX = CTX, + Output = InterpreterAction, >, - > + InstructionProviderGetter< - InstructionProvider: InstructionProvider, Host = CTX>, >, { type Context = CTX; @@ -494,10 +507,10 @@ where ) -> Result, Self::Error> { let memory = Rc::new(RefCell::new(SharedMemory::new())); - // Load precompiles addresses as warm. - for address in frame_context.precompiles().warm_addresses() { - context.journal().warm_account(address); - } + frame_context.precompiles().set_spec(context.cfg().spec()); + context + .journal() + .warm_precompiles(frame_context.precompiles().warm_addresses().collect()); memory.borrow_mut().new_context(); Self::init_with_context(0, frame_input, memory, context, frame_context) @@ -531,13 +544,11 @@ where &mut self, context: &mut Self::Context, frame_context: &mut Self::FrameContext, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { let spec = context.cfg().spec().into(); // Run interpreter - let next_action = self - .interpreter - .run(frame_context.instructions().table(), context); + 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)), diff --git a/crates/handler/src/handler.rs b/crates/handler/src/handler.rs index fa8103c9b6..e783e03ee2 100644 --- a/crates/handler/src/handler.rs +++ b/crates/handler/src/handler.rs @@ -1,8 +1,11 @@ pub mod types; -pub use types::{EthContext, EthError, EthHandlerImpl}; +pub use types::{EthContext, EthError, MainnetHandler}; -use crate::{execution, post_execution, pre_execution, validation, FrameContext, FrameResult}; +use crate::{ + execution, instructions::InstructionExecutor, post_execution, pre_execution, validation, + FrameContext, FrameResult, +}; use context_interface::{ result::{HaltReasonTrait, ResultAndState}, Cfg, CfgGetter, ErrorGetter, Journal, JournalGetter, Transaction, TransactionGetter, @@ -10,14 +13,14 @@ use context_interface::{ use handler_interface::{ Frame, FrameInitOrResult, FrameOrResult, ItemOrResult, PrecompileProvider, }; -use interpreter::{interpreter::InstructionProvider, FrameInput, InitialAndFloorGas}; +use interpreter::{FrameInput, InitialAndFloorGas}; use std::{vec, vec::Vec}; pub trait EthHandler { type Context: EthContext; type Error: EthError; type Precompiles: PrecompileProvider; - type Instructions: InstructionProvider; + type Instructions: InstructionExecutor; // TODO `FrameResult` should be a generic trait. // TODO `FrameInit` should be a generic. type Frame: Frame< @@ -40,10 +43,20 @@ pub trait EthHandler { self.post_execution(context, 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 { + FrameContext::new(self.precompile(context), self.instructions(context)) + } /// Call all validation functions fn validate(&self, context: &mut Self::Context) -> Result { diff --git a/crates/handler/src/handler/types.rs b/crates/handler/src/handler/types.rs index 8ea7a68990..2c82e3f8a0 100644 --- a/crates/handler/src/handler/types.rs +++ b/crates/handler/src/handler/types.rs @@ -1,3 +1,5 @@ +use super::EthHandler; +use crate::{instructions::InstructionExecutor, EthPrecompileProvider, FrameContext, FrameResult}; use context::Context; use context_interface::{ result::{HaltReason, InvalidHeader, InvalidTransaction}, @@ -5,42 +7,18 @@ use context_interface::{ JournalDBError, JournalGetter, PerformantContextAccess, Transaction, TransactionGetter, }; use handler_interface::{Frame, PrecompileProvider}; -use interpreter::{ - interpreter::{EthInstructionProvider, EthInterpreter, InstructionProvider}, - FrameInput, Host, -}; +use interpreter::{interpreter::EthInterpreter, FrameInput, Host}; use precompile::PrecompileErrors; use primitives::Log; -use specification::hardfork::SpecId; use state::EvmState; use std::vec::Vec; -use crate::{EthPrecompileProvider, FrameContext, FrameResult}; - -use super::EthHandler; - -pub struct EthHandlerImpl { - pub precompiles: PRECOMPILES, - pub instructions: INSTRUCTIONS, +pub struct MainnetHandler { pub _phantom: core::marker::PhantomData<(CTX, ERROR, FRAME, PRECOMPILES, INSTRUCTIONS)>, } -impl - EthHandlerImpl -where - PRECOMPILES: PrecompileProvider, - INSTRUCTIONS: InstructionProvider, -{ - pub fn crete_frame_context(&self) -> FrameContext { - FrameContext { - precompiles: self.precompiles.clone(), - instructions: self.instructions.clone(), - } - } -} - impl EthHandler - for EthHandlerImpl + for MainnetHandler where CTX: EthContext, ERROR: EthError, @@ -49,7 +27,7 @@ where Error = ERROR, Spec = <::Cfg as Cfg>::Spec, >, - INSTRUCTIONS: InstructionProvider, + INSTRUCTIONS: InstructionExecutor, // TODO `FrameResult` should be a generic trait. // TODO `FrameInit` should be a generic. FRAME: Frame< @@ -66,29 +44,13 @@ where type Precompiles = PRECOMPILES; type Instructions = INSTRUCTIONS; type HaltReason = HaltReason; - - fn frame_context( - &mut self, - context: &mut Self::Context, - ) -> ::FrameContext { - self.precompiles.set_spec(context.cfg().spec()); - self.crete_frame_context() - } } -impl Default - for EthHandlerImpl< - CTX, - ERROR, - FRAME, - EthPrecompileProvider, - EthInstructionProvider, - > +impl Default + for MainnetHandler, INSTRUCTIONS> { fn default() -> Self { Self { - precompiles: EthPrecompileProvider::new(SpecId::LATEST), - instructions: EthInstructionProvider::new(), _phantom: core::marker::PhantomData, } } diff --git a/crates/handler/src/instructions.rs b/crates/handler/src/instructions.rs new file mode 100644 index 0000000000..e0de485952 --- /dev/null +++ b/crates/handler/src/instructions.rs @@ -0,0 +1,80 @@ +use interpreter::{ + table::{make_instruction_table, InstructionTable}, + Host, Interpreter, InterpreterAction, InterpreterTypes, +}; +use std::rc::Rc; + +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; +} + +pub struct EthInstructionExecutor { + instruction_table: Rc>, +} + +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: Rc::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.as_ref(), context) + } +} + +impl Default for EthInstructionExecutor +where + WIRE: InterpreterTypes, + HOST: Host, +{ + fn default() -> Self { + Self::new() + } +} diff --git a/crates/handler/src/lib.rs b/crates/handler/src/lib.rs index bb7aae1987..355c7c5800 100644 --- a/crates/handler/src/lib.rs +++ b/crates/handler/src/lib.rs @@ -11,6 +11,7 @@ pub mod execution; mod frame; mod frame_data; pub mod handler; +pub mod instructions; pub mod post_execution; pub mod pre_execution; mod precompile_provider; @@ -19,5 +20,5 @@ pub mod validation; // Public exports pub use frame::{return_create, return_eofcreate, EthFrame, EthFrameContext, FrameContext}; pub use frame_data::{FrameData, FrameResult}; -pub use handler::{EthContext, EthError, EthHandler, EthHandlerImpl}; +pub use handler::{EthContext, EthError, EthHandler, MainnetHandler}; pub use precompile_provider::EthPrecompileProvider; diff --git a/crates/handler/src/precompile_provider.rs b/crates/handler/src/precompile_provider.rs index a61ad97927..95169573b3 100644 --- a/crates/handler/src/precompile_provider.rs +++ b/crates/handler/src/precompile_provider.rs @@ -22,6 +22,12 @@ impl Clone for EthPrecompileProvider { } } +impl Default for EthPrecompileProvider { + fn default() -> Self { + Self::new(SpecId::LATEST) + } +} + impl EthPrecompileProvider { pub fn new(spec: SpecId) -> Self { Self { diff --git a/crates/inspector/src/exec.rs b/crates/inspector/src/exec.rs new file mode 100644 index 0000000000..589fa79680 --- /dev/null +++ b/crates/inspector/src/exec.rs @@ -0,0 +1,126 @@ +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<&mut Self, EthInterpreter> 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<&mut Self, EthInterpreter> 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/inspector.rs b/crates/inspector/src/inspector.rs index c93a52f699..2edc545876 100644 --- a/crates/inspector/src/inspector.rs +++ b/crates/inspector/src/inspector.rs @@ -1,26 +1,22 @@ use crate::{ - inspector_instruction::InspectorInstructionProvider, + inspector_instruction::InspectorInstructionExecutor, journal::{JournalExt, JournalExtGetter}, }; use auto_impl::auto_impl; use revm::{ - context_interface::{ - result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, ResultAndState}, - DatabaseGetter, Journal, - }, + context_interface::Journal, database_interface::Database, handler::{ - handler::{EthContext, EthError, EthHandler as EthHandlerNew, EthHandlerImpl}, - EthFrame, EthPrecompileProvider, FrameContext, FrameResult, + handler::{EthContext, EthError, EthHandler}, + EthFrame, FrameContext, FrameResult, }, handler_interface::{Frame, ItemOrResult, PrecompileProvider}, interpreter::{ - interpreter::EthInterpreter, CallInputs, CallOutcome, CreateInputs, CreateOutcome, + table::InstructionTable, CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, FrameInput, Interpreter, InterpreterTypes, }, primitives::{Address, Log, U256}, - specification::hardfork::SpecId, - Context, DatabaseCommit, + Context, }; /// EVM [Interpreter] callbacks. @@ -188,17 +184,19 @@ impl + JournalExt, } } -pub struct InspectorHandlerImpl { +pub struct InspectorHandlerImpl { pub handler: HANDLER, - _phantom: core::marker::PhantomData<(CTX, ERROR, FRAME, PRECOMPILES, INSTRUCTIONS)>, + pub base_instructions: InstructionTable, + _phantom: core::marker::PhantomData<(CTX, ERROR, FRAME, PRECOMPILES)>, } -impl - InspectorHandlerImpl +impl + InspectorHandlerImpl { - pub fn new(handler: HANDLER) -> Self { + pub fn new(handler: HANDLER, base_instructions: InstructionTable) -> Self { Self { handler, + base_instructions, _phantom: core::marker::PhantomData, } } @@ -220,8 +218,8 @@ impl FrameInterpreterGetter } } -impl EthHandlerNew - for InspectorHandlerImpl +impl EthHandler + for InspectorHandlerImpl where CTX: EthContext + InspectorCtx + JournalExtGetter, INTR: InterpreterTypes, @@ -233,37 +231,31 @@ where Error = ERROR, FrameResult = FrameResult, FrameInit = FrameInput, - FrameContext = FrameContext>, + FrameContext = FrameContext>, > + FrameInterpreterGetter, PRECOMPILES: PrecompileProvider, - HANDLER: EthHandlerNew, + HANDLER: EthHandler, { type Context = CTX; type Error = ERROR; type Frame = FRAME; type Precompiles = PRECOMPILES; - type Instructions = InspectorInstructionProvider; - type HaltReason = ::HaltReason; + type Instructions = InspectorInstructionExecutor; + type HaltReason = ::HaltReason; - fn frame_context( - &mut self, - context: &mut Self::Context, - ) -> ::FrameContext { - FrameContext::new( - self.handler.frame_context(context).precompiles, - InspectorInstructionProvider::new(), - ) + 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, + frame_context: &mut <::Frame as Frame>::FrameContext, + mut frame_input: <::Frame as Frame>::FrameInit, ) -> Result< ItemOrResult< - ::Frame, - <::Frame as Frame>::FrameResult, + ::Frame, + <::Frame as Frame>::FrameResult, >, Self::Error, > { @@ -290,12 +282,12 @@ where &self, frame: &Self::Frame, context: &mut Self::Context, - frame_context: &mut <::Frame as Frame>::FrameContext, - mut frame_input: <::Frame as Frame>::FrameInit, + frame_context: &mut <::Frame as Frame>::FrameContext, + mut frame_input: <::Frame as Frame>::FrameInit, ) -> Result< ItemOrResult< - ::Frame, - <::Frame as Frame>::FrameResult, + ::Frame, + <::Frame as Frame>::FrameResult, >, Self::Error, > { @@ -321,8 +313,8 @@ where &mut self, frame: &mut Self::Frame, context: &mut Self::Context, - frame_context: &mut <::Frame as Frame>::FrameContext, - mut result: <::Frame as Frame>::FrameResult, + frame_context: &mut <::Frame as Frame>::FrameContext, + mut result: <::Frame as Frame>::FrameResult, ) -> Result<(), Self::Error> { context.frame_end(&mut result); self.handler @@ -331,49 +323,10 @@ where fn frame_final_return( context: &mut Self::Context, - _frame_context: &mut <::Frame as Frame>::FrameContext, - result: &mut <::Frame as Frame>::FrameResult, + _frame_context: &mut <::Frame as Frame>::FrameContext, + result: &mut <::Frame as Frame>::FrameResult, ) -> Result<(), Self::Error> { context.frame_end(result); Ok(()) } } - -pub fn inspect_main< - DB: Database, - CTX: EthContext - + JournalExtGetter - + DatabaseGetter - + InspectorCtx, ->( - ctx: &mut CTX, -) -> Result, EVMError<::Error, InvalidTransaction>> { - InspectorHandlerImpl::< - _, - _, - EthFrame<_, _, _, _>, - _, - _, - InspectorInstructionProvider, - >::new(EthHandlerImpl { - precompiles: EthPrecompileProvider::new(SpecId::LATEST), - instructions: InspectorInstructionProvider::new(), - _phantom: core::marker::PhantomData, - }) - .run(ctx) -} - -pub fn inspect_main_commit< - DB: Database + DatabaseCommit, - CTX: EthContext - + JournalExtGetter - + DatabaseGetter - + InspectorCtx, ->( - ctx: &mut CTX, -) -> Result, EVMError<::Error, InvalidTransaction>> { - inspect_main(ctx).map(|res| { - ctx.db().commit(res.state); - res.result - }) -} diff --git a/crates/inspector/src/inspector_context.rs b/crates/inspector/src/inspector_context.rs index 74faa10abc..b9bba3c57a 100644 --- a/crates/inspector/src/inspector_context.rs +++ b/crates/inspector/src/inspector_context.rs @@ -1,16 +1,12 @@ use revm::{ context_interface::{ - block::BlockSetter, journaled_state::AccountLoad, transaction::TransactionSetter, - BlockGetter, CfgGetter, DatabaseGetter, ErrorGetter, JournalGetter, - PerformantContextAccess, TransactionGetter, + block::BlockSetter, transaction::TransactionSetter, BlockGetter, CfgGetter, DatabaseGetter, + ErrorGetter, JournalGetter, PerformantContextAccess, TransactionGetter, }, database_interface::Database, handler::{handler::EthContext, FrameResult}, - interpreter::{ - interpreter::EthInterpreter, FrameInput, Host, Interpreter, SStoreResult, - SelfDestructResult, StateLoad, - }, - primitives::{Address, Bytes, Log, B256, U256}, + interpreter::{interpreter::EthInterpreter, FrameInput, Host, Interpreter}, + primitives::{Address, Log, U256}, }; use std::vec::Vec; @@ -62,64 +58,6 @@ where } } -impl, DB, CTX> Host for InspectorContext -where - CTX: Host + DatabaseGetter, -{ - fn block_hash(&mut self, requested_number: u64) -> Option { - self.inner.block_hash(requested_number) - } - - fn load_account_delegated(&mut self, address: Address) -> Option> { - self.inner.load_account_delegated(address) - } - - fn balance(&mut self, address: Address) -> Option> { - self.inner.balance(address) - } - - fn code(&mut self, address: Address) -> Option> { - self.inner.code(address) - } - - fn code_hash(&mut self, address: Address) -> Option> { - self.inner.code_hash(address) - } - - fn sload(&mut self, address: Address, index: U256) -> Option> { - self.inner.sload(address, index) - } - - fn sstore( - &mut self, - address: Address, - index: U256, - value: U256, - ) -> Option> { - self.inner.sstore(address, index, value) - } - - fn tload(&mut self, address: Address, index: U256) -> U256 { - self.inner.tload(address, index) - } - - fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.inner.tstore(address, index, value) - } - - fn log(&mut self, log: Log) { - self.inner.log(log); - } - - fn selfdestruct( - &mut self, - address: Address, - target: Address, - ) -> Option> { - self.inner.selfdestruct(address, target) - } -} - impl InspectorCtx for InspectorContext where INSP: GetInspector, @@ -232,6 +170,16 @@ where } } +impl, DB: Database, 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, diff --git a/crates/inspector/src/inspector_instruction.rs b/crates/inspector/src/inspector_instruction.rs index a979d205ca..b852bd6714 100644 --- a/crates/inspector/src/inspector_instruction.rs +++ b/crates/inspector/src/inspector_instruction.rs @@ -2,12 +2,12 @@ use core::mem::MaybeUninit; use revm::{ bytecode::opcode::OpCode, context_interface::JournalGetter, + handler::instructions::InstructionExecutor, interpreter::{ instructions::host::{log, selfdestruct}, - interpreter::InstructionProvider, interpreter_types::{Jumps, LoopControl}, - table::{self, CustomInstruction}, - Host, Instruction, InstructionResult, Interpreter, InterpreterTypes, + table::{make_instruction_table, CustomInstruction, InstructionTable}, + Host, Instruction, InstructionResult, Interpreter, InterpreterAction, InterpreterTypes, }, JournalEntry, }; @@ -31,24 +31,7 @@ where type Host = HOST; fn exec(&self, interpreter: &mut Interpreter, host: &mut Self::Host) { - // SAFETY: As the PC was already incremented we need to subtract 1 to preserve the - // old Inspector behavior. - interpreter.bytecode.relative_jump(-1); - - // Call step. - host.step(interpreter); - if interpreter.control.instruction_result() != InstructionResult::Continue { - return; - } - - // Reset PC to previous value. - interpreter.bytecode.relative_jump(1); - - // Execute instruction. (self.instruction)(interpreter, host); - - // Call step_end. - host.step_end(interpreter); } fn from_base(instruction: Instruction) -> Self { @@ -56,11 +39,11 @@ where } } -pub struct InspectorInstructionProvider { +pub struct InspectorInstructionExecutor { instruction_table: Rc<[InspectorInstruction; 256]>, } -impl Clone for InspectorInstructionProvider +impl Clone for InspectorInstructionExecutor where WIRE: InterpreterTypes, { @@ -71,19 +54,18 @@ where } } -impl InspectorInstructionProvider +impl InspectorInstructionExecutor where WIRE: InterpreterTypes, HOST: Host + JournalExtGetter + JournalGetter + InspectorCtx, { - pub fn new() -> Self { - let main_table = table::make_instruction_table::(); + 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 { - instruction: main_table[i], + let function: InspectorInstruction = InspectorInstruction { + instruction: base_table[i], }; *element = MaybeUninit::new(function); } @@ -166,25 +148,55 @@ where } } -impl InstructionProvider for InspectorInstructionProvider +impl InstructionExecutor for InspectorInstructionExecutor where - WIRE: InterpreterTypes, - HOST: Host + JournalExtGetter + JournalGetter + InspectorCtx, + IT: InterpreterTypes, + CTX: Host + JournalExtGetter + JournalGetter + InspectorCtx, { - type WIRE = WIRE; - type Host = HOST; + 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); + } - fn table(&mut self) -> &[impl CustomInstruction; 256] { - self.instruction_table.as_ref() + interpreter.take_next_action() } } -impl Default for InspectorInstructionProvider +impl Default for InspectorInstructionExecutor where WIRE: InterpreterTypes, HOST: Host + JournalExtGetter + JournalGetter + InspectorCtx, { fn default() -> Self { - Self::new() + Self::new(make_instruction_table()) } } diff --git a/crates/inspector/src/journal.rs b/crates/inspector/src/journal.rs index f15e41a16c..7d80678fd6 100644 --- a/crates/inspector/src/journal.rs +++ b/crates/inspector/src/journal.rs @@ -4,6 +4,7 @@ use revm::{ JournalEntry, }; +#[auto_impl(&mut, Box)] pub trait JournalExt { fn logs(&self) -> &[Log]; diff --git a/crates/inspector/src/lib.rs b/crates/inspector/src/lib.rs index d4a4896b4b..22b32b10fb 100644 --- a/crates/inspector/src/lib.rs +++ b/crates/inspector/src/lib.rs @@ -7,6 +7,7 @@ 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; diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index c3f94a33c6..0da9e40aa7 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -294,6 +294,7 @@ pub fn selfdestruct( if !interpreter.runtime_flag.spec_id().is_enabled_in(LONDON) && !res.previously_destroyed { interpreter.control.gas().record_refund(gas::SELFDESTRUCT) } + gas!( interpreter, gas::selfdestruct_cost(interpreter.runtime_flag.spec_id(), res) diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index ca7968c23c..f1016f7ab7 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -10,8 +10,9 @@ mod stack; mod subroutine_stack; use crate::{ - interpreter_types::*, table::CustomInstruction, Gas, Host, Instruction, InstructionResult, - InterpreterAction, + interpreter_types::*, + table::{CustomInstruction, InstructionTable}, + Gas, Host, Instruction, InstructionResult, InterpreterAction, }; use core::cell::RefCell; pub use ext_bytecode::ExtBytecode; @@ -88,60 +89,6 @@ impl InterpreterTypes for EthInterpreter { type Extend = EXT; } -pub trait InstructionProvider: Clone { - type WIRE: InterpreterTypes; - type Host; - - fn table(&mut self) -> &[impl CustomInstruction; 256]; -} - -pub trait InstructionProviderGetter { - type InstructionProvider: InstructionProvider; - - fn instructions(&mut self) -> &mut Self::InstructionProvider; -} - -pub struct EthInstructionProvider { - instruction_table: Rc<[Instruction; 256]>, -} - -impl Clone for EthInstructionProvider -where - WIRE: InterpreterTypes, -{ - fn clone(&self) -> Self { - Self { - instruction_table: self.instruction_table.clone(), - } - } -} - -impl EthInstructionProvider -where - WIRE: InterpreterTypes, - HOST: Host, -{ - pub fn new() -> Self { - Self { - instruction_table: Rc::new(crate::table::make_instruction_table::()), - } - } -} - -impl InstructionProvider for EthInstructionProvider -where - WIRE: InterpreterTypes, - HOST: Host, -{ - type WIRE = WIRE; - type Host = HOST; - - /// Returns the instruction table. - fn table(&mut self) -> &[impl CustomInstruction; 256] { - self.instruction_table.as_ref() - } -} - impl CustomInstruction for Instruction { type Wire = IW; type Host = H; @@ -162,10 +109,11 @@ impl Interpreter { /// /// Internally it will increment instruction pointer by one. #[inline] - pub(crate) fn step(&mut self, instruction_table: &[FN; 256], host: &mut H) - where - FN: CustomInstruction, - { + pub(crate) fn step( + &mut self, + instruction_table: &[Instruction; 256], + host: &mut H, + ) { // Get current opcode. let opcode = self.bytecode.opcode(); @@ -178,23 +126,13 @@ impl Interpreter { instruction_table[opcode as usize].exec(self, host) } - /// Executes the interpreter until it returns or stops. - pub fn run( - &mut self, - instruction_table: &[FN; 256], - host: &mut H, - ) -> InterpreterAction - where - FN: CustomInstruction, - { + #[inline] + pub fn reset_control(&mut self) { self.control .set_next_action(InterpreterAction::None, InstructionResult::Continue); + } - // Main loop - while self.control.instruction_result().is_continue() { - self.step(instruction_table, host); - } - + pub fn take_next_action(&mut self) -> InterpreterAction { // Return next action if it is some. let action = self.control.take_next_action(); if action.is_some() { @@ -210,6 +148,22 @@ impl Interpreter { }, } } + + /// Executes the interpreter until it returns or stops. + pub fn run_plain( + &mut self, + instruction_table: &InstructionTable, + host: &mut H, + ) -> InterpreterAction { + self.reset_control(); + + // Main loop + while self.control.instruction_result().is_continue() { + self.step(instruction_table, host); + } + + self.take_next_action() + } } /// The result of an interpreter operation. @@ -253,16 +207,6 @@ impl InterpreterResult { } } -impl Default for EthInstructionProvider -where - WIRE: InterpreterTypes, - HOST: Host, -{ - fn default() -> Self { - Self::new() - } -} - #[cfg(test)] mod tests { // use super::*; diff --git a/crates/interpreter/src/table.rs b/crates/interpreter/src/table.rs index ae311fea8c..3e4f4833be 100644 --- a/crates/interpreter/src/table.rs +++ b/crates/interpreter/src/table.rs @@ -115,15 +115,13 @@ where #[inline] pub const fn make_instruction_table( ) -> InstructionTable { - const { - let mut tables: InstructionTable = [control::unknown; 256]; - let mut i = 0; - while i < 256 { - tables[i] = instruction::(i as u8); - i += 1; - } - tables + let mut table: InstructionTable = [control::unknown; 256]; + let mut i = 0; + while i < 256 { + table[i] = instruction::(i as u8); + i += 1; } + table } /// Make boxed instruction table that calls `f` closure for every instruction. diff --git a/crates/optimism/Cargo.toml b/crates/optimism/Cargo.toml index de26584f1b..78e0250458 100644 --- a/crates/optimism/Cargo.toml +++ b/crates/optimism/Cargo.toml @@ -25,7 +25,9 @@ all = "warn" # revm revm.workspace = true precompile = { workspace = true, features = ["secp256r1"] } -#inspector.workspace = true +inspector.workspace = true +derive_more.workspace = true +auto_impl.workspace = true # static precompile sets. once_cell = { version = "1.19", default-features = false, features = ["alloc"] } diff --git a/crates/optimism/src/api.rs b/crates/optimism/src/api.rs new file mode 100644 index 0000000000..f129f18c03 --- /dev/null +++ b/crates/optimism/src/api.rs @@ -0,0 +1,4 @@ +pub mod exec; +pub mod exec_op; +pub mod inspect; +pub mod into_optimism; diff --git a/crates/optimism/src/api/exec.rs b/crates/optimism/src/api/exec.rs new file mode 100644 index 0000000000..3084bd1bf8 --- /dev/null +++ b/crates/optimism/src/api/exec.rs @@ -0,0 +1,27 @@ +use revm::context_interface::{ + block::BlockSetter, transaction::TransactionSetter, TransactionGetter, +}; + +/// Execute EVM transactions. +pub trait ExecuteOpEvm: BlockSetter + TransactionSetter { + type Output; + + fn op_exec_previous(&mut self) -> Self::Output; + + fn op_exec(&mut self, tx: ::Transaction) -> Self::Output { + self.set_tx(tx); + self.op_exec_previous() + } +} + +/// Execute EVM transactions and commit to the state. +pub trait ExecuteCommitOpEvm: ExecuteOpEvm { + type CommitOutput; + + fn op_exec_commit_previous(&mut self) -> Self::CommitOutput; + + fn op_exec_commit(&mut self, tx: Self::Transaction) -> Self::CommitOutput { + self.set_tx(tx); + self.op_exec_commit_previous() + } +} diff --git a/crates/optimism/src/api/exec_op.rs b/crates/optimism/src/api/exec_op.rs new file mode 100644 index 0000000000..8c7a187c2e --- /dev/null +++ b/crates/optimism/src/api/exec_op.rs @@ -0,0 +1,51 @@ +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, OpSpec, OpTransactionError, OptimismHaltReason, +}; + +/// 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 new file mode 100644 index 0000000000..3e0546a7da --- /dev/null +++ b/crates/optimism/src/api/inspect.rs @@ -0,0 +1,142 @@ +use crate::{ + context::OpContext, + handler::{precompiles::OpPrecompileProvider, OpHandler}, + transaction::{abstraction::OpTxGetter, OpTxTrait}, + L1BlockInfoGetter, OpSpec, OpTransactionError, OptimismHaltReason, +}; +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<&mut Self, EthInterpreter> 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<&mut Self, EthInterpreter> 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< + ExecutionResult, + 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 new file mode 100644 index 0000000000..20bcce4bcf --- /dev/null +++ b/crates/optimism/src/api/into_optimism.rs @@ -0,0 +1,72 @@ +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::default().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 new file mode 100644 index 0000000000..9fc5250db8 --- /dev/null +++ b/crates/optimism/src/context.rs @@ -0,0 +1,297 @@ +use crate::{ + api::exec_op::transact_op, + transaction::{abstraction::OpTxGetter, OpTxTrait}, + L1BlockInfo, L1BlockInfoGetter, OpSpec, OpSpecId, OpTransaction, OpTransactionError, + OptimismHaltReason, +}; +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::default().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::default().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< + ResultAndState, + 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/fast_lz.rs b/crates/optimism/src/fast_lz.rs index 358dfd5765..af03c9c6e6 100644 --- a/crates/optimism/src/fast_lz.rs +++ b/crates/optimism/src/fast_lz.rs @@ -106,90 +106,89 @@ fn u24(input: &[u8], idx: u32) -> u32 { + (u32::from(input[(idx + 2) as usize]) << 16) } -// #[cfg(test)] -// mod tests { -// use crate::wiring::OptimismEvmWiring; -// use crate::OpTransaction; - -// use super::*; -// use alloy_sol_types::sol; -// use alloy_sol_types::SolCall; -// use database::BenchmarkDB; -// use revm::{ -// bytecode::Bytecode, -// primitives::{address, bytes, Bytes, TxKind, U256}, -// Evm, -// }; -// use std::vec::Vec; - -// use rstest::rstest; - -// #[rstest] -// #[case::empty(&[], 0)] -// #[case::thousand_zeros(&[0; 1000], 21)] -// #[case::thousand_forty_twos(&[42; 1000], 21)] -// #[case::short_hex(&bytes!("FACADE"), 4)] -// #[case::sample_contract_call(&bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e"), 202)] -// #[case::base_0x5dadeb52979f29fc7a7494c43fdabc5be1d8ff404f3aafe93d729fa8e5d00769(&bytes!("b9047c02f904788221050883036ee48409c6c87383037f6f941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b90404d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000f6476f90447748c19248ccaa31e6b8bfda4eb9d830f5f47df7f0998f7c2123d9e6137761b75d3184efb0f788e3b14516000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000f38e53bd45c8225a7c94b513beadaa7afe5d222d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d656852577a743347745961776343347564745657557233454c587261436746434259416b66507331696f48610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd0d83d9e840f8e27d5c2e365fd365ff1c05b2480000000000000000000000000000000000000000000000000000000000000ce40000000000000000000000000000000000000000000000000000000000000041e4480d358dbae20880960a0a464d63b06565a0c9f9b1b37aa94b522247b23ce149c81359bf4239d1a879eeb41047ec710c15f5c0f67453da59a383e6abd742971c00000000000000000000000000000000000000000000000000000000000000c001a0b57f0ff8516ea29cb26a44ac5055a5420847d1e16a8e7b03b70f0c02291ff2d5a00ad3771e5f39ccacfff0faa8c5d25ef7a1c179f79e66e828ffddcb994c8b512e"), 471)] -// fn test_flz_compress_len(#[case] input: &[u8], #[case] expected: u32) { -// assert_eq!(flz_compress_len(input), expected); -// } - -// #[test] -// fn test_flz_compress_len_no_repeats() { -// let mut input = Vec::new(); -// let mut len = 0; - -// for i in 0..256 { -// input.push(i as u8); -// let prev_len = len; -// len = flz_compress_len(&input); -// assert!(len > prev_len); -// } -// } - -// #[rstest] -// #[case::short_hex(bytes!("FACADE"))] -// #[case::sample_contract_call(bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e"))] -// #[case::base_0x5dadeb52979f29fc7a7494c43fdabc5be1d8ff404f3aafe93d729fa8e5d00769(bytes!("b9047c02f904788221050883036ee48409c6c87383037f6f941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b90404d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000f6476f90447748c19248ccaa31e6b8bfda4eb9d830f5f47df7f0998f7c2123d9e6137761b75d3184efb0f788e3b14516000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000f38e53bd45c8225a7c94b513beadaa7afe5d222d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d656852577a743347745961776343347564745657557233454c587261436746434259416b66507331696f48610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd0d83d9e840f8e27d5c2e365fd365ff1c05b2480000000000000000000000000000000000000000000000000000000000000ce40000000000000000000000000000000000000000000000000000000000000041e4480d358dbae20880960a0a464d63b06565a0c9f9b1b37aa94b522247b23ce149c81359bf4239d1a879eeb41047ec710c15f5c0f67453da59a383e6abd742971c00000000000000000000000000000000000000000000000000000000000000c001a0b57f0ff8516ea29cb26a44ac5055a5420847d1e16a8e7b03b70f0c02291ff2d5a00ad3771e5f39ccacfff0faa8c5d25ef7a1c179f79e66e828ffddcb994c8b512e"))] -// #[case::base_0xfaada76a2dac09fc17f5a28d066aaabefc6d82ef6589b211ed8c9f766b070721(bytes!("b87602f873822105528304320f8409cfe5c98252089480c67432656d59144ceff962e8faf8926599bcf888011dfe52d06b633f80c001a08632f069f837aea7a28bab0affee14dda116956bd5a850a355c045d25afedd17a0084b8f273efffe17ece527116053e5781a4915ff89ab9c379f1e62c25b697687"))] -// #[case::base_0x112864e9b971af6a1dac840018833c5a5a659acc187cfdaba919ad1da013678d(bytes!("b8b302f8b0822105308304320f8409cfe5c9827496944ed4e862860bed51a9570b96d89af5e1b0efefed80b844095ea7b3000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba300000000000000000000000000000000000000000000015e10fb0973595fffffc001a02020e39f07917c1a852feb131c857e12478c7e88a20772b91a8bf5cee38c5aeea06055981727f9aaa3471c1af800555b35a77916c154be3f9d02ad1a63029455ab"))] -// #[case::base_0x6905051352691641888d0c427fb137c5b95afb5870d5169ff014eff1d0952195(bytes!("b87202f86f8221058303dc6c8310db1f84068fa8d7838954409436af2ff952a7355c8045fcd5e88bc9f6c8257f7b8080c001a0b89e7ff3d7694109e73e7f4244e032581670313c36e48e485c9c94b853bd81d2a038ffaf8f10859ce21d1f7f7046c3d08027fb8aa15b69038f6102be97aaa1179a"))] -// #[case::base_0x6a38e9a26d7202a2268de69d2d47531c1a9829867579a483fb48d78e9e0b080d(bytes!("b9049b02f904978221058201618506fc23ac008506fc23ac008306ddd0943fc91a3afd70395cd496c647d5a6cc9d4b2b7fad80b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006641d67b00000000000000000000000000000000000000000000000000000000000000030a000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000088487bd8c3222d64d1d0b3fa7098dcf9d94d79e000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006669635d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad000000000000000000000000000000000000000000000000000000006641d78900000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000418661369ca026f92ff88347bd0e3625a7b5ed65071b366368c68ad7c55aed136c18659b34f9246e30a784227a53dd374fbd3d2124696808c678cd987c4e954a681b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000549e5c020c764dbfffff00000000000000000000000000000000000000000000000002e5a629c093a2b600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002b088487bd8c3222d64d1d0b3fa7098dcf9d94d79e0027104200000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000c001a014a3acef764ff6d3bb9bd81e420bfa94171a5734ab997dfbc9b41b653ce018a4a01ff5fccb01ef5c60ba3aef67d4e74f3f47312dd78bfbbff9e5090fbf2d3d62bb"))] -// fn test_flz_native_evm_parity(#[case] input: Bytes) { -// // This bytecode and ABI is for a contract, which wraps the LibZip library for easier fuzz testing. -// // The source of this contract is here: https://github.com/danyalprout/fastlz/blob/main/src/FastLz.sol#L6-L10 -// sol! { -// interface FastLz { -// function fastLz(bytes input) external view returns (uint256); -// } -// } - -// let contract_bytecode = Bytecode::new_raw(bytes!("608060405234801561001057600080fd5b506004361061002b5760003560e01c8063920a769114610030575b600080fd5b61004361003e366004610374565b610055565b60405190815260200160405180910390f35b600061006082610067565b5192915050565b60606101e0565b818153600101919050565b600082840393505b838110156100a25782810151828201511860001a1590930292600101610081565b9392505050565b825b602082106100d75782516100c0601f8361006e565b5260209290920191601f19909101906021016100ab565b81156100a25782516100ec600184038361006e565b520160010192915050565b60006001830392505b61010782106101385761012a8360ff1661012560fd6101258760081c60e0018961006e565b61006e565b935061010682039150610100565b600782106101655761015e8360ff16610125600785036101258760081c60e0018961006e565b90506100a2565b61017e8360ff166101258560081c8560051b018761006e565b949350505050565b80516101d890838303906101bc90600081901a600182901a60081b1760029190911a60101b17639e3779b90260131c611fff1690565b8060021b6040510182815160e01c1860e01b8151188152505050565b600101919050565b5060405161800038823961800081016020830180600d8551820103826002015b81811015610313576000805b50508051604051600082901a600183901a60081b1760029290921a60101b91909117639e3779b9810260111c617ffc16909101805160e081811c878603811890911b9091189091528401908183039084841061026857506102a3565b600184019350611fff821161029d578251600081901a600182901a60081b1760029190911a60101b17810361029d57506102a3565b5061020c565b8383106102b1575050610313565b600183039250858311156102cf576102cc87878886036100a9565b96505b6102e3600985016003850160038501610079565b91506102f08782846100f7565b9650506103088461030386848601610186565b610186565b915050809350610200565b5050617fe061032884848589518601036100a9565b03925050506020820180820383525b81811161034e57617fe08101518152602001610337565b5060008152602001604052919050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561038657600080fd5b813567ffffffffffffffff8082111561039e57600080fd5b818401915084601f8301126103b257600080fd5b8135818111156103c4576103c461035e565b604051601f8201601f19908116603f011681019083821181831017156103ec576103ec61035e565b8160405282815287602084870101111561040557600080fd5b82602086016020830137600092810160200192909252509594505050505056fea264697066735822122000646b2953fc4a6f501bd0456ac52203089443937719e16b3190b7979c39511264736f6c63430008190033")); - -// let native_val = flz_compress_len(&input); - -// let mut evm = Evm::>::builder() -// .with_db(BenchmarkDB::new_bytecode(contract_bytecode.clone())) -// .with_default_ext_context() -// .modify_tx_env(|tx| { -// let OpTransaction::Base { tx, enveloped_tx } = tx else { -// panic!("Default is base tx"); -// }; -// tx.caller = address!("1000000000000000000000000000000000000000"); -// tx.kind = TxKind::Call(address!("0000000000000000000000000000000000000000")); -// tx.data = FastLz::fastLzCall::new((input,)).abi_encode().into(); -// tx.gas_limit = 300_000; -// *enveloped_tx = Some(Bytes::default()); -// }) -// .build(); - -// let result_and_state = evm.transact().unwrap(); -// let output = result_and_state.result.output().unwrap(); -// let evm_val = FastLz::fastLzCall::abi_decode_returns(output, true) -// .unwrap() -// ._0; - -// assert_eq!(U256::from(native_val), evm_val); -// } -// } +#[cfg(test)] +mod tests { + use crate::context::OpContext; + + use super::*; + use alloy_sol_types::sol; + use alloy_sol_types::SolCall; + use database::BenchmarkDB; + use database::EEADDRESS; + use revm::{ + bytecode::Bytecode, + primitives::{bytes, Bytes, TxKind, U256}, + }; + use std::vec::Vec; + + use rstest::rstest; + + #[rstest] + #[case::empty(&[], 0)] + #[case::thousand_zeros(&[0; 1000], 21)] + #[case::thousand_forty_twos(&[42; 1000], 21)] + #[case::short_hex(&bytes!("FACADE"), 4)] + #[case::sample_contract_call(&bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e"), 202)] + #[case::base_0x5dadeb52979f29fc7a7494c43fdabc5be1d8ff404f3aafe93d729fa8e5d00769(&bytes!("b9047c02f904788221050883036ee48409c6c87383037f6f941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b90404d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000f6476f90447748c19248ccaa31e6b8bfda4eb9d830f5f47df7f0998f7c2123d9e6137761b75d3184efb0f788e3b14516000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000f38e53bd45c8225a7c94b513beadaa7afe5d222d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d656852577a743347745961776343347564745657557233454c587261436746434259416b66507331696f48610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd0d83d9e840f8e27d5c2e365fd365ff1c05b2480000000000000000000000000000000000000000000000000000000000000ce40000000000000000000000000000000000000000000000000000000000000041e4480d358dbae20880960a0a464d63b06565a0c9f9b1b37aa94b522247b23ce149c81359bf4239d1a879eeb41047ec710c15f5c0f67453da59a383e6abd742971c00000000000000000000000000000000000000000000000000000000000000c001a0b57f0ff8516ea29cb26a44ac5055a5420847d1e16a8e7b03b70f0c02291ff2d5a00ad3771e5f39ccacfff0faa8c5d25ef7a1c179f79e66e828ffddcb994c8b512e"), 471)] + fn test_flz_compress_len(#[case] input: &[u8], #[case] expected: u32) { + assert_eq!(flz_compress_len(input), expected); + } + + #[test] + fn test_flz_compress_len_no_repeats() { + let mut input = Vec::new(); + let mut len = 0; + + for i in 0..256 { + input.push(i as u8); + let prev_len = len; + len = flz_compress_len(&input); + assert!(len > prev_len); + } + } + + #[rstest] + #[case::short_hex(bytes!("FACADE"))] + #[case::sample_contract_call(bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e"))] + #[case::base_0x5dadeb52979f29fc7a7494c43fdabc5be1d8ff404f3aafe93d729fa8e5d00769(bytes!("b9047c02f904788221050883036ee48409c6c87383037f6f941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b90404d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000f6476f90447748c19248ccaa31e6b8bfda4eb9d830f5f47df7f0998f7c2123d9e6137761b75d3184efb0f788e3b14516000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000f38e53bd45c8225a7c94b513beadaa7afe5d222d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d656852577a743347745961776343347564745657557233454c587261436746434259416b66507331696f48610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd0d83d9e840f8e27d5c2e365fd365ff1c05b2480000000000000000000000000000000000000000000000000000000000000ce40000000000000000000000000000000000000000000000000000000000000041e4480d358dbae20880960a0a464d63b06565a0c9f9b1b37aa94b522247b23ce149c81359bf4239d1a879eeb41047ec710c15f5c0f67453da59a383e6abd742971c00000000000000000000000000000000000000000000000000000000000000c001a0b57f0ff8516ea29cb26a44ac5055a5420847d1e16a8e7b03b70f0c02291ff2d5a00ad3771e5f39ccacfff0faa8c5d25ef7a1c179f79e66e828ffddcb994c8b512e"))] + #[case::base_0xfaada76a2dac09fc17f5a28d066aaabefc6d82ef6589b211ed8c9f766b070721(bytes!("b87602f873822105528304320f8409cfe5c98252089480c67432656d59144ceff962e8faf8926599bcf888011dfe52d06b633f80c001a08632f069f837aea7a28bab0affee14dda116956bd5a850a355c045d25afedd17a0084b8f273efffe17ece527116053e5781a4915ff89ab9c379f1e62c25b697687"))] + #[case::base_0x112864e9b971af6a1dac840018833c5a5a659acc187cfdaba919ad1da013678d(bytes!("b8b302f8b0822105308304320f8409cfe5c9827496944ed4e862860bed51a9570b96d89af5e1b0efefed80b844095ea7b3000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba300000000000000000000000000000000000000000000015e10fb0973595fffffc001a02020e39f07917c1a852feb131c857e12478c7e88a20772b91a8bf5cee38c5aeea06055981727f9aaa3471c1af800555b35a77916c154be3f9d02ad1a63029455ab"))] + #[case::base_0x6905051352691641888d0c427fb137c5b95afb5870d5169ff014eff1d0952195(bytes!("b87202f86f8221058303dc6c8310db1f84068fa8d7838954409436af2ff952a7355c8045fcd5e88bc9f6c8257f7b8080c001a0b89e7ff3d7694109e73e7f4244e032581670313c36e48e485c9c94b853bd81d2a038ffaf8f10859ce21d1f7f7046c3d08027fb8aa15b69038f6102be97aaa1179a"))] + #[case::base_0x6a38e9a26d7202a2268de69d2d47531c1a9829867579a483fb48d78e9e0b080d(bytes!("b9049b02f904978221058201618506fc23ac008506fc23ac008306ddd0943fc91a3afd70395cd496c647d5a6cc9d4b2b7fad80b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006641d67b00000000000000000000000000000000000000000000000000000000000000030a000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000088487bd8c3222d64d1d0b3fa7098dcf9d94d79e000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006669635d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad000000000000000000000000000000000000000000000000000000006641d78900000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000418661369ca026f92ff88347bd0e3625a7b5ed65071b366368c68ad7c55aed136c18659b34f9246e30a784227a53dd374fbd3d2124696808c678cd987c4e954a681b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000549e5c020c764dbfffff00000000000000000000000000000000000000000000000002e5a629c093a2b600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002b088487bd8c3222d64d1d0b3fa7098dcf9d94d79e0027104200000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000c001a014a3acef764ff6d3bb9bd81e420bfa94171a5734ab997dfbc9b41b653ce018a4a01ff5fccb01ef5c60ba3aef67d4e74f3f47312dd78bfbbff9e5090fbf2d3d62bb"))] + fn test_flz_native_evm_parity(#[case] input: Bytes) { + // This bytecode and ABI is for a contract, which wraps the LibZip library for easier fuzz testing. + // 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; + sol! { + interface FastLz { + function fastLz(bytes input) external view returns (uint256); + } + } + + let contract_bytecode = Bytecode::new_raw(bytes!("608060405234801561001057600080fd5b506004361061002b5760003560e01c8063920a769114610030575b600080fd5b61004361003e366004610374565b610055565b60405190815260200160405180910390f35b600061006082610067565b5192915050565b60606101e0565b818153600101919050565b600082840393505b838110156100a25782810151828201511860001a1590930292600101610081565b9392505050565b825b602082106100d75782516100c0601f8361006e565b5260209290920191601f19909101906021016100ab565b81156100a25782516100ec600184038361006e565b520160010192915050565b60006001830392505b61010782106101385761012a8360ff1661012560fd6101258760081c60e0018961006e565b61006e565b935061010682039150610100565b600782106101655761015e8360ff16610125600785036101258760081c60e0018961006e565b90506100a2565b61017e8360ff166101258560081c8560051b018761006e565b949350505050565b80516101d890838303906101bc90600081901a600182901a60081b1760029190911a60101b17639e3779b90260131c611fff1690565b8060021b6040510182815160e01c1860e01b8151188152505050565b600101919050565b5060405161800038823961800081016020830180600d8551820103826002015b81811015610313576000805b50508051604051600082901a600183901a60081b1760029290921a60101b91909117639e3779b9810260111c617ffc16909101805160e081811c878603811890911b9091189091528401908183039084841061026857506102a3565b600184019350611fff821161029d578251600081901a600182901a60081b1760029190911a60101b17810361029d57506102a3565b5061020c565b8383106102b1575050610313565b600183039250858311156102cf576102cc87878886036100a9565b96505b6102e3600985016003850160038501610079565b91506102f08782846100f7565b9650506103088461030386848601610186565b610186565b915050809350610200565b5050617fe061032884848589518601036100a9565b03925050506020820180820383525b81811161034e57617fe08101518152602001610337565b5060008152602001604052919050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561038657600080fd5b813567ffffffffffffffff8082111561039e57600080fd5b818401915084601f8301126103b257600080fd5b8135818111156103c4576103c461035e565b604051601f8201601f19908116603f011681019083821181831017156103ec576103ec61035e565b8160405282815287602084870101111561040557600080fd5b82602086016020830137600092810160200192909252509594505050505056fea264697066735822122000646b2953fc4a6f501bd0456ac52203089443937719e16b3190b7979c39511264736f6c63430008190033")); + + 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 output = result_and_state.result.output().unwrap(); + let evm_val = FastLz::fastLzCall::abi_decode_returns(output, true) + .unwrap() + ._0; + + assert_eq!(U256::from(native_val), evm_val); + } +} diff --git a/crates/optimism/src/handler.rs b/crates/optimism/src/handler.rs index dde96adb25..5a2127759c 100644 --- a/crates/optimism/src/handler.rs +++ b/crates/optimism/src/handler.rs @@ -10,35 +10,49 @@ use crate::{ }, L1BlockInfoGetter, OpSpec, OpSpecId, OptimismHaltReason, BASE_FEE_RECIPIENT, L1_FEE_RECIPIENT, }; +use precompiles::OpPrecompileProvider; use revm::{ context_interface::{ - result::{ExecutionResult, FromStringError, InvalidTransaction, ResultAndState}, + result::{EVMError, ExecutionResult, FromStringError, InvalidTransaction, ResultAndState}, Block, Cfg, CfgGetter, Journal, Transaction, TransactionGetter, }, - handler::{EthContext, EthError, EthHandler, EthHandlerImpl, FrameContext, FrameResult}, - handler_interface::{Frame, PrecompileProvider}, - interpreter::{ - interpreter::{EthInterpreter, InstructionProvider}, - FrameInput, Gas, Host, + handler::{ + instructions::InstructionExecutor, EthContext, EthError, EthHandler, FrameContext, + FrameResult, MainnetHandler, }, + handler_interface::Frame, + interpreter::{interpreter::EthInterpreter, FrameInput, Gas, Host}, primitives::{hash_map::HashMap, U256}, specification::hardfork::SpecId, state::Account, Database, }; -pub struct OpHandlerNew { - pub eth: EthHandlerImpl, +pub struct OpHandler { + pub main: MainnetHandler, } -impl - OpHandlerNew +impl + OpHandler, INSTRUCTIONS> where - PRECOMPILES: PrecompileProvider, - INSTRUCTIONS: InstructionProvider, + INSTRUCTIONS: InstructionExecutor, { - pub fn crete_frame_context(&self) -> FrameContext { - self.eth.crete_frame_context() + pub fn new() -> Self { + Self { + main: MainnetHandler { + _phantom: Default::default(), + }, + } + } +} + +impl Default + for OpHandler, INSTRUCTIONS> +where + INSTRUCTIONS: InstructionExecutor, +{ + fn default() -> Self { + Self::new() } } @@ -46,15 +60,20 @@ pub trait IsTxError { fn is_tx_error(&self) -> bool; } -impl EthHandler - for OpHandlerNew +impl IsTxError for EVMError { + fn is_tx_error(&self) -> bool { + matches!(self, EVMError::Transaction(_)) + } +} + +impl EthHandler + for OpHandler, INSTRUCTIONS> where CTX: EthContext + OpTxGetter + L1BlockInfoGetter, // Have Cfg with OpSpec ::Cfg: Cfg, ERROR: EthError + From + IsTxError + FromStringError, - PRECOMPILES: PrecompileProvider, - INSTRUCTIONS: InstructionProvider, + INSTRUCTIONS: InstructionExecutor, // TODO `FrameResult` should be a generic trait. // TODO `FrameInit` should be a generic. FRAME: Frame< @@ -62,22 +81,18 @@ where Error = ERROR, FrameResult = FrameResult, FrameInit = FrameInput, - FrameContext = FrameContext, + FrameContext = FrameContext, INSTRUCTIONS>, >, { type Context = CTX; type Error = ERROR; type Frame = FRAME; - type Precompiles = PRECOMPILES; + type Precompiles = OpPrecompileProvider; type Instructions = INSTRUCTIONS; type HaltReason = OptimismHaltReason; - fn frame_context( - &mut self, - context: &mut Self::Context, - ) -> ::FrameContext { - self.eth.precompiles.set_spec(context.cfg().spec()); - self.crete_frame_context() + fn precompile(&self, _context: &mut Self::Context) -> Self::Precompiles { + OpPrecompileProvider::default() } fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { @@ -92,14 +107,14 @@ where } return Ok(()); } - self.eth.validate_env(context) + self.main.validate_env(context) } fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { if context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE { return Ok(()); } - self.eth.validate_tx_against_state(context) + self.main.validate_tx_against_state(context) } fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { @@ -113,7 +128,7 @@ where *context.l1_block_info_mut() = l1_block_info; } - self.eth.load_accounts(context) + self.main.load_accounts(context) } fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { @@ -143,7 +158,7 @@ where // 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.eth.deduct_caller(context)?; + self.main.deduct_caller(context)?; // 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. @@ -253,13 +268,13 @@ where context: &mut Self::Context, exec_result: &mut ::FrameResult, ) -> Result<(), Self::Error> { - self.eth.reward_beneficiary(context, exec_result)?; + self.main.reward_beneficiary(context, exec_result)?; let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; // Transfer fee to coinbase/beneficiary. if !is_deposit { - self.eth.reward_beneficiary(context, exec_result)?; + self.main.reward_beneficiary(context, exec_result)?; let basefee = context.block().basefee() as u128; // If the transaction is not a deposit transaction, fees are paid out @@ -294,7 +309,7 @@ where context: &mut Self::Context, result: ::FrameResult, ) -> Result, Self::Error> { - let result = self.eth.output(context, result)?; + let result = self.main.output(context, result)?; let result = result.map_haltreason(OptimismHaltReason::Base); if result.result.is_halt() { // Post-regolith, if the transaction is a deposit transaction and it halts, diff --git a/crates/optimism/src/handler/precompiles.rs b/crates/optimism/src/handler/precompiles.rs index 1519d08138..17e96c8695 100644 --- a/crates/optimism/src/handler/precompiles.rs +++ b/crates/optimism/src/handler/precompiles.rs @@ -129,3 +129,9 @@ where self.precompile_provider.contains(address) } } + +impl Default for OpPrecompileProvider { + fn default() -> Self { + Self::new_with_spec(OpSpec::Op(OpSpecId::ISTHMUS)) + } +} diff --git a/crates/optimism/src/inspector.rs b/crates/optimism/src/inspector.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/optimism/src/l1block.rs b/crates/optimism/src/l1block.rs index 95e935f04b..a56f71d6f4 100644 --- a/crates/optimism/src/l1block.rs +++ b/crates/optimism/src/l1block.rs @@ -1,7 +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::Journal, + context_interface::{DatabaseGetter, Journal}, database_interface::Database, primitives::{address, Address, U256}, specification::hardfork::SpecId, @@ -244,6 +246,7 @@ impl L1BlockInfo { } } +#[auto_impl(&mut, Box)] pub trait L1BlockInfoGetter { fn l1_block_info(&self) -> &L1BlockInfo; fn l1_block_info_mut(&mut self) -> &mut L1BlockInfo; @@ -261,6 +264,18 @@ impl> L1BlockInfo } } +impl + L1BlockInfoGetter> 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 4e5b4de60e..d3706700b0 100644 --- a/crates/optimism/src/lib.rs +++ b/crates/optimism/src/lib.rs @@ -5,7 +5,9 @@ #[cfg(not(feature = "std"))] extern crate alloc as std; +pub mod api; pub mod bn128; +pub mod context; pub mod fast_lz; pub mod handler; pub mod l1block; diff --git a/crates/optimism/src/transaction/abstraction.rs b/crates/optimism/src/transaction/abstraction.rs index bd2ecf7703..6ae184677e 100644 --- a/crates/optimism/src/transaction/abstraction.rs +++ b/crates/optimism/src/transaction/abstraction.rs @@ -1,18 +1,23 @@ use super::deposit::{DepositTransaction, DepositTransactionParts}; +use auto_impl::auto_impl; +use inspector::inspector_context::InspectorContext; use revm::{ context::TxEnv, context_interface::{ transaction::{AuthorizationItem, Transaction}, - Journal, TransactionGetter, + DatabaseGetter, Journal, TransactionGetter, }, primitives::{Address, Bytes, TxKind, B256, U256}, Context, Database, }; +use std::vec; +#[auto_impl(&, &mut, Box, Arc)] pub trait OpTxTrait: Transaction + DepositTransaction { fn enveloped_tx(&self) -> Option<&Bytes>; } +#[auto_impl(&, &mut, Box, Arc)] pub trait OpTxGetter: TransactionGetter { type OpTransaction: OpTxTrait; @@ -29,24 +34,44 @@ impl, } } +impl + OpTxGetter + TransactionGetter> 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 { - tx: T, + pub base: T, /// An enveloped EIP-2718 typed transaction /// /// This is used to compute the L1 tx cost using the L1 block info, as /// opposed to requiring downstream apps to compute the cost /// externally. - enveloped_tx: Option, - deposit: DepositTransactionParts, + pub enveloped_tx: Option, + pub deposit: DepositTransactionParts, +} + +impl OpTransaction { + pub fn new(base: T) -> Self { + Self { + base, + enveloped_tx: None, + deposit: DepositTransactionParts::default(), + } + } } impl Default for OpTransaction { fn default() -> Self { Self { - tx: TxEnv::default(), - enveloped_tx: None, + base: TxEnv::default(), + enveloped_tx: Some(vec![0x00].into()), deposit: DepositTransactionParts::default(), } } @@ -54,71 +79,71 @@ impl Default for OpTransaction { impl Transaction for OpTransaction { fn tx_type(&self) -> u8 { - self.tx.tx_type() + self.base.tx_type() } fn caller(&self) -> Address { - self.tx.caller() + self.base.caller() } fn gas_limit(&self) -> u64 { - self.tx.gas_limit() + self.base.gas_limit() } fn value(&self) -> U256 { - self.tx.value() + self.base.value() } fn input(&self) -> &Bytes { - self.tx.input() + self.base.input() } fn nonce(&self) -> u64 { - self.tx.nonce() + self.base.nonce() } fn kind(&self) -> TxKind { - self.tx.kind() + self.base.kind() } fn chain_id(&self) -> Option { - self.tx.chain_id() + self.base.chain_id() } fn access_list(&self) -> Option> { - self.tx.access_list() + self.base.access_list() } fn max_priority_fee_per_gas(&self) -> Option { - self.tx.max_priority_fee_per_gas() + self.base.max_priority_fee_per_gas() } fn max_fee_per_gas(&self) -> u128 { - self.tx.max_fee_per_gas() + self.base.max_fee_per_gas() } fn gas_price(&self) -> u128 { - self.tx.gas_price() + self.base.gas_price() } fn blob_versioned_hashes(&self) -> &[B256] { - self.tx.blob_versioned_hashes() + self.base.blob_versioned_hashes() } fn max_fee_per_blob_gas(&self) -> u128 { - self.tx.max_fee_per_blob_gas() + self.base.max_fee_per_blob_gas() } fn effective_gas_price(&self, base_fee: u128) -> u128 { - self.tx.effective_gas_price(base_fee) + self.base.effective_gas_price(base_fee) } fn authorization_list_len(&self) -> usize { - self.tx.authorization_list_len() + self.base.authorization_list_len() } fn authorization_list(&self) -> impl Iterator { - self.tx.authorization_list() + self.base.authorization_list() } } @@ -152,7 +177,7 @@ mod tests { #[test] fn test_deposit_transaction_fields() { let op_tx = OpTransaction { - tx: TxEnv { + base: TxEnv { tx_type: DEPOSIT_TRANSACTION_TYPE, gas_limit: 10, gas_price: 100, diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs deleted file mode 100644 index 597b190bd5..0000000000 --- a/crates/revm/src/evm.rs +++ /dev/null @@ -1,199 +0,0 @@ -use context_interface::{ - result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, ResultAndState}, - DatabaseGetter, -}; -use database_interface::{Database, DatabaseCommit}; -use handler::{ - handler::{EthContext, EthHandler, EthHandlerImpl}, - EthFrame, EthPrecompileProvider, -}; - -pub fn transact_main>( - ctx: &mut CTX, -) -> Result, EVMError<::Error, InvalidTransaction>> { - EthHandlerImpl::, EthPrecompileProvider, _>::default() - .run(ctx) -} - -pub fn transact_main_commit< - DB: Database + DatabaseCommit, - CTX: EthContext + DatabaseGetter, ->( - ctx: &mut CTX, -) -> Result, EVMError<::Error, InvalidTransaction>> { - transact_main(ctx).map(|r| { - ctx.db().commit(r.state); - r.result - }) -} - -/* - -#[cfg(test)] -mod tests { - - use super::*; - use crate::{ - handler::mainnet::{EthExecution, EthPostExecution, EthPreExecution, EthValidation}, - EvmHandler, - }; - use bytecode::{ - opcode::{PUSH1, SSTORE}, - Bytecode, - }; - use core::{fmt::Debug, hash::Hash}; - use database::BenchmarkDB; - use database_interface::Database; - use interpreter::table::InstructionTables; - use primitives::{address, TxKind, U256}; - use specification::{ - eip7702::{Authorization, RecoveredAuthorization, Signature}, - hardfork::{Spec, SpecId}, - spec_to_generic, - }; - use transaction::TransactionType; - use context_interface::{ - default::{self, block::BlockEnv, Env, TxEnv}, - result::{EVMErrorWiring, HaltReason}, - EthereumWiring, EvmWiring as InnerEvmWiring, - }; - - #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] - struct CEthereumWiring<'a, DB: Database, EXT> { - phantom: core::marker::PhantomData<&'a (DB, EXT)>, - } - - impl<'a, DB: Database, EXT: Debug> InnerEvmWiring for CEthereumWiring<'a, DB, EXT> { - type Database = DB; - type ExternalContext = EXT; - type ChainContext = (); - type Block = default::block::BlockEnv; - type Transaction = &'a default::TxEnv; - type Hardfork = SpecId; - type HaltReason = HaltReason; - } - - impl<'a, DB: Database, EXT: Debug> EvmWiring for CEthereumWiring<'a, DB, EXT> { - fn handler<'evm>(hardfork: Self::Hardfork) -> EvmHandler<'evm, Self> - where - DB: Database, - 'a: 'evm, - { - spec_to_generic!( - hardfork, - EvmHandler { - spec_id: hardfork, - //instruction_table: InstructionTables::new_plain::(), - registers: Vec::new(), - pre_execution: - EthPreExecution::, EVMErrorWiring>::new_boxed( - SPEC::SPEC_ID - ), - validation: EthValidation::, EVMErrorWiring>::new_boxed( - SPEC::SPEC_ID - ), - post_execution: EthPostExecution::< - Context, - EVMErrorWiring, - HaltReason, - >::new_boxed(SPEC::SPEC_ID), - execution: EthExecution::, EVMErrorWiring>::new_boxed( - SPEC::SPEC_ID - ), - } - ) - } - } - - //pub type DefaultEthereumWiring = EthereumWiring; - - #[test] - fn sanity_tx_ref() { - let delegate = address!("0000000000000000000000000000000000000000"); - let caller = address!("0000000000000000000000000000000000000001"); - let auth = address!("0000000000000000000000000000000000000100"); - - let mut tx = TxEnv::default(); - tx.tx_type = TransactionType::Eip7702; - tx.gas_limit = 100_000; - tx.authorization_list = vec![RecoveredAuthorization::new_unchecked( - Authorization { - chain_id: U256::from(1), - address: delegate, - nonce: 0, - } - .into_signed(Signature::test_signature()), - Some(auth), - )] - .into(); - tx.caller = caller; - tx.kind = TxKind::Call(auth); - - let mut tx2 = TxEnv::default(); - tx2.tx_type = TransactionType::Legacy; - // `nonce` was bumped from 0 to 1 - tx2.nonce = 1; - - let mut evm = EvmBuilder::new_with( - BenchmarkDB::default(), - (), - Env::boxed(CfgEnv::default(), BlockEnv::default(), &tx), - CEthereumcontext_interface::handler(SpecId::LATEST), - ) - .build(); - - let _ = evm.transact().unwrap(); - - let mut evm = evm - .modify() - .modify_tx_env(|t| { - *t = &tx2; - }) - .build(); - - let _ = evm.transact().unwrap(); - } - - #[test] - fn sanity_eip7702_tx() { - let delegate = address!("0000000000000000000000000000000000000000"); - let caller = address!("0000000000000000000000000000000000000001"); - let auth = address!("0000000000000000000000000000000000000100"); - - let bytecode = Bytecode::new_legacy([PUSH1, 0x01, PUSH1, 0x01, SSTORE].into()); - - let mut evm = Evm::>::builder() - .with_spec_id(SpecId::PRAGUE) - .with_db(BenchmarkDB::new_bytecode(bytecode)) - .with_default_ext_context() - .modify_tx_env(|tx| { - tx.tx_type = TransactionType::Eip7702; - tx.gas_limit = 100_000; - tx.authorization_list = vec![RecoveredAuthorization::new_unchecked( - Authorization { - chain_id: U256::from(1), - address: delegate, - nonce: 0, - } - .into_signed(Signature::test_signature()), - Some(auth), - )] - .into(); - tx.caller = caller; - tx.kind = TxKind::Call(auth); - }) - .build(); - - let ok = evm.transact().unwrap(); - - let auth_acc = ok.state.get(&auth).unwrap(); - assert_eq!(auth_acc.info.code, Some(Bytecode::new_eip7702(delegate))); - 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.rs b/crates/revm/src/exec.rs index 2d48b049eb..b3f37986c1 100644 --- a/crates/revm/src/exec.rs +++ b/crates/revm/src/exec.rs @@ -1,29 +1,25 @@ -use context_interface::{Block, Transaction}; +use context_interface::{block::BlockSetter, transaction::TransactionSetter}; -pub trait EvmExec { - type Transaction: Transaction; - type Block: Block; +/// Execute EVM transactions. +pub trait ExecuteEvm: BlockSetter + TransactionSetter { type Output; - fn set_block(&mut self, block: Self::Block); + fn exec_previous(&mut self) -> Self::Output; - fn set_tx(&mut self, tx: Self::Transaction); - - fn exec(&mut self) -> Self::Output; - - fn exec_with_tx(&mut self, tx: Self::Transaction) -> Self::Output { + fn exec(&mut self, tx: Self::Transaction) -> Self::Output { self.set_tx(tx); - self.exec() + self.exec_previous() } } -pub trait EvmCommit: EvmExec { +/// Execute EVM transactions and commit to the state. +pub trait ExecuteCommitEvm: ExecuteEvm { type CommitOutput; - fn exec_commit(&mut self) -> Self::CommitOutput; + fn exec_commit_previous(&mut self) -> Self::CommitOutput; - fn exec_commit_with_tx(&mut self, tx: Self::Transaction) -> Self::CommitOutput { + fn exec_commit(&mut self, tx: Self::Transaction) -> Self::CommitOutput { self.set_tx(tx); - self.exec_commit() + self.exec_commit_previous() } } diff --git a/crates/revm/src/exec_eth.rs b/crates/revm/src/exec_eth.rs new file mode 100644 index 0000000000..735663164f --- /dev/null +++ b/crates/revm/src/exec_eth.rs @@ -0,0 +1,109 @@ +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/lib.rs b/crates/revm/src/lib.rs index bebfc2f30a..aed09f5041 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -2,8 +2,8 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(not(feature = "std"), no_std)] -//#[cfg(not(feature = "std"))] -//extern crate alloc as std; +#[cfg(not(feature = "std"))] +extern crate alloc as std; // reexport dependencies pub use bytecode; @@ -20,13 +20,13 @@ pub use state; // Modules. -mod evm; mod exec; +mod exec_eth; // Export items. pub use context::journaled_state::{JournalEntry, JournaledState}; pub use context::Context; pub use database_interface::{Database, DatabaseCommit, DatabaseRef}; -pub use evm::{transact_main, transact_main_commit}; -pub use exec::{EvmCommit, EvmExec}; +pub use exec::{ExecuteCommitEvm, ExecuteEvm}; +pub use exec_eth::transact_main; diff --git a/examples/block_traces/src/main.rs b/examples/block_traces/src/main.rs index d174721037..72e8c92563 100644 --- a/examples/block_traces/src/main.rs +++ b/examples/block_traces/src/main.rs @@ -9,14 +9,14 @@ use alloy_provider::{ }; use database::{AlloyDB, CacheDB, StateBuilder}; use indicatif::ProgressBar; -use inspector::{inspect_main, inspector_context::InspectorContext, inspectors::TracerEip3155}; +use inspector::{exec::InspectCommitEvm, inspectors::TracerEip3155}; use revm::{database_interface::WrapDatabaseAsync, primitives::TxKind, Context}; +use std::fs::OpenOptions; use std::io::BufWriter; use std::io::Write; use std::sync::Arc; use std::sync::Mutex; use std::time::Instant; -use std::{fs::OpenOptions, io::stdout}; struct FlushWriter { writer: Arc>>, @@ -38,8 +38,6 @@ impl Write for FlushWriter { } } -pub fn inspect_ctx_insp() {} - #[tokio::main] async fn main() -> anyhow::Result<()> { // Set up the HTTP transport which is consumed by the RPC client. @@ -88,8 +86,6 @@ async fn main() -> anyhow::Result<()> { .modify_cfg_chained(|c| { c.chain_id = chain_id; }); - let mut inspector = TracerEip3155::new(Box::new(stdout())); - let mut ctx = InspectorContext::new(&mut ctx, &mut inspector); let txs = block.transactions.len(); println!("Found {txs} transactions."); @@ -106,7 +102,7 @@ async fn main() -> anyhow::Result<()> { }; for tx in transactions { - ctx.inner.modify_tx(|etx| { + ctx.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()); @@ -142,9 +138,8 @@ async fn main() -> anyhow::Result<()> { let writer = FlushWriter::new(Arc::clone(&inner)); // Inspect and commit the transaction to the EVM - ctx.inspector.set_writer(Box::new(writer)); - let res = inspect_main(&mut ctx); + let res = ctx.inspect_commit_previous(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 eb2e620f19..5de8cc180f 100644 --- a/examples/cheatcode_inspector/src/main.rs +++ b/examples/cheatcode_inspector/src/main.rs @@ -9,7 +9,7 @@ use std::{convert::Infallible, fmt::Debug}; use database::InMemoryDB; use inspector::{ - inspect_main, inspector_context::InspectorContext, inspectors::TracerEip3155, + exec::inspect_main, inspector_context::InspectorContext, inspectors::TracerEip3155, journal::JournalExt, GetInspector, Inspector, }; use revm::{ @@ -137,6 +137,20 @@ impl Journal for Backend { self.journaled_state.touch(&address); } + fn code( + &mut self, + address: Address, + ) -> Result, ::Error> { + self.journaled_state.code(address) + } + + fn code_hash( + &mut self, + address: Address, + ) -> Result, ::Error> { + self.journaled_state.code_hash(address) + } + fn transfer( &mut self, from: &Address, diff --git a/examples/contract_deployment/src/main.rs b/examples/contract_deployment/src/main.rs index 3e0309c63c..ccc8523166 100644 --- a/examples/contract_deployment/src/main.rs +++ b/examples/contract_deployment/src/main.rs @@ -9,7 +9,7 @@ use revm::{ context_interface::result::{ExecutionResult, Output}, database_interface::EmptyDB, primitives::{hex, Bytes, TxKind, U256}, - transact_main, transact_main_commit, + transact_main, ExecuteCommitEvm, }; /// Load number parameter and set to storage with slot 0 @@ -55,7 +55,7 @@ fn main() -> anyhow::Result<()> { .with_db(CacheDB::::default()); println!("bytecode: {}", hex::encode(bytecode)); - let ref_tx = transact_main_commit(&mut ctx)?; + let ref_tx = ctx.exec_commit_previous()?; let ExecutionResult::Success { output: Output::Create(_, Some(address)), .. diff --git a/examples/database_components/Cargo.toml b/examples/database_components/Cargo.toml index 202adb075c..3d7b12429c 100644 --- a/examples/database_components/Cargo.toml +++ b/examples/database_components/Cargo.toml @@ -27,4 +27,4 @@ revm.workspace = true # mics auto_impl.workspace = true -derive_more = { version = "1.0", default-features = false } +derive_more.workspace = true diff --git a/examples/erc20_gas/src/handler.rs b/examples/erc20_gas/src/handler.rs index f4b3099807..6570c2f5fb 100644 --- a/examples/erc20_gas/src/handler.rs +++ b/examples/erc20_gas/src/handler.rs @@ -4,12 +4,12 @@ use revm::{ result::{HaltReason, InvalidTransaction}, Block, CfgGetter, Journal, Transaction, TransactionType, }, - handler::{EthContext, EthError, EthFrame, EthHandler, EthPrecompileProvider, FrameContext}, - handler_interface::Frame, - interpreter::{ - interpreter::{EthInstructionProvider, EthInterpreter}, - Host, + handler::{ + instructions::EthInstructionExecutor, EthContext, EthError, EthFrame, EthHandler, + EthPrecompileProvider, FrameContext, }, + handler_interface::Frame, + interpreter::{interpreter::EthInterpreter, Host}, precompile::PrecompileErrors, primitives::U256, specification::hardfork::SpecId, @@ -18,20 +18,14 @@ use std::cmp::Ordering; use crate::{erc_address_storage, token_operation, TOKEN, TREASURY}; -pub struct Erc20MainetHandler> { - frame_context: FrameContext< - EthPrecompileProvider, - EthInstructionProvider, - >, +pub struct Erc20MainetHandler { + _phantom: std::marker::PhantomData<(CTX, ERROR)>, } -impl> Erc20MainetHandler { +impl Erc20MainetHandler { pub fn new() -> Self { Self { - frame_context: FrameContext::new( - EthPrecompileProvider::new(SpecId::LATEST), - EthInstructionProvider::default(), - ), + _phantom: std::marker::PhantomData, } } } @@ -52,21 +46,11 @@ where type Context = CTX; type Error = ERROR; type Precompiles = EthPrecompileProvider; - type Instructions = EthInstructionProvider; + type Instructions = EthInstructionExecutor; type Frame = EthFrame>; type HaltReason = HaltReason; - fn frame_context( - &mut self, - _context: &mut Self::Context, - ) -> ::FrameContext { - FrameContext { - precompiles: self.frame_context.precompiles.clone(), - instructions: self.frame_context.instructions.clone(), - } - } - fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { let caller = context.tx().caller(); let caller_nonce = context.journal().load_account(caller)?.data.info.nonce; diff --git a/examples/uniswap_v2_usdc_swap/src/main.rs b/examples/uniswap_v2_usdc_swap/src/main.rs index 8ec72fbcd9..92788913a5 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, transact_main_commit, Context, + transact_main, Context, ExecuteCommitEvm, ExecuteEvm, }; use std::ops::Div; @@ -106,7 +106,7 @@ fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> tx.value = U256::from(0); }); - let ref_tx = transact_main(&mut ctx).unwrap(); + let ref_tx = ctx.exec_previous().unwrap(); let result = ref_tx.result; let value = match result { @@ -230,7 +230,7 @@ fn swap( tx.nonce = 1; }); - let ref_tx = transact_main_commit(&mut ctx).unwrap(); + let ref_tx = ctx.exec_commit_previous().unwrap(); match ref_tx { ExecutionResult::Success { .. } => {} @@ -262,7 +262,7 @@ fn transfer( tx.value = U256::from(0); }); - let ref_tx = transact_main_commit(&mut ctx).unwrap(); + let ref_tx = ctx.exec_commit_previous().unwrap(); let success: bool = match ref_tx { ExecutionResult::Success { output: Output::Call(value),