Skip to content
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

chore(state): Make Database more generic. #687

Merged
merged 3 commits into from
Sep 5, 2023
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
23 changes: 14 additions & 9 deletions crates/revm/src/db/states/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ use revm_interpreter::primitives::{
hash_map, Account, AccountInfo, Bytecode, HashMap, B160, B256, BLOCK_HASH_HISTORY, U256,
};

/// More constrained version of State that uses Boxed database with a lifetime.
///
/// This is used to make it easier to use State.
pub type StateDBBox<'a, DBError> = State<Box<dyn Database<Error = DBError> + Send + 'a>>;

/// State of blockchain.
///
/// State clear flag is set inside CacheState and by default it is enabled.
/// If you want to disable it use `set_state_clear_flag` function.
pub struct State<'a, DBError> {
pub struct State<DB: Database> {
/// Cached state contains both changed from evm execution and cached/loaded account/storages
/// from database. This allows us to have only one layer of cache where we can fetch data.
/// Additionaly we can introduce some preloading of data from database.
Expand All @@ -22,7 +27,7 @@ pub struct State<'a, DBError> {
/// return not existing account and storage.
///
/// Note: It is marked as Send so database can be shared between threads.
pub database: Box<dyn Database<Error = DBError> + Send + 'a>,
pub database: DB, //Box<dyn Database<Error = DBError> + Send + 'a>,
/// Block state, it aggregates transactions transitions into one state.
///
/// Build reverts and state that gets applied to the state.
Expand All @@ -46,15 +51,15 @@ pub struct State<'a, DBError> {
pub block_hashes: BTreeMap<u64, B256>,
}

impl<'a, DBError> State<'a, DBError> {
impl<DB: Database> State<DB> {
/// Iterate over received balances and increment all account balances.
/// If account is not found inside cache state it will be loaded from database.
///
/// Update will create transitions for all accounts that are updated.
pub fn increment_balances(
&mut self,
balances: impl IntoIterator<Item = (B160, u128)>,
) -> Result<(), DBError> {
) -> Result<(), DB::Error> {
// make transition and update cache state
let mut transitions = Vec::new();
for (address, balance) in balances {
Expand All @@ -74,7 +79,7 @@ impl<'a, DBError> State<'a, DBError> {
pub fn drain_balances(
&mut self,
addresses: impl IntoIterator<Item = B160>,
) -> Result<Vec<u128>, DBError> {
) -> Result<Vec<u128>, DB::Error> {
// make transition and update cache state
let mut transitions = Vec::new();
let mut balances = Vec::new();
Expand Down Expand Up @@ -134,7 +139,7 @@ impl<'a, DBError> State<'a, DBError> {
}
}

pub fn load_cache_account(&mut self, address: B160) -> Result<&mut CacheAccount, DBError> {
pub fn load_cache_account(&mut self, address: B160) -> Result<&mut CacheAccount, DB::Error> {
match self.cache.accounts.entry(address) {
hash_map::Entry::Vacant(entry) => {
if self.use_preloaded_bundle {
Expand Down Expand Up @@ -171,8 +176,8 @@ impl<'a, DBError> State<'a, DBError> {
}
}

impl<'a, DBError> Database for State<'a, DBError> {
type Error = DBError;
impl<DB: Database> Database for State<DB> {
type Error = DB::Error;

fn basic(&mut self, address: B160) -> Result<Option<AccountInfo>, Self::Error> {
self.load_cache_account(address).map(|a| a.account_info())
Expand Down Expand Up @@ -254,7 +259,7 @@ impl<'a, DBError> Database for State<'a, DBError> {
}
}

impl<'a, DBError> DatabaseCommit for State<'a, DBError> {
impl<DB: Database> DatabaseCommit for State<DB> {
fn commit(&mut self, evm_state: HashMap<B160, Account>) {
let transitions = self.cache.apply_evm_state(evm_state);
self.apply_transition(transitions);
Expand Down
19 changes: 9 additions & 10 deletions crates/revm/src/db/states/state_builder.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
use super::{cache::CacheState, BundleState, State, TransitionState};
use crate::db::EmptyDB;
use alloc::collections::BTreeMap;
use core::convert::Infallible;
use revm_interpreter::primitives::{db::Database, B256};

/// Allows building of State and initializing it with different options.
pub struct StateBuilder<'a, DBError> {
pub struct StateBuilder<DB> {
pub with_state_clear: bool,
/// Optional database that we use to fetch data from. If database is not present, we will
/// return not existing account and storage.
///
/// Note: It is marked as Send so database can be shared between threads.
pub database: Box<dyn Database<Error = DBError> + Send + 'a>,
pub database: DB, //Box<dyn Database<Error = DBError> + Send + 'a>,
/// if there is prestate that we want to use.
/// This would mean that we have additional state layer between evm and disk/database.
pub with_bundle_prestate: Option<BundleState>,
Expand All @@ -28,7 +27,7 @@ pub struct StateBuilder<'a, DBError> {
pub with_block_hashes: BTreeMap<u64, B256>,
}

impl Default for StateBuilder<'_, Infallible> {
impl Default for StateBuilder<Box<EmptyDB>> {
Comment on lines -31 to +30
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be made generic

impl<DB: Default> Default for StateBuilder<DB> {

fn default() -> Self {
Self {
with_state_clear: true,
Expand All @@ -42,16 +41,16 @@ impl Default for StateBuilder<'_, Infallible> {
}
}

impl<'a, DBError> StateBuilder<'a, DBError> {
impl<DB: Database> StateBuilder<DB> {
/// Create default instance of builder.
pub fn new() -> StateBuilder<'a, Infallible> {
StateBuilder::<'a, Infallible>::default()
pub fn new() -> StateBuilder<Box<EmptyDB>> {
StateBuilder::<Box<EmptyDB>>::default()
}

pub fn with_database<NewDBError>(
pub fn with_database_boxed<'a, NewDBError>(
self,
database: Box<dyn Database<Error = NewDBError> + Send + 'a>,
) -> StateBuilder<'a, NewDBError> {
) -> StateBuilder<Box<dyn Database<Error = NewDBError> + Send + 'a>> {
// cast to the different database,
// Note that we return different type depending of the database NewDBError.
StateBuilder {
Expand Down Expand Up @@ -124,7 +123,7 @@ impl<'a, DBError> StateBuilder<'a, DBError> {
}
}

pub fn build(mut self) -> State<'a, DBError> {
pub fn build(mut self) -> State<DB> {
let use_preloaded_bundle = if self.with_cache_prestate.is_some() {
self.with_bundle_prestate = None;
false
Expand Down