1use crate::{errors::DatabaseError, precompiles::PrecompileCache};
2use ethrex_common::{
3 Address, H256, U256,
4 types::{AccountState, ChainConfig, Code, CodeMetadata},
5};
6use rustc_hash::{FxHashMap, FxHashSet};
7use std::sync::{
8 Arc, OnceLock, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard,
9 atomic::{AtomicU64, Ordering},
10};
11
12pub mod gen_db;
13
14pub const BLOATED_BATCH_THRESHOLD: usize = 16_384;
21
22type AccountCache = FxHashMap<Address, AccountState>;
24type StorageCache = FxHashMap<(Address, H256), U256>;
25type CodeCache = FxHashMap<H256, Code>;
26pub struct TouchedKeys {
28 pub accounts: Vec<(Address, H256)>,
30 pub slots: Vec<(Address, H256)>,
32}
33
34pub trait Database: Send + Sync {
35 fn get_account_state(&self, address: Address) -> Result<AccountState, DatabaseError>;
36 fn get_storage_value(&self, address: Address, key: H256) -> Result<U256, DatabaseError>;
37 fn get_block_hash(&self, block_number: u64) -> Result<H256, DatabaseError>;
38 fn get_chain_config(&self) -> Result<ChainConfig, DatabaseError>;
39 fn get_account_code(&self, code_hash: H256) -> Result<Code, DatabaseError>;
40 fn get_code_metadata(&self, code_hash: H256) -> Result<CodeMetadata, DatabaseError>;
41 fn precompile_cache(&self) -> Option<&PrecompileCache> {
43 None
44 }
45 fn get_account_states_batch(
49 &self,
50 addresses: &[Address],
51 ) -> Result<Vec<AccountState>, DatabaseError> {
52 addresses
53 .iter()
54 .map(|a| self.get_account_state(*a))
55 .collect()
56 }
57 fn get_account_codes_batch(
62 &self,
63 code_hashes: &[H256],
64 ) -> Result<Vec<Option<Code>>, DatabaseError> {
65 code_hashes
66 .iter()
67 .map(|h| self.get_account_code(*h).map(Some))
68 .collect()
69 }
70 fn get_storage_values_batch(
74 &self,
75 keys: &[(Address, H256)],
76 ) -> Result<Vec<U256>, DatabaseError> {
77 keys.iter()
78 .map(|&(addr, key)| self.get_storage_value(addr, key))
79 .collect()
80 }
81
82 fn code_cache_budget_bytes(&self) -> u64 {
89 64 * 1024 * 1024
90 }
91
92 fn prefetch_codes(&self, code_hashes: &[H256]) -> Result<u64, DatabaseError> {
98 let mut warmed = 0u64;
99 for &hash in code_hashes {
100 if let Ok(code) = self.get_account_code(hash) {
101 warmed = warmed.saturating_add(u64::try_from(code.size()).unwrap_or(u64::MAX));
102 }
103 }
104 Ok(warmed)
105 }
106 fn prefetch_accounts(&self, addresses: &[Address]) -> Result<(), DatabaseError> {
108 for &addr in addresses {
109 self.get_account_state(addr)?;
110 }
111 Ok(())
112 }
113 fn prefetch_storage(&self, keys: &[(Address, H256)]) -> Result<(), DatabaseError> {
115 for &(addr, key) in keys {
116 self.get_storage_value(addr, key)?;
117 }
118 Ok(())
119 }
120}
121
122pub struct CachingDatabase {
148 inner: Arc<dyn Database>,
149 accounts: RwLock<AccountCache>,
151 storage: RwLock<StorageCache>,
153 code: RwLock<CodeCache>,
155 code_bytes: AtomicU64,
159 precompile_cache: Option<PrecompileCache>,
162 chain_config: OnceLock<ChainConfig>,
164}
165
166impl CachingDatabase {
167 pub fn new(inner: Arc<dyn Database>, precompile_cache_enabled: bool) -> Self {
168 Self {
169 inner,
170 accounts: RwLock::new(FxHashMap::default()),
171 storage: RwLock::new(FxHashMap::default()),
172 code: RwLock::new(FxHashMap::default()),
173 code_bytes: AtomicU64::new(0),
174 precompile_cache: precompile_cache_enabled.then(PrecompileCache::new),
175 chain_config: OnceLock::new(),
176 }
177 }
178
179 fn read_accounts(&self) -> Result<RwLockReadGuard<'_, AccountCache>, DatabaseError> {
180 self.accounts.read().map_err(poison_error_to_db_error)
181 }
182
183 fn write_accounts(&self) -> Result<RwLockWriteGuard<'_, AccountCache>, DatabaseError> {
184 self.accounts.write().map_err(poison_error_to_db_error)
185 }
186
187 fn read_storage(&self) -> Result<RwLockReadGuard<'_, StorageCache>, DatabaseError> {
188 self.storage.read().map_err(poison_error_to_db_error)
189 }
190
191 fn write_storage(&self) -> Result<RwLockWriteGuard<'_, StorageCache>, DatabaseError> {
192 self.storage.write().map_err(poison_error_to_db_error)
193 }
194
195 pub fn code_bytes(&self) -> u64 {
197 self.code_bytes.load(Ordering::Relaxed)
198 }
199
200 fn keep_code(&self, cache: &mut CodeCache, code_hash: H256, code: Code) {
202 if let std::collections::hash_map::Entry::Vacant(entry) = cache.entry(code_hash) {
203 let size = u64::try_from(code.size()).unwrap_or(u64::MAX);
204 entry.insert(code);
205 self.code_bytes.fetch_add(size, Ordering::Relaxed);
206 }
207 }
208
209 fn read_code(&self) -> Result<RwLockReadGuard<'_, CodeCache>, DatabaseError> {
210 self.code.read().map_err(poison_error_to_db_error)
211 }
212
213 fn write_code(&self) -> Result<RwLockWriteGuard<'_, CodeCache>, DatabaseError> {
214 self.code.write().map_err(poison_error_to_db_error)
215 }
216
217 #[cfg(feature = "rayon")]
221 fn point_get_storage_many(
222 &self,
223 missing: &[(Address, H256)],
224 ) -> Result<Vec<U256>, DatabaseError> {
225 use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
226 missing
227 .par_iter()
228 .map(|&(addr, key)| self.inner.get_storage_value(addr, key))
229 .collect()
230 }
231
232 #[cfg(not(feature = "rayon"))]
233 fn point_get_storage_many(
234 &self,
235 missing: &[(Address, H256)],
236 ) -> Result<Vec<U256>, DatabaseError> {
237 missing
238 .iter()
239 .map(|&(addr, key)| self.inner.get_storage_value(addr, key))
240 .collect()
241 }
242
243 pub fn touched_keys_where(
249 &self,
250 account_filter: &dyn Fn(&Address) -> bool,
251 slot_filter: &dyn Fn(&(Address, H256)) -> bool,
252 ) -> TouchedKeys {
253 let accounts = self
254 .accounts
255 .read()
256 .map(|a| {
257 a.iter()
258 .filter(|(addr, _)| account_filter(addr))
259 .map(|(addr, st)| (*addr, st.storage_root))
260 .collect()
261 })
262 .unwrap_or_default();
263 let storage = self
264 .storage
265 .read()
266 .map(|s| s.keys().filter(|k| slot_filter(k)).copied().collect())
267 .unwrap_or_default();
268 TouchedKeys {
269 accounts,
270 slots: storage,
271 }
272 }
273
274 #[cfg(feature = "rayon")]
278 fn point_get_accounts_many(
279 &self,
280 missing: &[Address],
281 ) -> Result<Vec<AccountState>, DatabaseError> {
282 use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
283 missing
284 .par_iter()
285 .map(|&addr| self.inner.get_account_state(addr))
286 .collect()
287 }
288
289 #[cfg(not(feature = "rayon"))]
290 fn point_get_accounts_many(
291 &self,
292 missing: &[Address],
293 ) -> Result<Vec<AccountState>, DatabaseError> {
294 missing
295 .iter()
296 .map(|&addr| self.inner.get_account_state(addr))
297 .collect()
298 }
299}
300
301fn poison_error_to_db_error<T>(err: PoisonError<T>) -> DatabaseError {
302 DatabaseError::Custom(format!("Cache lock poisoned: {err}"))
303}
304
305impl Database for CachingDatabase {
306 fn get_account_state(&self, address: Address) -> Result<AccountState, DatabaseError> {
307 if let Some(state) = self.read_accounts()?.get(&address).copied() {
309 return Ok(state);
310 }
311
312 let state = self.inner.get_account_state(address)?;
314
315 self.write_accounts()?.insert(address, state);
317
318 Ok(state)
319 }
320
321 fn get_storage_value(&self, address: Address, key: H256) -> Result<U256, DatabaseError> {
322 if let Some(value) = self.read_storage()?.get(&(address, key)).copied() {
324 return Ok(value);
325 }
326
327 let value = self.inner.get_storage_value(address, key)?;
329
330 self.write_storage()?.insert((address, key), value);
332
333 Ok(value)
334 }
335
336 fn get_block_hash(&self, block_number: u64) -> Result<H256, DatabaseError> {
337 self.inner.get_block_hash(block_number)
340 }
341
342 fn get_chain_config(&self) -> Result<ChainConfig, DatabaseError> {
343 if let Some(cfg) = self.chain_config.get() {
344 return Ok(*cfg);
345 }
346 let cfg = self.inner.get_chain_config()?;
347 let _ = self.chain_config.set(cfg);
349 Ok(*self.chain_config.get().unwrap_or(&cfg))
350 }
351
352 fn get_account_code(&self, code_hash: H256) -> Result<Code, DatabaseError> {
353 if let Some(code) = self.read_code()?.get(&code_hash).cloned() {
355 return Ok(code);
356 }
357
358 let code = self.inner.get_account_code(code_hash)?;
360
361 self.keep_code(&mut *self.write_code()?, code_hash, code.clone());
363
364 Ok(code)
365 }
366
367 fn get_code_metadata(&self, code_hash: H256) -> Result<CodeMetadata, DatabaseError> {
368 if let Some(code) = self.read_code()?.get(&code_hash) {
374 return Ok(CodeMetadata {
375 length: u64::try_from(code.len()).unwrap_or(u64::MAX),
376 });
377 }
378 self.inner.get_code_metadata(code_hash)
379 }
380
381 fn precompile_cache(&self) -> Option<&PrecompileCache> {
382 self.precompile_cache.as_ref()
383 }
384
385 fn get_account_states_batch(
393 &self,
394 addresses: &[Address],
395 ) -> Result<Vec<AccountState>, DatabaseError> {
396 self.prefetch_accounts(addresses)?;
397 let cache = self.read_accounts()?;
398 addresses
399 .iter()
400 .map(|addr| {
401 cache.get(addr).copied().ok_or_else(|| {
402 DatabaseError::Custom(format!("account {addr:?} missing after prefetch"))
403 })
404 })
405 .collect()
406 }
407
408 fn code_cache_budget_bytes(&self) -> u64 {
409 self.inner.code_cache_budget_bytes()
410 }
411
412 fn prefetch_codes(&self, code_hashes: &[H256]) -> Result<u64, DatabaseError> {
420 let missing: Vec<H256> = {
421 let cache = self.read_code()?;
422 let mut seen: FxHashSet<H256> = FxHashSet::default();
423 code_hashes
424 .iter()
425 .copied()
426 .filter(|h| !cache.contains_key(h) && seen.insert(*h))
427 .collect()
428 };
429
430 if missing.is_empty() {
431 return Ok(0);
432 }
433
434 let codes = self.inner.get_account_codes_batch(&missing)?;
435 let mut warmed = 0u64;
436 let mut cache = self.write_code()?;
437 for (hash, code) in missing.into_iter().zip(codes.into_iter()) {
438 if let Some(code) = code {
440 warmed = warmed.saturating_add(u64::try_from(code.size()).unwrap_or(u64::MAX));
441 self.keep_code(&mut cache, hash, code);
442 }
443 }
444 Ok(warmed)
445 }
446
447 fn prefetch_accounts(&self, addresses: &[Address]) -> Result<(), DatabaseError> {
448 let missing: Vec<Address> = {
450 let cache = self.read_accounts()?;
451 addresses
452 .iter()
453 .copied()
454 .filter(|a| !cache.contains_key(a))
455 .collect()
456 };
457 if missing.is_empty() {
458 return Ok(());
459 }
460 let states = if missing.len() >= BLOATED_BATCH_THRESHOLD {
469 self.inner.get_account_states_batch(&missing)?
470 } else {
471 self.point_get_accounts_many(&missing)?
472 };
473 let mut cache = self.write_accounts()?;
474 for (addr, state) in missing.into_iter().zip(states.into_iter()) {
475 cache.entry(addr).or_insert(state);
476 }
477 Ok(())
478 }
479
480 fn prefetch_storage(&self, keys: &[(Address, H256)]) -> Result<(), DatabaseError> {
481 let missing: Vec<(Address, H256)> = {
483 let cache = self.read_storage()?;
484 keys.iter()
485 .copied()
486 .filter(|k| !cache.contains_key(k))
487 .collect()
488 };
489 if missing.is_empty() {
490 return Ok(());
491 }
492 let values = if missing.len() >= BLOATED_BATCH_THRESHOLD {
509 self.inner.get_storage_values_batch(&missing)?
514 } else {
515 self.point_get_storage_many(&missing)?
516 };
517 let mut cache = self.write_storage()?;
518 for (key, value) in missing.into_iter().zip(values.into_iter()) {
519 cache.entry(key).or_insert(value);
520 }
521 Ok(())
522 }
523}
524
525#[cfg(test)]
526#[allow(
527 clippy::unwrap_used,
528 clippy::indexing_slicing,
529 clippy::arithmetic_side_effects,
530 clippy::as_conversions
531)]
532mod code_bytes_tests {
533 use super::*;
534 use bytes::Bytes;
535
536 struct BigCodeStore;
538
539 fn code_for(hash: H256) -> Code {
540 let mut bytecode = vec![0x5b; 4096];
541 bytecode[..32].copy_from_slice(hash.as_bytes());
542 Code::from_bytecode_unchecked(Bytes::from(bytecode), hash)
543 }
544
545 impl Database for BigCodeStore {
546 fn get_account_state(&self, _: Address) -> Result<AccountState, DatabaseError> {
547 Ok(AccountState::default())
548 }
549 fn get_storage_value(&self, _: Address, _: H256) -> Result<U256, DatabaseError> {
550 Ok(U256::zero())
551 }
552 fn get_block_hash(&self, _: u64) -> Result<H256, DatabaseError> {
553 Ok(H256::zero())
554 }
555 fn get_chain_config(&self) -> Result<ChainConfig, DatabaseError> {
556 Ok(ChainConfig::default())
557 }
558 fn get_account_code(&self, code_hash: H256) -> Result<Code, DatabaseError> {
559 Ok(code_for(code_hash))
560 }
561 fn get_code_metadata(&self, code_hash: H256) -> Result<CodeMetadata, DatabaseError> {
562 Ok(CodeMetadata {
563 length: code_for(code_hash).len() as u64,
564 })
565 }
566 }
567
568 #[test]
571 fn code_bytes_counts_each_distinct_code_once() {
572 let cache = CachingDatabase::new(Arc::new(BigCodeStore), false);
573 let size = code_for(H256::zero()).size() as u64;
574 let hashes: Vec<H256> = (1..=4u64).map(H256::from_low_u64_be).collect();
575
576 cache.get_account_code(hashes[0]).unwrap();
577 cache.get_account_code(hashes[0]).unwrap();
578 assert_eq!(cache.code_bytes(), size);
579
580 cache.prefetch_codes(&hashes).unwrap();
581 cache.prefetch_codes(&hashes).unwrap();
582 assert_eq!(cache.code_bytes(), 4 * size);
583 assert_eq!(cache.read_code().unwrap().len(), 4);
584 }
585}