Skip to content

Commit

Permalink
fix(anvil): set storage.best_number correctly (#9215)
Browse files Browse the repository at this point in the history
* feat(`anvil`): persist accounts in `ForkedStorage`

* load accounts

* test

* fix(`anvil`): override storage.best_number with fork_block_num if loading state on a fork url

* Revert "load accounts"

This reverts commit b650f56.

* Revert "feat(`anvil`): persist accounts in `ForkedStorage`"

This reverts commit 456da15.

* nit

---------

Co-authored-by: grandizzy <grandizzy.the.egg@gmail.com>

* nit

---------

Co-authored-by: grandizzy <grandizzy.the.egg@gmail.com>
  • Loading branch information
yash-atreya and grandizzy authored Oct 30, 2024
1 parent 2bb446e commit 45d5997
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 14 deletions.
32 changes: 20 additions & 12 deletions crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -907,19 +907,27 @@ impl Backend {

// Set the current best block number.
// Defaults to block number for compatibility with existing state files.
let fork_num_and_hash = self.get_fork().map(|f| (f.block_number(), f.block_hash()));

let best_number = state.best_block_number.unwrap_or(block.number.to::<U64>());
self.blockchain.storage.write().best_number = best_number;

// Set the current best block hash;
let best_hash =
self.blockchain.storage.read().hash(best_number.into()).ok_or_else(|| {
BlockchainError::RpcError(RpcError::internal_error_with(format!(
"Best hash not found for best number {best_number}",
)))
})?;

self.blockchain.storage.write().best_hash = best_hash;
if let Some((number, hash)) = fork_num_and_hash {
// If loading state file on a fork, set best number to the fork block number.
// Ref: https://github.com/foundry-rs/foundry/pull/9215#issue-2618681838
self.blockchain.storage.write().best_number = U64::from(number);
self.blockchain.storage.write().best_hash = hash;
} else {
let best_number = state.best_block_number.unwrap_or(block.number.to::<U64>());
self.blockchain.storage.write().best_number = best_number;

// Set the current best block hash;
let best_hash =
self.blockchain.storage.read().hash(best_number.into()).ok_or_else(|| {
BlockchainError::RpcError(RpcError::internal_error_with(format!(
"Best hash not found for best number {best_number}",
)))
})?;

self.blockchain.storage.write().best_hash = best_hash;
}
}

if !self.db.write().await.load_state(state.clone())? {
Expand Down
94 changes: 92 additions & 2 deletions crates/anvil/tests/it/state.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
//! general eth api tests
use crate::abi::Greeter;
use alloy_primitives::{Bytes, Uint, U256};
use alloy_network::{ReceiptResponse, TransactionBuilder};
use alloy_primitives::{address, utils::Unit, Bytes, Uint, U256};
use alloy_provider::Provider;
use alloy_rpc_types::BlockId;
use alloy_rpc_types::{BlockId, TransactionRequest};
use alloy_serde::WithOtherFields;
use anvil::{spawn, NodeConfig};
use foundry_test_utils::rpc::next_http_rpc_endpoint;

#[tokio::test(flavor = "multi_thread")]
async fn can_load_state() {
Expand Down Expand Up @@ -155,3 +158,90 @@ async fn can_preserve_historical_states_between_dump_and_load() {

assert_eq!(greeting_after_change, "World!");
}

// <https://github.com/foundry-rs/foundry/issues/9053>
#[tokio::test(flavor = "multi_thread")]
async fn test_fork_load_state() {
let (api, handle) = spawn(
NodeConfig::test()
.with_eth_rpc_url(Some(next_http_rpc_endpoint()))
.with_fork_block_number(Some(21070682u64)),
)
.await;

let bob = address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
let alice = address!("9276449EaC5b4f7Bc17cFC6700f7BeeB86F9bCd0");

let provider = handle.http_provider();

let init_nonce_bob = provider.get_transaction_count(bob).await.unwrap();

let init_balance_alice = provider.get_balance(alice).await.unwrap();

let value = Unit::ETHER.wei().saturating_mul(U256::from(1)); // 1 ether
let tx = TransactionRequest::default().with_to(alice).with_value(value).with_from(bob);
let tx = WithOtherFields::new(tx);

let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();

assert!(receipt.status());

let serialized_state = api.serialized_state(false).await.unwrap();

let state_dump_block = api.block_number().unwrap();

let (api, handle) = spawn(
NodeConfig::test()
.with_eth_rpc_url(Some(next_http_rpc_endpoint()))
.with_fork_block_number(Some(21070686u64)) // Forked chain has moved forward
.with_init_state(Some(serialized_state)),
)
.await;

// Ensure the initial block number is the fork_block_number and not the state_dump_block
let block_number = api.block_number().unwrap();
assert_eq!(block_number, U256::from(21070686u64));
assert_ne!(block_number, state_dump_block);

let provider = handle.http_provider();

let restart_nonce_bob = provider.get_transaction_count(bob).await.unwrap();

let restart_balance_alice = provider.get_balance(alice).await.unwrap();

assert_eq!(init_nonce_bob + 1, restart_nonce_bob);

assert_eq!(init_balance_alice + value, restart_balance_alice);

// Send another tx to check if the state is preserved

let tx = TransactionRequest::default().with_to(alice).with_value(value).with_from(bob);
let tx = WithOtherFields::new(tx);

let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();

assert!(receipt.status());

let nonce_bob = provider.get_transaction_count(bob).await.unwrap();

let balance_alice = provider.get_balance(alice).await.unwrap();

let tx = TransactionRequest::default()
.with_to(alice)
.with_value(value)
.with_from(bob)
.with_nonce(nonce_bob);
let tx = WithOtherFields::new(tx);

let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();

assert!(receipt.status());

let latest_nonce_bob = provider.get_transaction_count(bob).await.unwrap();

let latest_balance_alice = provider.get_balance(alice).await.unwrap();

assert_eq!(nonce_bob + 1, latest_nonce_bob);

assert_eq!(balance_alice + value, latest_balance_alice);
}

0 comments on commit 45d5997

Please sign in to comment.