Skip to content

feat: cachingdb traits #102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "trevm"
version = "0.20.7"
version = "0.20.8"
rust-version = "1.83.0"
edition = "2021"
authors = ["init4"]
Expand Down
69 changes: 60 additions & 9 deletions src/db/cow/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::db::CachingDb;
use alloy::{
consensus::constants::KECCAK_EMPTY,
primitives::{Address, B256, U256},
Expand All @@ -10,6 +11,8 @@ use revm::{
Database, DatabaseCommit, DatabaseRef,
};

use super::TryCachingDb;

/// A version of [`CacheDB`] that caches only on write, not on read.
///
/// This saves memory when wrapping some other caching database, like [`State`]
Expand Down Expand Up @@ -49,11 +52,21 @@ impl<Db> CacheOnWrite<Db> {
&self.inner
}

/// Get a mutable reference to the inner database.
pub const fn inner_mut(&mut self) -> &mut Db {
&mut self.inner
}

/// Get a refernce to the [`Cache`].
pub const fn cache(&self) -> &Cache {
&self.cache
}

/// Get a mutable reference to the [`Cache`].
pub const fn cache_mut(&mut self) -> &mut Cache {
&mut self.cache
}

/// Deconstruct the `CacheOnWrite` into its parts.
pub fn into_parts(self) -> (Db, Cache) {
(self.inner, self.cache)
Expand Down Expand Up @@ -91,29 +104,67 @@ impl<Db> CacheOnWrite<Db> {
}
}

impl<Db> CacheOnWrite<CacheOnWrite<Db>> {
/// Discard the outer cache, returning the inner.
pub fn discard_outer(self) -> CacheOnWrite<Db> {
self.inner
impl<Db> CachingDb for CacheOnWrite<Db> {
fn cache(&self) -> &Cache {
&self.cache
}

fn cache_mut(&mut self) -> &mut Cache {
&mut self.cache
}

fn into_cache(self) -> Cache {
self.cache
}
}

impl<Db> CacheOnWrite<Db>
where
Db: CachingDb,
{
/// Flattens a nested cache by applying the outer cache to the inner cache.
///
/// The behavior is as follows:
/// - Accounts are overridden with outer accounts
/// - Contracts are overridden with outer contracts
/// - Block hashes are overridden with outer block hashes
pub fn flatten(self) -> CacheOnWrite<Db> {
pub fn flatten(self) -> Db {
let Self { cache: Cache { accounts, contracts, logs, block_hashes }, mut inner } = self;

inner.cache.accounts.extend(accounts);
inner.cache.contracts.extend(contracts);
inner.cache.logs.extend(logs);
inner.cache.block_hashes.extend(block_hashes);
let inner_cache = inner.cache_mut();

inner_cache.accounts.extend(accounts);
inner_cache.contracts.extend(contracts);
inner_cache.logs.extend(logs);
inner_cache.block_hashes.extend(block_hashes);
inner
}
}

impl<Db> CacheOnWrite<Db>
where
Db: TryCachingDb,
{
/// Attempts to flatten a nested cache by applying the outer cache to the
/// inner cache. This is a fallible version of [`CacheOnWrite::flatten`].
///
/// The behavior is as follows:
/// - Accounts are overridden with outer accounts
/// - Contracts are overridden with outer contracts
/// - Block hashes are overridden with outer block hashes
pub fn try_flatten(self) -> Result<Db, Db::Error> {
let Self { cache: Cache { accounts, contracts, logs, block_hashes }, mut inner } = self;

let inner_cache = inner.try_cache_mut()?;

inner_cache.accounts.extend(accounts);
inner_cache.contracts.extend(contracts);
inner_cache.logs.extend(logs);
inner_cache.block_hashes.extend(block_hashes);
Ok(inner)
}
}

impl<Db: DatabaseRef> Database for CacheOnWrite<Db> {
type Error = Db::Error;

Expand Down
2 changes: 1 addition & 1 deletion src/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod sync;

/// Database abstraction traits.
mod traits;
pub use traits::{ArcUpgradeError, StateAcc, TryDatabaseCommit, TryStateAcc};
pub use traits::{ArcUpgradeError, CachingDb, StateAcc, TryCachingDb, TryStateAcc};

/// Cache-on-write database. A memory cache that caches only on write, not on
/// read. Intended to wrap some other caching database.
Expand Down
73 changes: 53 additions & 20 deletions src/db/traits.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use revm::{
database::{states::bundle_state::BundleRetention, BundleState, State},
primitives::{Address, B256},
state::Account,
Database, DatabaseCommit,
database::{states::bundle_state::BundleRetention, BundleState, Cache, CacheDB, State},
primitives::B256,
Database,
};
use std::{collections::BTreeMap, convert::Infallible, sync::Arc};

Expand Down Expand Up @@ -145,29 +144,63 @@ where
}
}

/// A fallible version of [`DatabaseCommit`].
pub trait TryDatabaseCommit {
/// Error type to be thrown when committing changes fails.
/// Trait for Databases that have a [`Cache`].
pub trait CachingDb {
/// Get the cache.
fn cache(&self) -> &Cache;

/// Get the cache mutably.
fn cache_mut(&mut self) -> &mut Cache;

/// Deconstruct into the cache
fn into_cache(self) -> Cache;
}

impl<Db> CachingDb for CacheDB<Db> {
fn cache(&self) -> &Cache {
&self.cache
}

fn cache_mut(&mut self) -> &mut Cache {
&mut self.cache
}

fn into_cache(self) -> Cache {
self.cache
}
}

/// Trait for Databases that have a [`Cache`] and can fail to mutably access
/// it. E.g. `Arc<CacheDB<Db>>`
pub trait TryCachingDb {
/// Error type to be thrown when cache access fails.
type Error: core::error::Error;

/// Attempt to commit changes to the database.
fn try_commit(
&mut self,
changes: revm::primitives::HashMap<Address, Account>,
) -> Result<(), Self::Error>;
/// Attempt to get the cache.
fn cache(&self) -> &Cache;

/// Attempt to get the cache mutably.
fn try_cache_mut(&mut self) -> Result<&mut Cache, Self::Error>;

/// Attempt to deconstruct into the cache
fn try_into_cache(self) -> Result<Cache, Self::Error>;
}

impl<Db> TryDatabaseCommit for Arc<Db>
impl<Db> TryCachingDb for Arc<Db>
where
Db: DatabaseCommit,
Db: CachingDb,
{
type Error = ArcUpgradeError;

fn try_commit(
&mut self,
changes: revm::primitives::HashMap<Address, Account>,
) -> Result<(), Self::Error> {
Self::get_mut(self).ok_or(ArcUpgradeError::NotUnique)?.commit(changes);
Ok(())
fn cache(&self) -> &Cache {
self.as_ref().cache()
}

fn try_cache_mut(&mut self) -> Result<&mut Cache, Self::Error> {
Self::get_mut(self).ok_or(ArcUpgradeError::NotUnique).map(|db| db.cache_mut())
}

fn try_into_cache(self) -> Result<Cache, Self::Error> {
Self::into_inner(self).ok_or(ArcUpgradeError::NotUnique).map(|db| db.into_cache())
}
}
4 changes: 2 additions & 2 deletions src/evm.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
db::{StateAcc, TryDatabaseCommit, TryStateAcc},
db::{StateAcc, TryStateAcc},
driver::DriveBlockResult,
helpers::{Ctx, Evm},
Block, BlockDriver, BundleDriver, Cfg, ChainDriver, DriveBundleResult, DriveChainResult,
Expand All @@ -16,7 +16,7 @@ use revm::{
result::{EVMError, ExecutionResult, InvalidTransaction, ResultAndState},
Block as _, BlockEnv, Cfg as _, ContextSetters, ContextTr, Transaction as _, TxEnv,
},
database::{states::bundle_state::BundleRetention, BundleState},
database::{states::bundle_state::BundleRetention, BundleState, TryDatabaseCommit},
inspector::NoOpInspector,
interpreter::gas::calculate_initial_tx_gas_for_tx,
primitives::{hardfork::SpecId, TxKind},
Expand Down