diff --git a/testing/simulator/src/checks.rs b/testing/simulator/src/checks.rs index 11be8781a..42bf61384 100644 --- a/testing/simulator/src/checks.rs +++ b/testing/simulator/src/checks.rs @@ -1,5 +1,5 @@ use crate::local_network::LocalNetwork; -use node_test_rig::eth2::types::StateId; +use node_test_rig::eth2::types::{BlockId, StateId}; use std::time::Duration; use types::{Epoch, EthSpec, Slot, Unsigned}; @@ -143,3 +143,67 @@ pub async fn verify_full_block_production_up_to( } Ok(()) } + +/// Verify that all nodes have the correct fork version after the `fork_epoch`. +pub async fn verify_fork_version( + network: LocalNetwork, + fork_epoch: Epoch, + slot_duration: Duration, + altair_fork_version: [u8; 4], +) -> Result<(), String> { + epoch_delay(fork_epoch, slot_duration, E::slots_per_epoch()).await; + for remote_node in network.remote_nodes()? { + let fork_version = remote_node + .get_beacon_states_fork(StateId::Head) + .await + .map(|resp| resp.unwrap().data.current_version) + .map_err(|e| format!("Failed to get fork from beacon node: {:?}", e))?; + if fork_version != altair_fork_version { + return Err(format!( + "Fork version after FORK_EPOCH is incorrect, got: {:?}, expected: {:?}", + fork_version, altair_fork_version, + )); + } + } + Ok(()) +} + +/// Verify that all sync aggregates from `sync_committee_start_slot` until `upto_slot` +/// have full aggregates. +pub async fn verify_full_sync_aggregates_up_to( + network: LocalNetwork, + sync_committee_start_slot: Slot, + upto_slot: Slot, + slot_duration: Duration, +) -> Result<(), String> { + slot_delay(upto_slot, slot_duration).await; + let remote_nodes = network.remote_nodes()?; + let remote_node = remote_nodes.first().unwrap(); + + for slot in sync_committee_start_slot.as_u64()..=upto_slot.as_u64() { + let sync_aggregate_count = remote_node + .get_beacon_blocks::(BlockId::Slot(Slot::new(slot))) + .await + .map(|resp| { + resp.unwrap() + .data + .message() + .body() + .sync_aggregate() + .map(|agg| agg.num_set_bits()) + }) + .map_err(|e| format!("Error while getting beacon block: {:?}", e))? + .ok_or(format!("Altair block {} should have sync aggregate", slot))?; + + if sync_aggregate_count != E::sync_committee_size() { + return Err(format!( + "Sync aggregate at slot {} was not full, got: {}, expected: {}", + slot, + sync_aggregate_count, + E::sync_committee_size() + )); + } + } + + Ok(()) +} diff --git a/testing/simulator/src/eth1_sim.rs b/testing/simulator/src/eth1_sim.rs index 08e960bee..0ab62b14e 100644 --- a/testing/simulator/src/eth1_sim.rs +++ b/testing/simulator/src/eth1_sim.rs @@ -15,7 +15,10 @@ use std::cmp::max; use std::net::{IpAddr, Ipv4Addr}; use std::time::Duration; use tokio::time::sleep; -use types::{Epoch, EthSpec, MainnetEthSpec}; +use types::{Epoch, EthSpec, MinimalEthSpec}; + +const FORK_EPOCH: u64 = 2; +const END_EPOCH: u64 = 16; pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> { let node_count = value_t!(matches, "nodes", usize).expect("missing nodes default"); @@ -59,6 +62,7 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> { let spec = &mut env.eth2_config.spec; let total_validator_count = validators_per_node * node_count; + let altair_fork_version = spec.altair_fork_version; spec.seconds_per_slot /= speed_up_factor; spec.seconds_per_slot = max(1, spec.seconds_per_slot); @@ -67,6 +71,7 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> { spec.min_genesis_time = 0; spec.min_genesis_active_validator_count = total_validator_count as u64; spec.seconds_per_eth1_block = 1; + spec.altair_fork_epoch = Some(Epoch::new(FORK_EPOCH)); let slot_duration = Duration::from_secs(spec.seconds_per_slot); let initial_validator_count = spec.min_genesis_active_validator_count as usize; @@ -174,13 +179,13 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> { * tests start at the right time. Whilst this is works well for now, it's subject to * breakage by changes to the VC. */ - let (finalization, block_prod, validator_count, onboarding) = futures::join!( + let (finalization, block_prod, validator_count, onboarding, fork, sync_aggregate) = futures::join!( // Check that the chain finalizes at the first given opportunity. checks::verify_first_finalization(network.clone(), slot_duration), // Check that a block is produced at every slot. checks::verify_full_block_production_up_to( network.clone(), - Epoch::new(4).start_slot(MainnetEthSpec::slots_per_epoch()), + Epoch::new(END_EPOCH).start_slot(MinimalEthSpec::slots_per_epoch()), slot_duration, ), // Check that the chain starts with the expected validator count. @@ -195,6 +200,22 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> { network.clone(), slot_duration, total_validator_count, + ), + // Check that all nodes have transitioned to the new fork. + checks::verify_fork_version( + network.clone(), + Epoch::new(FORK_EPOCH), + slot_duration, + altair_fork_version + ), + // Check that all sync aggregates are full. + checks::verify_full_sync_aggregates_up_to( + network.clone(), + // Start checking for sync_aggregates at `FORK_EPOCH + 1` to account for + // inefficiencies in finding subnet peers at the `fork_slot`. + Epoch::new(FORK_EPOCH + 1).start_slot(MinimalEthSpec::slots_per_epoch()), + Epoch::new(END_EPOCH).start_slot(MinimalEthSpec::slots_per_epoch()), + slot_duration, ) ); @@ -202,6 +223,8 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> { finalization?; validator_count?; onboarding?; + fork?; + sync_aggregate?; // The `final_future` either completes immediately or never completes, depending on the value // of `continue_after_checks`.