revm_database/states/cache.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
use super::{
plain_account::PlainStorage, transition_account::TransitionAccount, CacheAccount, PlainAccount,
};
use bytecode::Bytecode;
use primitives::{Address, HashMap, B256};
use state::{Account, AccountInfo, EvmState};
use std::vec::Vec;
/// Cache state contains both modified and original values.
///
/// Cache state is main state that revm uses to access state.
/// It loads all accounts from database and applies revm output to it.
///
/// It generates transitions that is used to build BundleState.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CacheState {
/// Block state account with account state.
pub accounts: HashMap<Address, CacheAccount>,
/// Created contracts.
// TODO add bytecode counter for number of bytecodes added/removed.
pub contracts: HashMap<B256, Bytecode>,
/// Has EIP-161 state clear enabled (Spurious Dragon hardfork).
pub has_state_clear: bool,
}
impl Default for CacheState {
fn default() -> Self {
Self::new(true)
}
}
impl CacheState {
/// New default state.
pub fn new(has_state_clear: bool) -> Self {
Self {
accounts: HashMap::default(),
contracts: HashMap::default(),
has_state_clear,
}
}
/// Set state clear flag. EIP-161.
pub fn set_state_clear_flag(&mut self, has_state_clear: bool) {
self.has_state_clear = has_state_clear;
}
/// Helper function that returns all accounts.
///
/// Used inside tests to generate merkle tree.
pub fn trie_account(&self) -> impl IntoIterator<Item = (Address, &PlainAccount)> {
self.accounts.iter().filter_map(|(address, account)| {
account
.account
.as_ref()
.map(|plain_acc| (*address, plain_acc))
})
}
/// Insert not existing account.
pub fn insert_not_existing(&mut self, address: Address) {
self.accounts
.insert(address, CacheAccount::new_loaded_not_existing());
}
/// Insert Loaded (Or LoadedEmptyEip161 if account is empty) account.
pub fn insert_account(&mut self, address: Address, info: AccountInfo) {
let account = if !info.is_empty() {
CacheAccount::new_loaded(info, HashMap::default())
} else {
CacheAccount::new_loaded_empty_eip161(HashMap::default())
};
self.accounts.insert(address, account);
}
/// Similar to `insert_account` but with storage.
pub fn insert_account_with_storage(
&mut 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);
}
/// Apply output of revm execution and create account transitions that are used to build BundleState.
pub fn apply_evm_state(&mut 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
}
/// Apply updated account state to the cached account.
/// Returns account transition if applicable.
fn apply_account_state(
&mut self,
address: Address,
account: Account,
) -> Option<TransitionAccount> {
// not touched account are never changed.
if !account.is_touched() {
return None;
}
let this_account = self
.accounts
.get_mut(&address)
.expect("All accounts should be present inside cache");
// If it is marked as selfdestructed inside revm
// we need to changed state to destroyed.
if account.is_selfdestructed() {
return this_account.selfdestruct();
}
let is_created = account.is_created();
let is_empty = account.is_empty();
// transform evm storage to storage with previous value.
let changed_storage = account
.storage
.into_iter()
.filter(|(_, slot)| slot.is_changed())
.map(|(key, slot)| (key, slot.into()))
.collect();
// Note: it can happen that created contract get selfdestructed in same block
// that is why is_created is checked after selfdestructed
//
// Note: Create2 opcode (Petersburg) was after state clear EIP (Spurious Dragon)
//
// Note: It is possibility to create KECCAK_EMPTY contract with some storage
// by just setting storage inside CRATE constructor. Overlap of those contracts
// is not possible because CREATE2 is introduced later.
if is_created {
return Some(this_account.newly_created(account.info, changed_storage));
}
// Account is touched, but not selfdestructed or newly created.
// Account can be touched and not changed.
// And when empty account is touched it needs to be removed from database.
// EIP-161 state clear
if is_empty {
if self.has_state_clear {
// touch empty account.
this_account.touch_empty_eip161()
} else {
// if account is empty and state clear is not enabled we should save
// empty account.
this_account.touch_create_pre_eip161(changed_storage)
}
} else {
Some(this_account.change(account.info, changed_storage))
}
}
}