Skip to main content

ethrex_vm/backends/levm/
db.rs

1use ethrex_common::U256 as CoreU256;
2use ethrex_common::constants::EMPTY_KECCAK_HASH;
3use ethrex_common::types::{AccountState, Code, CodeMetadata};
4use ethrex_common::{Address as CoreAddress, H256 as CoreH256};
5use ethrex_levm::db::Database as LevmDatabase;
6
7use crate::VmDatabase;
8use crate::db::DynVmDatabase;
9use ethrex_levm::errors::DatabaseError;
10use std::collections::HashMap;
11use std::result::Result;
12use std::sync::{Arc, Mutex};
13
14#[derive(Clone)]
15pub struct DatabaseLogger {
16    pub block_hashes_accessed: Arc<Mutex<HashMap<u64, CoreH256>>>,
17    pub state_accessed: Arc<Mutex<HashMap<CoreAddress, Vec<CoreH256>>>>,
18    pub code_accessed: Arc<Mutex<Vec<CoreH256>>>,
19    pub store: Arc<dyn LevmDatabase>,
20}
21
22impl DatabaseLogger {
23    pub fn new(store: Arc<dyn LevmDatabase>) -> Self {
24        Self {
25            block_hashes_accessed: Arc::new(Mutex::new(HashMap::new())),
26            state_accessed: Arc::new(Mutex::new(HashMap::new())),
27            code_accessed: Arc::new(Mutex::new(vec![])),
28            store,
29        }
30    }
31}
32
33impl LevmDatabase for DatabaseLogger {
34    fn get_account_state(&self, address: CoreAddress) -> Result<AccountState, DatabaseError> {
35        self.state_accessed
36            .lock()
37            .map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?
38            .entry(address)
39            .or_default();
40        let state = self.store.as_ref().get_account_state(address)?;
41        Ok(state)
42    }
43
44    fn get_storage_value(
45        &self,
46        address: CoreAddress,
47        key: CoreH256,
48    ) -> Result<CoreU256, DatabaseError> {
49        self.state_accessed
50            .lock()
51            .map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?
52            .entry(address)
53            .and_modify(|keys| keys.push(key))
54            .or_insert(vec![key]);
55        self.store.as_ref().get_storage_value(address, key)
56    }
57
58    fn get_block_hash(&self, block_number: u64) -> Result<CoreH256, DatabaseError> {
59        let block_hash = self.store.as_ref().get_block_hash(block_number)?;
60        self.block_hashes_accessed
61            .lock()
62            .map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?
63            .insert(block_number, block_hash);
64        Ok(block_hash)
65    }
66
67    fn get_chain_config(&self) -> Result<ethrex_common::types::ChainConfig, DatabaseError> {
68        self.store.as_ref().get_chain_config()
69    }
70
71    fn get_account_code(&self, code_hash: CoreH256) -> Result<Code, DatabaseError> {
72        if code_hash != *EMPTY_KECCAK_HASH {
73            let mut code_accessed = self
74                .code_accessed
75                .lock()
76                .map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?;
77            code_accessed.push(code_hash);
78        }
79        self.store.as_ref().get_account_code(code_hash)
80    }
81
82    fn code_cache_budget_bytes(&self) -> u64 {
83        self.store.as_ref().code_cache_budget_bytes()
84    }
85
86    fn prefetch_codes(&self, code_hashes: &[CoreH256]) -> Result<u64, DatabaseError> {
87        // Record before delegating, exactly as the per-hash path does, so a batched
88        // read cannot leave the witness short of a bytecode it observed.
89        {
90            let mut code_accessed = self
91                .code_accessed
92                .lock()
93                .map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?;
94            code_accessed.extend(
95                code_hashes
96                    .iter()
97                    .filter(|h| **h != *EMPTY_KECCAK_HASH)
98                    .copied(),
99            );
100        }
101        self.store.as_ref().prefetch_codes(code_hashes)
102    }
103
104    fn get_code_metadata(&self, code_hash: CoreH256) -> Result<CodeMetadata, DatabaseError> {
105        // A size-only read still observes the bytecode, so the witness must carry it:
106        // EIP-8025 stateless validation recomputes the length from the code itself, and
107        // that is also how `ExecutionWitness::get_code_metadata` answers.
108        if code_hash != *EMPTY_KECCAK_HASH {
109            self.code_accessed
110                .lock()
111                .map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?
112                .push(code_hash);
113        }
114        self.store.get_code_metadata(code_hash)
115    }
116}
117
118impl LevmDatabase for DynVmDatabase {
119    fn get_account_state(&self, address: CoreAddress) -> Result<AccountState, DatabaseError> {
120        let acc_state = <dyn VmDatabase>::get_account_state(self.as_ref(), address)
121            .map_err(|e| DatabaseError::Custom(e.to_string()))?
122            .unwrap_or_default();
123
124        Ok(acc_state)
125    }
126
127    fn get_account_states_batch(
128        &self,
129        addresses: &[CoreAddress],
130    ) -> Result<Vec<AccountState>, DatabaseError> {
131        let states = <dyn VmDatabase>::get_account_states_batch(self.as_ref(), addresses)
132            .map_err(|e| DatabaseError::Custom(e.to_string()))?;
133        Ok(states
134            .into_iter()
135            .map(|opt| opt.unwrap_or_default())
136            .collect())
137    }
138
139    fn code_cache_budget_bytes(&self) -> u64 {
140        <dyn VmDatabase>::code_cache_budget_bytes(self.as_ref())
141    }
142
143    fn get_storage_value(
144        &self,
145        address: CoreAddress,
146        key: CoreH256,
147    ) -> Result<ethrex_common::U256, DatabaseError> {
148        Ok(
149            <dyn VmDatabase>::get_storage_slot(self.as_ref(), address, key)
150                .map_err(|e| DatabaseError::Custom(e.to_string()))?
151                .unwrap_or_default(),
152        )
153    }
154
155    fn get_storage_values_batch(
156        &self,
157        keys: &[(CoreAddress, CoreH256)],
158    ) -> Result<Vec<CoreU256>, DatabaseError> {
159        let values = <dyn VmDatabase>::get_storage_slots_batch(self.as_ref(), keys)
160            .map_err(|e| DatabaseError::Custom(e.to_string()))?;
161        Ok(values
162            .into_iter()
163            .map(|opt| opt.unwrap_or_default())
164            .collect())
165    }
166
167    fn get_block_hash(&self, block_number: u64) -> Result<CoreH256, DatabaseError> {
168        <dyn VmDatabase>::get_block_hash(self.as_ref(), block_number)
169            .map_err(|e| DatabaseError::Custom(e.to_string()))
170    }
171
172    fn get_chain_config(&self) -> Result<ethrex_common::types::ChainConfig, DatabaseError> {
173        <dyn VmDatabase>::get_chain_config(self.as_ref())
174            .map_err(|e| DatabaseError::Custom(e.to_string()))
175    }
176
177    fn get_account_code(&self, code_hash: CoreH256) -> Result<Code, DatabaseError> {
178        <dyn VmDatabase>::get_account_code(self.as_ref(), code_hash)
179            .map_err(|e| DatabaseError::Custom(e.to_string()))
180    }
181
182    fn get_account_codes_batch(
183        &self,
184        code_hashes: &[CoreH256],
185    ) -> Result<Vec<Option<Code>>, DatabaseError> {
186        <dyn VmDatabase>::get_account_codes_batch(self.as_ref(), code_hashes)
187            .map_err(|e| DatabaseError::Custom(e.to_string()))
188    }
189
190    fn get_code_metadata(&self, code_hash: CoreH256) -> Result<CodeMetadata, DatabaseError> {
191        <dyn VmDatabase>::get_code_metadata(self.as_ref(), code_hash)
192            .map_err(|e| DatabaseError::Custom(e.to_string()))
193    }
194}