Skip to main content

anvil/eth/backend/
db.rs

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