1use crate::{errors::DatabaseError, precompiles::PrecompileCache};
2use ethrex_common::{
3 Address, H256, U256,
4 types::{AccountState, ChainConfig, Code, CodeMetadata},
5};
6#[cfg(all(feature = "rayon", not(feature = "eip-8025")))]
7use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
8use rustc_hash::FxHashMap;
9use std::sync::{Arc, OnceLock, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
10
11pub mod gen_db;
12
13type AccountCache = FxHashMap<Address, AccountState>;
15type StorageCache = FxHashMap<(Address, H256), U256>;
16type CodeCache = FxHashMap<H256, Code>;
17pub struct TouchedKeys {
19 pub accounts: Vec<(Address, H256)>,
21 pub slots: Vec<(Address, H256)>,
23}
24
25pub trait Database: Send + Sync {
26 fn get_account_state(&self, address: Address) -> Result<AccountState, DatabaseError>;
27 fn get_storage_value(&self, address: Address, key: H256) -> Result<U256, DatabaseError>;
28 fn get_block_hash(&self, block_number: u64) -> Result<H256, DatabaseError>;
29 fn get_chain_config(&self) -> Result<ChainConfig, DatabaseError>;
30 fn get_account_code(&self, code_hash: H256) -> Result<Code, DatabaseError>;
31 fn get_code_metadata(&self, code_hash: H256) -> Result<CodeMetadata, DatabaseError>;
32 fn precompile_cache(&self) -> Option<&PrecompileCache> {
34 None
35 }
36 fn get_account_states_batch(
40 &self,
41 addresses: &[Address],
42 ) -> Result<Vec<AccountState>, DatabaseError> {
43 addresses
44 .iter()
45 .map(|a| self.get_account_state(*a))
46 .collect()
47 }
48 fn prefetch_accounts(&self, addresses: &[Address]) -> Result<(), DatabaseError> {
50 for &addr in addresses {
51 self.get_account_state(addr)?;
52 }
53 Ok(())
54 }
55 fn prefetch_storage(&self, keys: &[(Address, H256)]) -> Result<(), DatabaseError> {
57 for &(addr, key) in keys {
58 self.get_storage_value(addr, key)?;
59 }
60 Ok(())
61 }
62}
63
64pub struct CachingDatabase {
90 inner: Arc<dyn Database>,
91 accounts: RwLock<AccountCache>,
93 storage: RwLock<StorageCache>,
95 code: RwLock<CodeCache>,
97 precompile_cache: Option<PrecompileCache>,
100 chain_config: OnceLock<ChainConfig>,
102}
103
104impl CachingDatabase {
105 pub fn new(inner: Arc<dyn Database>, precompile_cache_enabled: bool) -> Self {
106 Self {
107 inner,
108 accounts: RwLock::new(FxHashMap::default()),
109 storage: RwLock::new(FxHashMap::default()),
110 code: RwLock::new(FxHashMap::default()),
111 precompile_cache: precompile_cache_enabled.then(PrecompileCache::new),
112 chain_config: OnceLock::new(),
113 }
114 }
115
116 fn read_accounts(&self) -> Result<RwLockReadGuard<'_, AccountCache>, DatabaseError> {
117 self.accounts.read().map_err(poison_error_to_db_error)
118 }
119
120 fn write_accounts(&self) -> Result<RwLockWriteGuard<'_, AccountCache>, DatabaseError> {
121 self.accounts.write().map_err(poison_error_to_db_error)
122 }
123
124 fn read_storage(&self) -> Result<RwLockReadGuard<'_, StorageCache>, DatabaseError> {
125 self.storage.read().map_err(poison_error_to_db_error)
126 }
127
128 fn write_storage(&self) -> Result<RwLockWriteGuard<'_, StorageCache>, DatabaseError> {
129 self.storage.write().map_err(poison_error_to_db_error)
130 }
131
132 fn read_code(&self) -> Result<RwLockReadGuard<'_, CodeCache>, DatabaseError> {
133 self.code.read().map_err(poison_error_to_db_error)
134 }
135
136 fn write_code(&self) -> Result<RwLockWriteGuard<'_, CodeCache>, DatabaseError> {
137 self.code.write().map_err(poison_error_to_db_error)
138 }
139
140 pub fn touched_keys_where(
146 &self,
147 account_filter: &dyn Fn(&Address) -> bool,
148 slot_filter: &dyn Fn(&(Address, H256)) -> bool,
149 ) -> TouchedKeys {
150 let accounts = self
151 .accounts
152 .read()
153 .map(|a| {
154 a.iter()
155 .filter(|(addr, _)| account_filter(addr))
156 .map(|(addr, st)| (*addr, st.storage_root))
157 .collect()
158 })
159 .unwrap_or_default();
160 let storage = self
161 .storage
162 .read()
163 .map(|s| s.keys().filter(|k| slot_filter(k)).copied().collect())
164 .unwrap_or_default();
165 TouchedKeys {
166 accounts,
167 slots: storage,
168 }
169 }
170}
171
172fn poison_error_to_db_error<T>(err: PoisonError<T>) -> DatabaseError {
173 DatabaseError::Custom(format!("Cache lock poisoned: {err}"))
174}
175
176impl Database for CachingDatabase {
177 fn get_account_state(&self, address: Address) -> Result<AccountState, DatabaseError> {
178 if let Some(state) = self.read_accounts()?.get(&address).copied() {
180 return Ok(state);
181 }
182
183 let state = self.inner.get_account_state(address)?;
185
186 self.write_accounts()?.insert(address, state);
188
189 Ok(state)
190 }
191
192 fn get_storage_value(&self, address: Address, key: H256) -> Result<U256, DatabaseError> {
193 if let Some(value) = self.read_storage()?.get(&(address, key)).copied() {
195 return Ok(value);
196 }
197
198 let value = self.inner.get_storage_value(address, key)?;
200
201 self.write_storage()?.insert((address, key), value);
203
204 Ok(value)
205 }
206
207 fn get_block_hash(&self, block_number: u64) -> Result<H256, DatabaseError> {
208 self.inner.get_block_hash(block_number)
211 }
212
213 fn get_chain_config(&self) -> Result<ChainConfig, DatabaseError> {
214 if let Some(cfg) = self.chain_config.get() {
215 return Ok(*cfg);
216 }
217 let cfg = self.inner.get_chain_config()?;
218 let _ = self.chain_config.set(cfg);
220 Ok(*self.chain_config.get().unwrap_or(&cfg))
221 }
222
223 fn get_account_code(&self, code_hash: H256) -> Result<Code, DatabaseError> {
224 if let Some(code) = self.read_code()?.get(&code_hash).cloned() {
226 return Ok(code);
227 }
228
229 let code = self.inner.get_account_code(code_hash)?;
231
232 self.write_code()?.insert(code_hash, code.clone());
234
235 Ok(code)
236 }
237
238 fn get_code_metadata(&self, code_hash: H256) -> Result<CodeMetadata, DatabaseError> {
239 self.inner.get_code_metadata(code_hash)
243 }
244
245 fn precompile_cache(&self) -> Option<&PrecompileCache> {
246 self.precompile_cache.as_ref()
247 }
248
249 fn prefetch_accounts(&self, addresses: &[Address]) -> Result<(), DatabaseError> {
250 let missing: Vec<Address> = {
252 let cache = self.read_accounts()?;
253 addresses
254 .iter()
255 .copied()
256 .filter(|a| !cache.contains_key(a))
257 .collect()
258 };
259 if missing.is_empty() {
260 return Ok(());
261 }
262 let states = self.inner.get_account_states_batch(&missing)?;
266 let mut cache = self.write_accounts()?;
267 for (addr, state) in missing.into_iter().zip(states.into_iter()) {
268 cache.entry(addr).or_insert(state);
269 }
270 Ok(())
271 }
272
273 #[cfg(all(feature = "rayon", not(feature = "eip-8025")))]
274 fn prefetch_storage(&self, keys: &[(Address, H256)]) -> Result<(), DatabaseError> {
275 let fetched: Vec<((Address, H256), U256)> = keys
277 .par_iter()
278 .map(|&(addr, key)| {
279 self.inner
280 .get_storage_value(addr, key)
281 .map(|v| ((addr, key), v))
282 })
283 .collect::<Result<_, _>>()?;
284 let mut cache = self.write_storage()?;
285 for (key, value) in fetched {
286 cache.entry(key).or_insert(value);
287 }
288 Ok(())
289 }
290}