1use alloy_primitives::{
3 map::{AddressMap, B256Map, Entry, HashMap, U256Map},
4 Address, B256, U256,
5};
6pub use evm2::{bytecode::Bytecode, evm::AccountInfo};
7use evm2::{evm::Database, interpreter::Word};
8
9#[derive(Debug, Clone, Default)]
32pub struct CachedReads {
33 pub accounts: AddressMap<CachedAccount>,
35 pub contracts: B256Map<Bytecode>,
37 pub block_hashes: HashMap<u64, B256>,
39}
40
41impl CachedReads {
44 pub fn with_account_capacity(capacity: usize) -> Self {
46 Self {
47 accounts: AddressMap::with_capacity_and_hasher(capacity, Default::default()),
48 ..Default::default()
49 }
50 }
51
52 pub const fn as_db_mut<DB>(&mut self, db: DB) -> CachedReadsDbMut<'_, DB> {
54 CachedReadsDbMut { cached: self, db }
55 }
56
57 pub fn insert_account(&mut self, address: Address, info: AccountInfo, storage: U256Map<U256>) {
59 self.accounts.insert(address, CachedAccount { info: Some(info), storage });
60 }
61
62 pub fn extend(&mut self, other: Self) {
66 self.accounts.extend(other.accounts);
67 self.contracts.extend(other.contracts);
68 self.block_hashes.extend(other.block_hashes);
69 }
70}
71
72#[derive(Debug)]
78pub struct CachedReadsDbMut<'a, DB> {
79 pub cached: &'a mut CachedReads,
81 pub db: DB,
83}
84
85impl<'a, DB> CachedReadsDbMut<'a, DB> {
86 pub const fn inner(&self) -> &DB {
88 &self.db
89 }
90}
91
92impl<DB, T> AsRef<T> for CachedReadsDbMut<'_, DB>
93where
94 DB: AsRef<T>,
95{
96 fn as_ref(&self) -> &T {
97 self.inner().as_ref()
98 }
99}
100
101impl<DB: Database> Database for CachedReadsDbMut<'_, DB> {
102 type Error = DB::Error;
103
104 fn is_fatal(error: &Self::Error) -> bool {
105 DB::is_fatal(error)
106 }
107
108 fn get_account(&mut self, address: &Address) -> Result<Option<AccountInfo>, Self::Error> {
109 let basic = match self.cached.accounts.entry(*address) {
110 Entry::Occupied(entry) => entry.get().info.clone(),
111 Entry::Vacant(entry) => {
112 entry.insert(CachedAccount::new(self.db.get_account(address)?)).info.clone()
113 }
114 };
115 Ok(basic)
116 }
117
118 fn get_code_by_hash(&mut self, code_hash: &B256) -> Result<Bytecode, Self::Error> {
119 let code = match self.cached.contracts.entry(*code_hash) {
120 Entry::Occupied(entry) => entry.get().clone(),
121 Entry::Vacant(entry) => entry.insert(self.db.get_code_by_hash(code_hash)?).clone(),
122 };
123 Ok(code)
124 }
125
126 fn get_storage(&mut self, address: &Address, index: &Word) -> Result<Word, Self::Error> {
127 match self.cached.accounts.entry(*address) {
128 Entry::Occupied(mut acc_entry) => match acc_entry.get_mut().storage.entry(*index) {
129 Entry::Occupied(entry) => Ok(*entry.get()),
130 Entry::Vacant(entry) => Ok(*entry.insert(self.db.get_storage(address, index)?)),
131 },
132 Entry::Vacant(acc_entry) => {
133 let info = self.db.get_account(address)?;
135 let (account, value) = if info.is_some() {
136 let value = self.db.get_storage(address, index)?;
137 let mut account = CachedAccount::new(info);
138 account.storage.insert(*index, value);
139 (account, value)
140 } else {
141 (CachedAccount::new(info), U256::ZERO)
142 };
143 acc_entry.insert(account);
144 Ok(value)
145 }
146 }
147 }
148
149 fn get_block_hash(&mut self, number: &Word) -> Result<B256, Self::Error> {
150 let number = number.saturating_to();
151 let hash = match self.cached.block_hashes.entry(number) {
152 Entry::Occupied(entry) => *entry.get(),
153 Entry::Vacant(entry) => *entry.insert(self.db.get_block_hash(&U256::from(number))?),
154 };
155 Ok(hash)
156 }
157}
158
159#[derive(Debug, Clone)]
162pub struct CachedAccount {
163 pub info: Option<AccountInfo>,
165 pub storage: U256Map<U256>,
167}
168
169impl CachedAccount {
170 fn new(info: Option<AccountInfo>) -> Self {
171 Self { info, storage: U256Map::default() }
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use core::cell::Cell;
179 use std::{error::Error, fmt, rc::Rc};
180
181 #[test]
182 fn test_extend_with_two_cached_reads() {
183 let hash1 = B256::from_slice(&[1u8; 32]);
185 let hash2 = B256::from_slice(&[2u8; 32]);
186 let address1 = Address::from_slice(&[1u8; 20]);
187 let address2 = Address::from_slice(&[2u8; 20]);
188
189 let mut primary = {
191 let mut cache = CachedReads::default();
192 cache.accounts.insert(address1, CachedAccount::new(Some(AccountInfo::default())));
193 cache.contracts.insert(hash1, Bytecode::default());
194 cache.block_hashes.insert(1, hash1);
195 cache
196 };
197
198 let additional = {
200 let mut cache = CachedReads::default();
201 cache.accounts.insert(address2, CachedAccount::new(Some(AccountInfo::default())));
202 cache.contracts.insert(hash2, Bytecode::default());
203 cache.block_hashes.insert(2, hash2);
204 cache
205 };
206
207 primary.extend(additional);
209
210 assert!(
212 primary.accounts.len() == 2 &&
213 primary.contracts.len() == 2 &&
214 primary.block_hashes.len() == 2,
215 "All maps should contain 2 entries"
216 );
217
218 assert!(
220 primary.accounts.contains_key(&address1) &&
221 primary.accounts.contains_key(&address2) &&
222 primary.contracts.contains_key(&hash1) &&
223 primary.contracts.contains_key(&hash2) &&
224 primary.block_hashes.get(&1) == Some(&hash1) &&
225 primary.block_hashes.get(&2) == Some(&hash2),
226 "All expected entries should be present"
227 );
228 }
229
230 #[derive(Clone, Debug)]
231 struct CountingDb {
232 account: Option<AccountInfo>,
233 code: Bytecode,
234 storage: Word,
235 block_hash: B256,
236 reads: Rc<ReadCounts>,
237 }
238
239 #[derive(Debug, Default)]
240 struct ReadCounts {
241 accounts: Cell<usize>,
242 code: Cell<usize>,
243 storage: Cell<usize>,
244 block_hashes: Cell<usize>,
245 }
246
247 #[derive(Debug)]
248 struct TestError;
249
250 impl fmt::Display for TestError {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 f.write_str("test database error")
253 }
254 }
255
256 impl Error for TestError {}
257
258 impl Database for CountingDb {
259 type Error = TestError;
260
261 fn get_account(&mut self, _address: &Address) -> Result<Option<AccountInfo>, Self::Error> {
262 self.reads.accounts.set(self.reads.accounts.get() + 1);
263 Ok(self.account.clone())
264 }
265
266 fn get_code_by_hash(&mut self, _code_hash: &B256) -> Result<Bytecode, Self::Error> {
267 self.reads.code.set(self.reads.code.get() + 1);
268 Ok(self.code.clone())
269 }
270
271 fn get_storage(&mut self, _address: &Address, _key: &Word) -> Result<Word, Self::Error> {
272 self.reads.storage.set(self.reads.storage.get() + 1);
273 Ok(self.storage)
274 }
275
276 fn get_block_hash(&mut self, _number: &Word) -> Result<B256, Self::Error> {
277 self.reads.block_hashes.set(self.reads.block_hashes.get() + 1);
278 Ok(self.block_hash)
279 }
280 }
281
282 #[test]
283 fn cached_reads_database_avoids_repeated_backing_reads() {
284 let address = Address::repeat_byte(1);
285 let code_hash = B256::repeat_byte(2);
286 let block_hash = B256::repeat_byte(3);
287 let key = U256::from(4);
288 let value = U256::from(5);
289 let reads = Rc::new(ReadCounts::default());
290 let db = CountingDb {
291 account: Some(AccountInfo::new(U256::from(1), 2, code_hash, None)),
292 code: Bytecode::new_raw([0x00].as_slice().into()),
293 storage: value,
294 block_hash,
295 reads: reads.clone(),
296 };
297
298 let mut cached = CachedReads::default();
299 let mut db = cached.as_db_mut(db);
300
301 assert!(db.get_account(&address).unwrap().is_some());
302 assert!(db.get_account(&address).unwrap().is_some());
303 assert_eq!(db.get_code_by_hash(&code_hash).unwrap().original_bytes().as_ref(), [0x00]);
304 assert_eq!(db.get_code_by_hash(&code_hash).unwrap().original_bytes().as_ref(), [0x00]);
305 assert_eq!(db.get_storage(&address, &key).unwrap(), value);
306 assert_eq!(db.get_storage(&address, &key).unwrap(), value);
307 assert_eq!(db.get_block_hash(&U256::from(1)).unwrap(), block_hash);
308 assert_eq!(db.get_block_hash(&U256::from(1)).unwrap(), block_hash);
309
310 assert_eq!(reads.accounts.get(), 1);
311 assert_eq!(reads.code.get(), 1);
312 assert_eq!(reads.storage.get(), 1);
313 assert_eq!(reads.block_hashes.get(), 1);
314
315 assert!(cached.accounts.contains_key(&address));
316 assert!(cached.contracts.contains_key(&code_hash));
317 assert_eq!(cached.block_hashes.get(&1), Some(&block_hash));
318 }
319}