From 780dd7e3f1c1bbff4ab9e46b42f64f5c19f0a599 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 27 Nov 2025 19:14:14 +0100 Subject: [PATCH 1/3] feat: Restrict Database::Error. JournaledAccountTr --- Cargo.lock | 1 + crates/context/interface/src/context.rs | 2 +- .../context/interface/src/journaled_state.rs | 39 +++-- .../interface/src/journaled_state/account.rs | 134 +++++++++++++++--- crates/context/interface/src/lib.rs | 1 + crates/context/src/journal.rs | 10 +- crates/context/src/journal/inner.rs | 2 +- crates/database/interface/Cargo.toml | 1 + crates/database/interface/src/async_db.rs | 6 +- crates/database/interface/src/empty_db.rs | 8 +- crates/database/interface/src/erased_error.rs | 19 +++ crates/database/interface/src/lib.rs | 13 +- crates/database/src/states/state_builder.rs | 4 +- crates/handler/src/frame.rs | 4 +- crates/handler/src/post_execution.rs | 1 + crates/handler/src/pre_execution.rs | 14 +- crates/op-revm/src/handler.rs | 12 +- examples/cheatcode_inspector/src/main.rs | 14 +- .../database_components/src/block_hash.rs | 6 +- examples/database_components/src/lib.rs | 19 ++- examples/database_components/src/state.rs | 6 +- examples/erc20_gas/src/handler.rs | 6 +- 22 files changed, 238 insertions(+), 84 deletions(-) create mode 100644 crates/database/interface/src/erased_error.rs diff --git a/Cargo.lock b/Cargo.lock index 2bfea74c52..fa8cef2155 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3617,6 +3617,7 @@ dependencies = [ "revm-primitives", "revm-state", "serde", + "thiserror", "tokio", ] diff --git a/crates/context/interface/src/context.rs b/crates/context/interface/src/context.rs index 114235cb6e..48550e96e5 100644 --- a/crates/context/interface/src/context.rs +++ b/crates/context/interface/src/context.rs @@ -156,7 +156,7 @@ pub trait ContextTr: Host { /// Inner Context error used for Interpreter to set error without returning it from instruction #[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum ContextError { /// Database error. Db(DbError), diff --git a/crates/context/interface/src/journaled_state.rs b/crates/context/interface/src/journaled_state.rs index 8559699677..55994768d7 100644 --- a/crates/context/interface/src/journaled_state.rs +++ b/crates/context/interface/src/journaled_state.rs @@ -6,7 +6,8 @@ pub mod entry; use crate::{ context::{SStoreResult, SelfDestructResult}, host::LoadError, - journaled_state::{account::JournaledAccount, entry::JournalEntryTr}, + journaled_state::account::JournaledAccountTr, + ErasedError, }; use core::ops::{Deref, DerefMut}; use database_interface::Database; @@ -22,8 +23,10 @@ pub trait JournalTr { type Database: Database; /// State type that is returned by the journal after finalization. type State; - /// Journal Entry type that is used in the journal. - type JournalEntry: JournalEntryTr; + /// Journal account allows modification of account with all needed changes. + type JournaledAccount<'a>: JournaledAccountTr + where + Self: 'a; /// Creates new Journaled state. /// @@ -186,10 +189,7 @@ pub trait JournalTr { fn load_account_mut( &mut self, address: Address, - ) -> Result< - StateLoad>, - ::Error, - > { + ) -> Result>, ::Error> { self.load_account_mut_optional_code(address, false) } @@ -198,10 +198,7 @@ pub trait JournalTr { fn load_account_with_code_mut( &mut self, address: Address, - ) -> Result< - StateLoad>, - ::Error, - > { + ) -> Result>, ::Error> { self.load_account_mut_optional_code(address, true) } @@ -210,10 +207,7 @@ pub trait JournalTr { &mut self, address: Address, load_code: bool, - ) -> Result< - StateLoad>, - ::Error, - >; + ) -> Result>, ::Error>; /// Sets bytecode with hash. Assume that account is warm. fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256); @@ -311,6 +305,9 @@ pub enum JournalLoadError { ColdLoadSkipped, } +/// Journal error on loading of storage or account with Boxed Database error. +pub type JournalLoadErasedError = JournalLoadError; + impl JournalLoadError { /// Returns true if the error is a database error. #[inline] @@ -352,6 +349,18 @@ impl JournalLoadError { JournalLoadError::ColdLoadSkipped => (LoadError::ColdLoadSkipped, None), } } + + /// Maps the database error to a new error. + #[inline] + pub fn map(self, f: F) -> JournalLoadError + where + F: FnOnce(E) -> B, + { + match self { + JournalLoadError::DBError(e) => JournalLoadError::DBError(f(e)), + JournalLoadError::ColdLoadSkipped => JournalLoadError::ColdLoadSkipped, + } + } } impl From for JournalLoadError { diff --git a/crates/context/interface/src/journaled_state/account.rs b/crates/context/interface/src/journaled_state/account.rs index 2e219d4938..9b83552f4b 100644 --- a/crates/context/interface/src/journaled_state/account.rs +++ b/crates/context/interface/src/journaled_state/account.rs @@ -3,17 +3,103 @@ //! //! Useful to encapsulate account and journal entries together. So when account gets changed, we can add a journal entry for it. +use crate::journaled_state::{entry::JournalEntry, JournalLoadErasedError}; + use super::entry::JournalEntryTr; -use core::ops::Deref; +use auto_impl::auto_impl; use primitives::{Address, B256, KECCAK_EMPTY, U256}; use state::{Account, Bytecode}; use std::vec::Vec; +/// Trait that contains database and journal of all changes that were made to the account. +#[auto_impl(&mut, Box)] +pub trait JournaledAccountTr { + /// Returns the account. + fn account(&self) -> &Account; + + /// Loads the code of the account. and returns it as reference. + fn load_code(&mut self) -> Result<&Bytecode, JournalLoadErasedError>; + + /// Returns the balance of the account. + fn balance(&self) -> &U256; + + /// Returns the nonce of the account. + fn nonce(&self) -> u64; + + /// Returns the code hash of the account. + fn code_hash(&self) -> &B256; + + /// Returns the code of the account. + fn code(&self) -> Option<&Bytecode>; + + /// Touches the account. + fn touch(&mut self); + + /// Marks the account as cold without making a journal entry. + /// + /// Changing account without journal entry can be a footgun as reverting of the state change + /// would not happen without entry. It is the reason why this function has an `unsafe` prefix. + /// + /// If account is in access list, it would still be marked as warm if account get accessed again. + fn unsafe_mark_cold(&mut self); + + /// Sets the balance of the account. + /// + /// If balance is the same, we don't add a journal entry. + /// + /// Touches the account in all cases. + fn set_balance(&mut self, balance: U256); + + /// Increments the balance of the account. + /// + /// Touches the account in all cases. + fn incr_balance(&mut self, balance: U256) -> bool; + + /// Decrements the balance of the account. + /// + /// Touches the account in all cases. + fn decr_balance(&mut self, balance: U256) -> bool; + + /// Bumps the nonce of the account. + /// + /// Touches the account in all cases. + /// + /// Returns true if nonce was bumped, false if nonce is at the max value. + fn bump_nonce(&mut self) -> bool; + + /// Set the nonce of the account and create a journal entry. + /// + /// Touches the account in all cases. + fn set_nonce(&mut self, nonce: u64); + + /// Set the nonce of the account without creating a journal entry. + /// + /// Changing account without journal entry can be a footgun as reverting of the state change + /// would not happen without entry. It is the reason why this function has an `unsafe` prefix. + fn unsafe_set_nonce(&mut self, nonce: u64); + + /// Sets the code of the account. + /// + /// Touches the account in all cases. + fn set_code(&mut self, code_hash: B256, code: Bytecode); + + /// Sets the code of the account. Calculates hash of the code. + /// + /// Touches the account in all cases. + fn set_code_and_hash_slow(&mut self, code: Bytecode); + + /// Delegates the account to another address (EIP-7702). + /// + /// This touches the account, sets the code to the delegation designation, + /// and bumps the nonce. + fn delegate(&mut self, address: Address); +} + /// Journaled account contains both mutable account and journal entries. /// /// Useful to encapsulate account and journal entries together. So when account gets changed, we can add a journal entry for it. #[derive(Debug, PartialEq, Eq)] -pub struct JournaledAccount<'a, ENTRY: JournalEntryTr> { +pub struct JournaledAccount<'a, ENTRY: JournalEntryTr = JournalEntry> { /// Address of the account. address: Address, /// Mutable account. @@ -42,34 +128,36 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { journal_entries, } } +} +impl<'a, ENTRY: JournalEntryTr> JournaledAccountTr for JournaledAccount<'a, ENTRY> { /// Returns the balance of the account. #[inline] - pub fn balance(&self) -> &U256 { + fn balance(&self) -> &U256 { &self.account.info.balance } /// Returns the nonce of the account. #[inline] - pub fn nonce(&self) -> u64 { + fn nonce(&self) -> u64 { self.account.info.nonce } /// Returns the code hash of the account. #[inline] - pub fn code_hash(&self) -> &B256 { + fn code_hash(&self) -> &B256 { &self.account.info.code_hash } /// Returns the code of the account. #[inline] - pub fn code(&self) -> Option<&Bytecode> { + fn code(&self) -> Option<&Bytecode> { self.account.info.code.as_ref() } /// Touches the account. #[inline] - pub fn touch(&mut self) { + fn touch(&mut self) { if !self.account.status.is_touched() { self.account.mark_touch(); self.journal_entries @@ -84,7 +172,7 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { /// /// If account is in access list, it would still be marked as warm if account get accessed again. #[inline] - pub fn unsafe_mark_cold(&mut self) { + fn unsafe_mark_cold(&mut self) { self.account.mark_cold(); } @@ -94,7 +182,7 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { /// /// Touches the account in all cases. #[inline] - pub fn set_balance(&mut self, balance: U256) { + fn set_balance(&mut self, balance: U256) { self.touch(); if self.account.info.balance != balance { self.journal_entries.push(ENTRY::balance_changed( @@ -109,7 +197,7 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { /// /// Touches the account in all cases. #[inline] - pub fn incr_balance(&mut self, balance: U256) -> bool { + fn incr_balance(&mut self, balance: U256) -> bool { self.touch(); let Some(balance) = self.account.info.balance.checked_add(balance) else { return false; @@ -122,7 +210,7 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { /// /// Touches the account in all cases. #[inline] - pub fn decr_balance(&mut self, balance: U256) -> bool { + fn decr_balance(&mut self, balance: U256) -> bool { self.touch(); let Some(balance) = self.account.info.balance.checked_sub(balance) else { return false; @@ -137,7 +225,7 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { /// /// Returns true if nonce was bumped, false if nonce is at the max value. #[inline] - pub fn bump_nonce(&mut self) -> bool { + fn bump_nonce(&mut self) -> bool { self.touch(); let Some(nonce) = self.account.info.nonce.checked_add(1) else { return false; @@ -151,7 +239,7 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { /// /// Touches the account in all cases. #[inline] - pub fn set_nonce(&mut self, nonce: u64) { + fn set_nonce(&mut self, nonce: u64) { self.touch(); let previous_nonce = self.account.info.nonce; self.account.info.set_nonce(nonce); @@ -164,7 +252,7 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { /// Changing account without journal entry can be a footgun as reverting of the state change /// would not happen without entry. It is the reason why this function has an `unsafe` prefix. #[inline] - pub fn unsafe_set_nonce(&mut self, nonce: u64) { + fn unsafe_set_nonce(&mut self, nonce: u64) { self.account.info.set_nonce(nonce); } @@ -172,7 +260,7 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { /// /// Touches the account in all cases. #[inline] - pub fn set_code(&mut self, code_hash: B256, code: Bytecode) { + fn set_code(&mut self, code_hash: B256, code: Bytecode) { self.touch(); self.account.info.set_code_hash(code_hash); self.account.info.set_code(code); @@ -183,7 +271,7 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { /// /// Touches the account in all cases. #[inline] - pub fn set_code_and_hash_slow(&mut self, code: Bytecode) { + fn set_code_and_hash_slow(&mut self, code: Bytecode) { let code_hash = code.hash_slow(); self.set_code(code_hash, code); } @@ -193,7 +281,7 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { /// This touches the account, sets the code to the delegation designation, /// and bumps the nonce. #[inline] - pub fn delegate(&mut self, address: Address) { + fn delegate(&mut self, address: Address) { let (bytecode, hash) = if address.is_zero() { (Bytecode::default(), KECCAK_EMPTY) } else { @@ -205,12 +293,14 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { self.set_code(hash, bytecode); self.bump_nonce(); } -} - -impl<'a, ENTRY: JournalEntryTr> Deref for JournaledAccount<'a, ENTRY> { - type Target = Account; - fn deref(&self) -> &Self::Target { + /// Returns the account. + fn account(&self) -> &Account { self.account } + + #[doc = " Loads the code of the account. and returns it as reference."] + fn load_code(&mut self) -> Result<&Bytecode, JournalLoadErasedError> { + todo!() + } } diff --git a/crates/context/interface/src/lib.rs b/crates/context/interface/src/lib.rs index 0e66071368..dcb65e731d 100644 --- a/crates/context/interface/src/lib.rs +++ b/crates/context/interface/src/lib.rs @@ -17,6 +17,7 @@ pub mod transaction; pub use block::Block; pub use cfg::{Cfg, CreateScheme, TransactTo}; pub use context::{ContextError, ContextSetters, ContextTr}; +pub use database_interface::erased_error::ErasedError; pub use database_interface::{DBErrorMarker, Database}; pub use either; pub use host::{DummyHost, Host}; diff --git a/crates/context/src/journal.rs b/crates/context/src/journal.rs index dd1a85b89c..f9bde2ca74 100644 --- a/crates/context/src/journal.rs +++ b/crates/context/src/journal.rs @@ -92,7 +92,10 @@ impl Journal { impl JournalTr for Journal { type Database = DB; type State = EvmState; - type JournalEntry = ENTRY; + type JournaledAccount<'a> + = JournaledAccount<'a, ENTRY> + where + Self: 'a; fn new(database: DB) -> Journal { Self { @@ -257,10 +260,7 @@ impl JournalTr for Journal { &mut self, address: Address, load_code: bool, - ) -> Result< - StateLoad>, - ::Error, - > { + ) -> Result>, DB::Error> { self.inner .load_account_mut_optional_code(&mut self.database, address, load_code, false) .map_err(JournalLoadError::unwrap_db_error) diff --git a/crates/context/src/journal/inner.rs b/crates/context/src/journal/inner.rs index a624a3e378..a2255c6193 100644 --- a/crates/context/src/journal/inner.rs +++ b/crates/context/src/journal/inner.rs @@ -4,7 +4,7 @@ use bytecode::Bytecode; use context_interface::{ context::{SStoreResult, SelfDestructResult, StateLoad}, journaled_state::{ - account::JournaledAccount, + account::{JournaledAccount, JournaledAccountTr}, entry::{JournalEntryTr, SelfdestructionRevertStatus}, AccountLoad, JournalCheckpoint, JournalLoadError, TransferError, }, diff --git a/crates/database/interface/Cargo.toml b/crates/database/interface/Cargo.toml index f553d82806..f984588d22 100644 --- a/crates/database/interface/Cargo.toml +++ b/crates/database/interface/Cargo.toml @@ -25,6 +25,7 @@ primitives.workspace = true # misc auto_impl.workspace = true either.workspace = true +thiserror.workspace = true # Optional serde = { workspace = true, features = ["derive", "rc"], optional = true } diff --git a/crates/database/interface/src/async_db.rs b/crates/database/interface/src/async_db.rs index 5e42d0c9c1..7ae1ecd5f9 100644 --- a/crates/database/interface/src/async_db.rs +++ b/crates/database/interface/src/async_db.rs @@ -1,6 +1,6 @@ //! Async database interface. use crate::{DBErrorMarker, Database, DatabaseRef}; -use core::{error::Error, future::Future}; +use core::future::Future; use primitives::{Address, StorageKey, StorageValue, B256}; use state::{AccountInfo, Bytecode}; use tokio::runtime::{Handle, Runtime}; @@ -12,7 +12,7 @@ use tokio::runtime::{Handle, Runtime}; /// 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 + Error; + type Error: DBErrorMarker; /// Gets basic account information. fn basic_async( @@ -47,7 +47,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 + Error; + type Error: DBErrorMarker; /// 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 ab7264c2ad..8898e7a4ed 100644 --- a/crates/database/interface/src/empty_db.rs +++ b/crates/database/interface/src/empty_db.rs @@ -1,6 +1,6 @@ //! Empty database implementation. use crate::{DBErrorMarker, Database, DatabaseRef}; -use core::{convert::Infallible, error::Error, fmt, marker::PhantomData}; +use core::{convert::Infallible, fmt, marker::PhantomData}; use primitives::{keccak256, Address, StorageKey, StorageValue, B256}; use state::{AccountInfo, Bytecode}; use std::string::ToString; @@ -54,7 +54,7 @@ impl EmptyDBTyped { } } -impl Database for EmptyDBTyped { +impl Database for EmptyDBTyped { type Error = E; #[inline] @@ -82,7 +82,9 @@ impl Database for EmptyDBTyped { } } -impl DatabaseRef for EmptyDBTyped { +impl DatabaseRef + for EmptyDBTyped +{ type Error = E; #[inline] diff --git a/crates/database/interface/src/erased_error.rs b/crates/database/interface/src/erased_error.rs new file mode 100644 index 0000000000..00f8d613d5 --- /dev/null +++ b/crates/database/interface/src/erased_error.rs @@ -0,0 +1,19 @@ +//! Erased error type. + +/// Erased error type. +#[derive(thiserror::Error, Debug)] +#[error(transparent)] +pub struct ErasedError(Box); + +impl ErasedError { + /// Creates a new erased error. + pub fn new(error: impl core::error::Error + Send + Sync + 'static) -> Self { + Self(Box::new(error)) + } + + /// Consumes the erased error and returns the inner error. + #[inline] + pub fn into_inner(self) -> Box { + self.0 + } +} diff --git a/crates/database/interface/src/lib.rs b/crates/database/interface/src/lib.rs index 76e816b3e2..3f8164e111 100644 --- a/crates/database/interface/src/lib.rs +++ b/crates/database/interface/src/lib.rs @@ -8,10 +8,8 @@ extern crate alloc as std; use core::convert::Infallible; use auto_impl::auto_impl; -use core::error::Error; use primitives::{address, Address, HashMap, StorageKey, StorageValue, B256, U256}; use state::{Account, AccountInfo, Bytecode}; -use std::string::String; /// Address with all `0xff..ff` in it. Used for testing. pub const FFADDRESS: Address = address!("0xffffffffffffffffffffffffffffffffffffffff"); @@ -32,26 +30,27 @@ pub const BENCH_CALLER_BALANCE: U256 = TEST_BALANCE; pub mod async_db; pub mod either; pub mod empty_db; +pub mod erased_error; pub mod try_commit; #[cfg(feature = "asyncdb")] pub use async_db::{DatabaseAsync, WrapDatabaseAsync}; pub use empty_db::{EmptyDB, EmptyDBTyped}; +pub use erased_error::ErasedError; pub use try_commit::{ArcUpgradeError, TryDatabaseCommit}; /// Database error marker is needed to implement From conversion for Error type. -pub trait DBErrorMarker {} +pub trait DBErrorMarker: core::error::Error + Send + Sync + 'static {} /// Implement marker for `()`. -impl DBErrorMarker for () {} impl DBErrorMarker for Infallible {} -impl DBErrorMarker for String {} +impl DBErrorMarker for ErasedError {} /// EVM database interface. #[auto_impl(&mut, Box)] pub trait Database { /// The database error type. - type Error: DBErrorMarker + Error; + type Error: DBErrorMarker; /// Gets basic account information. fn basic(&mut self, address: Address) -> Result, Self::Error>; @@ -83,7 +82,7 @@ pub trait DatabaseCommit { #[auto_impl(&, &mut, Box, Rc, Arc)] pub trait DatabaseRef { /// The database error type. - type Error: DBErrorMarker + Error; + type Error: DBErrorMarker; /// Gets basic account information. fn basic_ref(&self, address: Address) -> Result, Self::Error>; diff --git a/crates/database/src/states/state_builder.rs b/crates/database/src/states/state_builder.rs index c2047d490c..217cd93ce7 100644 --- a/crates/database/src/states/state_builder.rs +++ b/crates/database/src/states/state_builder.rs @@ -85,7 +85,9 @@ impl StateBuilder { } /// With boxed version of database. - pub fn with_database_boxed( + pub fn with_database_boxed< + Error: DBErrorMarker + core::error::Error + Send + Sync + 'static, + >( self, database: DBBox<'_, Error>, ) -> StateBuilder> { diff --git a/crates/handler/src/frame.rs b/crates/handler/src/frame.rs index aeecddd1a3..50c2c59533 100644 --- a/crates/handler/src/frame.rs +++ b/crates/handler/src/frame.rs @@ -2,7 +2,7 @@ use crate::{ evm::FrameTr, item_or_result::FrameInitOrResult, precompile_provider::PrecompileProvider, CallFrame, CreateFrame, FrameData, FrameResult, ItemOrResult, }; -use context::result::FromStringError; +use context::{journaled_state::account::JournaledAccountTr, result::FromStringError}; use context_interface::{ context::ContextError, journaled_state::{JournalCheckpoint, JournalTr}, @@ -301,6 +301,8 @@ impl EthFrame { CreateScheme::Custom { address } => address, }; + drop(caller_info); // Drop caller info to avoid borrow checker issues. + // warm load account. context.journal_mut().load_account(created_address)?; diff --git a/crates/handler/src/post_execution.rs b/crates/handler/src/post_execution.rs index bd0471d717..b5a8bef35d 100644 --- a/crates/handler/src/post_execution.rs +++ b/crates/handler/src/post_execution.rs @@ -1,4 +1,5 @@ use crate::FrameResult; +use context::journaled_state::account::JournaledAccountTr; use context_interface::{ journaled_state::JournalTr, result::{ExecutionResult, HaltReason, HaltReasonTr}, diff --git a/crates/handler/src/pre_execution.rs b/crates/handler/src/pre_execution.rs index 6d916083a2..1112f81eb4 100644 --- a/crates/handler/src/pre_execution.rs +++ b/crates/handler/src/pre_execution.rs @@ -4,6 +4,7 @@ use crate::{EvmTr, PrecompileProvider}; use bytecode::Bytecode; +use context::journaled_state::account::JournaledAccountTr; use context_interface::{ journaled_state::JournalTr, result::InvalidTransaction, @@ -166,7 +167,7 @@ pub fn validate_against_state_and_deduct_caller< // Load caller's account. let mut caller = journal.load_account_with_code_mut(tx.caller())?.data; - validate_account_nonce_and_code_with_components(&caller.info, tx, cfg)?; + validate_account_nonce_and_code_with_components(&caller.account().info, tx, cfg)?; let new_balance = calculate_caller_fee(*caller.balance(), tx, block, cfg)?; @@ -234,9 +235,10 @@ pub fn apply_auth_list< // warm authority account and check nonce. // 4. Add `authority` to `accessed_addresses` (as defined in [EIP-2929](./eip-2929.md).) let mut authority_acc = journal.load_account_with_code_mut(authority)?; + let authority_acc_info = &authority_acc.account().info; // 5. Verify the code of `authority` is either empty or already delegated. - if let Some(bytecode) = &authority_acc.info.code { + if let Some(bytecode) = &authority_acc_info.code { // if it is not empty and it is not eip7702 if !bytecode.is_empty() && !bytecode.is_eip7702() { continue; @@ -244,12 +246,16 @@ pub fn apply_auth_list< } // 6. Verify the nonce of `authority` is equal to `nonce`. In case `authority` does not exist in the trie, verify that `nonce` is equal to `0`. - if authorization.nonce() != authority_acc.info.nonce { + if authorization.nonce() != authority_acc_info.nonce { continue; } // 7. Add `PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST` gas to the global refund counter if `authority` exists in the trie. - if !(authority_acc.is_empty() && authority_acc.is_loaded_as_not_existing_not_touched()) { + if !(authority_acc_info.is_empty() + && authority_acc + .account() + .is_loaded_as_not_existing_not_touched()) + { refunded_accounts += 1; } diff --git a/crates/op-revm/src/handler.rs b/crates/op-revm/src/handler.rs index f444e6f420..ca86a3b72e 100644 --- a/crates/op-revm/src/handler.rs +++ b/crates/op-revm/src/handler.rs @@ -6,7 +6,11 @@ use crate::{ L1BlockInfo, OpHaltReason, OpSpecId, }; use revm::{ - context::{journaled_state::JournalCheckpoint, result::InvalidTransaction, LocalContextTr}, + context::{ + journaled_state::{account::JournaledAccountTr, JournalCheckpoint}, + result::InvalidTransaction, + LocalContextTr, + }, context_interface::{ context::ContextError, result::{EVMError, ExecutionResult, FromStringError}, @@ -146,10 +150,10 @@ where let mut caller_account = journal.load_account_with_code_mut(tx.caller())?.data; // validates account nonce and code - validate_account_nonce_and_code_with_components(&caller_account.info, tx, cfg)?; + validate_account_nonce_and_code_with_components(&caller_account.account().info, tx, cfg)?; // check additional cost and deduct it from the caller's balances - let mut balance = caller_account.info.balance; + let mut balance = caller_account.account().info.balance; if !cfg.is_fee_charge_disabled() { let Some(additional_cost) = chain.tx_cost_with_tx(tx, spec) else { @@ -404,6 +408,8 @@ where acc.bump_nonce(); acc.incr_balance(U256::from(mint.unwrap_or_default())); + drop(acc); // Drop acc to avoid borrow checker issues. + // We can now commit the changes. journal.commit_tx(); diff --git a/examples/cheatcode_inspector/src/main.rs b/examples/cheatcode_inspector/src/main.rs index a40b0c5ca3..7142ea03f4 100644 --- a/examples/cheatcode_inspector/src/main.rs +++ b/examples/cheatcode_inspector/src/main.rs @@ -8,7 +8,7 @@ use revm::{ context::{ - journaled_state::{AccountInfoLoad, JournalLoadError}, + journaled_state::{account::JournaledAccount, AccountInfoLoad, JournalLoadError}, result::InvalidTransaction, BlockEnv, Cfg, CfgEnv, ContextTr, Evm, LocalContext, TxEnv, }, @@ -60,7 +60,10 @@ impl Backend { impl JournalTr for Backend { type Database = InMemoryDB; type State = EvmState; - type JournalEntry = JournalEntry; + type JournaledAccount<'a> + = JournaledAccount<'a> + where + Self: 'a; fn new(database: InMemoryDB) -> Self { Self::new(SpecId::default(), database) @@ -306,12 +309,7 @@ impl JournalTr for Backend { &mut self, address: Address, load_code: bool, - ) -> Result< - StateLoad< - revm::context::journaled_state::account::JournaledAccount<'_, Self::JournalEntry>, - >, - ::Error, - > { + ) -> Result>, ::Error> { self.journaled_state .load_account_mut_optional_code(address, load_code) } diff --git a/examples/database_components/src/block_hash.rs b/examples/database_components/src/block_hash.rs index 449929d204..fe7d7213b5 100644 --- a/examples/database_components/src/block_hash.rs +++ b/examples/database_components/src/block_hash.rs @@ -1,7 +1,7 @@ //! BlockHash database component from [`revm::Database`] use auto_impl::auto_impl; -use core::{error::Error as StdError, ops::Deref}; +use core::ops::Deref; use revm::primitives::B256; use std::sync::Arc; @@ -11,7 +11,7 @@ use std::sync::Arc; #[auto_impl(&mut, Box)] pub trait BlockHash { /// Error type for block hash operations - type Error: StdError; + type Error: core::error::Error + Send + Sync + 'static; /// Gets block hash by block number. fn block_hash(&mut self, number: u64) -> Result; @@ -23,7 +23,7 @@ pub trait BlockHash { #[auto_impl(&, &mut, Box, Rc, Arc)] pub trait BlockHashRef { /// Error type for block hash operations - type Error: StdError; + type Error: core::error::Error + Send + Sync + 'static; /// 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 c683d19f0e..276bc32587 100644 --- a/examples/database_components/src/lib.rs +++ b/examples/database_components/src/lib.rs @@ -28,7 +28,10 @@ pub struct DatabaseComponents { /// Error type for database component operations. /// Wraps errors from both state and block hash components. #[derive(Debug, thiserror::Error)] -pub enum DatabaseComponentError { +pub enum DatabaseComponentError< + SE: core::error::Error + Send + Sync + 'static, + BHE: core::error::Error + Send + Sync + 'static, +> { /// Error from state component operations #[error(transparent)] State(SE), @@ -37,7 +40,19 @@ pub enum DatabaseComponentError { BlockHash(BHE), } -impl DBErrorMarker for DatabaseComponentError {} +impl< + SE: core::error::Error + Send + Sync + 'static, + BHE: core::error::Error + Send + Sync + 'static, + > DBErrorMarker for DatabaseComponentError +{ +} + +unsafe impl< + SE: core::error::Error + Send + Sync + 'static, + BHE: core::error::Error + Send + Sync + 'static, + > Send for DatabaseComponentError +{ +} impl Database for DatabaseComponents { type Error = DatabaseComponentError; diff --git a/examples/database_components/src/state.rs b/examples/database_components/src/state.rs index 35f8d40ed8..b9dd75f636 100644 --- a/examples/database_components/src/state.rs +++ b/examples/database_components/src/state.rs @@ -6,7 +6,7 @@ use revm::{ primitives::{Address, StorageKey, StorageValue, B256}, state::{AccountInfo, Bytecode}, }; -use std::{error::Error as StdError, sync::Arc}; +use std::sync::Arc; /// Trait for mutable access to state data including accounts, code, and storage. /// This is typically used for database implementations that may modify state @@ -14,7 +14,7 @@ use std::{error::Error as StdError, sync::Arc}; #[auto_impl(&mut, Box)] pub trait State { /// Error type for state operations - type Error: StdError; + type Error: core::error::Error + Send + Sync + 'static; /// Gets basic account information. fn basic(&mut self, address: Address) -> Result, Self::Error>; @@ -33,7 +33,7 @@ pub trait State { #[auto_impl(&, &mut, Box, Rc, Arc)] pub trait StateRef { /// Error type for state operations - type Error: StdError; + type Error: core::error::Error + Send + Sync + 'static; /// Gets basic account information. fn basic(&self, address: Address) -> Result, Self::Error>; diff --git a/examples/erc20_gas/src/handler.rs b/examples/erc20_gas/src/handler.rs index b1ff47d890..29b673aad4 100644 --- a/examples/erc20_gas/src/handler.rs +++ b/examples/erc20_gas/src/handler.rs @@ -1,5 +1,5 @@ use revm::{ - context::Cfg, + context::{journaled_state::account::JournaledAccountTr, Cfg}, context_interface::{result::HaltReason, Block, ContextTr, JournalTr, Transaction}, handler::{ pre_execution::{calculate_caller_fee, validate_account_nonce_and_code_with_components}, @@ -54,7 +54,7 @@ where // Load caller's account. let mut caller_account = journal.load_account_with_code_mut(tx.caller())?; - validate_account_nonce_and_code_with_components(&caller_account.info, tx, cfg)?; + validate_account_nonce_and_code_with_components(&caller_account.account().info, tx, cfg)?; // make changes to the account. Account balance stays the same caller_account.touch(); @@ -64,6 +64,8 @@ where let account_balance_slot = erc_address_storage(tx.caller()); + drop(caller_account); // Drop caller_account to avoid borrow checker issues. + // load account balance let account_balance = journal.sload(TOKEN, account_balance_slot)?.data; From b001cdc06aefc747b4e717716386ea88eb3a9168 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 1 Dec 2025 18:50:35 +0100 Subject: [PATCH 2/3] nits --- crates/context/interface/src/context.rs | 2 +- .../context/interface/src/journaled_state.rs | 4 ---- .../interface/src/journaled_state/account.rs | 20 ++++++------------- crates/database/src/states/state_builder.rs | 4 +--- 4 files changed, 8 insertions(+), 22 deletions(-) diff --git a/crates/context/interface/src/context.rs b/crates/context/interface/src/context.rs index 48550e96e5..114235cb6e 100644 --- a/crates/context/interface/src/context.rs +++ b/crates/context/interface/src/context.rs @@ -156,7 +156,7 @@ pub trait ContextTr: Host { /// Inner Context error used for Interpreter to set error without returning it from instruction #[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] -//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum ContextError { /// Database error. Db(DbError), diff --git a/crates/context/interface/src/journaled_state.rs b/crates/context/interface/src/journaled_state.rs index 55994768d7..0883be6b4b 100644 --- a/crates/context/interface/src/journaled_state.rs +++ b/crates/context/interface/src/journaled_state.rs @@ -7,7 +7,6 @@ use crate::{ context::{SStoreResult, SelfDestructResult}, host::LoadError, journaled_state::account::JournaledAccountTr, - ErasedError, }; use core::ops::{Deref, DerefMut}; use database_interface::Database; @@ -305,9 +304,6 @@ pub enum JournalLoadError { ColdLoadSkipped, } -/// Journal error on loading of storage or account with Boxed Database error. -pub type JournalLoadErasedError = JournalLoadError; - impl JournalLoadError { /// Returns true if the error is a database error. #[inline] diff --git a/crates/context/interface/src/journaled_state/account.rs b/crates/context/interface/src/journaled_state/account.rs index 9b83552f4b..363b1feb31 100644 --- a/crates/context/interface/src/journaled_state/account.rs +++ b/crates/context/interface/src/journaled_state/account.rs @@ -3,7 +3,7 @@ //! //! Useful to encapsulate account and journal entries together. So when account gets changed, we can add a journal entry for it. -use crate::journaled_state::{entry::JournalEntry, JournalLoadErasedError}; +use crate::journaled_state::entry::JournalEntry; use super::entry::JournalEntryTr; use auto_impl::auto_impl; @@ -17,9 +17,6 @@ pub trait JournaledAccountTr { /// Returns the account. fn account(&self) -> &Account; - /// Loads the code of the account. and returns it as reference. - fn load_code(&mut self) -> Result<&Bytecode, JournalLoadErasedError>; - /// Returns the balance of the account. fn balance(&self) -> &U256; @@ -131,6 +128,11 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { } impl<'a, ENTRY: JournalEntryTr> JournaledAccountTr for JournaledAccount<'a, ENTRY> { + /// Returns the account. + fn account(&self) -> &Account { + self.account + } + /// Returns the balance of the account. #[inline] fn balance(&self) -> &U256 { @@ -293,14 +295,4 @@ impl<'a, ENTRY: JournalEntryTr> JournaledAccountTr for JournaledAccount<'a, ENTR self.set_code(hash, bytecode); self.bump_nonce(); } - - /// Returns the account. - fn account(&self) -> &Account { - self.account - } - - #[doc = " Loads the code of the account. and returns it as reference."] - fn load_code(&mut self) -> Result<&Bytecode, JournalLoadErasedError> { - todo!() - } } diff --git a/crates/database/src/states/state_builder.rs b/crates/database/src/states/state_builder.rs index 217cd93ce7..1b42990483 100644 --- a/crates/database/src/states/state_builder.rs +++ b/crates/database/src/states/state_builder.rs @@ -85,9 +85,7 @@ impl StateBuilder { } /// With boxed version of database. - pub fn with_database_boxed< - Error: DBErrorMarker + core::error::Error + Send + Sync + 'static, - >( + pub fn with_database_boxed( self, database: DBBox<'_, Error>, ) -> StateBuilder> { From e6410e520c7c2cf78b05f69fe787f5ef14e68c03 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 1 Dec 2025 19:14:03 +0100 Subject: [PATCH 3/3] nits std --- Cargo.toml | 2 +- crates/database/interface/Cargo.toml | 8 +++++++- crates/database/interface/src/erased_error.rs | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e60b33f097..f724c01f4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -123,7 +123,7 @@ indicatif = "0.18" plain_hasher = "0.2" rstest = "0.26.0" serde_derive = "1.0" -thiserror = "2.0" +thiserror = { version = "2.0", default-features = false } triehash = "0.8" walkdir = "2.5" diff --git a/crates/database/interface/Cargo.toml b/crates/database/interface/Cargo.toml index f984588d22..d668da62f5 100644 --- a/crates/database/interface/Cargo.toml +++ b/crates/database/interface/Cargo.toml @@ -37,6 +37,12 @@ tokio = { workspace = true, optional = true } [features] default = ["std"] -std = ["serde?/std", "primitives/std", "state/std", "either/std"] +std = [ + "serde?/std", + "primitives/std", + "state/std", + "either/std", + "thiserror/std", +] serde = ["dep:serde", "primitives/serde", "state/serde", "either/serde"] asyncdb = ["dep:tokio", "tokio/rt-multi-thread"] diff --git a/crates/database/interface/src/erased_error.rs b/crates/database/interface/src/erased_error.rs index 00f8d613d5..b5f6cf4cbd 100644 --- a/crates/database/interface/src/erased_error.rs +++ b/crates/database/interface/src/erased_error.rs @@ -1,5 +1,7 @@ //! Erased error type. +use std::boxed::Box; + /// Erased error type. #[derive(thiserror::Error, Debug)] #[error(transparent)]