reth_execution_cache/
txpool.rs1use alloy_primitives::{Address, StorageKey, StorageValue, B256, U256};
4use reth_primitives_traits::{Account, Bytecode};
5use reth_revm::cached::CachedReads;
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_revm::{
63 cached::CachedAccount,
64 revm::{bytecode::Bytecode as RevmBytecode, state::AccountInfo},
65 };
66
67 #[test]
68 fn lookups_preserve_cache_semantics() {
69 let owner = Address::repeat_byte(0x01);
70 let missing = Address::repeat_byte(0x02);
71 let code_hash = B256::repeat_byte(0x0C);
72 let empty_code_hash = B256::repeat_byte(0x0D);
73
74 let mut reads = CachedReads::default();
75 let mut storage = U256Map::default();
76 storage.insert(U256::from(1), U256::from(7));
77 storage.insert(U256::from(2), U256::ZERO);
78 reads.insert_account(owner, AccountInfo { nonce: 3, ..Default::default() }, storage);
79 reads.accounts.insert(missing, CachedAccount { info: None, storage: Default::default() });
80 reads.contracts.insert(code_hash, RevmBytecode::new_raw([0x60, 0x01].into()));
81 reads.contracts.insert(empty_code_hash, RevmBytecode::default());
82
83 let snapshot = TxPoolPrewarmCacheSnapshot::new(B256::ZERO, Arc::new(reads));
84
85 assert_eq!(snapshot.account(&owner).unwrap().unwrap().nonce, 3);
86 assert_eq!(snapshot.account(&missing), Some(None), "non-existence is a cacheable fact");
87 assert_eq!(snapshot.account(&Address::repeat_byte(0x03)), None);
88
89 let slot = |n: u64| B256::from(U256::from(n));
90 assert_eq!(snapshot.storage(owner, slot(1)), Some(U256::from(7)));
91 assert_eq!(snapshot.storage(owner, slot(2)), Some(U256::ZERO), "zero values are hits");
92 assert_eq!(snapshot.storage(owner, slot(9)), None);
93 assert_eq!(snapshot.storage(missing, slot(1)), None);
94
95 assert!(snapshot.bytecode(&code_hash).unwrap().is_some());
96 assert_eq!(snapshot.bytecode(&empty_code_hash), None, "empty code reads as a miss");
97 assert_eq!(snapshot.bytecode(&B256::repeat_byte(0x0E)), None);
98
99 assert_eq!(snapshot.entry_counts(), (2, 2, 2));
100 }
101}