From 3a7634f62e3b58a1d176314791bb01dd5546b2e8 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 27 Apr 2026 20:25:44 +0800 Subject: [PATCH 1/4] fix(anvil): respect configured genesis number when restoring `genesis_hash` `BlockchainStorage::load_blocks` was hardcoding `block_number == 0` to detect the genesis block during state-snapshot restore, so a non-zero genesis (e.g. `--block-number 73 --load-state ...`) left `genesis_hash` pointing at the dummy block created in `new()`. Store `genesis_number` on `BlockchainStorage` and compare against it, so `Earliest`/`Safe`/`Finalized` tag lookups return the actual restored hash. Extends the partial fix from #12645. --- crates/anvil/src/eth/backend/mem/storage.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index a80f6bfd1f5e8..6e1a025bc6670 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -286,6 +286,8 @@ pub struct BlockchainStorage { pub best_number: u64, /// genesis hash of the chain pub genesis_hash: B256, + /// genesis block number of the chain + pub genesis_number: u64, /// Mapping from the transaction hash to a tuple containing the transaction as well as the /// transaction receipt pub transactions: B256HashMap>, @@ -337,6 +339,7 @@ impl BlockchainStorage { best_hash, best_number, genesis_hash, + genesis_number, transactions: Default::default(), total_difficulty: Default::default(), } @@ -352,6 +355,7 @@ impl BlockchainStorage { best_hash: block_hash, best_number: block_number, genesis_hash: Default::default(), + genesis_number: 0, transactions: Default::default(), total_difficulty, } @@ -388,6 +392,7 @@ impl BlockchainStorage { best_hash: Default::default(), best_number: Default::default(), genesis_hash: Default::default(), + genesis_number: Default::default(), transactions: Default::default(), total_difficulty: Default::default(), } @@ -424,10 +429,11 @@ impl BlockchainStorage { self.blocks.insert(block_hash, block); self.hashes.insert(block_number, block_hash); - // Update genesis_hash if we are loading block 0, so that Finalized/Safe/Earliest - // block tag lookups return the correct hash. + // Update genesis_hash if we are loading the genesis block, so that + // Finalized/Safe/Earliest block tag lookups return the correct hash. The genesis + // number can be non-zero when configured via `--block-number`. // See: https://github.com/foundry-rs/foundry/issues/12645 - if block_number == 0 { + if block_number == self.genesis_number { self.genesis_hash = block_hash; } } From 52e84b6a9348e0f5a4f51ee6d75be0b1215ae398 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 28 Apr 2026 23:24:13 +0800 Subject: [PATCH 2/4] test(anvil): cover non-zero genesis in `BlockchainStorage::load_blocks` Regression test for the fix in 3a7634f62: when `BlockchainStorage` is configured with a non-zero `genesis_number` (e.g. `--block-number 73 --load-state ...`), `load_blocks` must rewrite `genesis_hash` to the loaded block matching `genesis_number` instead of the hardcoded block 0. --- crates/anvil/src/eth/backend/mem/storage.rs | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 6e1a025bc6670..d75959304a082 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -821,4 +821,47 @@ mod tests { let loaded_tx = loaded_block.body.transactions.first().unwrap(); assert_eq!(loaded_tx, &tx); } + + // Regression test for https://github.com/foundry-rs/foundry/issues/12645: + // when a non-zero genesis number is configured (e.g. `--block-number 73 --load-state ...`), + // `load_blocks` must set `genesis_hash` to the loaded block matching `genesis_number`, + // not the hardcoded block 0. + #[test] + fn test_load_blocks_sets_genesis_hash_with_non_zero_genesis_number() { + const GENESIS_NUMBER: u64 = 73; + + // Build a serialized block at the configured genesis number. + let header = Header { number: GENESIS_NUMBER, gas_limit: 123456, ..Default::default() }; + let block = create_block(header, Vec::>::new()); + let block_hash = block.header.hash_slow(); + let serialized_blocks: Vec = vec![block.into()]; + + // Simulate a fresh storage started with `--block-number 73`: the dummy block created by + // `new()` is hash X, and `genesis_number` is 73. Loading a state snapshot whose genesis + // is also 73 must rewrite `genesis_hash` to the loaded block's hash. + let mut load_storage = BlockchainStorage::::empty(); + load_storage.genesis_number = GENESIS_NUMBER; + let dummy_genesis_hash = B256::repeat_byte(0xab); + load_storage.genesis_hash = dummy_genesis_hash; + + load_storage.load_blocks(serialized_blocks); + + assert_eq!(load_storage.genesis_hash, block_hash); + assert_ne!(load_storage.genesis_hash, dummy_genesis_hash); + + // Sanity check: with the old hardcoded `block_number == 0` logic, `genesis_hash` would + // never be updated when no block 0 is present, so the dummy hash would leak through. + let mut sanity_storage = BlockchainStorage::::empty(); + sanity_storage.genesis_number = 0; + sanity_storage.genesis_hash = dummy_genesis_hash; + + let header_only_73 = + Header { number: GENESIS_NUMBER, gas_limit: 123456, ..Default::default() }; + let block_73 = create_block( + header_only_73, + Vec::>::new(), + ); + sanity_storage.load_blocks(vec![block_73.into()]); + assert_eq!(sanity_storage.genesis_hash, dummy_genesis_hash); + } } From b5fa4b718e4b25dddca95780a3dd8ebd6d6f38e4 Mon Sep 17 00:00:00 2001 From: Mablr <59505383+mablr@users.noreply.github.com> Date: Wed, 29 Apr 2026 10:08:39 +0200 Subject: [PATCH 3/4] fix: fmt --- crates/anvil/src/eth/backend/mem/storage.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index d75959304a082..ded6a2d24d948 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -832,7 +832,8 @@ mod tests { // Build a serialized block at the configured genesis number. let header = Header { number: GENESIS_NUMBER, gas_limit: 123456, ..Default::default() }; - let block = create_block(header, Vec::>::new()); + let block = + create_block(header, Vec::>::new()); let block_hash = block.header.hash_slow(); let serialized_blocks: Vec = vec![block.into()]; From 7a2f200a4d24cf3a1b3095b5c300f1f07ac27eaa Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 7 May 2026 03:04:17 +0800 Subject: [PATCH 4/4] chore: regenerate Cargo.lock after master merge Merge resolution left `foundry-block-explorers` pinned to `reqwest 0.13.2` while master had bumped it to 0.13.3, breaking `cargo --locked` in CI. Co-Authored-By: Claude Opus 4.7 (1M context) --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 01ac90a05bc82..d39f7b78f797a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5219,7 +5219,7 @@ dependencies = [ "path-slash", "rayon", "regex", - "reqwest 0.13.2", + "reqwest 0.13.3", "semver 1.0.28", "serde", "serde_json",