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);