verbs_rs/db/
local_db.rs

1use super::error::DatabaseError;
2use super::traits::DB;
3use revm::db::in_memory_db::DbAccount;
4use revm::db::{AccountState, DatabaseCommit};
5use revm::primitives::{
6    hash_map::Entry, Account, AccountInfo, Address, Bytecode, HashMap, Log, B256, KECCAK_EMPTY,
7    U256,
8};
9use revm::Database;
10
11/// Local in-memory EVm database
12#[derive(Debug, Clone)]
13pub struct LocalDB {
14    pub accounts: HashMap<Address, DbAccount>,
15    pub contracts: HashMap<B256, Bytecode>,
16    pub logs: Vec<Log>,
17    pub block_hashes: HashMap<U256, B256>,
18}
19
20impl Default for LocalDB {
21    fn default() -> Self {
22        Self::new()
23    }
24}
25
26impl LocalDB {
27    pub fn new() -> Self {
28        let mut contracts = HashMap::new();
29        contracts.insert(KECCAK_EMPTY, Bytecode::new());
30        contracts.insert(B256::ZERO, Bytecode::new());
31        Self {
32            accounts: HashMap::new(),
33            contracts,
34            logs: Vec::default(),
35            block_hashes: HashMap::new(),
36        }
37    }
38
39    pub fn insert_contract(&mut self, account: &mut AccountInfo) {
40        if let Some(code) = &account.code {
41            if !code.is_empty() {
42                if account.code_hash == KECCAK_EMPTY {
43                    account.code_hash = code.hash_slow();
44                }
45                self.contracts
46                    .entry(account.code_hash)
47                    .or_insert_with(|| code.clone());
48            }
49        }
50        if account.code_hash == B256::ZERO {
51            account.code_hash = KECCAK_EMPTY;
52        }
53    }
54
55    pub fn insert_account_info(&mut self, address: Address, mut info: AccountInfo) {
56        self.insert_contract(&mut info);
57        self.accounts.entry(address).or_default().info = info;
58    }
59
60    pub fn load_account(&mut self, address: Address) -> Result<&mut DbAccount, DatabaseError> {
61        match self.accounts.entry(address) {
62            Entry::Occupied(entry) => Ok(entry.into_mut()),
63            Entry::Vacant(_) => Err(DatabaseError::GetAccount(address)),
64        }
65    }
66
67    pub fn insert_account_storage(
68        &mut self,
69        address: Address,
70        slot: U256,
71        value: U256,
72    ) -> Result<(), DatabaseError> {
73        let account = self.load_account(address)?;
74        account.storage.insert(slot, value);
75        Ok(())
76    }
77
78    pub fn replace_account_storage(
79        &mut self,
80        address: Address,
81        storage: HashMap<U256, U256>,
82    ) -> Result<(), DatabaseError> {
83        let account = self.load_account(address)?;
84        account.account_state = AccountState::StorageCleared;
85        account.storage = storage.into_iter().collect();
86        Ok(())
87    }
88}
89
90impl DB for LocalDB {
91    fn insert_account_info(&mut self, address: Address, account_info: AccountInfo) {
92        self.insert_account_info(address, account_info)
93    }
94
95    fn accounts(&self) -> &HashMap<Address, DbAccount> {
96        &self.accounts
97    }
98
99    fn contracts(&self) -> &HashMap<B256, Bytecode> {
100        &self.contracts
101    }
102
103    fn logs(&self) -> &Vec<Log> {
104        &self.logs
105    }
106
107    fn block_hashes(&self) -> &HashMap<U256, B256> {
108        &self.block_hashes
109    }
110}
111
112impl DatabaseCommit for LocalDB {
113    fn commit(&mut self, changes: HashMap<Address, Account>) {
114        for (address, mut account) in changes {
115            if !account.is_touched() {
116                continue;
117            }
118            if account.is_selfdestructed() {
119                let db_account = self.accounts.entry(address).or_default();
120                db_account.storage.clear();
121                db_account.account_state = AccountState::NotExisting;
122                db_account.info = AccountInfo::default();
123                continue;
124            }
125            let is_newly_created = account.is_created();
126            self.insert_contract(&mut account.info);
127
128            let db_account = self.accounts.entry(address).or_default();
129            db_account.info = account.info;
130
131            db_account.account_state = if is_newly_created {
132                db_account.storage.clear();
133                AccountState::StorageCleared
134            } else if db_account.account_state.is_storage_cleared() {
135                // Preserve old account state if it already exists
136                AccountState::StorageCleared
137            } else {
138                AccountState::Touched
139            };
140            db_account.storage.extend(
141                account
142                    .storage
143                    .into_iter()
144                    .map(|(key, value)| (key, value.present_value())),
145            );
146        }
147    }
148}
149
150impl Database for LocalDB {
151    type Error = DatabaseError;
152
153    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
154        let basic = match self.accounts.entry(address) {
155            Entry::Occupied(entry) => entry.into_mut(),
156            Entry::Vacant(entry) => entry.insert(DbAccount::new_not_existing()),
157        };
158        Ok(basic.info())
159    }
160
161    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
162        match self.contracts.entry(code_hash) {
163            Entry::Occupied(entry) => Ok(entry.get().clone()),
164            Entry::Vacant(_) => Err(DatabaseError::MissingCode(code_hash)),
165        }
166    }
167
168    /// Get the value in an account's storage slot.
169    ///
170    /// It is assumed that account is already loaded.
171    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
172        match self.accounts.entry(address) {
173            Entry::Occupied(mut acc_entry) => {
174                let acc_entry = acc_entry.get_mut();
175                match acc_entry.storage.entry(index) {
176                    Entry::Occupied(entry) => Ok(*entry.get()),
177                    Entry::Vacant(_) => {
178                        if matches!(
179                            acc_entry.account_state,
180                            AccountState::StorageCleared | AccountState::NotExisting
181                        ) {
182                            Ok(U256::ZERO)
183                        } else {
184                            Err(DatabaseError::GetStorage(address, index))
185                        }
186                    }
187                }
188            }
189            Entry::Vacant(_) => Err(DatabaseError::GetStorage(address, index)),
190        }
191    }
192
193    fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error> {
194        match self.block_hashes.entry(number) {
195            Entry::Occupied(entry) => Ok(*entry.get()),
196            Entry::Vacant(_) => Err(DatabaseError::GetBlockHash(number)),
197        }
198    }
199}