verbs_rs/db/
fork_db.rs

1use super::error::DatabaseError;
2use super::provider::ProviderBuilder;
3use super::runtime_client::RuntimeClient;
4use super::traits::DB;
5use super::types::RequestCache;
6use super::types::{ToAlloy, ToEthers};
7use alloy_primitives::{keccak256, Address, Bytes};
8pub use ethers_core::types::BlockNumber;
9use ethers_core::types::{BigEndianHash, Block, BlockId, NameOrAddress, H256};
10use ethers_providers::{Middleware, Provider, ProviderError};
11use revm::db::in_memory_db::DbAccount;
12use revm::db::{AccountState, DatabaseCommit};
13use revm::primitives::{
14    hash_map::Entry, Account, AccountInfo, Bytecode, HashMap, Log, B256, KECCAK_EMPTY, U256,
15};
16use revm::Database;
17
18/// Database with ability to load data from a remote fork
19///
20/// In memory database that has the ability to request
21/// missing account/storage values from a remote
22/// endpoint (e.g. [Alchemy](https://www.alchemy.com/)).
23/// This means simulations can be run from live deployments
24/// of contracts and protocols.
25///
26/// <div class="warning">
27///
28/// Since it makes requests from a remote endpoint
29/// this database can be significantly slower than
30/// a purely local DB like [super::LocalDB].
31///
32/// </div>
33///
34#[derive(Debug, Clone)]
35pub struct ForkDb {
36    pub accounts: HashMap<Address, DbAccount>,
37    pub contracts: HashMap<B256, Bytecode>,
38    pub logs: Vec<Log>,
39    pub block_hashes: HashMap<U256, B256>,
40    provider: Provider<RuntimeClient>,
41    block_id: Option<BlockId>,
42    pub block: Block<H256>,
43    pub requests: RequestCache,
44}
45
46impl ForkDb {
47    pub fn new(node_url: &str, block_number: Option<u64>) -> Self {
48        let block_number = match block_number {
49            Some(n) => BlockNumber::Number(n.into()),
50            None => BlockNumber::Latest,
51        };
52
53        let provider = ProviderBuilder::new(node_url).build().unwrap();
54
55        let rt = tokio::runtime::Builder::new_current_thread()
56            .enable_all()
57            .build()
58            .unwrap();
59
60        let block = rt.block_on(provider.get_block(block_number)).unwrap();
61
62        let block = match block {
63            Some(b) => b,
64            None => panic!("Could not retrieve block"),
65        };
66
67        let mut contracts = HashMap::new();
68        contracts.insert(KECCAK_EMPTY, Bytecode::new());
69        contracts.insert(B256::ZERO, Bytecode::new());
70
71        // Track the original time and block for when we want
72        //  to run a sim using the same cache
73        let timestamp = U256::try_from(block.timestamp.as_u128()).unwrap();
74        let block_number = match block.number {
75            Some(n) => U256::try_from(n.as_u64()).unwrap(),
76            None => U256::ZERO,
77        };
78
79        Self {
80            accounts: HashMap::new(),
81            contracts,
82            logs: Vec::default(),
83            block_hashes: HashMap::new(),
84            provider,
85            block_id: Some(block.number.unwrap().into()),
86            block,
87            requests: RequestCache {
88                start_timestamp: timestamp,
89                start_block_number: block_number,
90                accounts: Vec::new(),
91                storage: Vec::new(),
92            },
93        }
94    }
95
96    pub fn insert_contract(&mut self, account: &mut AccountInfo) {
97        if let Some(code) = &account.code {
98            if !code.is_empty() {
99                if account.code_hash == KECCAK_EMPTY {
100                    account.code_hash = code.hash_slow();
101                }
102                self.contracts
103                    .entry(account.code_hash)
104                    .or_insert_with(|| code.clone());
105            }
106        }
107        if account.code_hash == B256::ZERO {
108            account.code_hash = KECCAK_EMPTY;
109        }
110    }
111
112    pub fn insert_account_info(&mut self, address: Address, mut info: AccountInfo) {
113        self.insert_contract(&mut info);
114        self.accounts.entry(address).or_default().info = info;
115    }
116
117    pub fn load_account(&mut self, address: Address) -> Result<&mut DbAccount, DatabaseError> {
118        match self.accounts.entry(address) {
119            Entry::Occupied(entry) => Ok(entry.into_mut()),
120            Entry::Vacant(_) => Err(DatabaseError::GetAccount(address)),
121        }
122    }
123
124    pub fn insert_account_storage(
125        &mut self,
126        address: Address,
127        slot: U256,
128        value: U256,
129    ) -> Result<(), DatabaseError> {
130        let account = self.load_account(address)?;
131        account.storage.insert(slot, value);
132        Ok(())
133    }
134
135    pub fn replace_account_storage(
136        &mut self,
137        address: Address,
138        storage: HashMap<U256, U256>,
139    ) -> Result<(), DatabaseError> {
140        let account = self.load_account(address)?;
141        account.account_state = AccountState::StorageCleared;
142        account.storage = storage.into_iter().collect();
143        Ok(())
144    }
145}
146
147impl DB for ForkDb {
148    fn insert_account_info(&mut self, address: Address, account_info: AccountInfo) {
149        self.insert_account_info(address, account_info)
150    }
151
152    fn accounts(&self) -> &HashMap<Address, DbAccount> {
153        &self.accounts
154    }
155
156    fn contracts(&self) -> &HashMap<B256, Bytecode> {
157        &self.contracts
158    }
159
160    fn logs(&self) -> &Vec<Log> {
161        &self.logs
162    }
163
164    fn block_hashes(&self) -> &HashMap<U256, B256> {
165        &self.block_hashes
166    }
167}
168
169impl DatabaseCommit for ForkDb {
170    fn commit(&mut self, changes: HashMap<Address, Account>) {
171        for (address, mut account) in changes {
172            if !account.is_touched() {
173                continue;
174            }
175            if account.is_selfdestructed() {
176                let db_account = self.accounts.entry(address).or_default();
177                db_account.storage.clear();
178                db_account.account_state = AccountState::NotExisting;
179                db_account.info = AccountInfo::default();
180                continue;
181            }
182            let is_newly_created = account.is_created();
183            self.insert_contract(&mut account.info);
184
185            let db_account = self.accounts.entry(address).or_default();
186            db_account.info = account.info;
187
188            db_account.account_state = if is_newly_created {
189                db_account.storage.clear();
190                AccountState::StorageCleared
191            } else if db_account.account_state.is_storage_cleared() {
192                // Preserve old account state if it already exists
193                AccountState::StorageCleared
194            } else {
195                AccountState::Touched
196            };
197            db_account.storage.extend(
198                account
199                    .storage
200                    .into_iter()
201                    .map(|(key, value)| (key, value.present_value())),
202            );
203        }
204    }
205}
206
207impl Database for ForkDb {
208    type Error = DatabaseError;
209
210    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
211        let basic = self.accounts.entry(address);
212        let basic = match basic {
213            Entry::Occupied(entry) => entry.into_mut(),
214            Entry::Vacant(entry) => {
215                let info = basic_from_fork(&self.provider, address, self.block_id);
216                let account = match info {
217                    Ok(i) => {
218                        self.requests.accounts.push((address, i.clone()));
219                        DbAccount {
220                            info: i,
221                            ..Default::default()
222                        }
223                    }
224                    Err(_) => DbAccount::new_not_existing(),
225                };
226                entry.insert(account)
227            }
228        };
229        Ok(basic.info())
230    }
231
232    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
233        match self.contracts.entry(code_hash) {
234            Entry::Occupied(entry) => Ok(entry.get().clone()),
235            Entry::Vacant(_) => Err(DatabaseError::MissingCode(code_hash)),
236        }
237    }
238
239    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
240        match self.accounts.entry(address) {
241            Entry::Occupied(mut acc_entry) => {
242                let acc_entry = acc_entry.get_mut();
243                match acc_entry.storage.entry(index) {
244                    Entry::Occupied(entry) => Ok(*entry.get()),
245                    Entry::Vacant(entry) => {
246                        if matches!(
247                            acc_entry.account_state,
248                            AccountState::StorageCleared | AccountState::NotExisting
249                        ) {
250                            Ok(U256::ZERO)
251                        } else {
252                            let slot =
253                                storage_from_fork(&self.provider, address, index, self.block_id);
254                            match slot {
255                                Ok(s) => {
256                                    self.requests.storage.push((address, index, s));
257                                    entry.insert(s);
258                                    Ok(s)
259                                }
260                                Err(_) => Err(DatabaseError::GetStorage(address, index)),
261                            }
262                        }
263                    }
264                }
265            }
266            Entry::Vacant(_) => Err(DatabaseError::GetAccount(address)),
267        }
268    }
269
270    fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error> {
271        match self.block_hashes.entry(number) {
272            Entry::Occupied(entry) => Ok(*entry.get()),
273            Entry::Vacant(entry) => {
274                let hash = block_hash_from_fork(&self.provider, number);
275                match hash {
276                    Ok(h) => {
277                        entry.insert(h);
278                        Ok(h)
279                    }
280                    Err(_) => Err(DatabaseError::GetBlockHash(number)),
281                }
282            }
283        }
284    }
285}
286
287fn basic_from_fork(
288    provider: &Provider<RuntimeClient>,
289    address: Address,
290    block_id: Option<BlockId>,
291) -> Result<AccountInfo, ProviderError> {
292    let add = NameOrAddress::Address(address.to_ethers());
293
294    let rt = tokio::runtime::Builder::new_current_thread()
295        .enable_all()
296        .build()
297        .unwrap();
298
299    let balance = rt.block_on(provider.get_balance(add.clone(), block_id))?;
300    let nonce = rt.block_on(provider.get_transaction_count(add.clone(), block_id))?;
301    let code = rt.block_on(provider.get_code(add, block_id))?;
302    let code = Bytes::from(code.0);
303
304    let (code, code_hash) = if !code.is_empty() {
305        (code.clone(), keccak256(&code))
306    } else {
307        (Bytes::default(), KECCAK_EMPTY)
308    };
309
310    Ok(AccountInfo {
311        balance: balance.to_alloy(),
312        nonce: nonce.as_u64(),
313        code_hash,
314        code: Some(Bytecode::new_raw(code).to_checked()),
315    })
316}
317
318fn storage_from_fork(
319    provider: &Provider<RuntimeClient>,
320    address: Address,
321    index: U256,
322    block_id: Option<BlockId>,
323) -> Result<U256, ProviderError> {
324    let idx_req = B256::from(index);
325
326    let rt = tokio::runtime::Builder::new_current_thread()
327        .enable_all()
328        .build()
329        .unwrap();
330    let storage = rt.block_on(provider.get_storage_at(
331        NameOrAddress::Address(address.to_ethers()),
332        idx_req.to_ethers(),
333        block_id,
334    ))?;
335    Ok(storage.into_uint().to_alloy())
336}
337
338fn block_hash_from_fork(
339    provider: &Provider<RuntimeClient>,
340    number: U256,
341) -> Result<B256, ProviderError> {
342    let n: u64 = number.try_into().unwrap();
343    let block_id = BlockId::from(n);
344
345    let rt = tokio::runtime::Builder::new_current_thread()
346        .enable_all()
347        .build()
348        .unwrap();
349
350    let block = rt.block_on(provider.get_block(block_id));
351
352    match block {
353        Ok(Some(block)) => Ok(block
354            .hash
355            .expect("empty block hash on mined block, this should never happen")
356            .to_alloy()),
357        Ok(None) => Ok(KECCAK_EMPTY),
358        Err(e) => Err(e),
359    }
360}