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

fix(anvil): set storage.best_number correctly #9215

Merged
merged 8 commits into from
Oct 30, 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
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);
}
Loading