Skip to content

Commit

Permalink
feat(anvil): add mined transactions to state dump
Browse files Browse the repository at this point in the history
  • Loading branch information
tuler committed Jul 16, 2024
1 parent ea7817c commit f78df87
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 11 deletions.
4 changes: 2 additions & 2 deletions crates/anvil/core/src/eth/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Address>,
Expand Down Expand Up @@ -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,
Expand Down
43 changes: 40 additions & 3 deletions crates/anvil/src/eth/backend/db.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -120,6 +123,7 @@ pub trait Db:
at: BlockEnv,
best_number: U64,
blocks: Vec<SerializableBlock>,
transactions: Vec<SerializableTransaction>,
) -> DatabaseResult<Option<SerializableState>>;

/// Deserialize and add all chain data to the backend storage
Expand Down Expand Up @@ -193,6 +197,7 @@ impl<T: DatabaseRef<Error = DatabaseError> + Send + Sync + Clone + fmt::Debug> D
_at: BlockEnv,
_best_number: U64,
_blocks: Vec<SerializableBlock>,
_transaction: Vec<SerializableTransaction>,
) -> DatabaseResult<Option<SerializableState>> {
Ok(None)
}
Expand Down Expand Up @@ -325,6 +330,8 @@ pub struct SerializableState {
pub best_block_number: Option<U64>,
#[serde(default)]
pub blocks: Vec<SerializableBlock>,
#[serde(default)]
pub transactions: Vec<SerializableTransaction>,
}

impl SerializableState {
Expand Down Expand Up @@ -355,7 +362,7 @@ pub struct SerializableAccountRecord {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SerializableBlock {
pub header: Header,
pub transactions: Vec<TypedTransaction>,
pub transactions: Vec<MaybeImpersonatedTransaction>,
pub ommers: Vec<Header>,
}

Expand All @@ -378,3 +385,33 @@ impl From<SerializableBlock> 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<MinedTransaction> 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<SerializableTransaction> for MinedTransaction {
fn from(transaction: SerializableTransaction) -> Self {
Self {
info: transaction.info,
receipt: transaction.receipt,
block_hash: transaction.block_hash,
block_number: transaction.block_number,
}
}
}
4 changes: 3 additions & 1 deletion crates/anvil/src/eth/backend/mem/fork_db.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
eth::backend::db::{
Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableBlock,
SerializableState, StateDb,
SerializableState, SerializableTransaction, StateDb,
},
revm::primitives::AccountInfo,
};
Expand Down Expand Up @@ -37,6 +37,7 @@ impl Db for ForkedDatabase {
at: BlockEnv,
best_number: U64,
blocks: Vec<SerializableBlock>,
transactions: Vec<SerializableTransaction>,
) -> DatabaseResult<Option<SerializableState>> {
let mut db = self.database().clone();
let accounts = self
Expand Down Expand Up @@ -66,6 +67,7 @@ impl Db for ForkedDatabase {
accounts,
best_block_number: Some(best_number),
blocks,
transactions,
}))
}

Expand Down
9 changes: 7 additions & 2 deletions crates/anvil/src/eth/backend/mem/in_memory_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -37,6 +37,7 @@ impl Db for MemDb {
at: BlockEnv,
best_number: U64,
blocks: Vec<SerializableBlock>,
transactions: Vec<SerializableTransaction>,
) -> DatabaseResult<Option<SerializableState>> {
let accounts = self
.inner
Expand Down Expand Up @@ -66,6 +67,7 @@ impl Db for MemDb {
accounts,
best_block_number: Some(best_number),
blocks,
transactions,
}))
}

Expand Down Expand Up @@ -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();

Expand Down
4 changes: 3 additions & 1 deletion crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,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()
Expand Down Expand Up @@ -758,6 +759,7 @@ impl Backend {
}

self.blockchain.storage.write().load_blocks(state.blocks.clone());
self.blockchain.storage.write().load_transactions(state.transactions.clone());

Ok(true)
}
Expand Down
19 changes: 17 additions & 2 deletions crates/anvil/src/eth/backend/mem/storage.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -334,6 +334,10 @@ impl BlockchainStorage {
self.blocks.values().map(|block| block.clone().into()).collect()
}

pub fn serialized_transactions(&self) -> Vec<SerializableTransaction> {
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<SerializableBlock>) {
for serializable_block in serializable_blocks.iter() {
Expand All @@ -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<SerializableTransaction>) {
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
Expand Down Expand Up @@ -590,7 +602,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();
Expand All @@ -608,10 +621,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);
Expand Down

0 comments on commit f78df87

Please sign in to comment.