use alloy::primitives::{Address, B256};
use dashmap::DashMap;
use revm::{
database::{
states::{plain_account::PlainStorage, CacheAccount},
CacheState, TransitionAccount,
},
state::{Account, AccountInfo, Bytecode, EvmState},
};
#[derive(Debug, Clone)]
pub struct ConcurrentCacheState {
pub accounts: DashMap<Address, CacheAccount>,
pub contracts: DashMap<B256, Bytecode>,
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)
}
}
impl ConcurrentCacheState {
pub fn new(has_state_clear: bool) -> Self {
Self { accounts: DashMap::default(), contracts: DashMap::default(), has_state_clear }
}
pub fn absorb(&self, other: Self) {
for pair in other.accounts.into_iter() {
self.accounts.insert(pair.0, pair.1);
}
for pair in other.contracts.into_iter() {
self.contracts.insert(pair.0, pair.1);
}
}
pub const fn set_state_clear_flag(&mut self, has_state_clear: bool) {
self.has_state_clear = has_state_clear;
}
pub fn insert_not_existing(&self, address: Address) {
self.accounts.insert(address, CacheAccount::new_loaded_not_existing());
}
pub fn insert_account(&self, address: Address, info: AccountInfo) {
let account = if !info.is_empty() {
CacheAccount::new_loaded(info, PlainStorage::default())
} else {
CacheAccount::new_loaded_empty_eip161(PlainStorage::default())
};
self.accounts.insert(address, account);
}
pub fn insert_account_with_storage(
&self,
address: Address,
info: AccountInfo,
storage: PlainStorage,
) {
let account = if !info.is_empty() {
CacheAccount::new_loaded(info, storage)
} else {
CacheAccount::new_loaded_empty_eip161(storage)
};
self.accounts.insert(address, account);
}
pub fn apply_evm_state(&self, evm_state: EvmState) -> Vec<(Address, TransitionAccount)> {
let mut transitions = Vec::with_capacity(evm_state.len());
for (address, account) in evm_state {
if let Some(transition) = self.apply_account_state(address, account) {
transitions.push((address, transition));
}
}
transitions
}
fn apply_account_state(&self, address: Address, account: Account) -> Option<TransitionAccount> {
if !account.is_touched() {
return None;
}
let mut this_account =
self.accounts.get_mut(&address).expect("All accounts should be present inside cache");
if account.is_selfdestructed() {
return this_account.selfdestruct();
}
let is_created = account.is_created();
let is_empty = account.is_empty();
let changed_storage = account
.storage
.into_iter()
.filter(|(_, slot)| slot.is_changed())
.map(|(key, slot)| (key, slot.into()))
.collect();
if is_created {
return Some(this_account.newly_created(account.info, changed_storage));
}
if is_empty {
if self.has_state_clear {
this_account.touch_empty_eip161()
} else {
this_account.touch_create_pre_eip161(changed_storage)
}
} else {
Some(this_account.change(account.info, changed_storage))
}
}
}