From 7d060e18b3e926e9e7763ff44516b0f0cb941ea5 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 26 Dec 2024 16:51:07 +0100 Subject: [PATCH] feat: expose precompile address in Journal, DB::Error: StdError (#1956) * chore: relax some impl and exec_with_tx * add helper created_address * add error to database error * ext journal with state * fix: handle inspector final return * call final_return * expose precompile addresses to Journal * precompile addresses fn for journal --- Cargo.lock | 1 + .../context/interface/src/journaled_state.rs | 10 +++++-- crates/context/interface/src/result.rs | 13 +++++++-- crates/context/src/context.rs | 10 +++++-- crates/context/src/journaled_state.rs | 19 ++++++++++-- crates/database/interface/src/async_db.rs | 8 ++--- crates/database/interface/src/empty_db.rs | 5 ++-- crates/database/interface/src/lib.rs | 7 +++-- crates/database/src/alloydb.rs | 10 +++++++ crates/database/src/states/state_builder.rs | 2 +- crates/handler/interface/src/execution.rs | 3 +- crates/handler/interface/src/frame.rs | 5 ++++ crates/handler/src/frame.rs | 7 +++++ crates/inspector/src/inspector.rs | 29 ++++++++++++++++--- crates/optimism/src/handler.rs | 1 - crates/revm/src/exec.rs | 10 +++++++ examples/database_components/Cargo.toml | 1 + .../database_components/src/block_hash.rs | 6 ++-- examples/database_components/src/lib.rs | 6 +++- examples/database_components/src/state.rs | 6 ++-- 20 files changed, 127 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8a72552de..37496615df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1460,6 +1460,7 @@ name = "example-database-components" version = "0.0.0" dependencies = [ "auto_impl", + "derive_more 1.0.0", "revm", ] diff --git a/crates/context/interface/src/journaled_state.rs b/crates/context/interface/src/journaled_state.rs index 7ec3f4840a..c4eee4717d 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, Log, B256, U256}; +use primitives::{Address, HashSet, Log, B256, U256}; use specification::hardfork::SpecId; use state::{Account, Bytecode}; use std::boxed::Box; @@ -17,10 +17,10 @@ pub trait Journal { fn new(database: Self::Database) -> Self; /// Returns the database. - fn db(&self) -> &Self::Database; + fn db_ref(&self) -> &Self::Database; /// Returns the mutable database. - fn db_mut(&mut self) -> &mut Self::Database; + fn db(&mut self) -> &mut Self::Database; /// Returns the storage value from Journal state. /// @@ -63,6 +63,10 @@ pub trait Journal { fn warm_account(&mut self, address: Address); + fn warm_precompiles(&mut self, addresses: HashSet
); + + fn precompile_addresses(&self) -> &HashSet
; + fn set_spec_id(&mut self, spec_id: SpecId); fn touch_account(&mut self, address: Address); diff --git a/crates/context/interface/src/result.rs b/crates/context/interface/src/result.rs index ea79bdfb78..dafd85ccc3 100644 --- a/crates/context/interface/src/result.rs +++ b/crates/context/interface/src/result.rs @@ -54,6 +54,15 @@ impl ExecutionResult { matches!(self, Self::Success { .. }) } + /// Returns created address if execution is Create transaction + /// and Contract was created. + pub fn created_address(&self) -> Option
{ + match self { + Self::Success { output, .. } => output.address().cloned(), + _ => None, + } + } + /// Returns true if execution result is a Halt. pub fn is_halt(&self) -> bool { matches!(self, Self::Halt { .. }) @@ -175,9 +184,9 @@ impl FromStringError for EVMError { } } -impl> From for EVMError { +impl From for EVMError { fn from(value: InvalidTransaction) -> Self { - Self::Transaction(value.into()) + Self::Transaction(value) } } diff --git a/crates/context/src/context.rs b/crates/context/src/context.rs index 21383a0713..8608c04aa5 100644 --- a/crates/context/src/context.rs +++ b/crates/context/src/context.rs @@ -313,7 +313,7 @@ where where F: FnOnce(&mut DB), { - f(self.journaled_state.db_mut()); + f(self.journaled_state.db()); } pub fn modify_journal(&mut self, f: F) @@ -395,7 +395,7 @@ where if diff <= BLOCK_HASH_HISTORY { return self .journaled_state - .db_mut() + .db() .block_hash(requested_number) .map_err(|e| self.error = Err(e)) .ok(); @@ -508,7 +508,11 @@ where type Database = DB; fn db(&mut self) -> &mut Self::Database { - self.journaled_state.db_mut() + self.journaled_state.db() + } + + fn db_ref(&self) -> &Self::Database { + self.journaled_state.db_ref() } } diff --git a/crates/context/src/journaled_state.rs b/crates/context/src/journaled_state.rs index 7badf33c8c..9926f3a098 100644 --- a/crates/context/src/journaled_state.rs +++ b/crates/context/src/journaled_state.rs @@ -52,6 +52,8 @@ pub struct JournaledState { /// Note that this not include newly loaded accounts, account and storage /// is considered warm if it is found in the `State`. pub warm_preloaded_addresses: HashSet
, + /// Precompile addresses + pub precompiles: HashSet
, } impl Journal for JournaledState { @@ -63,11 +65,11 @@ impl Journal for JournaledState { Self::new(SpecId::LATEST, database) } - fn db(&self) -> &Self::Database { + fn db_ref(&self) -> &Self::Database { &self.database } - fn db_mut(&mut self) -> &mut Self::Database { + fn db(&mut self) -> &mut Self::Database { &mut self.database } @@ -112,6 +114,17 @@ impl Journal for JournaledState { self.warm_preloaded_addresses.insert(address); } + fn warm_precompiles(&mut self, address: HashSet
) { + self.precompiles = address; + self.warm_preloaded_addresses + .extend(self.precompiles.iter()); + } + + #[inline] + fn precompile_addresses(&self) -> &HashSet
{ + &self.precompiles + } + /// Returns call depth. #[inline] fn depth(&self) -> usize { @@ -212,6 +225,7 @@ impl Journal for JournaledState { spec: _, database: _, warm_preloaded_addresses: _, + precompiles: _, } = self; *transient_storage = TransientStorage::default(); @@ -243,6 +257,7 @@ impl JournaledState { depth: 0, spec, warm_preloaded_addresses: HashSet::default(), + precompiles: HashSet::default(), } } diff --git a/crates/database/interface/src/async_db.rs b/crates/database/interface/src/async_db.rs index 2b99a6bff2..0b85216e71 100644 --- a/crates/database/interface/src/async_db.rs +++ b/crates/database/interface/src/async_db.rs @@ -1,11 +1,11 @@ use core::future::Future; +use crate::{DBErrorMarker, Database, DatabaseRef}; +use core::error::Error; use primitives::{Address, B256, U256}; use state::{AccountInfo, Bytecode}; use tokio::runtime::{Handle, Runtime}; -use crate::{DBErrorMarker, Database, DatabaseRef}; - /// The async EVM database interface /// /// Contains the same methods as [Database], but it returns [Future] type instead. @@ -13,7 +13,7 @@ use crate::{DBErrorMarker, Database, DatabaseRef}; /// Use [WrapDatabaseAsync] to provide [Database] implementation for a type that only implements this trait. pub trait DatabaseAsync { /// The database error type - type Error: Send + DBErrorMarker; + type Error: Send + DBErrorMarker + Error; /// Gets basic account information. fn basic_async( @@ -48,7 +48,7 @@ pub trait DatabaseAsync { /// Use [WrapDatabaseAsync] to provide [DatabaseRef] implementation for a type that only implements this trait. pub trait DatabaseAsyncRef { /// The database error type - type Error: Send + DBErrorMarker; + type Error: Send + DBErrorMarker + Error; /// Gets basic account information. fn basic_async_ref( diff --git a/crates/database/interface/src/empty_db.rs b/crates/database/interface/src/empty_db.rs index ec373139bd..db810a7872 100644 --- a/crates/database/interface/src/empty_db.rs +++ b/crates/database/interface/src/empty_db.rs @@ -1,4 +1,5 @@ use crate::{DBErrorMarker, Database, DatabaseRef}; +use core::error::Error; use core::{convert::Infallible, fmt, marker::PhantomData}; use primitives::{keccak256, Address, B256, U256}; use state::{AccountInfo, Bytecode}; @@ -52,7 +53,7 @@ impl EmptyDBTyped { } } -impl Database for EmptyDBTyped { +impl Database for EmptyDBTyped { type Error = E; #[inline] @@ -76,7 +77,7 @@ impl Database for EmptyDBTyped { } } -impl DatabaseRef for EmptyDBTyped { +impl DatabaseRef for EmptyDBTyped { type Error = E; #[inline] diff --git a/crates/database/interface/src/lib.rs b/crates/database/interface/src/lib.rs index d1678b6be5..a8772af3d4 100644 --- a/crates/database/interface/src/lib.rs +++ b/crates/database/interface/src/lib.rs @@ -8,6 +8,7 @@ extern crate alloc as std; use core::convert::Infallible; use auto_impl::auto_impl; +use core::error::Error; use primitives::{Address, HashMap, B256, U256}; use state::{Account, AccountInfo, Bytecode}; use std::string::String; @@ -35,7 +36,7 @@ impl DBErrorMarker for String {} #[auto_impl(&mut, Box)] pub trait Database { /// The database error type. - type Error: DBErrorMarker; + type Error: DBErrorMarker + Error; //type Bytecode: BytecodeTrait; /// Gets basic account information. @@ -67,7 +68,7 @@ pub trait DatabaseCommit { #[auto_impl(&, &mut, Box, Rc, Arc)] pub trait DatabaseRef { /// The database error type. - type Error: DBErrorMarker; + type Error: DBErrorMarker + Error; /// Gets basic account information. fn basic_ref(&self, address: Address) -> Result, Self::Error>; @@ -129,4 +130,6 @@ pub trait DatabaseGetter { type Database: Database; fn db(&mut self) -> &mut Self::Database; + + fn db_ref(&self) -> &Self::Database; } diff --git a/crates/database/src/alloydb.rs b/crates/database/src/alloydb.rs index 0ae8531ffb..8121acb7bf 100644 --- a/crates/database/src/alloydb.rs +++ b/crates/database/src/alloydb.rs @@ -7,15 +7,25 @@ use alloy_provider::{ Network, Provider, }; use alloy_transport::{Transport, TransportError}; +use core::error::Error; use database_interface::{async_db::DatabaseAsyncRef, DBErrorMarker}; use primitives::{Address, B256, U256}; use state::{AccountInfo, Bytecode}; +use std::fmt::Display; #[derive(Debug)] pub struct DBTransportError(pub TransportError); impl DBErrorMarker for DBTransportError {} +impl Display for DBTransportError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Transport error: {}", self.0) + } +} + +impl Error for DBTransportError {} + impl From for DBTransportError { fn from(e: TransportError) -> Self { Self(e) diff --git a/crates/database/src/states/state_builder.rs b/crates/database/src/states/state_builder.rs index 4ae5292d8d..fc4cd0c00c 100644 --- a/crates/database/src/states/state_builder.rs +++ b/crates/database/src/states/state_builder.rs @@ -85,7 +85,7 @@ impl StateBuilder { } /// With boxed version of database. - pub fn with_database_boxed( + pub fn with_database_boxed( self, database: DBBox<'_, Error>, ) -> StateBuilder> { diff --git a/crates/handler/interface/src/execution.rs b/crates/handler/interface/src/execution.rs index 5a15b118ea..19e4bd3e54 100644 --- a/crates/handler/interface/src/execution.rs +++ b/crates/handler/interface/src/execution.rs @@ -32,7 +32,7 @@ pub trait ExecutionHandler { let frame = frame_stack.last_mut().unwrap(); let call_or_result = frame.run(context)?; - let result = match call_or_result { + let mut result = match call_or_result { FrameOrResultGen::Frame(init) => match frame.init(context, init)? { FrameOrResultGen::Frame(new_frame) => { frame_stack.push(new_frame); @@ -49,6 +49,7 @@ pub trait ExecutionHandler { }; let Some(frame) = frame_stack.last_mut() else { + Self::Frame::final_return(context, &mut result)?; return self.last_frame_result(context, result); }; frame.return_result(context, result)?; diff --git a/crates/handler/interface/src/frame.rs b/crates/handler/interface/src/frame.rs index 9452c91eec..6012946dc1 100644 --- a/crates/handler/interface/src/frame.rs +++ b/crates/handler/interface/src/frame.rs @@ -12,6 +12,11 @@ pub trait Frame: Sized { frame_input: Self::FrameInit, ) -> Result, Self::Error>; + fn final_return( + context: &mut Self::Context, + result: &mut Self::FrameResult, + ) -> Result<(), Self::Error>; + fn init( &self, context: &mut Self::Context, diff --git a/crates/handler/src/frame.rs b/crates/handler/src/frame.rs index df37db4274..9b2e8f81fb 100644 --- a/crates/handler/src/frame.rs +++ b/crates/handler/src/frame.rs @@ -483,6 +483,13 @@ where Self::init_with_context(0, frame_input, memory, precompiles, instructions, context) } + fn final_return( + _context: &mut Self::Context, + _result: &mut Self::FrameResult, + ) -> Result<(), Self::Error> { + Ok(()) + } + fn init( &self, context: &mut CTX, diff --git a/crates/inspector/src/inspector.rs b/crates/inspector/src/inspector.rs index be25d415b3..7b7833789d 100644 --- a/crates/inspector/src/inspector.rs +++ b/crates/inspector/src/inspector.rs @@ -27,6 +27,7 @@ use revm::{ }, precompile::PrecompileErrors, primitives::{Address, Bytes, Log, B256, U256}, + state::EvmState, Context, Error, Evm, JournalEntry, }; use std::{rc::Rc, vec::Vec}; @@ -396,6 +397,10 @@ where fn db(&mut self) -> &mut Self::Database { self.inner.db() } + + fn db_ref(&self) -> &Self::Database { + self.inner.db_ref() + } } impl ErrorGetter for InspectorContext @@ -527,6 +532,10 @@ pub trait JournalExt { fn logs(&self) -> &[Log]; fn last_journal(&self) -> &[JournalEntry]; + + fn evm_state(&self) -> &EvmState; + + fn evm_state_mut(&mut self) -> &mut EvmState; } impl JournalExt for JournaledState { @@ -537,6 +546,14 @@ impl JournalExt for JournaledState { fn last_journal(&self) -> &[JournalEntry] { self.journal.last().expect("Journal is never empty") } + + fn evm_state(&self) -> &EvmState { + &self.state + } + + fn evm_state_mut(&mut self) -> &mut EvmState { + &mut self.state + } } #[auto_impl(&, &mut, Box, Arc)] @@ -699,10 +716,17 @@ where } _ => (), } - ret } + fn final_return( + context: &mut Self::Context, + result: &mut Self::FrameResult, + ) -> Result<(), Self::Error> { + context.frame_end(result); + Ok(()) + } + fn init( &self, context: &mut CTX, @@ -719,9 +743,6 @@ where if let Ok(FrameOrResultGen::Frame(frame)) = &mut ret { context.initialize_interp(&mut frame.eth_frame.interpreter); } - - // TODO : Handle last frame_end. MAKE a separate function for `last_return_result`. - ret } diff --git a/crates/optimism/src/handler.rs b/crates/optimism/src/handler.rs index 692b1ca5b7..65d0f40253 100644 --- a/crates/optimism/src/handler.rs +++ b/crates/optimism/src/handler.rs @@ -70,7 +70,6 @@ where if tx_type == OpTransactionType::Deposit { let tx = context.op_tx().deposit(); // Do not allow for a system transaction to be processed if Regolith is enabled. - // TODO : Check if this is correct. if tx.is_system_transaction() && context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH) { return Err(OpTransactionError::DepositSystemTxPostRegolith.into()); diff --git a/crates/revm/src/exec.rs b/crates/revm/src/exec.rs index 3963e97fb2..2d48b049eb 100644 --- a/crates/revm/src/exec.rs +++ b/crates/revm/src/exec.rs @@ -10,10 +10,20 @@ pub trait EvmExec { 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 { + self.set_tx(tx); + self.exec() + } } pub trait EvmCommit: EvmExec { type CommitOutput; fn exec_commit(&mut self) -> Self::CommitOutput; + + fn exec_commit_with_tx(&mut self, tx: Self::Transaction) -> Self::CommitOutput { + self.set_tx(tx); + self.exec_commit() + } } diff --git a/examples/database_components/Cargo.toml b/examples/database_components/Cargo.toml index 5e1fe4bd61..202adb075c 100644 --- a/examples/database_components/Cargo.toml +++ b/examples/database_components/Cargo.toml @@ -27,3 +27,4 @@ revm.workspace = true # mics auto_impl.workspace = true +derive_more = { version = "1.0", default-features = false } diff --git a/examples/database_components/src/block_hash.rs b/examples/database_components/src/block_hash.rs index dd95c3d172..858d48e177 100644 --- a/examples/database_components/src/block_hash.rs +++ b/examples/database_components/src/block_hash.rs @@ -1,13 +1,13 @@ //! BlockHash database component from [`revm::Database`] use auto_impl::auto_impl; -use core::ops::Deref; +use core::{error::Error as StdError, ops::Deref}; use revm::primitives::B256; use std::sync::Arc; #[auto_impl(&mut, Box)] pub trait BlockHash { - type Error; + type Error: StdError; /// Gets block hash by block number. fn block_hash(&mut self, number: u64) -> Result; @@ -15,7 +15,7 @@ pub trait BlockHash { #[auto_impl(&, &mut, Box, Rc, Arc)] pub trait BlockHashRef { - type Error; + type Error: StdError; /// Gets block hash by block number. fn block_hash(&self, number: u64) -> Result; diff --git a/examples/database_components/src/lib.rs b/examples/database_components/src/lib.rs index 16fc50aed8..e9ec25bb4c 100644 --- a/examples/database_components/src/lib.rs +++ b/examples/database_components/src/lib.rs @@ -8,6 +8,8 @@ pub mod state; pub use block_hash::{BlockHash, BlockHashRef}; pub use state::{State, StateRef}; +use core::{error::Error as StdError, fmt::Debug}; +use derive_more::Display; use revm::{ database_interface::{DBErrorMarker, Database, DatabaseCommit, DatabaseRef}, primitives::{Address, HashMap, B256, U256}, @@ -20,12 +22,14 @@ pub struct DatabaseComponents { pub block_hash: BH, } -#[derive(Debug)] +#[derive(Debug, Display)] pub enum DatabaseComponentError { State(SE), BlockHash(BHE), } +impl StdError for DatabaseComponentError {} + impl DBErrorMarker for DatabaseComponentError {} impl Database for DatabaseComponents { diff --git a/examples/database_components/src/state.rs b/examples/database_components/src/state.rs index 0499cce405..0e64bf07e8 100644 --- a/examples/database_components/src/state.rs +++ b/examples/database_components/src/state.rs @@ -6,11 +6,11 @@ use revm::{ primitives::{Address, B256, U256}, state::{AccountInfo, Bytecode}, }; -use std::sync::Arc; +use std::{error::Error as StdError, sync::Arc}; #[auto_impl(&mut, Box)] pub trait State { - type Error; + type Error: StdError; /// Gets basic account information. fn basic(&mut self, address: Address) -> Result, Self::Error>; @@ -24,7 +24,7 @@ pub trait State { #[auto_impl(&, &mut, Box, Rc, Arc)] pub trait StateRef { - type Error; + type Error: StdError; /// Gets basic account information. fn basic(&self, address: Address) -> Result, Self::Error>;