simular_core/db/
mod.rs

1//!
2//! Provides access to EVM storage
3//!
4pub(crate) mod fork;
5pub(crate) mod fork_backend;
6pub(crate) mod in_memory_db;
7
8use alloy_primitives::{Address, U256};
9use anyhow::{anyhow, Result};
10use revm::{
11    interpreter::primitives::EnvWithHandlerCfg,
12    primitives::{
13        Account, AccountInfo, Bytecode, HashMap as Map, ResultAndState, B256, KECCAK_EMPTY,
14    },
15    Database, DatabaseCommit, DatabaseRef, EvmBuilder,
16};
17use std::time::{SystemTime, UNIX_EPOCH};
18
19use self::{fork::Fork, in_memory_db::MemDb};
20use crate::{errors::DatabaseError, snapshot::SnapShot};
21
22/// Information related to creating a fork
23#[derive(Clone, Debug)]
24pub struct CreateFork {
25    /// the url of the RPC endpoint
26    pub url: String,
27    /// optional block number of the fork.  If none, it will use the latest block.
28    pub blocknumber: Option<u64>,
29}
30
31impl CreateFork {
32    /// Fork at the given URL and block number
33    pub fn new(url: String, blocknumber: Option<u64>) -> Self {
34        Self { url, blocknumber }
35    }
36
37    /// For at the given URL and use the latest block available
38    pub fn latest_block(url: String) -> Self {
39        Self {
40            url,
41            blocknumber: None,
42        }
43    }
44}
45
46// Used by the EVM to access storage.  This can either be an in-memory only db or a forked db.
47// The EVM delegates transact() and transact_commit to this module
48//
49// This is based heavily on Foundry's approach.
50pub struct StorageBackend {
51    mem_db: MemDb, // impl wrapper to handle DbErrors
52    forkdb: Option<Fork>,
53    pub block_number: u64, // used to record in the snapshot...
54    pub timestamp: u64,
55}
56
57impl Default for StorageBackend {
58    fn default() -> Self {
59        StorageBackend::new(None)
60    }
61}
62
63impl StorageBackend {
64    pub fn new(fork: Option<CreateFork>) -> Self {
65        if let Some(fork) = fork {
66            let backend = Fork::new(&fork.url, fork.blocknumber);
67            let block_number = backend.block_number;
68            let timestamp = backend.timestamp;
69            Self {
70                mem_db: MemDb::default(),
71                forkdb: Some(backend),
72                block_number,
73                timestamp,
74            }
75        } else {
76            let timestamp = SystemTime::now()
77                .duration_since(UNIX_EPOCH)
78                .expect("StorageBackend: failed to get unix epoch time")
79                .as_secs();
80            Self {
81                mem_db: MemDb::default(),
82                forkdb: None,
83                block_number: 1,
84                timestamp,
85            }
86        }
87    }
88
89    pub fn insert_account_info(&mut self, address: Address, info: AccountInfo) {
90        if let Some(fork) = self.forkdb.as_mut() {
91            fork.database_mut().insert_account_info(address, info)
92        } else {
93            // use mem...
94            self.mem_db.db.insert_account_info(address, info)
95        }
96    }
97
98    pub fn insert_account_storage(
99        &mut self,
100        address: Address,
101        slot: U256,
102        value: U256,
103    ) -> Result<(), DatabaseError> {
104        let ret = if let Some(fork) = self.forkdb.as_mut() {
105            fork.database_mut()
106                .insert_account_storage(address, slot, value)
107        } else {
108            self.mem_db.db.insert_account_storage(address, slot, value)
109        };
110        ret
111    }
112
113    pub fn replace_account_storage(
114        &mut self,
115        address: Address,
116        storage: Map<U256, U256>,
117    ) -> Result<(), DatabaseError> {
118        if let Some(fork) = self.forkdb.as_mut() {
119            fork.database_mut()
120                .replace_account_storage(address, storage)
121        } else {
122            self.mem_db.db.replace_account_storage(address, storage)
123        }
124    }
125
126    pub fn run_transact(&mut self, env: &mut EnvWithHandlerCfg) -> Result<ResultAndState> {
127        let mut evm = create_evm(self, env.clone());
128        let res = evm
129            .transact()
130            .map_err(|e| anyhow!("backend failed while executing transaction:  {:?}", e))?;
131        env.env = evm.context.evm.inner.env;
132
133        Ok(res)
134    }
135
136    /// Create a snapshot of the current state, delegates
137    /// to the current backend database.
138    pub fn create_snapshot(&self) -> Result<SnapShot> {
139        if let Some(fork) = self.forkdb.as_ref() {
140            fork.create_snapshot(self.block_number, self.timestamp)
141        } else {
142            self.mem_db
143                .create_snapshot(self.block_number, self.timestamp)
144        }
145    }
146
147    /// Load a snapshot into an in-memory database
148    pub fn load_snapshot(&mut self, snapshot: SnapShot) {
149        self.block_number = snapshot.block_num;
150        self.timestamp = snapshot.timestamp;
151
152        for (addr, account) in snapshot.accounts.into_iter() {
153            // note: this will populate both 'accounts' and 'contracts'
154            self.mem_db.db.insert_account_info(
155                addr,
156                AccountInfo {
157                    balance: account.balance,
158                    nonce: account.nonce,
159                    code_hash: KECCAK_EMPTY,
160                    code: if account.code.0.is_empty() {
161                        None
162                    } else {
163                        Some(
164                            Bytecode::new_raw(alloy_primitives::Bytes(account.code.0)).to_checked(),
165                        )
166                    },
167                },
168            );
169
170            // ... but we still need to load the account storage map
171            for (k, v) in account.storage.into_iter() {
172                self.mem_db
173                    .db
174                    .accounts
175                    .entry(addr)
176                    .or_default()
177                    .storage
178                    .insert(k, v);
179            }
180        }
181    }
182
183    /// See EVM update_block
184    pub fn update_block_info(&mut self, interval: u64) {
185        self.block_number += 1;
186        self.timestamp += interval;
187    }
188}
189
190impl DatabaseRef for StorageBackend {
191    type Error = DatabaseError;
192
193    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
194        if let Some(db) = self.forkdb.as_ref() {
195            db.basic_ref(address)
196        } else {
197            Ok(self.mem_db.basic_ref(address)?)
198        }
199    }
200
201    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
202        if let Some(db) = self.forkdb.as_ref() {
203            db.code_by_hash_ref(code_hash)
204        } else {
205            Ok(self.mem_db.code_by_hash_ref(code_hash)?)
206        }
207    }
208
209    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
210        if let Some(db) = self.forkdb.as_ref() {
211            DatabaseRef::storage_ref(db, address, index)
212        } else {
213            Ok(DatabaseRef::storage_ref(&self.mem_db, address, index)?)
214        }
215    }
216
217    fn block_hash_ref(&self, number: U256) -> Result<B256, Self::Error> {
218        if let Some(db) = self.forkdb.as_ref() {
219            db.block_hash_ref(number)
220        } else {
221            Ok(self.mem_db.block_hash_ref(number)?)
222        }
223    }
224}
225
226impl Database for StorageBackend {
227    type Error = DatabaseError;
228    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
229        if let Some(db) = self.forkdb.as_mut() {
230            db.basic(address)
231        } else {
232            Ok(self.mem_db.basic(address)?)
233        }
234    }
235
236    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
237        if let Some(db) = self.forkdb.as_mut() {
238            db.code_by_hash(code_hash)
239        } else {
240            Ok(self.mem_db.code_by_hash(code_hash)?)
241        }
242    }
243
244    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
245        if let Some(db) = self.forkdb.as_mut() {
246            Database::storage(db, address, index)
247        } else {
248            Ok(Database::storage(&mut self.mem_db, address, index)?)
249        }
250    }
251
252    fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error> {
253        if let Some(db) = self.forkdb.as_mut() {
254            db.block_hash(number)
255        } else {
256            Ok(self.mem_db.block_hash(number)?)
257        }
258    }
259}
260
261impl DatabaseCommit for StorageBackend {
262    fn commit(&mut self, changes: Map<Address, Account>) {
263        if let Some(db) = self.forkdb.as_mut() {
264            db.commit(changes)
265        } else {
266            self.mem_db.commit(changes)
267        }
268    }
269}
270
271fn create_evm<'a, DB: Database>(
272    db: DB,
273    env: revm::primitives::EnvWithHandlerCfg,
274) -> revm::Evm<'a, (), DB> {
275    EvmBuilder::default()
276        .with_db(db)
277        .with_env(env.env.clone())
278        .build()
279}