Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(anvil): add mined transactions to state dump #8411

Merged
merged 1 commit into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -169,7 +169,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 @@ -1110,7 +1110,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
Loading