Run altair specific checks with the simulator (#2556)

## Issue Addressed

N/A

## Proposed Changes

Set a valid fork epoch in the simulator and run checks on 
1. If all nodes transitioned at the fork
2. If all altair block sync aggregates are full
This commit is contained in:
Pawan Dhananjay 2021-09-03 02:41:11 +00:00
parent 6f18f95893
commit ac274221c5
2 changed files with 91 additions and 4 deletions

View File

@ -1,5 +1,5 @@
use crate::local_network::LocalNetwork; 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 std::time::Duration;
use types::{Epoch, EthSpec, Slot, Unsigned}; use types::{Epoch, EthSpec, Slot, Unsigned};
@ -143,3 +143,67 @@ pub async fn verify_full_block_production_up_to<E: EthSpec>(
} }
Ok(()) Ok(())
} }
/// Verify that all nodes have the correct fork version after the `fork_epoch`.
pub async fn verify_fork_version<E: EthSpec>(
network: LocalNetwork<E>,
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<E: EthSpec>(
network: LocalNetwork<E>,
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::<E>(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(())
}

View File

@ -15,7 +15,10 @@ use std::cmp::max;
use std::net::{IpAddr, Ipv4Addr}; use std::net::{IpAddr, Ipv4Addr};
use std::time::Duration; use std::time::Duration;
use tokio::time::sleep; 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> { pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> {
let node_count = value_t!(matches, "nodes", usize).expect("missing nodes default"); 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 spec = &mut env.eth2_config.spec;
let total_validator_count = validators_per_node * node_count; 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 /= speed_up_factor;
spec.seconds_per_slot = max(1, spec.seconds_per_slot); 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_time = 0;
spec.min_genesis_active_validator_count = total_validator_count as u64; spec.min_genesis_active_validator_count = total_validator_count as u64;
spec.seconds_per_eth1_block = 1; 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 slot_duration = Duration::from_secs(spec.seconds_per_slot);
let initial_validator_count = spec.min_genesis_active_validator_count as usize; 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 * tests start at the right time. Whilst this is works well for now, it's subject to
* breakage by changes to the VC. * 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. // Check that the chain finalizes at the first given opportunity.
checks::verify_first_finalization(network.clone(), slot_duration), checks::verify_first_finalization(network.clone(), slot_duration),
// Check that a block is produced at every slot. // Check that a block is produced at every slot.
checks::verify_full_block_production_up_to( checks::verify_full_block_production_up_to(
network.clone(), network.clone(),
Epoch::new(4).start_slot(MainnetEthSpec::slots_per_epoch()), Epoch::new(END_EPOCH).start_slot(MinimalEthSpec::slots_per_epoch()),
slot_duration, slot_duration,
), ),
// Check that the chain starts with the expected validator count. // 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(), network.clone(),
slot_duration, slot_duration,
total_validator_count, 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?; finalization?;
validator_count?; validator_count?;
onboarding?; onboarding?;
fork?;
sync_aggregate?;
// The `final_future` either completes immediately or never completes, depending on the value // The `final_future` either completes immediately or never completes, depending on the value
// of `continue_after_checks`. // of `continue_after_checks`.