// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Polkadot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . //! Test configuration definition and helpers. use crate::keyring::Keyring; use itertools::Itertools; use polkadot_primitives::{AssignmentId, AuthorityDiscoveryId, ValidatorId}; use rand::thread_rng; use rand_distr::{Distribution, Normal, Uniform}; use sc_network_types::PeerId; use serde::{Deserialize, Serialize}; use sp_consensus_babe::AuthorityId; use std::collections::HashMap; /// Peer networking latency configuration. #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct PeerLatency { /// The mean latency(milliseconds) of the peers. pub mean_latency_ms: usize, /// The standard deviation pub std_dev: f64, } // Based on Kusama `max_validators` fn default_n_validators() -> usize { 300 } // Based on Kusama cores fn default_n_cores() -> usize { 60 } // Default PoV size in KiB. fn default_pov_size() -> usize { 5 * 1024 } // Default bandwidth in bytes, based stats from Kusama validators fn default_bandwidth() -> usize { 42 * 1024 * 1024 } // Default peer latency fn default_peer_latency() -> Option { Some(PeerLatency { mean_latency_ms: 30, std_dev: 2.0 }) } // Default connectivity percentage fn default_connectivity() -> usize { 90 } // Default backing group size fn default_backing_group_size() -> usize { 5 } // Default needed approvals fn default_needed_approvals() -> usize { 30 } fn default_zeroth_delay_tranche_width() -> usize { 0 } fn default_relay_vrf_modulo_samples() -> usize { 6 } fn default_n_delay_tranches() -> usize { 89 } fn default_no_show_slots() -> usize { 3 } /// The test input parameters #[derive(Clone, Debug, Serialize, Deserialize)] pub struct TestConfiguration { /// Number of validators #[serde(default = "default_n_validators")] pub n_validators: usize, /// Number of cores #[serde(default = "default_n_cores")] pub n_cores: usize, /// The number of needed votes to approve a candidate. #[serde(default = "default_needed_approvals")] pub needed_approvals: usize, #[serde(default = "default_zeroth_delay_tranche_width")] pub zeroth_delay_tranche_width: usize, #[serde(default = "default_relay_vrf_modulo_samples")] pub relay_vrf_modulo_samples: usize, #[serde(default = "default_n_delay_tranches")] pub n_delay_tranches: usize, #[serde(default = "default_no_show_slots")] pub no_show_slots: usize, /// Maximum backing group size #[serde(default = "default_backing_group_size")] pub max_validators_per_core: usize, /// The min PoV size #[serde(default = "default_pov_size")] pub min_pov_size: usize, /// The max PoV size, #[serde(default = "default_pov_size")] pub max_pov_size: usize, /// Randomly sampled pov_sizes #[serde(skip)] pub pov_sizes: Vec, /// The amount of bandwidth remote validators have. #[serde(default = "default_bandwidth")] pub peer_bandwidth: usize, /// The amount of bandwidth our node has. #[serde(default = "default_bandwidth")] pub bandwidth: usize, /// Optional peer emulation latency (round trip time) wrt node under test #[serde(default = "default_peer_latency")] pub latency: Option, /// Connectivity ratio, the percentage of peers we are connected to, but as part of the /// topology. #[serde(default = "default_connectivity")] pub connectivity: usize, /// Number of blocks to run the test for pub num_blocks: usize, } impl Default for TestConfiguration { fn default() -> Self { Self { n_validators: default_n_validators(), n_cores: default_n_cores(), needed_approvals: default_needed_approvals(), zeroth_delay_tranche_width: default_zeroth_delay_tranche_width(), relay_vrf_modulo_samples: default_relay_vrf_modulo_samples(), n_delay_tranches: default_n_delay_tranches(), no_show_slots: default_no_show_slots(), max_validators_per_core: default_backing_group_size(), min_pov_size: default_pov_size(), max_pov_size: default_pov_size(), pov_sizes: Default::default(), peer_bandwidth: default_bandwidth(), bandwidth: default_bandwidth(), latency: default_peer_latency(), connectivity: default_connectivity(), num_blocks: Default::default(), } } } impl TestConfiguration { pub fn generate_pov_sizes(&mut self) { self.pov_sizes = generate_pov_sizes(self.n_cores, self.min_pov_size, self.max_pov_size); } pub fn pov_sizes(&self) -> &[usize] { &self.pov_sizes } /// Return the number of peers connected to our node. pub fn connected_count(&self) -> usize { ((self.n_validators - 1) as f64 / (100.0 / self.connectivity as f64)) as usize } /// Generates the authority keys we need for the network emulation. pub fn generate_authorities(&self) -> TestAuthorities { let keyring = Keyring::default(); let key_seeds = (0..self.n_validators) .map(|peer_index| format!("//Node{}", peer_index)) .collect_vec(); let keys = key_seeds .iter() .map(|seed| keyring.sr25519_new(seed.as_str())) .collect::>(); // Generate keys and peers ids in each of the format needed by the tests. let validator_public: Vec = keys.iter().map(|key| (*key).into()).collect::>(); let validator_authority_id: Vec = keys.iter().map(|key| (*key).into()).collect::>(); let validator_babe_id: Vec = keys.iter().map(|key| (*key).into()).collect::>(); let validator_assignment_id: Vec = keys.iter().map(|key| (*key).into()).collect::>(); let peer_ids: Vec = keys.iter().map(|_| PeerId::random()).collect::>(); let peer_id_to_authority = peer_ids .iter() .zip(validator_authority_id.iter()) .map(|(peer_id, authority_id)| (*peer_id, authority_id.clone())) .collect(); TestAuthorities { keyring, validator_public, validator_authority_id, peer_ids, validator_babe_id, validator_assignment_id, key_seeds, peer_id_to_authority, } } } fn random_uniform_sample + From>(min_value: T, max_value: T) -> T { Uniform::from(min_value.into()..=max_value.into()) .sample(&mut thread_rng()) .into() } fn random_pov_size(min_pov_size: usize, max_pov_size: usize) -> usize { random_uniform_sample(min_pov_size, max_pov_size) } fn generate_pov_sizes(count: usize, min_kib: usize, max_kib: usize) -> Vec { (0..count).map(|_| random_pov_size(min_kib * 1024, max_kib * 1024)).collect() } /// Helper struct for authority related state. #[derive(Clone)] pub struct TestAuthorities { pub keyring: Keyring, pub validator_public: Vec, pub validator_authority_id: Vec, pub validator_babe_id: Vec, pub validator_assignment_id: Vec, pub key_seeds: Vec, pub peer_ids: Vec, pub peer_id_to_authority: HashMap, } /// Sample latency (in milliseconds) from a normal distribution with parameters /// specified in `maybe_peer_latency`. pub fn random_latency(maybe_peer_latency: Option<&PeerLatency>) -> usize { maybe_peer_latency .map(|latency_config| { Normal::new(latency_config.mean_latency_ms as f64, latency_config.std_dev) .expect("normal distribution parameters are good") .sample(&mut thread_rng()) }) .unwrap_or(0.0) as usize }