referrerpolicy=no-referrer-when-downgrade

polkadot_subsystem_bench/
configuration.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Test configuration definition and helpers.
18
19use crate::keyring::Keyring;
20use itertools::Itertools;
21use polkadot_node_network_protocol::authority_discovery::AuthorityDiscovery;
22use polkadot_primitives::{AssignmentId, AuthorityDiscoveryId, ValidatorId, ValidatorPair};
23use rand::thread_rng;
24use rand_distr::{Distribution, Normal, Uniform};
25use sc_network::Multiaddr;
26use sc_network_types::PeerId;
27use serde::{Deserialize, Serialize};
28use sp_consensus_babe::AuthorityId;
29use sp_core::Pair;
30use std::collections::{HashMap, HashSet};
31
32/// Peer networking latency configuration.
33#[derive(Clone, Debug, Default, Serialize, Deserialize)]
34pub struct PeerLatency {
35	/// The mean latency(milliseconds) of the peers.
36	pub mean_latency_ms: usize,
37	/// The standard deviation
38	pub std_dev: f64,
39}
40
41// Based on Kusama `max_validators`
42fn default_n_validators() -> usize {
43	300
44}
45
46// Based on Kusama cores
47fn default_n_cores() -> usize {
48	60
49}
50
51// Default PoV size in KiB.
52fn default_pov_size() -> usize {
53	5 * 1024
54}
55
56// Default bandwidth in bytes, based stats from Kusama validators
57fn default_bandwidth() -> usize {
58	42 * 1024 * 1024
59}
60
61// Default peer latency
62fn default_peer_latency() -> Option<PeerLatency> {
63	Some(PeerLatency { mean_latency_ms: 30, std_dev: 2.0 })
64}
65
66// Default connectivity percentage
67fn default_connectivity() -> usize {
68	90
69}
70
71// Default backing group size
72fn default_backing_group_size() -> usize {
73	5
74}
75
76// Default needed approvals
77fn default_needed_approvals() -> usize {
78	30
79}
80
81fn default_zeroth_delay_tranche_width() -> usize {
82	0
83}
84
85fn default_relay_vrf_modulo_samples() -> usize {
86	6
87}
88
89fn default_n_delay_tranches() -> usize {
90	89
91}
92fn default_no_show_slots() -> usize {
93	3
94}
95fn default_minimum_backing_votes() -> u32 {
96	2
97}
98fn default_max_candidate_depth() -> u32 {
99	3
100}
101fn default_allowed_ancestry_len() -> u32 {
102	2
103}
104
105/// The test input parameters
106#[derive(Clone, Debug, Serialize, Deserialize)]
107pub struct TestConfiguration {
108	/// Number of validators
109	#[serde(default = "default_n_validators")]
110	pub n_validators: usize,
111	/// Number of cores
112	#[serde(default = "default_n_cores")]
113	pub n_cores: usize,
114	/// The number of needed votes to approve a candidate.
115	#[serde(default = "default_needed_approvals")]
116	pub needed_approvals: usize,
117	#[serde(default = "default_zeroth_delay_tranche_width")]
118	pub zeroth_delay_tranche_width: usize,
119	#[serde(default = "default_relay_vrf_modulo_samples")]
120	pub relay_vrf_modulo_samples: usize,
121	#[serde(default = "default_n_delay_tranches")]
122	pub n_delay_tranches: usize,
123	#[serde(default = "default_no_show_slots")]
124	pub no_show_slots: usize,
125	/// Maximum backing group size
126	#[serde(default = "default_backing_group_size")]
127	pub max_validators_per_core: usize,
128	/// The min PoV size
129	#[serde(default = "default_pov_size")]
130	pub min_pov_size: usize,
131	/// The max PoV size,
132	#[serde(default = "default_pov_size")]
133	pub max_pov_size: usize,
134	/// Randomly sampled pov_sizes
135	#[serde(skip)]
136	pub pov_sizes: Vec<usize>,
137	/// The amount of bandwidth remote validators have.
138	#[serde(default = "default_bandwidth")]
139	pub peer_bandwidth: usize,
140	/// The amount of bandwidth our node has.
141	#[serde(default = "default_bandwidth")]
142	pub bandwidth: usize,
143	/// Optional peer emulation latency (round trip time) wrt node under test
144	#[serde(default = "default_peer_latency")]
145	pub latency: Option<PeerLatency>,
146	/// Connectivity ratio, the percentage of peers we are connected to, but as part of the
147	/// topology.
148	#[serde(default = "default_connectivity")]
149	pub connectivity: usize,
150	/// Number of blocks to run the test for
151	pub num_blocks: usize,
152	/// Number of minimum backing votes
153	#[serde(default = "default_minimum_backing_votes")]
154	pub minimum_backing_votes: u32,
155	/// Async Backing max_candidate_depth
156	#[serde(default = "default_max_candidate_depth")]
157	pub max_candidate_depth: u32,
158	/// Async Backing allowed_ancestry_len
159	#[serde(default = "default_allowed_ancestry_len")]
160	pub allowed_ancestry_len: u32,
161}
162
163impl Default for TestConfiguration {
164	fn default() -> Self {
165		Self {
166			n_validators: default_n_validators(),
167			n_cores: default_n_cores(),
168			needed_approvals: default_needed_approvals(),
169			zeroth_delay_tranche_width: default_zeroth_delay_tranche_width(),
170			relay_vrf_modulo_samples: default_relay_vrf_modulo_samples(),
171			n_delay_tranches: default_n_delay_tranches(),
172			no_show_slots: default_no_show_slots(),
173			max_validators_per_core: default_backing_group_size(),
174			min_pov_size: default_pov_size(),
175			max_pov_size: default_pov_size(),
176			pov_sizes: Default::default(),
177			peer_bandwidth: default_bandwidth(),
178			bandwidth: default_bandwidth(),
179			latency: default_peer_latency(),
180			connectivity: default_connectivity(),
181			num_blocks: Default::default(),
182			minimum_backing_votes: default_minimum_backing_votes(),
183			max_candidate_depth: default_max_candidate_depth(),
184			allowed_ancestry_len: default_allowed_ancestry_len(),
185		}
186	}
187}
188
189impl TestConfiguration {
190	pub fn generate_pov_sizes(&mut self) {
191		self.pov_sizes = generate_pov_sizes(self.n_cores, self.min_pov_size, self.max_pov_size);
192	}
193
194	pub fn pov_sizes(&self) -> &[usize] {
195		&self.pov_sizes
196	}
197	/// Return the number of peers connected to our node.
198	pub fn connected_count(&self) -> usize {
199		((self.n_validators - 1) as f64 / (100.0 / self.connectivity as f64)) as usize
200	}
201
202	/// Generates the authority keys we need for the network emulation.
203	pub fn generate_authorities(&self) -> TestAuthorities {
204		let keyring = Keyring::default();
205
206		let key_seeds = (0..self.n_validators)
207			.map(|peer_index| format!("//Node{peer_index}"))
208			.collect_vec();
209
210		let keys = key_seeds
211			.iter()
212			.map(|seed| keyring.sr25519_new(seed.as_str()))
213			.collect::<Vec<_>>();
214
215		// Generate keys and peers ids in each of the format needed by the tests.
216		let validator_public: Vec<ValidatorId> =
217			keys.iter().map(|key| (*key).into()).collect::<Vec<_>>();
218
219		let validator_authority_id: Vec<AuthorityDiscoveryId> =
220			keys.iter().map(|key| (*key).into()).collect::<Vec<_>>();
221
222		let validator_babe_id: Vec<AuthorityId> =
223			keys.iter().map(|key| (*key).into()).collect::<Vec<_>>();
224
225		let validator_assignment_id: Vec<AssignmentId> =
226			keys.iter().map(|key| (*key).into()).collect::<Vec<_>>();
227		let peer_ids: Vec<PeerId> = keys.iter().map(|_| PeerId::random()).collect::<Vec<_>>();
228
229		let peer_id_to_authority = peer_ids
230			.iter()
231			.zip(validator_authority_id.iter())
232			.map(|(peer_id, authority_id)| (*peer_id, authority_id.clone()))
233			.collect();
234
235		let validator_pairs = key_seeds
236			.iter()
237			.map(|seed| ValidatorPair::from_string_with_seed(seed, None).unwrap().0)
238			.collect();
239
240		TestAuthorities {
241			keyring,
242			validator_public,
243			validator_authority_id,
244			peer_ids,
245			validator_babe_id,
246			validator_assignment_id,
247			key_seeds,
248			peer_id_to_authority,
249			validator_pairs,
250		}
251	}
252}
253
254fn random_uniform_sample<T: Into<usize> + From<usize>>(min_value: T, max_value: T) -> T {
255	Uniform::from(min_value.into()..=max_value.into())
256		.sample(&mut thread_rng())
257		.into()
258}
259
260fn random_pov_size(min_pov_size: usize, max_pov_size: usize) -> usize {
261	random_uniform_sample(min_pov_size, max_pov_size)
262}
263
264fn generate_pov_sizes(count: usize, min_kib: usize, max_kib: usize) -> Vec<usize> {
265	(0..count).map(|_| random_pov_size(min_kib * 1024, max_kib * 1024)).collect()
266}
267
268/// Helper struct for authority related state.
269#[derive(Clone)]
270pub struct TestAuthorities {
271	pub keyring: Keyring,
272	pub validator_public: Vec<ValidatorId>,
273	pub validator_authority_id: Vec<AuthorityDiscoveryId>,
274	pub validator_babe_id: Vec<AuthorityId>,
275	pub validator_assignment_id: Vec<AssignmentId>,
276	pub key_seeds: Vec<String>,
277	pub peer_ids: Vec<PeerId>,
278	pub peer_id_to_authority: HashMap<PeerId, AuthorityDiscoveryId>,
279	pub validator_pairs: Vec<ValidatorPair>,
280}
281
282impl std::fmt::Debug for TestAuthorities {
283	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
284		write!(f, "TestAuthorities")
285	}
286}
287
288/// Sample latency (in milliseconds) from a normal distribution with parameters
289/// specified in `maybe_peer_latency`.
290pub fn random_latency(maybe_peer_latency: Option<&PeerLatency>) -> usize {
291	maybe_peer_latency
292		.map(|latency_config| {
293			Normal::new(latency_config.mean_latency_ms as f64, latency_config.std_dev)
294				.expect("normal distribution parameters are good")
295				.sample(&mut thread_rng())
296		})
297		.unwrap_or(0.0) as usize
298}
299
300#[async_trait::async_trait]
301impl AuthorityDiscovery for TestAuthorities {
302	/// Get the addresses for the given [`AuthorityDiscoveryId`] from the local address cache.
303	async fn get_addresses_by_authority_id(
304		&mut self,
305		_authority: AuthorityDiscoveryId,
306	) -> Option<HashSet<Multiaddr>> {
307		None
308	}
309
310	/// Get the [`AuthorityDiscoveryId`] for the given [`PeerId`] from the local address cache.
311	async fn get_authority_ids_by_peer_id(
312		&mut self,
313		peer_id: PeerId,
314	) -> Option<HashSet<AuthorityDiscoveryId>> {
315		self.peer_id_to_authority.get(&peer_id).cloned().map(|id| HashSet::from([id]))
316	}
317}