Skip to content

Commit

Permalink
feat: builder port
Browse files Browse the repository at this point in the history
  • Loading branch information
prestwich committed Dec 3, 2024
1 parent 061add3 commit d644ffa
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 15 deletions.
189 changes: 189 additions & 0 deletions src/db/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
use crate::db::ConcurrentState;
use revm::{
db::{
states::{state::DBBox, BundleState, TransitionState},
EmptyDB,
},
primitives::{
db::{Database, DatabaseRef, WrapDatabaseRef},
B256,
},
};
use std::collections::BTreeMap;

use super::{ConcurrentCacheState, ConcurrentStateCache};

/// Allows building of State and initializing it with different options.
#[derive(Clone, Debug)]
pub struct ConcurrentStateBuilder<DB> {
/// Database that we use to fetch data from.
database: DB,
/// Enabled state clear flag that is introduced in Spurious Dragon hardfork.
/// Default is true as spurious dragon happened long time ago.
with_state_clear: bool,
/// if there is prestate that we want to use.
/// This would mean that we have additional state layer between evm and disk/database.
with_bundle_prestate: Option<BundleState>,
/// This will initialize cache to this state.
with_cache_prestate: Option<ConcurrentCacheState>,
/// Do we want to create reverts and update bundle state.
/// Default is false.
with_bundle_update: bool,
/// Do we want to merge transitions in background.
/// This will allows evm to continue executing.
/// Default is false.
with_background_transition_merge: bool,
/// If we want to set different block hashes
with_block_hashes: BTreeMap<u64, B256>,
}

impl ConcurrentStateBuilder<EmptyDB> {
/// Create a new builder with an empty database.
///
/// If you want to instantiate it with a specific database, use
/// [`new_with_database`](Self::new_with_database).
pub fn new() -> Self {
Self::default()
}
}

impl<DB: DatabaseRef + Default> Default for ConcurrentStateBuilder<DB> {
fn default() -> Self {
Self::new_with_database(DB::default())
}
}

impl<DB: DatabaseRef> ConcurrentStateBuilder<DB> {
/// Create a new builder with the given database.
pub const fn new_with_database(database: DB) -> Self {
Self {
database,
with_state_clear: true,
with_cache_prestate: None,
with_bundle_prestate: None,
with_bundle_update: false,
with_background_transition_merge: false,
with_block_hashes: BTreeMap::new(),
}
}

/// Set the database.
pub fn with_database<ODB: Database>(self, database: ODB) -> ConcurrentStateBuilder<ODB> {
// cast to the different database,
// Note that we return different type depending of the database NewDBError.
ConcurrentStateBuilder {
with_state_clear: self.with_state_clear,
database,
with_cache_prestate: self.with_cache_prestate,
with_bundle_prestate: self.with_bundle_prestate,
with_bundle_update: self.with_bundle_update,
with_background_transition_merge: self.with_background_transition_merge,
with_block_hashes: self.with_block_hashes,
}
}

/// Takes [DatabaseRef] and wraps it with [WrapDatabaseRef].
pub fn with_database_ref<ODB: DatabaseRef>(
self,
database: ODB,
) -> ConcurrentStateBuilder<WrapDatabaseRef<ODB>> {
self.with_database(WrapDatabaseRef(database))
}

/// With boxed version of database.
pub fn with_database_boxed<Error>(
self,
database: DBBox<'_, Error>,
) -> ConcurrentStateBuilder<DBBox<'_, Error>> {
self.with_database(database)
}

/// By default state clear flag is enabled but for initial sync on mainnet
/// we want to disable it so proper consensus changes are in place.
pub fn without_state_clear(self) -> Self {
Self { with_state_clear: false, ..self }
}

/// Allows setting prestate that is going to be used for execution.
/// This bundle state will act as additional layer of cache.
/// and State after not finding data inside StateCache will try to find it inside BundleState.
///
/// On update Bundle state will be changed and updated.
pub fn with_bundle_prestate(self, bundle: BundleState) -> Self {
Self { with_bundle_prestate: Some(bundle), ..self }
}

/// Make transitions and update bundle state.
///
/// This is needed option if we want to create reverts
/// and getting output of changed states.
pub fn with_bundle_update(self) -> Self {
Self { with_bundle_update: true, ..self }
}

/// It will use different cache for the state. If set, it will ignore bundle prestate.
/// and will ignore `without_state_clear` flag as cache contains its own state_clear flag.
///
/// This is useful for testing.
pub fn with_cached_prestate(self, cache: impl Into<ConcurrentCacheState>) -> Self {
Self { with_cache_prestate: Some(cache.into()), ..self }
}

/// Starts the thread that will take transitions and do merge to the bundle state
/// in the background.
pub fn with_background_transition_merge(self) -> Self {
Self { with_background_transition_merge: true, ..self }
}

/// Add block hashes to the state.
pub fn with_block_hashes(self, block_hashes: BTreeMap<u64, B256>) -> Self {
Self { with_block_hashes: block_hashes, ..self }
}

/// Build the concurrent state.
pub fn build(mut self) -> ConcurrentState<DB> {
let use_preloaded_bundle = if self.with_cache_prestate.is_some() {
self.with_bundle_prestate = None;
false
} else {
self.with_bundle_prestate.is_some()
};
ConcurrentState::new(
self.database,
ConcurrentStateCache {
cache: self
.with_cache_prestate
.unwrap_or_else(|| ConcurrentCacheState::new(self.with_state_clear)),
transition_state: self.with_bundle_update.then(TransitionState::default),
bundle_state: self.with_bundle_prestate.unwrap_or_default(),
use_preloaded_bundle,
block_hashes: self.with_block_hashes.into(),
},
)
}
}

// Some code above and documentation is adapted from the revm crate, and is
// reproduced here under the terms of the MIT license.
//
// MIT License
//
// Copyright (c) 2021-2024 draganrakita
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
20 changes: 13 additions & 7 deletions src/db/cache_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ use dashmap::DashMap;
use revm::{
db::states::{plain_account::PlainStorage, CacheAccount},
primitives::{Account, AccountInfo, Bytecode, EvmState},
TransitionAccount,
CacheState, TransitionAccount,
};

/// A concurrent version of [`revm::db::CacheState`].
///
/// Most of the code for this has been reproduced from revm.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ConcurrentCacheState {
/// Block state account with account state.
pub accounts: DashMap<Address, CacheAccount>,
Expand All @@ -22,6 +22,16 @@ pub struct ConcurrentCacheState {
pub has_state_clear: bool,
}

impl From<CacheState> for ConcurrentCacheState {
fn from(other: CacheState) -> Self {
Self {
accounts: other.accounts.into_iter().collect(),
contracts: other.contracts.into_iter().collect(),
has_state_clear: other.has_state_clear,
}
}
}

impl Default for ConcurrentCacheState {
fn default() -> Self {
Self::new(true)
Expand All @@ -31,11 +41,7 @@ impl Default for ConcurrentCacheState {
impl ConcurrentCacheState {
/// New default state.
pub fn new(has_state_clear: bool) -> Self {
Self {
accounts: DashMap::default(),
contracts: DashMap::default(),
has_state_clear: has_state_clear.into(),
}
Self { accounts: DashMap::default(), contracts: DashMap::default(), has_state_clear }
}

/// Set state clear flag. EIP-161.
Expand Down
7 changes: 5 additions & 2 deletions src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
mod sync_state;
pub use sync_state::{ConcurrentState, ConcurrentStateCache};
mod builder;
pub use builder::ConcurrentStateBuilder;

mod cache_state;
pub use cache_state::ConcurrentCacheState;

mod sync_state;
pub use sync_state::{ConcurrentState, ConcurrentStateCache};

use crate::{EvmNeedsBlock, Trevm};
use revm::{
db::{states::bundle_state::BundleRetention, BundleState},
Expand Down
41 changes: 35 additions & 6 deletions src/db/sync_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use dashmap::mapref::one::RefMut;
use revm::{
db::{
states::{bundle_state::BundleRetention, plain_account::PlainStorage, CacheAccount},
BundleState,
BundleState, State,
},
primitives::{Account, AccountInfo, Bytecode},
Database, DatabaseCommit, DatabaseRef, TransitionAccount, TransitionState,
Expand All @@ -22,6 +22,24 @@ pub struct ConcurrentState<Db> {
pub info: ConcurrentStateCache,
}

impl<Db> From<State<Db>> for ConcurrentState<Db>
where
Db: DatabaseRef,
{
fn from(value: State<Db>) -> Self {
Self {
database: value.database,
info: ConcurrentStateCache {
cache: value.cache.into(),
transition_state: value.transition_state,
bundle_state: value.bundle_state,
use_preloaded_bundle: value.use_preloaded_bundle,
block_hashes: value.block_hashes.into(),
},
}
}
}

/// Non-DB contents of [`ConcurrentState`]
#[derive(Debug, Default)]
pub struct ConcurrentStateCache {
Expand Down Expand Up @@ -54,7 +72,18 @@ pub struct ConcurrentStateCache {
pub block_hashes: RwLock<BTreeMap<u64, B256>>,
}

impl<DB: DatabaseRef> ConcurrentState<DB> {
impl<Db: DatabaseRef> ConcurrentState<Db> {
/// Create a new [`ConcurrentState`] with the given database and cache
/// state.
pub const fn new(database: Db, info: ConcurrentStateCache) -> Self {
Self { database, info }
}

/// Deconstruct the [`ConcurrentState`] into its parts.
pub fn into_parts(self) -> (Db, ConcurrentStateCache) {
(self.database, self.info)
}

/// Returns the size hint for the inner bundle state.
/// See [BundleState::size_hint] for more info.
pub fn bundle_size_hint(&self) -> usize {
Expand All @@ -72,7 +101,7 @@ impl<DB: DatabaseRef> ConcurrentState<DB> {
pub fn increment_balances(
&mut self,
balances: impl IntoIterator<Item = (Address, u128)>,
) -> Result<(), DB::Error> {
) -> Result<(), Db::Error> {
// make transition and update cache state
let mut transitions = Vec::new();
for (address, balance) in balances {
Expand All @@ -98,7 +127,7 @@ impl<DB: DatabaseRef> ConcurrentState<DB> {
pub fn drain_balances(
&mut self,
addresses: impl IntoIterator<Item = Address>,
) -> Result<Vec<u128>, DB::Error> {
) -> 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 @@ -171,7 +200,7 @@ impl<DB: DatabaseRef> ConcurrentState<DB> {
pub fn load_cache_account_mut(
&self,
address: Address,
) -> Result<RefMut<'_, Address, CacheAccount>, DB::Error> {
) -> Result<RefMut<'_, Address, CacheAccount>, Db::Error> {
match self.info.cache.accounts.entry(address) {
dashmap::Entry::Vacant(entry) => {
if self.info.use_preloaded_bundle {
Expand Down Expand Up @@ -206,7 +235,7 @@ impl<DB: DatabaseRef> ConcurrentState<DB> {
/// to call [`ConcurrentState::merge_transitions`] before taking the bundle.
///
/// If the `State` has been built with the
/// [`StateBuilder::with_bundle_prestate`] option, the pre-state will be
/// [`revm::StateBuilder::with_bundle_prestate`] option, the pre-state will be
/// taken along with any changes made by
/// [`ConcurrentState::merge_transitions`].
pub fn take_bundle(&mut self) -> BundleState {
Expand Down

0 comments on commit d644ffa

Please sign in to comment.