Skip to content

Commit 071934a

Browse files
authored
fix(anvil): deterministic block production (#11415)
1 parent f21cbb5 commit 071934a

File tree

1 file changed

+64
-3
lines changed
  • crates/anvil/src/eth/backend/mem

1 file changed

+64
-3
lines changed

crates/anvil/src/eth/backend/mem/mod.rs

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,11 +1329,13 @@ impl Backend {
13291329
env.evm_env.block_env.basefee = current_base_fee;
13301330
env.evm_env.block_env.blob_excess_gas_and_price = current_excess_blob_gas_and_price;
13311331

1332-
// pick a random value for prevrandao
1333-
env.evm_env.block_env.prevrandao = Some(B256::random());
1334-
13351332
let best_hash = self.blockchain.storage.read().best_hash;
13361333

1334+
let mut input = Vec::with_capacity(40);
1335+
input.extend_from_slice(best_hash.as_slice());
1336+
input.extend_from_slice(&block_number.to_le_bytes());
1337+
env.evm_env.block_env.prevrandao = Some(keccak256(&input));
1338+
13371339
if self.prune_state_history_config.is_state_history_supported() {
13381340
let db = self.db.read().await.current_state();
13391341
// store current state before executing all transactions
@@ -3659,3 +3661,62 @@ pub fn op_haltreason_to_instruction_result(op_reason: OpHaltReason) -> Instructi
36593661
OpHaltReason::FailedDeposit => InstructionResult::Stop,
36603662
}
36613663
}
3664+
3665+
#[cfg(test)]
3666+
mod tests {
3667+
use crate::{NodeConfig, spawn};
3668+
3669+
#[tokio::test]
3670+
async fn test_deterministic_block_mining() {
3671+
// Test that mine_block produces deterministic block hashes with same initial conditions
3672+
let genesis_timestamp = 1743944919u64;
3673+
3674+
// Create two identical backends
3675+
let config_a = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
3676+
let config_b = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
3677+
3678+
let (api_a, _handle_a) = spawn(config_a).await;
3679+
let (api_b, _handle_b) = spawn(config_b).await;
3680+
3681+
// Mine empty blocks (no transactions) on both backends
3682+
let outcome_a_1 = api_a.backend.mine_block(vec![]).await;
3683+
let outcome_b_1 = api_b.backend.mine_block(vec![]).await;
3684+
3685+
// Both should mine the same block number
3686+
assert_eq!(outcome_a_1.block_number, outcome_b_1.block_number);
3687+
3688+
// Get the actual blocks to compare hashes
3689+
let block_a_1 =
3690+
api_a.block_by_number(outcome_a_1.block_number.into()).await.unwrap().unwrap();
3691+
let block_b_1 =
3692+
api_b.block_by_number(outcome_b_1.block_number.into()).await.unwrap().unwrap();
3693+
3694+
// The block hashes should be identical
3695+
assert_eq!(
3696+
block_a_1.header.hash, block_b_1.header.hash,
3697+
"Block hashes should be deterministic. Got {} vs {}",
3698+
block_a_1.header.hash, block_b_1.header.hash
3699+
);
3700+
3701+
// Mine another block to ensure it remains deterministic
3702+
let outcome_a_2 = api_a.backend.mine_block(vec![]).await;
3703+
let outcome_b_2 = api_b.backend.mine_block(vec![]).await;
3704+
3705+
let block_a_2 =
3706+
api_a.block_by_number(outcome_a_2.block_number.into()).await.unwrap().unwrap();
3707+
let block_b_2 =
3708+
api_b.block_by_number(outcome_b_2.block_number.into()).await.unwrap().unwrap();
3709+
3710+
assert_eq!(
3711+
block_a_2.header.hash, block_b_2.header.hash,
3712+
"Second block hashes should also be deterministic. Got {} vs {}",
3713+
block_a_2.header.hash, block_b_2.header.hash
3714+
);
3715+
3716+
// Ensure the blocks are different (sanity check)
3717+
assert_ne!(
3718+
block_a_1.header.hash, block_a_2.header.hash,
3719+
"Different blocks should have different hashes"
3720+
);
3721+
}
3722+
}

0 commit comments

Comments
 (0)