1use core::convert::Infallible;
2use database_interface::{
3 Database, DatabaseCommit, DatabaseRef, EmptyDB, BENCH_CALLER, BENCH_CALLER_BALANCE,
4 BENCH_TARGET, BENCH_TARGET_BALANCE,
5};
6use primitives::{
7 hash_map::Entry, Address, AddressMap, B256Map, HashMap, Log, StorageKey, StorageKeyMap,
8 StorageValue, U256Map, B256, KECCAK_EMPTY, U256,
9};
10use state::{Account, AccountInfo, Bytecode};
11use std::vec::Vec;
12
13pub type InMemoryDB = CacheDB<EmptyDB>;
15
16#[derive(Debug, Clone)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct Cache {
24 pub accounts: AddressMap<DbAccount>,
27 pub contracts: B256Map<Bytecode>,
29 pub logs: Vec<Log>,
31 pub block_hashes: U256Map<B256>,
33}
34
35impl Default for Cache {
36 fn default() -> Self {
37 let mut contracts = HashMap::default();
38 contracts.insert(KECCAK_EMPTY, Bytecode::default());
39 contracts.insert(B256::ZERO, Bytecode::default());
40
41 Cache {
42 accounts: HashMap::default(),
43 contracts,
44 logs: Vec::default(),
45 block_hashes: HashMap::default(),
46 }
47 }
48}
49
50#[derive(Debug, Clone)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
55pub struct CacheDB<ExtDB> {
56 pub cache: Cache,
58 pub db: ExtDB,
62}
63
64impl<ExtDB: Default> Default for CacheDB<ExtDB> {
65 fn default() -> Self {
66 Self::new(ExtDB::default())
67 }
68}
69
70impl<ExtDb> CacheDB<CacheDB<ExtDb>> {
71 pub fn flatten(self) -> CacheDB<ExtDb> {
79 let CacheDB {
80 cache:
81 Cache {
82 accounts,
83 contracts,
84 logs,
85 block_hashes,
86 },
87 db: mut inner,
88 ..
89 } = self;
90
91 inner.cache.accounts.extend(accounts);
92 inner.cache.contracts.extend(contracts);
93 inner.cache.logs.extend(logs);
94 inner.cache.block_hashes.extend(block_hashes);
95 inner
96 }
97
98 pub fn discard_outer(self) -> CacheDB<ExtDb> {
100 self.db
101 }
102}
103
104impl<ExtDB> CacheDB<ExtDB> {
105 pub fn new(db: ExtDB) -> Self {
107 Self {
108 cache: Cache::default(),
109 db,
110 }
111 }
112
113 pub fn insert_contract(&mut self, account: &mut AccountInfo) {
119 if let Some(code) = &account.code {
120 if !code.is_empty() {
121 if account.code_hash == KECCAK_EMPTY {
122 account.code_hash = code.hash_slow();
123 }
124 self.cache
125 .contracts
126 .entry(account.code_hash)
127 .or_insert_with(|| code.clone());
128 }
129 }
130 if account.code_hash.is_zero() {
131 account.code_hash = KECCAK_EMPTY;
132 }
133 }
134
135 pub fn insert_account_info(&mut self, address: Address, mut info: AccountInfo) {
137 self.insert_contract(&mut info);
138 let account_entry = self.cache.accounts.entry(address).or_default();
139 account_entry.update_info(info);
140 if account_entry.account_state == AccountState::NotExisting {
141 account_entry.update_account_state(AccountState::None);
142 }
143 }
144
145 fn commit_account(&mut self, address: Address, mut account: Account) {
146 if !account.is_touched() {
147 return;
148 }
149 if account.is_selfdestructed() {
150 let db_account = self.cache.accounts.entry(address).or_default();
151 db_account.storage.clear();
152 db_account.account_state = AccountState::NotExisting;
153 db_account.info = AccountInfo::default();
154 return;
155 }
156 let is_newly_created = account.is_created();
157 self.insert_contract(&mut account.info);
158
159 let db_account = self.cache.accounts.entry(address).or_default();
160 db_account.info = account.info;
161
162 db_account.account_state = if is_newly_created {
163 db_account.storage.clear();
164 AccountState::StorageCleared
165 } else if db_account.account_state.is_storage_cleared() {
166 AccountState::StorageCleared
168 } else {
169 AccountState::Touched
170 };
171 db_account.storage.extend(
172 account
173 .storage
174 .into_iter()
175 .map(|(key, value)| (key, value.present_value())),
176 );
177 }
178
179 pub fn nest(self) -> CacheDB<Self> {
181 CacheDB::new(self)
182 }
183}
184
185impl<ExtDB: DatabaseRef> CacheDB<ExtDB> {
186 pub fn load_account(&mut self, address: Address) -> Result<&mut DbAccount, ExtDB::Error> {
190 let db = &self.db;
191 match self.cache.accounts.entry(address) {
192 Entry::Occupied(entry) => Ok(entry.into_mut()),
193 Entry::Vacant(entry) => Ok(entry.insert(
194 db.basic_ref(address)?
195 .map(|info| DbAccount {
196 info,
197 ..Default::default()
198 })
199 .unwrap_or_else(DbAccount::new_not_existing),
200 )),
201 }
202 }
203
204 pub fn insert_account_storage(
206 &mut self,
207 address: Address,
208 slot: StorageKey,
209 value: StorageValue,
210 ) -> Result<(), ExtDB::Error> {
211 let account = self.load_account(address)?;
212 account.storage.insert(slot, value);
213 Ok(())
214 }
215
216 pub fn replace_account_storage(
218 &mut self,
219 address: Address,
220 storage: StorageKeyMap<StorageValue>,
221 ) -> Result<(), ExtDB::Error> {
222 let account = self.load_account(address)?;
223 account.account_state = AccountState::StorageCleared;
224 account.storage = storage.into_iter().collect();
225 Ok(())
226 }
227
228 #[cfg(feature = "std")]
230 pub fn pretty_print(&self) -> String {
231 let mut output = String::new();
232 output.push_str("CacheDB:\n");
233 output.push_str(&format!(
234 " accounts: {} total\n",
235 self.cache.accounts.len()
236 ));
237
238 let mut accounts: Vec<_> = self.cache.accounts.iter().collect();
240 accounts.sort_by_key(|(addr, _)| *addr);
241
242 for (address, db_account) in accounts {
243 output.push_str(&format!(" [{address}]:\n"));
244 output.push_str(&format!(" state: {:?}\n", db_account.account_state));
245
246 if let Some(info) = db_account.info() {
247 output.push_str(&format!(" balance: {}\n", info.balance));
248 output.push_str(&format!(" nonce: {}\n", info.nonce));
249 output.push_str(&format!(" code_hash: {}\n", info.code_hash));
250
251 if let Some(code) = info.code {
252 if !code.is_empty() {
253 output.push_str(&format!(" code: {} bytes\n", code.len()));
254 }
255 }
256 } else {
257 output.push_str(" account: None (not existing)\n");
258 }
259
260 if !db_account.storage.is_empty() {
261 output.push_str(&format!(
262 " storage: {} slots\n",
263 db_account.storage.len()
264 ));
265 let mut storage: Vec<_> = db_account.storage.iter().collect();
266 storage.sort_by_key(|(k, _)| *k);
267 for (key, value) in storage {
268 output.push_str(&format!(" [{key:#x}]: {value:#x}\n"));
269 }
270 }
271 }
272
273 if !self.cache.contracts.is_empty() {
274 output.push_str(&format!(
275 " contracts: {} total\n",
276 self.cache.contracts.len()
277 ));
278 let mut contracts: Vec<_> = self.cache.contracts.iter().collect();
279 contracts.sort_by_key(|(h, _)| *h);
280 for (hash, bytecode) in contracts {
281 output.push_str(&format!(" [{hash}]: {} bytes\n", bytecode.len()));
282 }
283 }
284
285 if !self.cache.logs.is_empty() {
287 output.push_str(&format!(" logs: {} total\n", self.cache.logs.len()));
288 for (i, log) in self.cache.logs.iter().enumerate() {
289 output.push_str(&format!(
291 " [{i}]: address: {:?}, topics: {}\n",
292 log.address,
293 log.data.topics().len()
294 ));
295 }
296 }
297
298 if !self.cache.block_hashes.is_empty() {
300 output.push_str(&format!(
301 " block_hashes: {} total\n",
302 self.cache.block_hashes.len()
303 ));
304 let mut block_hashes: Vec<_> = self.cache.block_hashes.iter().collect();
305 block_hashes.sort_by_key(|(num, _)| *num);
306 for (num, hash) in block_hashes {
307 output.push_str(&format!(" [{num}]: {hash}\n"));
308 }
309 }
310
311 output.push_str("}\n");
312 output
313 }
314}
315
316impl<ExtDB> DatabaseCommit for CacheDB<ExtDB> {
317 fn commit(&mut self, changes: AddressMap<Account>) {
318 for (address, account) in changes {
319 self.commit_account(address, account);
320 }
321 }
322
323 fn commit_iter(&mut self, changes: &mut dyn Iterator<Item = (Address, Account)>) {
324 for (address, account) in changes {
325 self.commit_account(address, account);
326 }
327 }
328}
329
330impl<ExtDB: DatabaseRef> Database for CacheDB<ExtDB> {
331 type Error = ExtDB::Error;
332
333 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
334 Ok(self.load_account(address)?.info())
335 }
336
337 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
338 match self.cache.contracts.entry(code_hash) {
339 Entry::Occupied(entry) => Ok(entry.get().clone()),
340 Entry::Vacant(entry) => {
341 Ok(entry.insert(self.db.code_by_hash_ref(code_hash)?).clone())
343 }
344 }
345 }
346
347 fn storage(
351 &mut self,
352 address: Address,
353 index: StorageKey,
354 ) -> Result<StorageValue, Self::Error> {
355 match self.cache.accounts.entry(address) {
356 Entry::Occupied(mut acc_entry) => {
357 let acc_entry = acc_entry.get_mut();
358 match acc_entry.storage.entry(index) {
359 Entry::Occupied(entry) => Ok(*entry.get()),
360 Entry::Vacant(entry) => {
361 if matches!(
362 acc_entry.account_state,
363 AccountState::StorageCleared | AccountState::NotExisting
364 ) {
365 Ok(StorageValue::ZERO)
366 } else {
367 let slot = self.db.storage_ref(address, index)?;
368 entry.insert(slot);
369 Ok(slot)
370 }
371 }
372 }
373 }
374 Entry::Vacant(acc_entry) => {
375 let info = self.db.basic_ref(address)?;
377 let (account, value) = if info.is_some() {
378 let value = self.db.storage_ref(address, index)?;
379 let mut account: DbAccount = info.into();
380 account.storage.insert(index, value);
381 (account, value)
382 } else {
383 (info.into(), StorageValue::ZERO)
384 };
385 acc_entry.insert(account);
386 Ok(value)
387 }
388 }
389 }
390
391 fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
392 match self.cache.block_hashes.entry(U256::from(number)) {
393 Entry::Occupied(entry) => Ok(*entry.get()),
394 Entry::Vacant(entry) => {
395 let hash = self.db.block_hash_ref(number)?;
396 entry.insert(hash);
397 Ok(hash)
398 }
399 }
400 }
401}
402
403impl<ExtDB: DatabaseRef> DatabaseRef for CacheDB<ExtDB> {
404 type Error = ExtDB::Error;
405
406 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
407 match self.cache.accounts.get(&address) {
408 Some(acc) => Ok(acc.info()),
409 None => self.db.basic_ref(address),
410 }
411 }
412
413 fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
414 match self.cache.contracts.get(&code_hash) {
415 Some(entry) => Ok(entry.clone()),
416 None => self.db.code_by_hash_ref(code_hash),
417 }
418 }
419
420 fn storage_ref(
421 &self,
422 address: Address,
423 index: StorageKey,
424 ) -> Result<StorageValue, Self::Error> {
425 match self.cache.accounts.get(&address) {
426 Some(acc_entry) => match acc_entry.storage.get(&index) {
427 Some(entry) => Ok(*entry),
428 None => {
429 if matches!(
430 acc_entry.account_state,
431 AccountState::StorageCleared | AccountState::NotExisting
432 ) {
433 Ok(StorageValue::ZERO)
434 } else {
435 self.db.storage_ref(address, index)
436 }
437 }
438 },
439 None => self.db.storage_ref(address, index),
440 }
441 }
442
443 fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
444 match self.cache.block_hashes.get(&U256::from(number)) {
445 Some(entry) => Ok(*entry),
446 None => self.db.block_hash_ref(number),
447 }
448 }
449}
450
451#[derive(Debug, Clone, Default)]
453#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
454pub struct DbAccount {
455 pub info: AccountInfo,
457 pub account_state: AccountState,
459 pub storage: StorageKeyMap<StorageValue>,
461}
462
463impl DbAccount {
464 pub fn new_not_existing() -> Self {
466 Self {
467 account_state: AccountState::NotExisting,
468 ..Default::default()
469 }
470 }
471
472 pub fn info(&self) -> Option<AccountInfo> {
474 if matches!(self.account_state, AccountState::NotExisting) {
475 None
476 } else {
477 Some(self.info.clone())
478 }
479 }
480
481 #[inline(always)]
483 pub fn update_info(&mut self, info: AccountInfo) {
484 self.info = info;
485 }
486
487 #[inline(always)]
489 pub const fn update_account_state(&mut self, account_state: AccountState) {
490 self.account_state = account_state;
491 }
492}
493
494impl From<Option<AccountInfo>> for DbAccount {
495 fn from(from: Option<AccountInfo>) -> Self {
496 from.map(Self::from).unwrap_or_else(Self::new_not_existing)
497 }
498}
499
500impl From<AccountInfo> for DbAccount {
501 fn from(info: AccountInfo) -> Self {
502 Self {
503 info,
504 account_state: AccountState::None,
505 ..Default::default()
506 }
507 }
508}
509
510#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
512#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
513pub enum AccountState {
514 NotExisting,
517 Touched,
519 StorageCleared,
522 #[default]
524 None,
525}
526
527impl AccountState {
528 pub const fn is_storage_cleared(&self) -> bool {
530 matches!(self, AccountState::StorageCleared)
531 }
532}
533
534#[derive(Debug, Default, Clone)]
538pub struct BenchmarkDB(pub Bytecode, B256);
539
540impl BenchmarkDB {
541 pub fn new_bytecode(bytecode: Bytecode) -> Self {
543 let hash = bytecode.hash_slow();
544 Self(bytecode, hash)
545 }
546}
547
548impl Database for BenchmarkDB {
549 type Error = Infallible;
550 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
552 if address == BENCH_TARGET {
553 return Ok(Some(AccountInfo {
554 nonce: 1,
555 balance: BENCH_TARGET_BALANCE,
556 code: Some(self.0.clone()),
557 code_hash: self.1,
558 ..Default::default()
559 }));
560 }
561 if address == BENCH_CALLER {
562 return Ok(Some(AccountInfo {
563 nonce: 0,
564 balance: BENCH_CALLER_BALANCE,
565 code: None,
566 code_hash: KECCAK_EMPTY,
567 ..Default::default()
568 }));
569 }
570 Ok(None)
571 }
572
573 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
575 if code_hash == self.1 {
576 Ok(self.0.clone())
577 } else {
578 Ok(Bytecode::default())
579 }
580 }
581
582 fn storage(
584 &mut self,
585 _address: Address,
586 _index: StorageKey,
587 ) -> Result<StorageValue, Self::Error> {
588 Ok(StorageValue::default())
589 }
590
591 fn block_hash(&mut self, _number: u64) -> Result<B256, Self::Error> {
593 Ok(B256::default())
594 }
595}
596
597#[cfg(test)]
598mod tests {
599 use super::{CacheDB, EmptyDB};
600 use database_interface::{Database, DatabaseCommit};
601 use primitives::{Address, HashMap, StorageKey, StorageValue};
602 use state::{Account, AccountInfo, EvmStorageSlot, TransactionId};
603
604 #[test]
605 fn test_insert_account_storage() {
606 let account = Address::with_last_byte(42);
607 let nonce = 42;
608 let mut init_state = CacheDB::new(EmptyDB::default());
609 init_state.insert_account_info(
610 account,
611 AccountInfo {
612 nonce,
613 ..Default::default()
614 },
615 );
616
617 let (key, value) = (StorageKey::from(123), StorageValue::from(456));
618 let mut new_state = CacheDB::new(init_state);
619 new_state
620 .insert_account_storage(account, key, value)
621 .unwrap();
622
623 assert_eq!(new_state.basic(account).unwrap().unwrap().nonce, nonce);
624 assert_eq!(new_state.storage(account, key), Ok(value));
625 }
626
627 #[test]
628 fn test_replace_account_storage() {
629 let account = Address::with_last_byte(42);
630 let nonce = 42;
631 let mut init_state = CacheDB::new(EmptyDB::default());
632 init_state.insert_account_info(
633 account,
634 AccountInfo {
635 nonce,
636 ..Default::default()
637 },
638 );
639
640 let (key0, value0) = (StorageKey::from(123), StorageValue::from(456));
641 let (key1, value1) = (StorageKey::from(789), StorageValue::from(999));
642 init_state
643 .insert_account_storage(account, key0, value0)
644 .unwrap();
645
646 let mut new_state = CacheDB::new(init_state);
647 new_state
648 .replace_account_storage(account, HashMap::from_iter([(key1, value1)]))
649 .unwrap();
650
651 assert_eq!(new_state.basic(account).unwrap().unwrap().nonce, nonce);
652 assert_eq!(new_state.storage(account, key0), Ok(StorageValue::ZERO));
653 assert_eq!(new_state.storage(account, key1), Ok(value1));
654 }
655
656 #[test]
657 fn commit_iter_applies_repeated_account_updates_in_order() {
658 let address = Address::with_last_byte(42);
659 let key = StorageKey::from(123);
660 let value = StorageValue::from(456);
661 let mut db = CacheDB::new(EmptyDB::default());
662
663 let first = Account::from(AccountInfo {
664 nonce: 1,
665 ..Default::default()
666 })
667 .with_touched_mark()
668 .with_storage(
669 [(
670 key,
671 EvmStorageSlot::new_changed(StorageValue::ZERO, value, TransactionId::ZERO),
672 )]
673 .into_iter(),
674 );
675 let second = Account::from(AccountInfo {
676 nonce: 2,
677 ..Default::default()
678 })
679 .with_touched_mark();
680
681 db.commit_iter(&mut [(address, first), (address, second)].into_iter());
682
683 assert_eq!(db.basic(address).unwrap().unwrap().nonce, 2);
684 assert_eq!(db.storage(address, key), Ok(value));
685 }
686
687 #[cfg(feature = "std")]
688 #[test]
689 fn test_pretty_print_cachedb() {
690 use primitives::{Bytes, Log, LogData, B256, U256};
691
692 let account = Address::with_last_byte(55);
693 let mut cachedb = CacheDB::new(EmptyDB::default());
694 cachedb.insert_account_info(
695 account,
696 AccountInfo {
697 nonce: 7,
698 ..Default::default()
699 },
700 );
701 let key = StorageKey::from(1);
702 let value = StorageValue::from(2);
703 cachedb.insert_account_storage(account, key, value).unwrap();
704
705 let log = Log {
707 address: account,
708 data: LogData::new(Vec::new(), Bytes::from(vec![0x01u8]))
709 .expect("LogData should have <=4 topics"),
710 };
711 cachedb.cache.logs.push(log);
712
713 cachedb
715 .cache
716 .block_hashes
717 .insert(U256::from(123u64), B256::from([1u8; 32]));
718
719 let s = cachedb.pretty_print();
720 assert!(s.contains("CacheDB:"));
721 assert!(s.contains("accounts: 1 total"));
722 assert!(s.contains("storage: 1 slots"));
724
725 assert!(s.contains("logs: 1 total"));
727 assert!(s.contains("block_hashes: 1 total"));
728 }
729
730 #[cfg(feature = "serde")]
731 #[test]
732 fn test_serialize_deserialize_cachedb() {
733 let account = Address::with_last_byte(69);
734 let nonce = 420;
735 let mut init_state = CacheDB::new(EmptyDB::default());
736 init_state.insert_account_info(
737 account,
738 AccountInfo {
739 nonce,
740 ..Default::default()
741 },
742 );
743
744 let serialized = serde_json::to_string(&init_state).unwrap();
745 let deserialized: CacheDB<EmptyDB> = serde_json::from_str(&serialized).unwrap();
746
747 assert!(deserialized.cache.accounts.contains_key(&account));
748 assert_eq!(
749 deserialized
750 .cache
751 .accounts
752 .get(&account)
753 .unwrap()
754 .info
755 .nonce,
756 nonce
757 );
758 }
759}