1use core::convert::Infallible;
2use database_interface::{Database, DatabaseCommit, DatabaseRef, EmptyDB};
3use primitives::{
4 address, hash_map::Entry, Address, HashMap, Log, StorageKey, StorageValue, B256, KECCAK_EMPTY,
5 U256,
6};
7use state::{Account, AccountInfo, Bytecode};
8use std::vec::Vec;
9
10pub type InMemoryDB = CacheDB<EmptyDB>;
12
13#[derive(Debug, Clone)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct Cache {
21 pub accounts: HashMap<Address, DbAccount>,
24 pub contracts: HashMap<B256, Bytecode>,
26 pub logs: Vec<Log>,
28 pub block_hashes: HashMap<U256, B256>,
30}
31
32impl Default for Cache {
33 fn default() -> Self {
34 let mut contracts = HashMap::default();
35 contracts.insert(KECCAK_EMPTY, Bytecode::default());
36 contracts.insert(B256::ZERO, Bytecode::default());
37
38 Cache {
39 accounts: HashMap::default(),
40 contracts,
41 logs: Vec::default(),
42 block_hashes: HashMap::default(),
43 }
44 }
45}
46
47#[derive(Debug, Clone)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct CacheDB<ExtDB> {
53 pub cache: Cache,
55 pub db: ExtDB,
59}
60
61impl<ExtDB: Default> Default for CacheDB<ExtDB> {
62 fn default() -> Self {
63 Self::new(ExtDB::default())
64 }
65}
66
67impl<ExtDb> CacheDB<CacheDB<ExtDb>> {
68 pub fn flatten(self) -> CacheDB<ExtDb> {
76 let CacheDB {
77 cache:
78 Cache {
79 accounts,
80 contracts,
81 logs,
82 block_hashes,
83 },
84 db: mut inner,
85 } = self;
86
87 inner.cache.accounts.extend(accounts);
88 inner.cache.contracts.extend(contracts);
89 inner.cache.logs.extend(logs);
90 inner.cache.block_hashes.extend(block_hashes);
91 inner
92 }
93
94 pub fn discard_outer(self) -> CacheDB<ExtDb> {
96 self.db
97 }
98}
99
100impl<ExtDB> CacheDB<ExtDB> {
101 pub fn new(db: ExtDB) -> Self {
103 Self {
104 cache: Cache::default(),
105 db,
106 }
107 }
108
109 pub fn insert_contract(&mut self, account: &mut AccountInfo) {
115 if let Some(code) = &account.code {
116 if !code.is_empty() {
117 if account.code_hash == KECCAK_EMPTY {
118 account.code_hash = code.hash_slow();
119 }
120 self.cache
121 .contracts
122 .entry(account.code_hash)
123 .or_insert_with(|| code.clone());
124 }
125 }
126 if account.code_hash.is_zero() {
127 account.code_hash = KECCAK_EMPTY;
128 }
129 }
130
131 pub fn insert_account_info(&mut self, address: Address, mut info: AccountInfo) {
133 self.insert_contract(&mut info);
134 self.cache.accounts.entry(address).or_default().info = info;
135 }
136
137 pub fn nest(self) -> CacheDB<Self> {
139 CacheDB::new(self)
140 }
141}
142
143impl<ExtDB: DatabaseRef> CacheDB<ExtDB> {
144 pub fn load_account(&mut self, address: Address) -> Result<&mut DbAccount, ExtDB::Error> {
148 let db = &self.db;
149 match self.cache.accounts.entry(address) {
150 Entry::Occupied(entry) => Ok(entry.into_mut()),
151 Entry::Vacant(entry) => Ok(entry.insert(
152 db.basic_ref(address)?
153 .map(|info| DbAccount {
154 info,
155 ..Default::default()
156 })
157 .unwrap_or_else(DbAccount::new_not_existing),
158 )),
159 }
160 }
161
162 pub fn insert_account_storage(
164 &mut self,
165 address: Address,
166 slot: StorageKey,
167 value: StorageValue,
168 ) -> Result<(), ExtDB::Error> {
169 let account = self.load_account(address)?;
170 account.storage.insert(slot, value);
171 Ok(())
172 }
173
174 pub fn replace_account_storage(
176 &mut self,
177 address: Address,
178 storage: HashMap<StorageKey, StorageValue>,
179 ) -> Result<(), ExtDB::Error> {
180 let account = self.load_account(address)?;
181 account.account_state = AccountState::StorageCleared;
182 account.storage = storage.into_iter().collect();
183 Ok(())
184 }
185}
186
187impl<ExtDB> DatabaseCommit for CacheDB<ExtDB> {
188 fn commit(&mut self, changes: HashMap<Address, Account>) {
189 for (address, mut account) in changes {
190 if !account.is_touched() {
191 continue;
192 }
193 if account.is_selfdestructed() {
194 let db_account = self.cache.accounts.entry(address).or_default();
195 db_account.storage.clear();
196 db_account.account_state = AccountState::NotExisting;
197 db_account.info = AccountInfo::default();
198 continue;
199 }
200 let is_newly_created = account.is_created();
201 self.insert_contract(&mut account.info);
202
203 let db_account = self.cache.accounts.entry(address).or_default();
204 db_account.info = account.info;
205
206 db_account.account_state = if is_newly_created {
207 db_account.storage.clear();
208 AccountState::StorageCleared
209 } else if db_account.account_state.is_storage_cleared() {
210 AccountState::StorageCleared
212 } else {
213 AccountState::Touched
214 };
215 db_account.storage.extend(
216 account
217 .storage
218 .into_iter()
219 .map(|(key, value)| (key, value.present_value())),
220 );
221 }
222 }
223}
224
225impl<ExtDB: DatabaseRef> Database for CacheDB<ExtDB> {
226 type Error = ExtDB::Error;
227
228 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
229 let basic = match self.cache.accounts.entry(address) {
230 Entry::Occupied(entry) => entry.into_mut(),
231 Entry::Vacant(entry) => entry.insert(
232 self.db
233 .basic_ref(address)?
234 .map(|info| DbAccount {
235 info,
236 ..Default::default()
237 })
238 .unwrap_or_else(DbAccount::new_not_existing),
239 ),
240 };
241 Ok(basic.info())
242 }
243
244 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
245 match self.cache.contracts.entry(code_hash) {
246 Entry::Occupied(entry) => Ok(entry.get().clone()),
247 Entry::Vacant(entry) => {
248 Ok(entry.insert(self.db.code_by_hash_ref(code_hash)?).clone())
250 }
251 }
252 }
253
254 fn storage(
258 &mut self,
259 address: Address,
260 index: StorageKey,
261 ) -> Result<StorageValue, Self::Error> {
262 match self.cache.accounts.entry(address) {
263 Entry::Occupied(mut acc_entry) => {
264 let acc_entry = acc_entry.get_mut();
265 match acc_entry.storage.entry(index) {
266 Entry::Occupied(entry) => Ok(*entry.get()),
267 Entry::Vacant(entry) => {
268 if matches!(
269 acc_entry.account_state,
270 AccountState::StorageCleared | AccountState::NotExisting
271 ) {
272 Ok(StorageValue::ZERO)
273 } else {
274 let slot = self.db.storage_ref(address, index)?;
275 entry.insert(slot);
276 Ok(slot)
277 }
278 }
279 }
280 }
281 Entry::Vacant(acc_entry) => {
282 let info = self.db.basic_ref(address)?;
284 let (account, value) = if info.is_some() {
285 let value = self.db.storage_ref(address, index)?;
286 let mut account: DbAccount = info.into();
287 account.storage.insert(index, value);
288 (account, value)
289 } else {
290 (info.into(), StorageValue::ZERO)
291 };
292 acc_entry.insert(account);
293 Ok(value)
294 }
295 }
296 }
297
298 fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
299 match self.cache.block_hashes.entry(U256::from(number)) {
300 Entry::Occupied(entry) => Ok(*entry.get()),
301 Entry::Vacant(entry) => {
302 let hash = self.db.block_hash_ref(number)?;
303 entry.insert(hash);
304 Ok(hash)
305 }
306 }
307 }
308}
309
310impl<ExtDB: DatabaseRef> DatabaseRef for CacheDB<ExtDB> {
311 type Error = ExtDB::Error;
312
313 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
314 match self.cache.accounts.get(&address) {
315 Some(acc) => Ok(acc.info()),
316 None => self.db.basic_ref(address),
317 }
318 }
319
320 fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
321 match self.cache.contracts.get(&code_hash) {
322 Some(entry) => Ok(entry.clone()),
323 None => self.db.code_by_hash_ref(code_hash),
324 }
325 }
326
327 fn storage_ref(
328 &self,
329 address: Address,
330 index: StorageKey,
331 ) -> Result<StorageValue, Self::Error> {
332 match self.cache.accounts.get(&address) {
333 Some(acc_entry) => match acc_entry.storage.get(&index) {
334 Some(entry) => Ok(*entry),
335 None => {
336 if matches!(
337 acc_entry.account_state,
338 AccountState::StorageCleared | AccountState::NotExisting
339 ) {
340 Ok(StorageValue::ZERO)
341 } else {
342 self.db.storage_ref(address, index)
343 }
344 }
345 },
346 None => self.db.storage_ref(address, index),
347 }
348 }
349
350 fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
351 match self.cache.block_hashes.get(&U256::from(number)) {
352 Some(entry) => Ok(*entry),
353 None => self.db.block_hash_ref(number),
354 }
355 }
356}
357
358#[derive(Debug, Clone, Default)]
359#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
360pub struct DbAccount {
361 pub info: AccountInfo,
362 pub account_state: AccountState,
364 pub storage: HashMap<StorageKey, StorageValue>,
366}
367
368impl DbAccount {
369 pub fn new_not_existing() -> Self {
370 Self {
371 account_state: AccountState::NotExisting,
372 ..Default::default()
373 }
374 }
375
376 pub fn info(&self) -> Option<AccountInfo> {
377 if matches!(self.account_state, AccountState::NotExisting) {
378 None
379 } else {
380 Some(self.info.clone())
381 }
382 }
383}
384
385impl From<Option<AccountInfo>> for DbAccount {
386 fn from(from: Option<AccountInfo>) -> Self {
387 from.map(Self::from).unwrap_or_else(Self::new_not_existing)
388 }
389}
390
391impl From<AccountInfo> for DbAccount {
392 fn from(info: AccountInfo) -> Self {
393 Self {
394 info,
395 account_state: AccountState::None,
396 ..Default::default()
397 }
398 }
399}
400
401#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
402#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
403pub enum AccountState {
404 NotExisting,
407 Touched,
409 StorageCleared,
412 #[default]
414 None,
415}
416
417impl AccountState {
418 pub fn is_storage_cleared(&self) -> bool {
420 matches!(self, AccountState::StorageCleared)
421 }
422}
423
424#[derive(Debug, Default, Clone)]
428pub struct BenchmarkDB(pub Bytecode, B256);
429
430impl BenchmarkDB {
431 pub fn new_bytecode(bytecode: Bytecode) -> Self {
432 let hash = bytecode.hash_slow();
433 Self(bytecode, hash)
434 }
435}
436
437pub const FFADDRESS: Address = address!("0xffffffffffffffffffffffffffffffffffffffff");
439pub const BENCH_TARGET: Address = FFADDRESS;
440pub const BENCH_TARGET_BALANCE: U256 = U256::from_limbs([10_000_000, 0, 0, 0]);
441pub const EEADDRESS: Address = address!("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
443pub const BENCH_CALLER: Address = EEADDRESS;
444pub const BENCH_CALLER_BALANCE: U256 = U256::from_limbs([10_000_000, 0, 0, 0]);
445
446impl Database for BenchmarkDB {
447 type Error = Infallible;
448 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
450 if address == BENCH_TARGET {
451 return Ok(Some(AccountInfo {
452 nonce: 1,
453 balance: BENCH_TARGET_BALANCE,
454 code: Some(self.0.clone()),
455 code_hash: self.1,
456 }));
457 }
458 if address == BENCH_CALLER {
459 return Ok(Some(AccountInfo {
460 nonce: 0,
461 balance: BENCH_CALLER_BALANCE,
462 code: None,
463 code_hash: KECCAK_EMPTY,
464 }));
465 }
466 Ok(None)
467 }
468
469 fn code_by_hash(&mut self, _code_hash: B256) -> Result<Bytecode, Self::Error> {
471 Ok(Bytecode::default())
472 }
473
474 fn storage(
476 &mut self,
477 _address: Address,
478 _index: StorageKey,
479 ) -> Result<StorageValue, Self::Error> {
480 Ok(StorageValue::default())
481 }
482
483 fn block_hash(&mut self, _number: u64) -> Result<B256, Self::Error> {
485 Ok(B256::default())
486 }
487}
488
489#[cfg(test)]
490mod tests {
491 use super::{CacheDB, EmptyDB};
492 use database_interface::Database;
493 use primitives::{Address, HashMap, StorageKey, StorageValue};
494 use state::AccountInfo;
495
496 #[test]
497 fn test_insert_account_storage() {
498 let account = Address::with_last_byte(42);
499 let nonce = 42;
500 let mut init_state = CacheDB::new(EmptyDB::default());
501 init_state.insert_account_info(
502 account,
503 AccountInfo {
504 nonce,
505 ..Default::default()
506 },
507 );
508
509 let (key, value) = (StorageKey::from(123), StorageValue::from(456));
510 let mut new_state = CacheDB::new(init_state);
511 new_state
512 .insert_account_storage(account, key, value)
513 .unwrap();
514
515 assert_eq!(new_state.basic(account).unwrap().unwrap().nonce, nonce);
516 assert_eq!(new_state.storage(account, key), Ok(value));
517 }
518
519 #[test]
520 fn test_replace_account_storage() {
521 let account = Address::with_last_byte(42);
522 let nonce = 42;
523 let mut init_state = CacheDB::new(EmptyDB::default());
524 init_state.insert_account_info(
525 account,
526 AccountInfo {
527 nonce,
528 ..Default::default()
529 },
530 );
531
532 let (key0, value0) = (StorageKey::from(123), StorageValue::from(456));
533 let (key1, value1) = (StorageKey::from(789), StorageValue::from(999));
534 init_state
535 .insert_account_storage(account, key0, value0)
536 .unwrap();
537
538 let mut new_state = CacheDB::new(init_state);
539 new_state
540 .replace_account_storage(account, HashMap::from_iter([(key1, value1)]))
541 .unwrap();
542
543 assert_eq!(new_state.basic(account).unwrap().unwrap().nonce, nonce);
544 assert_eq!(new_state.storage(account, key0), Ok(StorageValue::ZERO));
545 assert_eq!(new_state.storage(account, key1), Ok(value1));
546 }
547
548 #[cfg(feature = "serde")]
549 #[test]
550 fn test_serialize_deserialize_cachedb() {
551 let account = Address::with_last_byte(69);
552 let nonce = 420;
553 let mut init_state = CacheDB::new(EmptyDB::default());
554 init_state.insert_account_info(
555 account,
556 AccountInfo {
557 nonce,
558 ..Default::default()
559 },
560 );
561
562 let serialized = serde_json::to_string(&init_state).unwrap();
563 let deserialized: CacheDB<EmptyDB> = serde_json::from_str(&serialized).unwrap();
564
565 assert!(deserialized.cache.accounts.contains_key(&account));
566 assert_eq!(
567 deserialized
568 .cache
569 .accounts
570 .get(&account)
571 .unwrap()
572 .info
573 .nonce,
574 nonce
575 );
576 }
577}