1use std::{
4 collections::BTreeMap,
5 fmt::{self, Debug},
6 path::Path,
7};
8
9use alloy_consensus::{BlockBody, Header};
10use alloy_primitives::{Address, B256, Bytes, U256, keccak256, map::HashMap};
11use alloy_rpc_types::BlockId;
12use anvil_core::eth::{
13 block::Block,
14 transaction::{MaybeImpersonatedTransaction, TransactionInfo},
15};
16use foundry_common::errors::FsPathError;
17use foundry_evm::backend::{
18 BlockchainDb, DatabaseError, DatabaseResult, MemDb, RevertStateSnapshotAction, StateSnapshot,
19};
20use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope};
21use revm::{
22 Database, DatabaseCommit,
23 bytecode::Bytecode,
24 context::BlockEnv,
25 context_interface::block::BlobExcessGasAndPrice,
26 database::{CacheDB, DatabaseRef, DbAccount},
27 primitives::{KECCAK_EMPTY, eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE},
28 state::AccountInfo,
29};
30use serde::{
31 Deserialize, Deserializer, Serialize,
32 de::{Error as DeError, MapAccess, Visitor},
33};
34use serde_json::Value;
35
36use crate::mem::storage::MinedTransaction;
37
38pub trait MaybeFullDatabase: DatabaseRef<Error = DatabaseError> + Debug {
40 fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
41 None
42 }
43
44 fn clear_into_state_snapshot(&mut self) -> StateSnapshot;
46
47 fn read_as_state_snapshot(&self) -> StateSnapshot;
51
52 fn clear(&mut self);
54
55 fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot);
57}
58
59impl<'a, T: 'a + MaybeFullDatabase + ?Sized> MaybeFullDatabase for &'a T
60where
61 &'a T: DatabaseRef<Error = DatabaseError>,
62{
63 fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
64 T::maybe_as_full_db(self)
65 }
66
67 fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
68 unreachable!("never called for DatabaseRef")
69 }
70
71 fn read_as_state_snapshot(&self) -> StateSnapshot {
72 unreachable!("never called for DatabaseRef")
73 }
74
75 fn clear(&mut self) {}
76
77 fn init_from_state_snapshot(&mut self, _state_snapshot: StateSnapshot) {}
78}
79
80pub trait MaybeForkedDatabase {
82 fn maybe_reset(&mut self, _url: Option<String>, block_number: BlockId) -> Result<(), String>;
83
84 fn maybe_flush_cache(&self) -> Result<(), String>;
85
86 fn maybe_inner(&self) -> Result<&BlockchainDb, String>;
87}
88
89pub trait Db:
91 DatabaseRef<Error = DatabaseError>
92 + Database<Error = DatabaseError>
93 + DatabaseCommit
94 + MaybeFullDatabase
95 + MaybeForkedDatabase
96 + fmt::Debug
97 + Send
98 + Sync
99{
100 fn insert_account(&mut self, address: Address, account: AccountInfo);
102
103 fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<()> {
105 let mut info = self.basic(address)?.unwrap_or_default();
106 info.nonce = nonce;
107 self.insert_account(address, info);
108 Ok(())
109 }
110
111 fn set_balance(&mut self, address: Address, balance: U256) -> DatabaseResult<()> {
113 let mut info = self.basic(address)?.unwrap_or_default();
114 info.balance = balance;
115 self.insert_account(address, info);
116 Ok(())
117 }
118
119 fn set_code(&mut self, address: Address, code: Bytes) -> DatabaseResult<()> {
121 let mut info = self.basic(address)?.unwrap_or_default();
122 let code_hash = if code.as_ref().is_empty() {
123 KECCAK_EMPTY
124 } else {
125 B256::from_slice(&keccak256(code.as_ref())[..])
126 };
127 info.code_hash = code_hash;
128 info.code = Some(Bytecode::new_raw(alloy_primitives::Bytes(code.0)));
129 self.insert_account(address, info);
130 Ok(())
131 }
132
133 fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()>;
135
136 fn insert_block_hash(&mut self, number: U256, hash: B256);
138
139 fn dump_state(
141 &self,
142 at: BlockEnv,
143 best_number: u64,
144 blocks: Vec<SerializableBlock>,
145 transactions: Vec<SerializableTransaction>,
146 historical_states: Option<SerializableHistoricalStates>,
147 ) -> DatabaseResult<Option<SerializableState>>;
148
149 fn load_state(&mut self, state: SerializableState) -> DatabaseResult<bool> {
151 for (addr, account) in state.accounts.into_iter() {
152 let old_account_nonce = DatabaseRef::basic_ref(self, addr)
153 .ok()
154 .and_then(|acc| acc.map(|acc| acc.nonce))
155 .unwrap_or_default();
156 let nonce = std::cmp::max(old_account_nonce, account.nonce);
159
160 self.insert_account(
161 addr,
162 AccountInfo {
163 balance: account.balance,
164 code_hash: KECCAK_EMPTY, code: if account.code.0.is_empty() {
166 None
167 } else {
168 Some(Bytecode::new_raw(alloy_primitives::Bytes(account.code.0)))
169 },
170 nonce,
171 },
172 );
173
174 for (k, v) in account.storage.into_iter() {
175 self.set_storage_at(addr, k, v)?;
176 }
177 }
178 Ok(true)
179 }
180
181 fn snapshot_state(&mut self) -> U256;
183
184 fn revert_state(&mut self, state_snapshot: U256, action: RevertStateSnapshotAction) -> bool;
188
189 fn maybe_state_root(&self) -> Option<B256> {
191 None
192 }
193
194 fn current_state(&self) -> StateDb;
196}
197
198impl<T: DatabaseRef<Error = DatabaseError> + Send + Sync + Clone + fmt::Debug> Db for CacheDB<T> {
203 fn insert_account(&mut self, address: Address, account: AccountInfo) {
204 self.insert_account_info(address, account)
205 }
206
207 fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()> {
208 self.insert_account_storage(address, slot.into(), val.into())
209 }
210
211 fn insert_block_hash(&mut self, number: U256, hash: B256) {
212 self.cache.block_hashes.insert(number, hash);
213 }
214
215 fn dump_state(
216 &self,
217 _at: BlockEnv,
218 _best_number: u64,
219 _blocks: Vec<SerializableBlock>,
220 _transaction: Vec<SerializableTransaction>,
221 _historical_states: Option<SerializableHistoricalStates>,
222 ) -> DatabaseResult<Option<SerializableState>> {
223 Ok(None)
224 }
225
226 fn snapshot_state(&mut self) -> U256 {
227 U256::ZERO
228 }
229
230 fn revert_state(&mut self, _state_snapshot: U256, _action: RevertStateSnapshotAction) -> bool {
231 false
232 }
233
234 fn current_state(&self) -> StateDb {
235 StateDb::new(MemDb::default())
236 }
237}
238
239impl<T: DatabaseRef<Error = DatabaseError> + Debug> MaybeFullDatabase for CacheDB<T> {
240 fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
241 Some(&self.cache.accounts)
242 }
243
244 fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
245 let db_accounts = std::mem::take(&mut self.cache.accounts);
246 let mut accounts = HashMap::default();
247 let mut account_storage = HashMap::default();
248
249 for (addr, mut acc) in db_accounts {
250 account_storage.insert(addr, std::mem::take(&mut acc.storage));
251 let mut info = acc.info;
252 info.code = self.cache.contracts.remove(&info.code_hash);
253 accounts.insert(addr, info);
254 }
255 let block_hashes = std::mem::take(&mut self.cache.block_hashes);
256 StateSnapshot { accounts, storage: account_storage, block_hashes }
257 }
258
259 fn read_as_state_snapshot(&self) -> StateSnapshot {
260 let mut accounts = HashMap::default();
261 let mut account_storage = HashMap::default();
262
263 for (addr, acc) in &self.cache.accounts {
264 account_storage.insert(*addr, acc.storage.clone());
265 let mut info = acc.info.clone();
266 info.code = self.cache.contracts.get(&info.code_hash).cloned();
267 accounts.insert(*addr, info);
268 }
269
270 let block_hashes = self.cache.block_hashes.clone();
271 StateSnapshot { accounts, storage: account_storage, block_hashes }
272 }
273
274 fn clear(&mut self) {
275 self.clear_into_state_snapshot();
276 }
277
278 fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) {
279 let StateSnapshot { accounts, mut storage, block_hashes } = state_snapshot;
280
281 for (addr, mut acc) in accounts {
282 if let Some(code) = acc.code.take() {
283 self.cache.contracts.insert(acc.code_hash, code);
284 }
285 self.cache.accounts.insert(
286 addr,
287 DbAccount {
288 info: acc,
289 storage: storage.remove(&addr).unwrap_or_default(),
290 ..Default::default()
291 },
292 );
293 }
294 self.cache.block_hashes = block_hashes;
295 }
296}
297
298impl<T: DatabaseRef<Error = DatabaseError>> MaybeForkedDatabase for CacheDB<T> {
299 fn maybe_reset(&mut self, _url: Option<String>, _block_number: BlockId) -> Result<(), String> {
300 Err("not supported".to_string())
301 }
302
303 fn maybe_flush_cache(&self) -> Result<(), String> {
304 Err("not supported".to_string())
305 }
306
307 fn maybe_inner(&self) -> Result<&BlockchainDb, String> {
308 Err("not supported".to_string())
309 }
310}
311
312#[derive(Debug)]
314pub struct StateDb(pub(crate) Box<dyn MaybeFullDatabase + Send + Sync>);
315
316impl StateDb {
317 pub fn new(db: impl MaybeFullDatabase + Send + Sync + 'static) -> Self {
318 Self(Box::new(db))
319 }
320
321 pub fn serialize_state(&mut self) -> StateSnapshot {
322 self.read_as_state_snapshot()
325 }
326}
327
328impl DatabaseRef for StateDb {
329 type Error = DatabaseError;
330 fn basic_ref(&self, address: Address) -> DatabaseResult<Option<AccountInfo>> {
331 self.0.basic_ref(address)
332 }
333
334 fn code_by_hash_ref(&self, code_hash: B256) -> DatabaseResult<Bytecode> {
335 self.0.code_by_hash_ref(code_hash)
336 }
337
338 fn storage_ref(&self, address: Address, index: U256) -> DatabaseResult<U256> {
339 self.0.storage_ref(address, index)
340 }
341
342 fn block_hash_ref(&self, number: u64) -> DatabaseResult<B256> {
343 self.0.block_hash_ref(number)
344 }
345}
346
347impl MaybeFullDatabase for StateDb {
348 fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
349 self.0.maybe_as_full_db()
350 }
351
352 fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
353 self.0.clear_into_state_snapshot()
354 }
355
356 fn read_as_state_snapshot(&self) -> StateSnapshot {
357 self.0.read_as_state_snapshot()
358 }
359
360 fn clear(&mut self) {
361 self.0.clear()
362 }
363
364 fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) {
365 self.0.init_from_state_snapshot(state_snapshot)
366 }
367}
368
369#[derive(Debug, Deserialize)]
371#[serde(rename_all = "snake_case")]
372pub struct LegacyBlockEnv {
373 pub number: Option<StringOrU64>,
374 #[serde(alias = "coinbase")]
375 pub beneficiary: Option<Address>,
376 pub timestamp: Option<StringOrU64>,
377 pub gas_limit: Option<StringOrU64>,
378 pub basefee: Option<StringOrU64>,
379 pub difficulty: Option<StringOrU64>,
380 pub prevrandao: Option<B256>,
381 pub blob_excess_gas_and_price: Option<LegacyBlobExcessGasAndPrice>,
382}
383
384#[derive(Debug, Deserialize)]
386pub struct LegacyBlobExcessGasAndPrice {
387 pub excess_blob_gas: u64,
388 pub blob_gasprice: u64,
389}
390
391#[derive(Debug, Deserialize)]
393#[serde(untagged)]
394pub enum StringOrU64 {
395 Hex(String),
396 Dec(u64),
397}
398
399impl StringOrU64 {
400 pub fn to_u64(&self) -> Option<u64> {
401 match self {
402 Self::Dec(n) => Some(*n),
403 Self::Hex(s) => s.strip_prefix("0x").and_then(|s| u64::from_str_radix(s, 16).ok()),
404 }
405 }
406
407 pub fn to_u256(&self) -> Option<U256> {
408 match self {
409 Self::Dec(n) => Some(U256::from(*n)),
410 Self::Hex(s) => s.strip_prefix("0x").and_then(|s| U256::from_str_radix(s, 16).ok()),
411 }
412 }
413}
414
415impl TryFrom<LegacyBlockEnv> for BlockEnv {
417 type Error = &'static str;
418
419 fn try_from(legacy: LegacyBlockEnv) -> Result<Self, Self::Error> {
420 Ok(Self {
421 number: legacy.number.and_then(|v| v.to_u256()).unwrap_or(U256::ZERO),
422 beneficiary: legacy.beneficiary.unwrap_or(Address::ZERO),
423 timestamp: legacy.timestamp.and_then(|v| v.to_u256()).unwrap_or(U256::ONE),
424 gas_limit: legacy.gas_limit.and_then(|v| v.to_u64()).unwrap_or(u64::MAX),
425 basefee: legacy.basefee.and_then(|v| v.to_u64()).unwrap_or(0),
426 difficulty: legacy.difficulty.and_then(|v| v.to_u256()).unwrap_or(U256::ZERO),
427 prevrandao: legacy.prevrandao.or(Some(B256::ZERO)),
428 blob_excess_gas_and_price: legacy
429 .blob_excess_gas_and_price
430 .map(|v| BlobExcessGasAndPrice::new(v.excess_blob_gas, v.blob_gasprice))
431 .or_else(|| {
432 Some(BlobExcessGasAndPrice::new(0, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE))
433 }),
434 })
435 }
436}
437
438fn deserialize_block_env_compat<'de, D>(deserializer: D) -> Result<Option<BlockEnv>, D::Error>
440where
441 D: Deserializer<'de>,
442{
443 let value: Option<Value> = Option::deserialize(deserializer)?;
444 let Some(value) = value else {
445 return Ok(None);
446 };
447
448 if let Ok(env) = BlockEnv::deserialize(&value) {
449 return Ok(Some(env));
450 }
451
452 let legacy: LegacyBlockEnv = serde_json::from_value(value).map_err(|e| {
453 D::Error::custom(format!("Legacy deserialization of `BlockEnv` failed: {e}"))
454 })?;
455
456 Ok(Some(BlockEnv::try_from(legacy).map_err(D::Error::custom)?))
457}
458
459fn deserialize_best_block_number_compat<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
461where
462 D: Deserializer<'de>,
463{
464 let value: Option<Value> = Option::deserialize(deserializer)?;
465 let Some(value) = value else {
466 return Ok(None);
467 };
468
469 let number = match value {
470 Value::Number(n) => n.as_u64(),
471 Value::String(s) => {
472 if let Some(s) = s.strip_prefix("0x") {
473 u64::from_str_radix(s, 16).ok()
474 } else {
475 s.parse().ok()
476 }
477 }
478 _ => None,
479 };
480
481 Ok(number)
482}
483
484#[derive(Clone, Debug, Default, Serialize, Deserialize)]
485pub struct SerializableState {
486 #[serde(deserialize_with = "deserialize_block_env_compat")]
490 pub block: Option<BlockEnv>,
491 pub accounts: BTreeMap<Address, SerializableAccountRecord>,
492 #[serde(deserialize_with = "deserialize_best_block_number_compat")]
494 pub best_block_number: Option<u64>,
495 #[serde(default)]
496 pub blocks: Vec<SerializableBlock>,
497 #[serde(default)]
498 pub transactions: Vec<SerializableTransaction>,
499 #[serde(default)]
503 pub historical_states: Option<SerializableHistoricalStates>,
504}
505
506impl SerializableState {
507 pub fn load(path: impl AsRef<Path>) -> Result<Self, FsPathError> {
509 let path = path.as_ref();
510 if path.is_dir() {
511 foundry_common::fs::read_json_file(&path.join("state.json"))
512 } else {
513 foundry_common::fs::read_json_file(path)
514 }
515 }
516
517 #[cfg(feature = "cmd")]
519 pub(crate) fn parse(path: &str) -> Result<Self, String> {
520 Self::load(path).map_err(|err| err.to_string())
521 }
522}
523
524#[derive(Clone, Debug, Serialize, Deserialize)]
525pub struct SerializableAccountRecord {
526 pub nonce: u64,
527 pub balance: U256,
528 pub code: Bytes,
529
530 #[serde(deserialize_with = "deserialize_btree")]
531 pub storage: BTreeMap<B256, B256>,
532}
533
534fn deserialize_btree<'de, D>(deserializer: D) -> Result<BTreeMap<B256, B256>, D::Error>
535where
536 D: Deserializer<'de>,
537{
538 struct BTreeVisitor;
539
540 impl<'de> Visitor<'de> for BTreeVisitor {
541 type Value = BTreeMap<B256, B256>;
542
543 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
544 formatter.write_str("a mapping of hex encoded storage slots to hex encoded state data")
545 }
546
547 fn visit_map<M>(self, mut mapping: M) -> Result<BTreeMap<B256, B256>, M::Error>
548 where
549 M: MapAccess<'de>,
550 {
551 let mut btree = BTreeMap::new();
552 while let Some((key, value)) = mapping.next_entry::<U256, U256>()? {
553 btree.insert(B256::from(key), B256::from(value));
554 }
555
556 Ok(btree)
557 }
558 }
559
560 deserializer.deserialize_map(BTreeVisitor)
561}
562
563#[derive(Clone, Debug, Serialize, Deserialize)]
571#[serde(untagged)]
572pub enum SerializableTransactionType {
573 TypedTransaction(FoundryTxEnvelope),
574 MaybeImpersonatedTransaction(MaybeImpersonatedTransaction),
575}
576
577#[derive(Clone, Debug, Serialize, Deserialize)]
578pub struct SerializableBlock {
579 pub header: Header,
580 pub transactions: Vec<SerializableTransactionType>,
581 pub ommers: Vec<Header>,
582}
583
584impl From<Block> for SerializableBlock {
585 fn from(block: Block) -> Self {
586 Self {
587 header: block.header,
588 transactions: block.body.transactions.into_iter().map(Into::into).collect(),
589 ommers: block.body.ommers.into_iter().collect(),
590 }
591 }
592}
593
594impl From<SerializableBlock> for Block {
595 fn from(block: SerializableBlock) -> Self {
596 let transactions = block.transactions.into_iter().map(Into::into).collect();
597 let ommers = block.ommers;
598 let body = BlockBody { transactions, ommers, withdrawals: None };
599 Self::new(block.header, body)
600 }
601}
602
603impl From<MaybeImpersonatedTransaction> for SerializableTransactionType {
604 fn from(transaction: MaybeImpersonatedTransaction) -> Self {
605 Self::MaybeImpersonatedTransaction(transaction)
606 }
607}
608
609impl From<SerializableTransactionType> for MaybeImpersonatedTransaction {
610 fn from(transaction: SerializableTransactionType) -> Self {
611 match transaction {
612 SerializableTransactionType::TypedTransaction(tx) => Self::new(tx),
613 SerializableTransactionType::MaybeImpersonatedTransaction(tx) => tx,
614 }
615 }
616}
617
618#[derive(Clone, Debug, Serialize, Deserialize)]
619pub struct SerializableTransaction {
620 pub info: TransactionInfo,
621 pub receipt: FoundryReceiptEnvelope,
622 pub block_hash: B256,
623 pub block_number: u64,
624}
625
626impl From<MinedTransaction> for SerializableTransaction {
627 fn from(transaction: MinedTransaction) -> Self {
628 Self {
629 info: transaction.info,
630 receipt: transaction.receipt,
631 block_hash: transaction.block_hash,
632 block_number: transaction.block_number,
633 }
634 }
635}
636
637impl From<SerializableTransaction> for MinedTransaction {
638 fn from(transaction: SerializableTransaction) -> Self {
639 Self {
640 info: transaction.info,
641 receipt: transaction.receipt,
642 block_hash: transaction.block_hash,
643 block_number: transaction.block_number,
644 }
645 }
646}
647
648#[derive(Clone, Debug, Serialize, Deserialize, Default)]
649pub struct SerializableHistoricalStates(Vec<(B256, StateSnapshot)>);
650
651impl SerializableHistoricalStates {
652 pub const fn new(states: Vec<(B256, StateSnapshot)>) -> Self {
653 Self(states)
654 }
655}
656
657impl IntoIterator for SerializableHistoricalStates {
658 type Item = (B256, StateSnapshot);
659 type IntoIter = std::vec::IntoIter<Self::Item>;
660
661 fn into_iter(self) -> Self::IntoIter {
662 self.0.into_iter()
663 }
664}
665
666#[cfg(test)]
667mod test {
668 use super::*;
669
670 #[test]
671 fn test_deser_block() {
672 let block = r#"{
673 "header": {
674 "parentHash": "0xceb0fe420d6f14a8eeec4319515b89acbb0bb4861cad9983d529ab4b1e4af929",
675 "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
676 "miner": "0x0000000000000000000000000000000000000000",
677 "stateRoot": "0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1",
678 "transactionsRoot": "0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988",
679 "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa",
680 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
681 "difficulty": "0x0",
682 "number": "0x2",
683 "gasLimit": "0x1c9c380",
684 "gasUsed": "0x5208",
685 "timestamp": "0x66cdc823",
686 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
687 "nonce": "0x0000000000000000",
688 "baseFeePerGas": "0x342a1c58",
689 "blobGasUsed": "0x0",
690 "excessBlobGas": "0x0",
691 "extraData": "0x"
692 },
693 "transactions": [
694 {
695 "type": "0x2",
696 "chainId": "0x7a69",
697 "nonce": "0x0",
698 "gas": "0x5209",
699 "maxFeePerGas": "0x77359401",
700 "maxPriorityFeePerGas": "0x1",
701 "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
702 "value": "0x0",
703 "accessList": [],
704 "input": "0x",
705 "r": "0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0",
706 "s": "0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd",
707 "yParity": "0x0",
708 "hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"
709 }
710 ],
711 "ommers": []
712 }
713 "#;
714
715 let _block: SerializableBlock = serde_json::from_str(block).unwrap();
716 }
717}