diff --git a/crates/database/interface/src/lib.rs b/crates/database/interface/src/lib.rs index 7a4469d09d..533fb301ae 100644 --- a/crates/database/interface/src/lib.rs +++ b/crates/database/interface/src/lib.rs @@ -12,10 +12,12 @@ use state::{Account, AccountInfo, Bytecode}; #[cfg(feature = "asyncdb")] pub mod async_db; pub mod empty_db; +pub mod try_commit; #[cfg(feature = "asyncdb")] pub use async_db::{DatabaseAsync, WrapDatabaseAsync}; pub use empty_db::{EmptyDB, EmptyDBTyped}; +pub use try_commit::{ArcUpgradeError, TryDatabaseCommit}; /// EVM database interface. #[auto_impl(&mut, Box)] diff --git a/crates/database/interface/src/try_commit.rs b/crates/database/interface/src/try_commit.rs new file mode 100644 index 0000000000..9a6b39c33b --- /dev/null +++ b/crates/database/interface/src/try_commit.rs @@ -0,0 +1,84 @@ +use crate::DatabaseCommit; +use core::{convert::Infallible, error::Error, fmt}; +use primitives::{Address, HashMap}; +use state::Account; +use std::sync::Arc; + +/// EVM database commit interface that can fail. +/// +/// This is intended for use with types that may fail to commit changes, e.g. +/// because they are directly interacting with the filesystem, or must arrange +/// access to a shared resource. +pub trait TryDatabaseCommit { + /// Error type for when [`TryDatabaseCommit::try_commit`] fails. + type Error: Error; + + /// Attempt to commit changes to the database. + fn try_commit(&mut self, changes: HashMap) -> Result<(), Self::Error>; +} + +impl TryDatabaseCommit for Db +where + Db: DatabaseCommit, +{ + type Error = Infallible; + + #[inline] + fn try_commit(&mut self, changes: HashMap) -> Result<(), Self::Error> { + self.commit(changes); + Ok(()) + } +} + +/// Error type for implementation of [`TryDatabaseCommit`] on +/// [`Arc`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ArcUpgradeError; + +impl fmt::Display for ArcUpgradeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Arc reference is not unique, cannot mutate") + } +} + +impl Error for ArcUpgradeError {} + +impl TryDatabaseCommit for Arc +where + Db: DatabaseCommit + Send + Sync, +{ + type Error = ArcUpgradeError; + + #[inline] + fn try_commit(&mut self, changes: HashMap) -> Result<(), Self::Error> { + Arc::get_mut(self) + .map(|db| db.commit(changes)) + .ok_or(ArcUpgradeError) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::DatabaseCommit; + use std::sync::Arc; + + struct MockDb; + + impl DatabaseCommit for MockDb { + fn commit(&mut self, _changes: HashMap) {} + } + + #[test] + fn arc_try_commit() { + let mut db = Arc::new(MockDb); + let db_2 = Arc::clone(&db); + + assert_eq!( + db.try_commit(Default::default()).unwrap_err(), + ArcUpgradeError + ); + drop(db_2); + db.try_commit(Default::default()).unwrap(); + } +}