Skip to main content

anvil/eth/backend/
db.rs

1//! Helper types for working with [revm]
2
3use std::{
4    collections::BTreeMap,
5    fmt::{self, Debug},
6    fs::File,
7    io::BufReader,
8    path::Path,
9};
10
11use alloy_consensus::BlockBody;
12#[cfg(test)]
13use alloy_consensus::Header;
14use alloy_eips::eip4895::Withdrawals;
15use alloy_network::Network;
16use alloy_primitives::{
17    Address, B256, Bytes, U256, keccak256,
18    map::{AddressMap, HashMap, U256Map},
19};
20use alloy_rpc_types::BlockId;
21use anvil_core::eth::{
22    block::Block,
23    transaction::{MaybeImpersonatedTransaction, TransactionInfo},
24};
25use foundry_common::errors::FsPathError;
26use foundry_evm::backend::{
27    BlockchainDb, DatabaseError, DatabaseResult, EmptyDBWrapper, MemDb, RevertStateSnapshotAction,
28    StateSnapshot,
29};
30use foundry_primitives::{FoundryHeader, FoundryReceiptEnvelope, FoundryTxEnvelope};
31use revm::{
32    Database, DatabaseCommit,
33    bytecode::Bytecode,
34    context::BlockEnv,
35    context_interface::block::BlobExcessGasAndPrice,
36    database::{AccountState, CacheDB, DatabaseRef, DbAccount},
37    primitives::{KECCAK_EMPTY, eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE},
38    state::AccountInfo,
39};
40use serde::{
41    Deserialize, Deserializer, Serialize,
42    de::{Error as DeError, MapAccess, Visitor},
43};
44use serde_json::Value;
45
46use crate::mem::storage::MinedTransaction;
47
48/// Number of preceding block hashes available to the EVM's `BLOCKHASH` opcode.
49pub(crate) const BLOCKHASH_HISTORY: u64 = 256;
50
51/// Inserts a block hash, discards entries outside the EVM-visible cache, and returns its head.
52pub(crate) fn cache_block_hash(block_hashes: &mut U256Map<B256>, number: U256, hash: B256) -> U256 {
53    let head = block_hashes.keys().copied().max().map_or(number, |head| head.max(number));
54    let min_number = head.saturating_sub(U256::from(BLOCKHASH_HISTORY));
55    block_hashes.retain(|cached, _| *cached >= min_number && *cached <= head);
56    if number >= min_number {
57        block_hashes.insert(number, hash);
58    }
59    head
60}
61
62/// Helper trait get access to the full state data of the database
63pub trait MaybeFullDatabase: DatabaseRef<Error = DatabaseError> + Debug {
64    fn maybe_as_full_db(&self) -> Option<&AddressMap<DbAccount>> {
65        None
66    }
67
68    /// Returns an owned, recursively merged view of all available accounts.
69    fn maybe_full_db(&self) -> Option<AddressMap<DbAccount>> {
70        self.maybe_as_full_db().cloned()
71    }
72
73    /// Returns whether snapshots of this database use structural sharing.
74    fn is_persistent(&self) -> bool {
75        false
76    }
77
78    /// Clear the state and move it into a new `StateSnapshot`.
79    fn clear_into_state_snapshot(&mut self) -> StateSnapshot;
80
81    /// Read the state snapshot.
82    ///
83    /// This clones all the states and returns a new `StateSnapshot`.
84    fn read_as_state_snapshot(&self) -> StateSnapshot;
85
86    /// Clears the entire database
87    fn clear(&mut self);
88
89    /// Reverses `clear_into_snapshot` by initializing the db's state with the state snapshot.
90    fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot);
91}
92
93impl<'a, T: 'a + MaybeFullDatabase + ?Sized> MaybeFullDatabase for &'a T
94where
95    &'a T: DatabaseRef<Error = DatabaseError>,
96{
97    fn maybe_as_full_db(&self) -> Option<&AddressMap<DbAccount>> {
98        T::maybe_as_full_db(self)
99    }
100
101    fn maybe_full_db(&self) -> Option<AddressMap<DbAccount>> {
102        T::maybe_full_db(self)
103    }
104
105    fn is_persistent(&self) -> bool {
106        T::is_persistent(self)
107    }
108
109    fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
110        unreachable!("never called for DatabaseRef")
111    }
112
113    fn read_as_state_snapshot(&self) -> StateSnapshot {
114        unreachable!("never called for DatabaseRef")
115    }
116
117    fn clear(&mut self) {}
118
119    fn init_from_state_snapshot(&mut self, _state_snapshot: StateSnapshot) {}
120}
121
122impl<T: MaybeFullDatabase + ?Sized> MaybeFullDatabase for Box<T>
123where
124    Self: DatabaseRef<Error = DatabaseError>,
125{
126    fn maybe_as_full_db(&self) -> Option<&AddressMap<DbAccount>> {
127        T::maybe_as_full_db(self)
128    }
129
130    fn maybe_full_db(&self) -> Option<AddressMap<DbAccount>> {
131        T::maybe_full_db(self)
132    }
133
134    fn is_persistent(&self) -> bool {
135        T::is_persistent(self)
136    }
137
138    fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
139        T::clear_into_state_snapshot(self)
140    }
141
142    fn read_as_state_snapshot(&self) -> StateSnapshot {
143        T::read_as_state_snapshot(self)
144    }
145
146    fn clear(&mut self) {
147        T::clear(self)
148    }
149
150    fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) {
151        T::init_from_state_snapshot(self, state_snapshot)
152    }
153}
154
155/// Helper trait to reset the DB if it's forked
156pub trait MaybeForkedDatabase {
157    fn maybe_reset(&mut self, _urls: Vec<String>, block_number: BlockId) -> Result<(), String>;
158
159    fn maybe_flush_cache(&self) -> Result<(), String>;
160
161    fn maybe_inner(&self) -> Result<&BlockchainDb, String>;
162}
163
164/// `dyn Db` satisfies all `alloy_evm::Database` requirements via its supertraits, but the
165/// blanket impl has an implicit `Sized` bound. Provide an explicit impl.
166impl alloy_evm::Database for dyn Db {}
167
168/// A wrapper around [`CacheDB`].
169#[derive(Debug)]
170pub struct AnvilCacheDB<T>(pub CacheDB<T>);
171
172impl<T: DatabaseRef<Error = DatabaseError>> AnvilCacheDB<T> {
173    pub fn new(inner: T) -> Self {
174        Self(CacheDB::new(inner))
175    }
176}
177
178impl<T: DatabaseRef<Error = DatabaseError>> std::ops::Deref for AnvilCacheDB<T> {
179    type Target = CacheDB<T>;
180    fn deref(&self) -> &Self::Target {
181        &self.0
182    }
183}
184
185impl<T: DatabaseRef<Error = DatabaseError>> std::ops::DerefMut for AnvilCacheDB<T> {
186    fn deref_mut(&mut self) -> &mut Self::Target {
187        &mut self.0
188    }
189}
190
191impl<T: DatabaseRef<Error = DatabaseError> + fmt::Debug> Database for AnvilCacheDB<T> {
192    type Error = DatabaseError;
193
194    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
195        self.0.basic(address)
196    }
197
198    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
199        self.0.code_by_hash(code_hash)
200    }
201
202    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
203        self.0.storage(address, index)
204    }
205
206    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
207        self.0.block_hash(number)
208    }
209}
210
211impl<T: DatabaseRef<Error = DatabaseError>> DatabaseRef for AnvilCacheDB<T> {
212    type Error = DatabaseError;
213
214    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
215        self.0.basic_ref(address)
216    }
217
218    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
219        self.0.code_by_hash_ref(code_hash)
220    }
221
222    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
223        self.0.storage_ref(address, index)
224    }
225
226    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
227        self.0.block_hash_ref(number)
228    }
229}
230
231impl<T: DatabaseRef<Error = DatabaseError> + fmt::Debug> DatabaseCommit for AnvilCacheDB<T> {
232    fn commit(&mut self, changes: revm::state::EvmState) {
233        self.0.commit(changes)
234    }
235}
236
237/// This bundles all required revm traits
238pub trait Db:
239    DatabaseRef<Error = DatabaseError>
240    + Database<Error = DatabaseError>
241    + DatabaseCommit
242    + MaybeFullDatabase
243    + MaybeForkedDatabase
244    + fmt::Debug
245    + Send
246    + Sync
247{
248    /// Inserts an account
249    fn insert_account(&mut self, address: Address, account: AccountInfo);
250
251    /// Sets the nonce of the given address
252    fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<()> {
253        let mut info = self.basic(address)?.unwrap_or_default();
254        info.nonce = nonce;
255        self.insert_account(address, info);
256        Ok(())
257    }
258
259    /// Sets the balance of the given address
260    fn set_balance(&mut self, address: Address, balance: U256) -> DatabaseResult<()> {
261        let mut info = self.basic(address)?.unwrap_or_default();
262        info.balance = balance;
263        self.insert_account(address, info);
264        Ok(())
265    }
266
267    /// Sets the code of the given address
268    fn set_code(&mut self, address: Address, code: Bytes) -> DatabaseResult<()> {
269        let mut info = self.basic(address)?.unwrap_or_default();
270        let code_hash = if code.as_ref().is_empty() {
271            KECCAK_EMPTY
272        } else {
273            B256::from_slice(&keccak256(code.as_ref())[..])
274        };
275        info.code_hash = code_hash;
276        info.code = Some(Bytecode::new_raw(code));
277        self.insert_account(address, info);
278        Ok(())
279    }
280
281    /// Sets the storage value at the given slot for the address
282    fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()>;
283
284    /// inserts a blockhash for the given number
285    fn insert_block_hash(&mut self, number: U256, hash: B256);
286
287    /// Replaces all cached block hashes.
288    fn set_block_hashes(&mut self, block_hashes: Vec<(U256, B256)>);
289
290    /// Write all chain data to serialized bytes buffer
291    fn dump_state(
292        &self,
293        at: BlockEnv,
294        best_number: u64,
295        blocks: Vec<SerializableBlock>,
296        transactions: Vec<SerializableTransaction>,
297        historical_states: Option<SerializableHistoricalStates>,
298    ) -> DatabaseResult<Option<SerializableState>>;
299
300    /// Deserialize and add all accounts to the backend storage.
301    fn load_state(&mut self, state: SerializableState) -> DatabaseResult<bool> {
302        for (addr, account) in state.accounts {
303            let old_account_nonce = DatabaseRef::basic_ref(self, addr)
304                .ok()
305                .and_then(|acc| acc.map(|acc| acc.nonce))
306                .unwrap_or_default();
307            // use max nonce in case account is imported multiple times with difference
308            // nonces to prevent collisions
309            let nonce = std::cmp::max(old_account_nonce, account.nonce);
310
311            self.insert_account(
312                addr,
313                AccountInfo {
314                    balance: account.balance,
315                    code_hash: KECCAK_EMPTY, // will be set automatically
316                    code: if account.code.0.is_empty() {
317                        None
318                    } else {
319                        Some(Bytecode::new_raw(account.code))
320                    },
321                    nonce,
322                    account_id: None,
323                },
324            );
325
326            for (k, v) in account.storage {
327                self.set_storage_at(addr, k, v)?;
328            }
329        }
330        Ok(true)
331    }
332
333    /// Creates a new state snapshot.
334    fn snapshot_state(&mut self) -> U256;
335
336    /// Reverts a state snapshot.
337    ///
338    /// Returns `true` if the state snapshot was reverted.
339    fn revert_state(&mut self, state_snapshot: U256, action: RevertStateSnapshotAction) -> bool;
340
341    /// Returns the state root if possible to compute
342    fn maybe_state_root(&self) -> Option<B256> {
343        None
344    }
345
346    /// Returns the current, standalone state of the Db
347    fn current_state(&self) -> StateDb;
348}
349
350/// Convenience impl only used to use any `Db` on the fly as the db layer for revm's CacheDB
351/// This is useful to create blocks without actually writing to the `Db`, but rather in the cache of
352/// the `CacheDB` see also
353/// [Backend::pending_block()](crate::eth::backend::mem::Backend::pending_block())
354impl<T> Db for CacheDB<T>
355where
356    T: DatabaseRef<Error = DatabaseError> + MaybeFullDatabase + Send + Sync + Clone + fmt::Debug,
357{
358    fn insert_account(&mut self, address: Address, account: AccountInfo) {
359        self.insert_account_info(address, account)
360    }
361
362    fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()> {
363        self.insert_account_storage(address, slot.into(), val.into())
364    }
365
366    fn insert_block_hash(&mut self, number: U256, hash: B256) {
367        cache_block_hash(&mut self.cache.block_hashes, number, hash);
368    }
369
370    fn set_block_hashes(&mut self, block_hashes: Vec<(U256, B256)>) {
371        self.cache.block_hashes = block_hashes.into_iter().collect();
372    }
373
374    fn dump_state(
375        &self,
376        _at: BlockEnv,
377        _best_number: u64,
378        _blocks: Vec<SerializableBlock>,
379        _transaction: Vec<SerializableTransaction>,
380        _historical_states: Option<SerializableHistoricalStates>,
381    ) -> DatabaseResult<Option<SerializableState>> {
382        Ok(None)
383    }
384
385    fn snapshot_state(&mut self) -> U256 {
386        U256::ZERO
387    }
388
389    fn revert_state(&mut self, _state_snapshot: U256, _action: RevertStateSnapshotAction) -> bool {
390        false
391    }
392
393    fn maybe_state_root(&self) -> Option<B256> {
394        self.maybe_full_db().map(|accounts| crate::mem::state::state_root(&accounts))
395    }
396
397    fn current_state(&self) -> StateDb {
398        StateDb::new(MemDb::default())
399    }
400}
401
402impl<T: MaybeFullDatabase> MaybeFullDatabase for CacheDB<T> {
403    fn maybe_as_full_db(&self) -> Option<&AddressMap<DbAccount>> {
404        Some(&self.cache.accounts)
405    }
406
407    fn maybe_full_db(&self) -> Option<AddressMap<DbAccount>> {
408        let mut accounts = self.db.maybe_full_db()?;
409        for (address, overlay) in &self.cache.accounts {
410            if overlay.account_state == AccountState::NotExisting {
411                accounts.remove(address);
412                continue;
413            }
414            if overlay.account_state == AccountState::StorageCleared {
415                accounts.insert(*address, overlay.clone());
416                continue;
417            }
418
419            let mut account = accounts.remove(address).unwrap_or_default();
420            account.info = overlay.info.clone();
421            account.account_state = overlay.account_state.clone();
422            account.storage.extend(overlay.storage.clone());
423            accounts.insert(*address, account);
424        }
425        Some(accounts)
426    }
427
428    fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
429        let db_accounts = std::mem::take(&mut self.cache.accounts);
430        let mut accounts = HashMap::default();
431        let mut account_storage = HashMap::default();
432
433        for (addr, mut acc) in db_accounts {
434            account_storage.insert(addr, std::mem::take(&mut acc.storage));
435            let mut info = acc.info;
436            info.code = self.cache.contracts.remove(&info.code_hash);
437            accounts.insert(addr, info);
438        }
439        let block_hashes = std::mem::take(&mut self.cache.block_hashes);
440        StateSnapshot { accounts, storage: account_storage, block_hashes }
441    }
442
443    fn read_as_state_snapshot(&self) -> StateSnapshot {
444        let mut accounts = HashMap::default();
445        let mut account_storage = HashMap::default();
446
447        for (addr, acc) in &self.cache.accounts {
448            account_storage.insert(*addr, acc.storage.clone());
449            let mut info = acc.info.clone();
450            info.code = self.cache.contracts.get(&info.code_hash).cloned();
451            accounts.insert(*addr, info);
452        }
453
454        let block_hashes = self.cache.block_hashes.clone();
455        StateSnapshot { accounts, storage: account_storage, block_hashes }
456    }
457
458    fn clear(&mut self) {
459        self.clear_into_state_snapshot();
460    }
461
462    fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) {
463        let StateSnapshot { accounts, mut storage, block_hashes } = state_snapshot;
464
465        for (addr, mut acc) in accounts {
466            if let Some(code) = acc.code.take() {
467                self.cache.contracts.insert(acc.code_hash, code);
468            }
469            self.cache.accounts.insert(
470                addr,
471                DbAccount {
472                    info: acc,
473                    storage: storage.remove(&addr).unwrap_or_default(),
474                    ..Default::default()
475                },
476            );
477        }
478        self.cache.block_hashes = block_hashes;
479    }
480}
481
482impl MaybeFullDatabase for EmptyDBWrapper {
483    fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
484        StateSnapshot::default()
485    }
486
487    fn read_as_state_snapshot(&self) -> StateSnapshot {
488        StateSnapshot::default()
489    }
490
491    fn clear(&mut self) {}
492
493    fn init_from_state_snapshot(&mut self, _state_snapshot: StateSnapshot) {}
494}
495
496impl<T: DatabaseRef<Error = DatabaseError>> MaybeForkedDatabase for CacheDB<T> {
497    fn maybe_reset(&mut self, _urls: Vec<String>, _block_number: BlockId) -> Result<(), String> {
498        Err("not supported".to_string())
499    }
500
501    fn maybe_flush_cache(&self) -> Result<(), String> {
502        Err("not supported".to_string())
503    }
504
505    fn maybe_inner(&self) -> Result<&BlockchainDb, String> {
506        Err("not supported".to_string())
507    }
508}
509
510/// Represents a state at certain point
511#[derive(Debug)]
512pub struct StateDb(pub(crate) Box<dyn MaybeFullDatabase + Send + Sync>);
513
514impl StateDb {
515    pub fn new(db: impl MaybeFullDatabase + Send + Sync + 'static) -> Self {
516        Self(Box::new(db))
517    }
518
519    pub fn serialize_state(&mut self) -> StateSnapshot {
520        // Using read_as_snapshot makes sures we don't clear the historical state from the current
521        // instance.
522        self.read_as_state_snapshot()
523    }
524}
525
526impl DatabaseRef for StateDb {
527    type Error = DatabaseError;
528    fn basic_ref(&self, address: Address) -> DatabaseResult<Option<AccountInfo>> {
529        self.0.basic_ref(address)
530    }
531
532    fn code_by_hash_ref(&self, code_hash: B256) -> DatabaseResult<Bytecode> {
533        self.0.code_by_hash_ref(code_hash)
534    }
535
536    fn storage_ref(&self, address: Address, index: U256) -> DatabaseResult<U256> {
537        self.0.storage_ref(address, index)
538    }
539
540    fn block_hash_ref(&self, number: u64) -> DatabaseResult<B256> {
541        self.0.block_hash_ref(number)
542    }
543}
544
545impl MaybeFullDatabase for StateDb {
546    fn maybe_as_full_db(&self) -> Option<&AddressMap<DbAccount>> {
547        self.0.maybe_as_full_db()
548    }
549
550    fn maybe_full_db(&self) -> Option<AddressMap<DbAccount>> {
551        self.0.maybe_full_db()
552    }
553
554    fn is_persistent(&self) -> bool {
555        self.0.is_persistent()
556    }
557
558    fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
559        self.0.clear_into_state_snapshot()
560    }
561
562    fn read_as_state_snapshot(&self) -> StateSnapshot {
563        self.0.read_as_state_snapshot()
564    }
565
566    fn clear(&mut self) {
567        self.0.clear()
568    }
569
570    fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) {
571        self.0.init_from_state_snapshot(state_snapshot)
572    }
573}
574
575/// Legacy block environment from before v1.3.
576#[derive(Debug, Deserialize)]
577#[serde(rename_all = "snake_case")]
578pub struct LegacyBlockEnv {
579    pub number: Option<StringOrU64>,
580    #[serde(alias = "coinbase")]
581    pub beneficiary: Option<Address>,
582    pub timestamp: Option<StringOrU64>,
583    pub gas_limit: Option<StringOrU64>,
584    pub basefee: Option<StringOrU64>,
585    pub difficulty: Option<StringOrU64>,
586    pub prevrandao: Option<B256>,
587    pub blob_excess_gas_and_price: Option<LegacyBlobExcessGasAndPrice>,
588}
589
590/// Legacy blob excess gas and price structure from before v1.3.
591#[derive(Debug, Deserialize)]
592pub struct LegacyBlobExcessGasAndPrice {
593    pub excess_blob_gas: u64,
594    pub blob_gasprice: u128,
595}
596
597/// Legacy string or u64 type from before v1.3.
598#[derive(Debug, Deserialize)]
599#[serde(untagged)]
600pub enum StringOrU64 {
601    Hex(String),
602    Dec(u64),
603}
604
605impl StringOrU64 {
606    pub fn to_u64(&self) -> Option<u64> {
607        match self {
608            Self::Dec(n) => Some(*n),
609            Self::Hex(s) => s.strip_prefix("0x").and_then(|s| u64::from_str_radix(s, 16).ok()),
610        }
611    }
612
613    pub fn to_u256(&self) -> Option<U256> {
614        match self {
615            Self::Dec(n) => Some(U256::from(*n)),
616            Self::Hex(s) => s.strip_prefix("0x").and_then(|s| U256::from_str_radix(s, 16).ok()),
617        }
618    }
619}
620
621/// Converts a `LegacyBlockEnv` to a `BlockEnv`, handling the conversion of legacy fields.
622impl TryFrom<LegacyBlockEnv> for BlockEnv {
623    type Error = &'static str;
624
625    fn try_from(legacy: LegacyBlockEnv) -> Result<Self, Self::Error> {
626        Ok(Self {
627            number: legacy.number.and_then(|v| v.to_u256()).unwrap_or(U256::ZERO),
628            beneficiary: legacy.beneficiary.unwrap_or(Address::ZERO),
629            timestamp: legacy.timestamp.and_then(|v| v.to_u256()).unwrap_or(U256::ONE),
630            gas_limit: legacy.gas_limit.and_then(|v| v.to_u64()).unwrap_or(u64::MAX),
631            basefee: legacy.basefee.and_then(|v| v.to_u64()).unwrap_or(0),
632            difficulty: legacy.difficulty.and_then(|v| v.to_u256()).unwrap_or(U256::ZERO),
633            prevrandao: legacy.prevrandao.or(Some(B256::ZERO)),
634            slot_num: 0,
635            blob_excess_gas_and_price: legacy
636                .blob_excess_gas_and_price
637                .map(|v| BlobExcessGasAndPrice {
638                    excess_blob_gas: v.excess_blob_gas,
639                    blob_gasprice: v.blob_gasprice,
640                })
641                .or_else(|| {
642                    Some(BlobExcessGasAndPrice::new(0, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE))
643                }),
644        })
645    }
646}
647
648/// Custom deserializer for `BlockEnv` that handles both v1.2 and v1.3+ formats.
649fn deserialize_block_env_compat<'de, D>(deserializer: D) -> Result<Option<BlockEnv>, D::Error>
650where
651    D: Deserializer<'de>,
652{
653    let value: Option<Value> = Option::deserialize(deserializer)?;
654    let Some(value) = value else {
655        return Ok(None);
656    };
657
658    if let Ok(env) = BlockEnv::deserialize(&value) {
659        return Ok(Some(env));
660    }
661
662    let legacy: LegacyBlockEnv = serde_json::from_value(value).map_err(|e| {
663        D::Error::custom(format!("Legacy deserialization of `BlockEnv` failed: {e}"))
664    })?;
665
666    Ok(Some(BlockEnv::try_from(legacy).map_err(D::Error::custom)?))
667}
668
669/// Custom deserializer for `best_block_number` that handles both v1.2 and v1.3+ formats.
670fn deserialize_best_block_number_compat<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
671where
672    D: Deserializer<'de>,
673{
674    let value: Option<Value> = Option::deserialize(deserializer)?;
675    let Some(value) = value else {
676        return Ok(None);
677    };
678
679    let number = match value {
680        Value::Number(n) => n.as_u64(),
681        Value::String(s) => {
682            if let Some(s) = s.strip_prefix("0x") {
683                u64::from_str_radix(s, 16).ok()
684            } else {
685                s.parse().ok()
686            }
687        }
688        _ => None,
689    };
690
691    Ok(number)
692}
693
694#[derive(Clone, Debug, Default, Serialize, Deserialize)]
695pub struct SerializableState {
696    /// The block number of the state
697    ///
698    /// Note: This is an Option for backwards compatibility: <https://github.com/foundry-rs/foundry/issues/5460>
699    #[serde(deserialize_with = "deserialize_block_env_compat")]
700    pub block: Option<BlockEnv>,
701    pub accounts: BTreeMap<Address, SerializableAccountRecord>,
702    /// The best block number of the state, can be different from block number (Arbitrum chain).
703    #[serde(deserialize_with = "deserialize_best_block_number_compat")]
704    pub best_block_number: Option<u64>,
705    #[serde(default)]
706    pub blocks: Vec<SerializableBlock>,
707    #[serde(default)]
708    pub transactions: Vec<SerializableTransaction>,
709    /// Historical states of accounts and storage at particular block hashes.
710    ///
711    /// Note: This is an Option for backwards compatibility.
712    #[serde(default)]
713    pub historical_states: Option<SerializableHistoricalStates>,
714}
715
716impl SerializableState {
717    /// Loads the `Genesis` object from the given json file path
718    pub fn load(path: impl AsRef<Path>) -> Result<Self, FsPathError> {
719        let mut path = path.as_ref().to_path_buf();
720        if path.is_dir() {
721            path = path.join("state.json");
722        }
723
724        let file = File::open(&path).map_err(|err| FsPathError::read(err, &path))?;
725        serde_json::from_reader(BufReader::new(file)).map_err(|err| {
726            if err.is_io() {
727                FsPathError::read(err.into(), &path)
728            } else {
729                FsPathError::ReadJson { source: err, path }
730            }
731        })
732    }
733
734    /// This is used as the clap `value_parser` implementation
735    #[cfg(feature = "cmd")]
736    pub(crate) fn parse(path: &str) -> Result<Self, String> {
737        Self::load(path).map_err(|err| err.to_string())
738    }
739}
740
741#[derive(Clone, Debug, Serialize, Deserialize)]
742pub struct SerializableAccountRecord {
743    pub nonce: u64,
744    pub balance: U256,
745    pub code: Bytes,
746
747    #[serde(deserialize_with = "deserialize_btree")]
748    pub storage: BTreeMap<B256, B256>,
749}
750
751fn deserialize_btree<'de, D>(deserializer: D) -> Result<BTreeMap<B256, B256>, D::Error>
752where
753    D: Deserializer<'de>,
754{
755    struct BTreeVisitor;
756
757    impl<'de> Visitor<'de> for BTreeVisitor {
758        type Value = BTreeMap<B256, B256>;
759
760        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
761            formatter.write_str("a mapping of hex encoded storage slots to hex encoded state data")
762        }
763
764        fn visit_map<M>(self, mut mapping: M) -> Result<BTreeMap<B256, B256>, M::Error>
765        where
766            M: MapAccess<'de>,
767        {
768            let mut btree = BTreeMap::new();
769            while let Some((key, value)) = mapping.next_entry::<U256, U256>()? {
770                btree.insert(B256::from(key), B256::from(value));
771            }
772
773            Ok(btree)
774        }
775    }
776
777    deserializer.deserialize_map(BTreeVisitor)
778}
779
780/// Defines a backwards-compatible enum for transactions.
781/// This is essential for maintaining compatibility with state dumps
782/// created before the changes introduced in PR #8411.
783///
784/// The enum can represent either a `TypedTransaction` or a `MaybeImpersonatedTransaction`,
785/// depending on the data being deserialized. This flexibility ensures that older state
786/// dumps can still be loaded correctly, even after the changes in #8411.
787#[derive(Clone, Debug, Serialize, Deserialize)]
788#[serde(untagged)]
789pub enum SerializableTransactionType {
790    TypedTransaction(FoundryTxEnvelope),
791    MaybeImpersonatedTransaction(MaybeImpersonatedTransaction<FoundryTxEnvelope>),
792}
793
794#[derive(Clone, Debug, Serialize, Deserialize)]
795pub struct SerializableBlock {
796    pub header: FoundryHeader,
797    pub transactions: Vec<SerializableTransactionType>,
798    pub ommers: Vec<FoundryHeader>,
799    #[serde(default)]
800    pub withdrawals: Option<Withdrawals>,
801}
802
803impl From<Block> for SerializableBlock {
804    fn from(block: Block) -> Self {
805        Self {
806            header: block.header,
807            transactions: block.body.transactions.into_iter().map(Into::into).collect(),
808            ommers: block.body.ommers.into_iter().collect(),
809            withdrawals: block.body.withdrawals,
810        }
811    }
812}
813
814impl From<SerializableBlock> for Block {
815    fn from(block: SerializableBlock) -> Self {
816        let transactions = block.transactions.into_iter().map(Into::into).collect();
817        let ommers = block.ommers;
818        let body = BlockBody { transactions, ommers, withdrawals: block.withdrawals };
819        Self::new(block.header, body)
820    }
821}
822
823impl From<MaybeImpersonatedTransaction<FoundryTxEnvelope>> for SerializableTransactionType {
824    fn from(transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>) -> Self {
825        Self::MaybeImpersonatedTransaction(transaction)
826    }
827}
828
829impl From<SerializableTransactionType> for MaybeImpersonatedTransaction<FoundryTxEnvelope> {
830    fn from(transaction: SerializableTransactionType) -> Self {
831        match transaction {
832            SerializableTransactionType::TypedTransaction(tx) => Self::new(tx),
833            SerializableTransactionType::MaybeImpersonatedTransaction(tx) => tx,
834        }
835    }
836}
837
838#[derive(Clone, Debug, Serialize, Deserialize)]
839pub struct SerializableTransaction {
840    pub info: TransactionInfo,
841    pub receipt: FoundryReceiptEnvelope,
842    pub block_hash: B256,
843    pub block_number: u64,
844}
845
846impl<N: Network<ReceiptEnvelope = FoundryReceiptEnvelope>> From<MinedTransaction<N>>
847    for SerializableTransaction
848{
849    fn from(transaction: MinedTransaction<N>) -> Self {
850        Self {
851            info: transaction.info,
852            receipt: transaction.receipt,
853            block_hash: transaction.block_hash,
854            block_number: transaction.block_number,
855        }
856    }
857}
858
859impl<N: Network<ReceiptEnvelope = FoundryReceiptEnvelope>> From<SerializableTransaction>
860    for MinedTransaction<N>
861{
862    fn from(transaction: SerializableTransaction) -> Self {
863        Self {
864            info: transaction.info,
865            receipt: transaction.receipt,
866            block_hash: transaction.block_hash,
867            block_number: transaction.block_number,
868        }
869    }
870}
871
872#[derive(Clone, Debug, Serialize, Deserialize, Default)]
873pub struct SerializableHistoricalStates(Vec<(B256, StateSnapshot)>);
874
875impl SerializableHistoricalStates {
876    pub const fn new(states: Vec<(B256, StateSnapshot)>) -> Self {
877        Self(states)
878    }
879}
880
881impl IntoIterator for SerializableHistoricalStates {
882    type Item = (B256, StateSnapshot);
883    type IntoIter = std::vec::IntoIter<Self::Item>;
884
885    fn into_iter(self) -> Self::IntoIter {
886        self.0.into_iter()
887    }
888}
889
890#[cfg(test)]
891mod test {
892    use super::*;
893    use std::fs;
894
895    #[test]
896    fn loads_state_from_file_or_directory() {
897        let tmp = tempfile::tempdir().unwrap();
898        let state_path = tmp.path().join("state.json");
899        fs::write(&state_path, serde_json::to_vec(&SerializableState::default()).unwrap()).unwrap();
900
901        assert!(SerializableState::load(&state_path).unwrap().accounts.is_empty());
902        assert!(SerializableState::load(tmp.path()).unwrap().accounts.is_empty());
903
904        fs::write(&state_path, b"not json").unwrap();
905        let Err(FsPathError::ReadJson { path, .. }) = SerializableState::load(tmp.path()) else {
906            panic!("expected invalid JSON error")
907        };
908        assert_eq!(path, state_path);
909
910        let missing_path = tmp.path().join("missing.json");
911        let Err(FsPathError::Read { path, .. }) = SerializableState::load(&missing_path) else {
912            panic!("expected file read error")
913        };
914        assert_eq!(path, missing_path);
915    }
916
917    #[test]
918    fn cache_db_full_state_merges_base_and_overlay() {
919        let preserved = Address::with_last_byte(1);
920        let updated = Address::with_last_byte(2);
921        let deleted = Address::with_last_byte(3);
922        let cleared = Address::with_last_byte(4);
923        let deleted_slot = U256::from(1);
924        let updated_slot = U256::from(2);
925
926        let mut base = MemDb::default();
927        base.insert_account(preserved, AccountInfo::from_balance(U256::from(1)));
928        base.insert_account(updated, AccountInfo::from_balance(U256::from(2)));
929        base.set_storage_at(updated, deleted_slot.into(), B256::from(U256::from(10))).unwrap();
930        base.set_storage_at(updated, updated_slot.into(), B256::from(U256::from(11))).unwrap();
931        base.insert_account(deleted, AccountInfo::from_balance(U256::from(3)));
932        base.insert_account(cleared, AccountInfo::from_balance(U256::from(4)));
933        base.set_storage_at(cleared, deleted_slot.into(), B256::from(U256::from(11))).unwrap();
934
935        let mut cache = CacheDB::new(base);
936        cache.insert_account_info(updated, AccountInfo::from_balance(U256::from(20)));
937        cache.insert_account_storage(updated, deleted_slot, U256::ZERO).unwrap();
938        cache.insert_account_storage(updated, updated_slot, U256::from(12)).unwrap();
939        cache.cache.accounts.insert(
940            deleted,
941            DbAccount { account_state: AccountState::NotExisting, ..Default::default() },
942        );
943        cache.cache.accounts.insert(
944            cleared,
945            DbAccount {
946                info: AccountInfo::from_balance(U256::from(40)),
947                account_state: AccountState::StorageCleared,
948                ..Default::default()
949            },
950        );
951
952        let accounts = cache.maybe_full_db().unwrap();
953        assert_eq!(accounts[&preserved].info.balance, U256::from(1));
954        assert_eq!(accounts[&updated].info.balance, U256::from(20));
955        assert_eq!(accounts[&updated].storage[&deleted_slot], U256::ZERO);
956        assert_eq!(accounts[&updated].storage[&updated_slot], U256::from(12));
957        assert!(!accounts.contains_key(&deleted));
958        assert!(accounts[&cleared].storage.is_empty());
959        let mut expected = accounts;
960        expected.get_mut(&updated).unwrap().storage.remove(&deleted_slot);
961        assert_eq!(
962            cache.maybe_full_db().map(|accounts| crate::mem::state::state_root(&accounts)),
963            Some(crate::mem::state::state_root(&expected))
964        );
965    }
966
967    #[test]
968    fn test_deser_block() {
969        let block = r#"{
970            "header": {
971                "parentHash": "0xceb0fe420d6f14a8eeec4319515b89acbb0bb4861cad9983d529ab4b1e4af929",
972                "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
973                "miner": "0x0000000000000000000000000000000000000000",
974                "stateRoot": "0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1",
975                "transactionsRoot": "0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988",
976                "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa",
977                "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
978                "difficulty": "0x0",
979                "number": "0x2",
980                "gasLimit": "0x1c9c380",
981                "gasUsed": "0x5208",
982                "timestamp": "0x66cdc823",
983                "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
984                "nonce": "0x0000000000000000",
985                "baseFeePerGas": "0x342a1c58",
986                "blobGasUsed": "0x0",
987                "excessBlobGas": "0x0",
988                "extraData": "0x"
989            },
990            "transactions": [
991                {
992                    "type": "0x2",
993                    "chainId": "0x7a69",
994                    "nonce": "0x0",
995                    "gas": "0x5209",
996                    "maxFeePerGas": "0x77359401",
997                    "maxPriorityFeePerGas": "0x1",
998                    "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
999                    "value": "0x0",
1000                    "accessList": [],
1001                    "input": "0x",
1002                    "r": "0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0",
1003                    "s": "0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd",
1004                    "yParity": "0x0",
1005                    "hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"
1006                }
1007            ],
1008            "ommers": []
1009        }
1010        "#;
1011
1012        let _block: SerializableBlock = serde_json::from_str(block).unwrap();
1013    }
1014
1015    #[test]
1016    fn test_block_withdrawals_preserved() {
1017        use alloy_eips::eip4895::Withdrawal;
1018
1019        // create a block with withdrawals (like post-Shanghai blocks)
1020        let withdrawal = Withdrawal {
1021            index: 42,
1022            validator_index: 123,
1023            address: Address::repeat_byte(1),
1024            amount: 1000,
1025        };
1026
1027        let header = Header::default();
1028        let body = BlockBody {
1029            transactions: vec![],
1030            ommers: vec![],
1031            withdrawals: Some(vec![withdrawal].into()),
1032        };
1033        let block = Block::new(header.into(), body);
1034
1035        // convert to SerializableBlock and back
1036        let serializable = SerializableBlock::from(block);
1037        let restored = Block::from(serializable);
1038
1039        // withdrawals should be preserved
1040        assert!(restored.body.withdrawals.is_some());
1041        let withdrawals = restored.body.withdrawals.unwrap();
1042        assert_eq!(withdrawals.len(), 1);
1043        assert_eq!(withdrawals[0].index, 42);
1044        assert_eq!(withdrawals[0].validator_index, 123);
1045    }
1046}