anvil/eth/backend/
db.rs

1//! Helper types for working with [revm](foundry_evm::revm)
2
3use crate::{mem::storage::MinedTransaction, revm::primitives::AccountInfo};
4use alloy_consensus::Header;
5use alloy_primitives::{keccak256, Address, Bytes, B256, U256, U64};
6use alloy_rpc_types::BlockId;
7use anvil_core::eth::{
8    block::Block,
9    transaction::{MaybeImpersonatedTransaction, TransactionInfo, TypedReceipt, TypedTransaction},
10};
11use foundry_common::errors::FsPathError;
12use foundry_evm::{
13    backend::{
14        BlockchainDb, DatabaseError, DatabaseResult, MemDb, RevertStateSnapshotAction,
15        StateSnapshot,
16    },
17    revm::{
18        db::{CacheDB, DatabaseRef, DbAccount},
19        primitives::{BlockEnv, Bytecode, HashMap, KECCAK_EMPTY},
20        Database, DatabaseCommit,
21    },
22};
23use serde::{
24    de::{MapAccess, Visitor},
25    Deserialize, Deserializer, Serialize,
26};
27use std::{collections::BTreeMap, fmt, path::Path};
28
29/// Helper trait get access to the full state data of the database
30pub trait MaybeFullDatabase: DatabaseRef<Error = DatabaseError> {
31    /// Returns a reference to the database as a `dyn DatabaseRef`.
32    // TODO: Required until trait upcasting is stabilized: <https://github.com/rust-lang/rust/issues/65991>
33    fn as_dyn(&self) -> &dyn DatabaseRef<Error = DatabaseError>;
34
35    fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
36        None
37    }
38
39    /// Clear the state and move it into a new `StateSnapshot`.
40    fn clear_into_state_snapshot(&mut self) -> StateSnapshot;
41
42    /// Read the state snapshot.
43    ///
44    /// This clones all the states and returns a new `StateSnapshot`.
45    fn read_as_state_snapshot(&self) -> StateSnapshot;
46
47    /// Clears the entire database
48    fn clear(&mut self);
49
50    /// Reverses `clear_into_snapshot` by initializing the db's state with the state snapshot.
51    fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot);
52}
53
54impl<'a, T: 'a + MaybeFullDatabase + ?Sized> MaybeFullDatabase for &'a T
55where
56    &'a T: DatabaseRef<Error = DatabaseError>,
57{
58    fn as_dyn(&self) -> &dyn DatabaseRef<Error = DatabaseError> {
59        T::as_dyn(self)
60    }
61
62    fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
63        T::maybe_as_full_db(self)
64    }
65
66    fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
67        unreachable!("never called for DatabaseRef")
68    }
69
70    fn read_as_state_snapshot(&self) -> StateSnapshot {
71        unreachable!("never called for DatabaseRef")
72    }
73
74    fn clear(&mut self) {}
75
76    fn init_from_state_snapshot(&mut self, _state_snapshot: StateSnapshot) {}
77}
78
79/// Helper trait to reset the DB if it's forked
80pub trait MaybeForkedDatabase {
81    fn maybe_reset(&mut self, _url: Option<String>, block_number: BlockId) -> Result<(), String>;
82
83    fn maybe_flush_cache(&self) -> Result<(), String>;
84
85    fn maybe_inner(&self) -> Result<&BlockchainDb, String>;
86}
87
88/// This bundles all required revm traits
89pub trait Db:
90    DatabaseRef<Error = DatabaseError>
91    + Database<Error = DatabaseError>
92    + DatabaseCommit
93    + MaybeFullDatabase
94    + MaybeForkedDatabase
95    + fmt::Debug
96    + Send
97    + Sync
98{
99    /// Inserts an account
100    fn insert_account(&mut self, address: Address, account: AccountInfo);
101
102    /// Sets the nonce of the given address
103    fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<()> {
104        let mut info = self.basic(address)?.unwrap_or_default();
105        info.nonce = nonce;
106        self.insert_account(address, info);
107        Ok(())
108    }
109
110    /// Sets the balance of the given address
111    fn set_balance(&mut self, address: Address, balance: U256) -> DatabaseResult<()> {
112        let mut info = self.basic(address)?.unwrap_or_default();
113        info.balance = balance;
114        self.insert_account(address, info);
115        Ok(())
116    }
117
118    /// Sets the balance of the given address
119    fn set_code(&mut self, address: Address, code: Bytes) -> DatabaseResult<()> {
120        let mut info = self.basic(address)?.unwrap_or_default();
121        let code_hash = if code.as_ref().is_empty() {
122            KECCAK_EMPTY
123        } else {
124            B256::from_slice(&keccak256(code.as_ref())[..])
125        };
126        info.code_hash = code_hash;
127        info.code = Some(Bytecode::new_raw(alloy_primitives::Bytes(code.0)));
128        self.insert_account(address, info);
129        Ok(())
130    }
131
132    /// Sets the balance of the given address
133    fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()>;
134
135    /// inserts a blockhash for the given number
136    fn insert_block_hash(&mut self, number: U256, hash: B256);
137
138    /// Write all chain data to serialized bytes buffer
139    fn dump_state(
140        &self,
141        at: BlockEnv,
142        best_number: U64,
143        blocks: Vec<SerializableBlock>,
144        transactions: Vec<SerializableTransaction>,
145        historical_states: Option<SerializableHistoricalStates>,
146    ) -> DatabaseResult<Option<SerializableState>>;
147
148    /// Deserialize and add all chain data to the backend storage
149    fn load_state(&mut self, state: SerializableState) -> DatabaseResult<bool> {
150        for (addr, account) in state.accounts.into_iter() {
151            let old_account_nonce = DatabaseRef::basic_ref(self, addr)
152                .ok()
153                .and_then(|acc| acc.map(|acc| acc.nonce))
154                .unwrap_or_default();
155            // use max nonce in case account is imported multiple times with difference
156            // nonces to prevent collisions
157            let nonce = std::cmp::max(old_account_nonce, account.nonce);
158
159            self.insert_account(
160                addr,
161                AccountInfo {
162                    balance: account.balance,
163                    code_hash: KECCAK_EMPTY, // will be set automatically
164                    code: if account.code.0.is_empty() {
165                        None
166                    } else {
167                        Some(Bytecode::new_raw(alloy_primitives::Bytes(account.code.0)))
168                    },
169                    nonce,
170                },
171            );
172
173            for (k, v) in account.storage.into_iter() {
174                self.set_storage_at(addr, k, v)?;
175            }
176        }
177        Ok(true)
178    }
179
180    /// Creates a new state snapshot.
181    fn snapshot_state(&mut self) -> U256;
182
183    /// Reverts a state snapshot.
184    ///
185    /// Returns `true` if the state snapshot was reverted.
186    fn revert_state(&mut self, state_snapshot: U256, action: RevertStateSnapshotAction) -> bool;
187
188    /// Returns the state root if possible to compute
189    fn maybe_state_root(&self) -> Option<B256> {
190        None
191    }
192
193    /// Returns the current, standalone state of the Db
194    fn current_state(&self) -> StateDb;
195}
196
197impl dyn Db {
198    // TODO: Required until trait upcasting is stabilized: <https://github.com/rust-lang/rust/issues/65991>
199    pub fn as_dbref(&self) -> &dyn DatabaseRef<Error = DatabaseError> {
200        self.as_dyn()
201    }
202}
203
204/// Convenience impl only used to use any `Db` on the fly as the db layer for revm's CacheDB
205/// This is useful to create blocks without actually writing to the `Db`, but rather in the cache of
206/// the `CacheDB` see also
207/// [Backend::pending_block()](crate::eth::backend::mem::Backend::pending_block())
208impl<T: DatabaseRef<Error = DatabaseError> + Send + Sync + Clone + fmt::Debug> Db for CacheDB<T> {
209    fn insert_account(&mut self, address: Address, account: AccountInfo) {
210        self.insert_account_info(address, account)
211    }
212
213    fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()> {
214        self.insert_account_storage(address, slot.into(), val.into())
215    }
216
217    fn insert_block_hash(&mut self, number: U256, hash: B256) {
218        self.block_hashes.insert(number, hash);
219    }
220
221    fn dump_state(
222        &self,
223        _at: BlockEnv,
224        _best_number: U64,
225        _blocks: Vec<SerializableBlock>,
226        _transaction: Vec<SerializableTransaction>,
227        _historical_states: Option<SerializableHistoricalStates>,
228    ) -> DatabaseResult<Option<SerializableState>> {
229        Ok(None)
230    }
231
232    fn snapshot_state(&mut self) -> U256 {
233        U256::ZERO
234    }
235
236    fn revert_state(&mut self, _state_snapshot: U256, _action: RevertStateSnapshotAction) -> bool {
237        false
238    }
239
240    fn current_state(&self) -> StateDb {
241        StateDb::new(MemDb::default())
242    }
243}
244
245impl<T: DatabaseRef<Error = DatabaseError>> MaybeFullDatabase for CacheDB<T> {
246    fn as_dyn(&self) -> &dyn DatabaseRef<Error = DatabaseError> {
247        self
248    }
249
250    fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
251        Some(&self.accounts)
252    }
253
254    fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
255        let db_accounts = std::mem::take(&mut self.accounts);
256        let mut accounts = HashMap::default();
257        let mut account_storage = HashMap::default();
258
259        for (addr, mut acc) in db_accounts {
260            account_storage.insert(addr, std::mem::take(&mut acc.storage));
261            let mut info = acc.info;
262            info.code = self.contracts.remove(&info.code_hash);
263            accounts.insert(addr, info);
264        }
265        let block_hashes = std::mem::take(&mut self.block_hashes);
266        StateSnapshot { accounts, storage: account_storage, block_hashes }
267    }
268
269    fn read_as_state_snapshot(&self) -> StateSnapshot {
270        let db_accounts = self.accounts.clone();
271        let mut accounts = HashMap::default();
272        let mut account_storage = HashMap::default();
273
274        for (addr, acc) in db_accounts {
275            account_storage.insert(addr, acc.storage.clone());
276            let mut info = acc.info;
277            info.code = self.contracts.get(&info.code_hash).cloned();
278            accounts.insert(addr, info);
279        }
280
281        let block_hashes = self.block_hashes.clone();
282        StateSnapshot { accounts, storage: account_storage, block_hashes }
283    }
284
285    fn clear(&mut self) {
286        self.clear_into_state_snapshot();
287    }
288
289    fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) {
290        let StateSnapshot { accounts, mut storage, block_hashes } = state_snapshot;
291
292        for (addr, mut acc) in accounts {
293            if let Some(code) = acc.code.take() {
294                self.contracts.insert(acc.code_hash, code);
295            }
296            self.accounts.insert(
297                addr,
298                DbAccount {
299                    info: acc,
300                    storage: storage.remove(&addr).unwrap_or_default(),
301                    ..Default::default()
302                },
303            );
304        }
305        self.block_hashes = block_hashes;
306    }
307}
308
309impl<T: DatabaseRef<Error = DatabaseError>> MaybeForkedDatabase for CacheDB<T> {
310    fn maybe_reset(&mut self, _url: Option<String>, _block_number: BlockId) -> Result<(), String> {
311        Err("not supported".to_string())
312    }
313
314    fn maybe_flush_cache(&self) -> Result<(), String> {
315        Err("not supported".to_string())
316    }
317
318    fn maybe_inner(&self) -> Result<&BlockchainDb, String> {
319        Err("not supported".to_string())
320    }
321}
322
323/// Represents a state at certain point
324pub struct StateDb(pub(crate) Box<dyn MaybeFullDatabase + Send + Sync>);
325
326impl StateDb {
327    pub fn new(db: impl MaybeFullDatabase + Send + Sync + 'static) -> Self {
328        Self(Box::new(db))
329    }
330
331    pub fn serialize_state(&mut self) -> StateSnapshot {
332        // Using read_as_snapshot makes sures we don't clear the historical state from the current
333        // instance.
334        self.read_as_state_snapshot()
335    }
336}
337
338impl DatabaseRef for StateDb {
339    type Error = DatabaseError;
340    fn basic_ref(&self, address: Address) -> DatabaseResult<Option<AccountInfo>> {
341        self.0.basic_ref(address)
342    }
343
344    fn code_by_hash_ref(&self, code_hash: B256) -> DatabaseResult<Bytecode> {
345        self.0.code_by_hash_ref(code_hash)
346    }
347
348    fn storage_ref(&self, address: Address, index: U256) -> DatabaseResult<U256> {
349        self.0.storage_ref(address, index)
350    }
351
352    fn block_hash_ref(&self, number: u64) -> DatabaseResult<B256> {
353        self.0.block_hash_ref(number)
354    }
355}
356
357impl MaybeFullDatabase for StateDb {
358    fn as_dyn(&self) -> &dyn DatabaseRef<Error = DatabaseError> {
359        self.0.as_dyn()
360    }
361
362    fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
363        self.0.maybe_as_full_db()
364    }
365
366    fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
367        self.0.clear_into_state_snapshot()
368    }
369
370    fn read_as_state_snapshot(&self) -> StateSnapshot {
371        self.0.read_as_state_snapshot()
372    }
373
374    fn clear(&mut self) {
375        self.0.clear()
376    }
377
378    fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) {
379        self.0.init_from_state_snapshot(state_snapshot)
380    }
381}
382
383#[derive(Clone, Debug, Default, Serialize, Deserialize)]
384pub struct SerializableState {
385    /// The block number of the state
386    ///
387    /// Note: This is an Option for backwards compatibility: <https://github.com/foundry-rs/foundry/issues/5460>
388    pub block: Option<BlockEnv>,
389    pub accounts: BTreeMap<Address, SerializableAccountRecord>,
390    /// The best block number of the state, can be different from block number (Arbitrum chain).
391    pub best_block_number: Option<U64>,
392    #[serde(default)]
393    pub blocks: Vec<SerializableBlock>,
394    #[serde(default)]
395    pub transactions: Vec<SerializableTransaction>,
396    /// Historical states of accounts and storage at particular block hashes.
397    ///
398    /// Note: This is an Option for backwards compatibility.
399    #[serde(default)]
400    pub historical_states: Option<SerializableHistoricalStates>,
401}
402
403impl SerializableState {
404    /// Loads the `Genesis` object from the given json file path
405    pub fn load(path: impl AsRef<Path>) -> Result<Self, FsPathError> {
406        let path = path.as_ref();
407        if path.is_dir() {
408            foundry_common::fs::read_json_file(&path.join("state.json"))
409        } else {
410            foundry_common::fs::read_json_file(path)
411        }
412    }
413
414    /// This is used as the clap `value_parser` implementation
415    #[allow(dead_code)]
416    pub(crate) fn parse(path: &str) -> Result<Self, String> {
417        Self::load(path).map_err(|err| err.to_string())
418    }
419}
420
421#[derive(Clone, Debug, Serialize, Deserialize)]
422pub struct SerializableAccountRecord {
423    pub nonce: u64,
424    pub balance: U256,
425    pub code: Bytes,
426
427    #[serde(deserialize_with = "deserialize_btree")]
428    pub storage: BTreeMap<B256, B256>,
429}
430
431fn deserialize_btree<'de, D>(deserializer: D) -> Result<BTreeMap<B256, B256>, D::Error>
432where
433    D: Deserializer<'de>,
434{
435    struct BTreeVisitor;
436
437    impl<'de> Visitor<'de> for BTreeVisitor {
438        type Value = BTreeMap<B256, B256>;
439
440        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
441            formatter.write_str("a mapping of hex encoded storage slots to hex encoded state data")
442        }
443
444        fn visit_map<M>(self, mut mapping: M) -> Result<BTreeMap<B256, B256>, M::Error>
445        where
446            M: MapAccess<'de>,
447        {
448            let mut btree = BTreeMap::new();
449            while let Some((key, value)) = mapping.next_entry::<U256, U256>()? {
450                btree.insert(B256::from(key), B256::from(value));
451            }
452
453            Ok(btree)
454        }
455    }
456
457    deserializer.deserialize_map(BTreeVisitor)
458}
459
460/// Defines a backwards-compatible enum for transactions.
461/// This is essential for maintaining compatibility with state dumps
462/// created before the changes introduced in PR #8411.
463///
464/// The enum can represent either a `TypedTransaction` or a `MaybeImpersonatedTransaction`,
465/// depending on the data being deserialized. This flexibility ensures that older state
466/// dumps can still be loaded correctly, even after the changes in #8411.
467#[derive(Clone, Debug, Serialize, Deserialize)]
468#[serde(untagged)]
469pub enum SerializableTransactionType {
470    TypedTransaction(TypedTransaction),
471    MaybeImpersonatedTransaction(MaybeImpersonatedTransaction),
472}
473
474#[derive(Clone, Debug, Serialize, Deserialize)]
475pub struct SerializableBlock {
476    pub header: Header,
477    pub transactions: Vec<SerializableTransactionType>,
478    pub ommers: Vec<Header>,
479}
480
481impl From<Block> for SerializableBlock {
482    fn from(block: Block) -> Self {
483        Self {
484            header: block.header,
485            transactions: block.transactions.into_iter().map(Into::into).collect(),
486            ommers: block.ommers.into_iter().collect(),
487        }
488    }
489}
490
491impl From<SerializableBlock> for Block {
492    fn from(block: SerializableBlock) -> Self {
493        Self {
494            header: block.header,
495            transactions: block.transactions.into_iter().map(Into::into).collect(),
496            ommers: block.ommers.into_iter().collect(),
497        }
498    }
499}
500
501impl From<MaybeImpersonatedTransaction> for SerializableTransactionType {
502    fn from(transaction: MaybeImpersonatedTransaction) -> Self {
503        Self::MaybeImpersonatedTransaction(transaction)
504    }
505}
506
507impl From<SerializableTransactionType> for MaybeImpersonatedTransaction {
508    fn from(transaction: SerializableTransactionType) -> Self {
509        match transaction {
510            SerializableTransactionType::TypedTransaction(tx) => Self::new(tx),
511            SerializableTransactionType::MaybeImpersonatedTransaction(tx) => tx,
512        }
513    }
514}
515
516#[derive(Clone, Debug, Serialize, Deserialize)]
517pub struct SerializableTransaction {
518    pub info: TransactionInfo,
519    pub receipt: TypedReceipt,
520    pub block_hash: B256,
521    pub block_number: u64,
522}
523
524impl From<MinedTransaction> for SerializableTransaction {
525    fn from(transaction: MinedTransaction) -> Self {
526        Self {
527            info: transaction.info,
528            receipt: transaction.receipt,
529            block_hash: transaction.block_hash,
530            block_number: transaction.block_number,
531        }
532    }
533}
534
535impl From<SerializableTransaction> for MinedTransaction {
536    fn from(transaction: SerializableTransaction) -> Self {
537        Self {
538            info: transaction.info,
539            receipt: transaction.receipt,
540            block_hash: transaction.block_hash,
541            block_number: transaction.block_number,
542        }
543    }
544}
545
546#[derive(Clone, Debug, Serialize, Deserialize, Default)]
547pub struct SerializableHistoricalStates(Vec<(B256, StateSnapshot)>);
548
549impl SerializableHistoricalStates {
550    pub const fn new(states: Vec<(B256, StateSnapshot)>) -> Self {
551        Self(states)
552    }
553}
554
555impl IntoIterator for SerializableHistoricalStates {
556    type Item = (B256, StateSnapshot);
557    type IntoIter = std::vec::IntoIter<Self::Item>;
558
559    fn into_iter(self) -> Self::IntoIter {
560        self.0.into_iter()
561    }
562}
563
564#[cfg(test)]
565mod test {
566    use super::*;
567
568    #[test]
569    fn test_deser_block() {
570        let block = r#"{
571            "header": {
572                "parentHash": "0xceb0fe420d6f14a8eeec4319515b89acbb0bb4861cad9983d529ab4b1e4af929",
573                "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
574                "miner": "0x0000000000000000000000000000000000000000",
575                "stateRoot": "0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1",
576                "transactionsRoot": "0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988",
577                "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa",
578                "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
579                "difficulty": "0x0",
580                "number": "0x2",
581                "gasLimit": "0x1c9c380",
582                "gasUsed": "0x5208",
583                "timestamp": "0x66cdc823",
584                "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
585                "nonce": "0x0000000000000000",
586                "baseFeePerGas": "0x342a1c58",
587                "blobGasUsed": "0x0",
588                "excessBlobGas": "0x0",
589                "extraData": "0x"
590            },
591            "transactions": [
592                {
593                    "EIP1559": {
594                        "chainId": "0x7a69",
595                        "nonce": "0x0",
596                        "gas": "0x5209",
597                        "maxFeePerGas": "0x77359401",
598                        "maxPriorityFeePerGas": "0x1",
599                        "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
600                        "value": "0x0",
601                        "accessList": [],
602                        "input": "0x",
603                        "r": "0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0",
604                        "s": "0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd",
605                        "yParity": "0x0",
606                        "hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"
607                    }
608                }
609            ],
610            "ommers": []
611        }
612        "#;
613
614        let _block: SerializableBlock = serde_json::from_str(block).unwrap();
615    }
616}