diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index dc758a52aad75..5a1fe7b03bde7 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -174,7 +174,7 @@ pub enum TypedTransactionRequest { /// /// This is a helper that carries the `impersonated` sender so that the right hash /// [TypedTransaction::impersonated_hash] can be created. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct MaybeImpersonatedTransaction { pub transaction: TypedTransaction, pub impersonated_sender: Option
, @@ -1109,7 +1109,7 @@ pub struct TransactionEssentials { } /// Represents all relevant information of an executed transaction -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct TransactionInfo { pub transaction_hash: B256, pub transaction_index: u64, diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 48b0eaaf5b139..5dd37e5eed958 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -1,10 +1,13 @@ //! Helper types for working with [revm](foundry_evm::revm) -use crate::revm::primitives::AccountInfo; +use crate::{mem::storage::MinedTransaction, revm::primitives::AccountInfo}; use alloy_consensus::Header; use alloy_primitives::{keccak256, Address, Bytes, B256, U256, U64}; use alloy_rpc_types::BlockId; -use anvil_core::eth::{block::Block, transaction::TypedTransaction}; +use anvil_core::eth::{ + block::Block, + transaction::{MaybeImpersonatedTransaction, TransactionInfo, TypedReceipt}, +}; use foundry_common::errors::FsPathError; use foundry_evm::{ backend::{ @@ -120,6 +123,7 @@ pub trait Db: at: BlockEnv, best_number: U64, blocks: Vec, + transactions: Vec, ) -> DatabaseResult>; /// Deserialize and add all chain data to the backend storage @@ -193,6 +197,7 @@ impl + Send + Sync + Clone + fmt::Debug> D _at: BlockEnv, _best_number: U64, _blocks: Vec, + _transaction: Vec, ) -> DatabaseResult> { Ok(None) } @@ -325,6 +330,8 @@ pub struct SerializableState { pub best_block_number: Option, #[serde(default)] pub blocks: Vec, + #[serde(default)] + pub transactions: Vec, } impl SerializableState { @@ -355,7 +362,7 @@ pub struct SerializableAccountRecord { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct SerializableBlock { pub header: Header, - pub transactions: Vec, + pub transactions: Vec, pub ommers: Vec
, } @@ -378,3 +385,33 @@ impl From for Block { } } } + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SerializableTransaction { + pub info: TransactionInfo, + pub receipt: TypedReceipt, + pub block_hash: B256, + pub block_number: u64, +} + +impl From for SerializableTransaction { + fn from(transaction: MinedTransaction) -> Self { + Self { + info: transaction.info, + receipt: transaction.receipt, + block_hash: transaction.block_hash, + block_number: transaction.block_number, + } + } +} + +impl From for MinedTransaction { + fn from(transaction: SerializableTransaction) -> Self { + Self { + info: transaction.info, + receipt: transaction.receipt, + block_hash: transaction.block_hash, + block_number: transaction.block_number, + } + } +} diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index cf86699c5b1ca..a179f50c3b494 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -1,7 +1,7 @@ use crate::{ eth::backend::db::{ Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableBlock, - SerializableState, StateDb, + SerializableState, SerializableTransaction, StateDb, }, revm::primitives::AccountInfo, }; @@ -37,6 +37,7 @@ impl Db for ForkedDatabase { at: BlockEnv, best_number: U64, blocks: Vec, + transactions: Vec, ) -> DatabaseResult> { let mut db = self.database().clone(); let accounts = self @@ -66,6 +67,7 @@ impl Db for ForkedDatabase { accounts, best_block_number: Some(best_number), blocks, + transactions, })) } diff --git a/crates/anvil/src/eth/backend/mem/in_memory_db.rs b/crates/anvil/src/eth/backend/mem/in_memory_db.rs index b6c3db12c2b1b..059c00f326bf5 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -3,7 +3,7 @@ use crate::{ eth::backend::db::{ Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableBlock, - SerializableState, StateDb, + SerializableState, SerializableTransaction, StateDb, }, mem::state::state_root, revm::{db::DbAccount, primitives::AccountInfo}, @@ -37,6 +37,7 @@ impl Db for MemDb { at: BlockEnv, best_number: U64, blocks: Vec, + transactions: Vec, ) -> DatabaseResult> { let accounts = self .inner @@ -66,6 +67,7 @@ impl Db for MemDb { accounts, best_block_number: Some(best_number), blocks, + transactions, })) } @@ -160,7 +162,10 @@ mod tests { dump_db.set_storage_at(test_addr, U256::from(1234567), U256::from(1)).unwrap(); // blocks dumping/loading tested in storage.rs - let state = dump_db.dump_state(Default::default(), U64::ZERO, Vec::new()).unwrap().unwrap(); + let state = dump_db + .dump_state(Default::default(), U64::ZERO, Vec::new(), Vec::new()) + .unwrap() + .unwrap(); let mut load_db = MemDb::default(); diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 9bb431e2e79f9..f38f1bd290cbc 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -744,7 +744,8 @@ impl Backend { let at = self.env.read().block.clone(); let best_number = self.blockchain.storage.read().best_number; let blocks = self.blockchain.storage.read().serialized_blocks(); - let state = self.db.read().await.dump_state(at, best_number, blocks)?; + let transactions = self.blockchain.storage.read().serialized_transactions(); + let state = self.db.read().await.dump_state(at, best_number, blocks, transactions)?; state.ok_or_else(|| { RpcError::invalid_params("Dumping state not supported with the current configuration") .into() @@ -781,6 +782,7 @@ impl Backend { } self.blockchain.storage.write().load_blocks(state.blocks.clone()); + self.blockchain.storage.write().load_transactions(state.transactions.clone()); Ok(true) } diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 8a92e242df802..f5d790c2b8059 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -1,7 +1,7 @@ //! In-memory blockchain storage use crate::eth::{ backend::{ - db::{MaybeFullDatabase, SerializableBlock, StateDb}, + db::{MaybeFullDatabase, SerializableBlock, SerializableTransaction, StateDb}, mem::cache::DiskStateCache, }, error::BlockchainError, @@ -334,6 +334,10 @@ impl BlockchainStorage { self.blocks.values().map(|block| block.clone().into()).collect() } + pub fn serialized_transactions(&self) -> Vec { + self.transactions.values().map(|tx: &MinedTransaction| tx.clone().into()).collect() + } + /// Deserialize and add all blocks data to the backend storage pub fn load_blocks(&mut self, serializable_blocks: Vec) { for serializable_block in serializable_blocks.iter() { @@ -344,6 +348,14 @@ impl BlockchainStorage { self.hashes.insert(U64::from(block_number), block_hash); } } + + /// Deserialize and add all blocks data to the backend storage + pub fn load_transactions(&mut self, serializable_transactions: Vec) { + for serializable_transaction in serializable_transactions.iter() { + let transaction: MinedTransaction = serializable_transaction.clone().into(); + self.transactions.insert(transaction.info.transaction_hash, transaction); + } + } } /// A simple in-memory blockchain @@ -525,6 +537,7 @@ mod tests { primitives::{AccountInfo, U256}, }, }; + use yansi::Paint; #[test] fn test_interval_update() { @@ -590,7 +603,8 @@ mod tests { } } - // verifies that blocks in BlockchainStorage remain the same when dumped and reloaded + // verifies that blocks and transactions in BlockchainStorage remain the same when dumped and + // reloaded #[test] fn test_storage_dump_reload_cycle() { let mut dump_storage = BlockchainStorage::empty(); @@ -608,10 +622,12 @@ mod tests { dump_storage.blocks.insert(block_hash, block); let serialized_blocks = dump_storage.serialized_blocks(); + let serialized_transactions = dump_storage.serialized_transactions(); let mut load_storage = BlockchainStorage::empty(); load_storage.load_blocks(serialized_blocks); + load_storage.load_transactions(serialized_transactions); let loaded_block = load_storage.blocks.get(&block_hash).unwrap(); assert_eq!(loaded_block.header.gas_limit, partial_header.gas_limit);