reth_execution_cache/
txpool.rs1use alloy_primitives::{Address, StorageKey, StorageValue, B256, U256};
4use reth_evm::cached::CachedReads;
5use reth_primitives_traits::{Account, Bytecode};
6use std::sync::Arc;
7
8#[derive(Clone, Debug)]
13pub struct TxPoolPrewarmCacheSnapshot {
14 parent_hash: B256,
15 reads: Arc<CachedReads>,
16}
17
18impl TxPoolPrewarmCacheSnapshot {
19 pub const fn new(parent_hash: B256, reads: Arc<CachedReads>) -> Self {
21 Self { parent_hash, reads }
22 }
23
24 pub const fn parent_hash(&self) -> B256 {
26 self.parent_hash
27 }
28
29 pub fn account(&self, address: &Address) -> Option<Option<Account>> {
31 self.reads.accounts.get(address).map(|account| account.info.as_ref().map(Account::from))
32 }
33
34 pub fn storage(&self, address: Address, key: StorageKey) -> Option<StorageValue> {
36 self.reads.accounts.get(&address)?.storage.get(&U256::from_be_bytes(key.0)).copied()
37 }
38
39 pub fn bytecode(&self, code_hash: &B256) -> Option<Option<Bytecode>> {
44 let code = self.reads.contracts.get(code_hash)?;
45 (!code.is_empty()).then(|| Some(Bytecode(code.clone())))
46 }
47
48 pub fn entry_counts(&self) -> (usize, usize, usize) {
50 (
51 self.reads.accounts.len(),
52 self.reads.accounts.values().map(|account| account.storage.len()).sum(),
53 self.reads.contracts.len(),
54 )
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use alloy_primitives::map::U256Map;
62 use reth_evm::cached::{AccountInfo, Bytecode as EvmBytecode, CachedAccount};
63
64 #[test]
65 fn lookups_preserve_cache_semantics() {
66 let owner = Address::repeat_byte(0x01);
67 let missing = Address::repeat_byte(0x02);
68 let code_hash = B256::repeat_byte(0x0C);
69 let empty_code_hash = B256::repeat_byte(0x0D);
70
71 let mut reads = CachedReads::default();
72 let mut storage = U256Map::default();
73 storage.insert(U256::ONE, U256::from(7));
74 storage.insert(U256::from(2), U256::ZERO);
75 reads.insert_account(owner, AccountInfo { nonce: 3, ..Default::default() }, storage);
76 reads.accounts.insert(missing, CachedAccount { info: None, storage: Default::default() });
77 reads.contracts.insert(code_hash, EvmBytecode::new_legacy([0xef, 0x01].into()));
78 reads.contracts.insert(empty_code_hash, EvmBytecode::default());
79
80 let snapshot = TxPoolPrewarmCacheSnapshot::new(B256::ZERO, Arc::new(reads));
81
82 assert_eq!(snapshot.account(&owner).unwrap().unwrap().nonce, 3);
83 assert_eq!(snapshot.account(&missing), Some(None), "non-existence is a cacheable fact");
84 assert_eq!(snapshot.account(&Address::repeat_byte(0x03)), None);
85
86 let slot = |n: u64| B256::from(U256::from(n));
87 assert_eq!(snapshot.storage(owner, slot(1)), Some(U256::from(7)));
88 assert_eq!(snapshot.storage(owner, slot(2)), Some(U256::ZERO), "zero values are hits");
89 assert_eq!(snapshot.storage(owner, slot(9)), None);
90 assert_eq!(snapshot.storage(missing, slot(1)), None);
91
92 let code = snapshot.bytecode(&code_hash).unwrap().unwrap();
93 assert!(code.is_legacy());
94 assert_eq!(code.original_bytes().as_ref(), &[0xef, 0x01]);
95 assert_eq!(snapshot.bytecode(&empty_code_hash), None, "empty code reads as a miss");
96 assert_eq!(snapshot.bytecode(&B256::repeat_byte(0x0E)), None);
97
98 assert_eq!(snapshot.entry_counts(), (2, 2, 2));
99 }
100}