referrerpolicy=no-referrer-when-downgrade

polkadot_subsystem_bench/approval/
helpers.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
17use crate::configuration::TestAuthorities;
18use itertools::Itertools;
19use polkadot_node_network_protocol::{
20	grid_topology::{SessionGridTopology, TopologyPeerInfo},
21	View,
22};
23use polkadot_node_primitives::approval::time::{Clock, SystemClock, Tick};
24use polkadot_node_subsystem::messages::{
25	ApprovalDistributionMessage, ApprovalVotingParallelMessage,
26};
27use polkadot_node_subsystem_types::messages::{
28	network_bridge_event::NewGossipTopology, NetworkBridgeEvent,
29};
30use polkadot_overseer::AllMessages;
31use polkadot_primitives::{
32	BlockNumber, CandidateEvent, CandidateReceiptV2, CoreIndex, GroupIndex, Hash, Header,
33	Id as ParaId, MutateDescriptorV2, Slot, ValidatorIndex,
34};
35use polkadot_primitives_test_helpers::dummy_candidate_receipt_v2_bad_sig;
36use rand::{seq::SliceRandom, SeedableRng};
37use rand_chacha::ChaCha20Rng;
38use sc_network_types::PeerId;
39use sp_consensus_babe::{
40	digests::{CompatibleDigestItem, PreDigest, SecondaryVRFPreDigest},
41	AllowedSlots, BabeEpochConfiguration, Epoch as BabeEpoch, VrfSignature, VrfTranscript,
42};
43use sp_core::crypto::VrfSecret;
44use sp_keyring::sr25519::Keyring as Sr25519Keyring;
45use sp_runtime::{Digest, DigestItem};
46use std::sync::{atomic::AtomicU64, Arc};
47
48/// A fake system clock used for driving the approval voting and make
49/// it process blocks, assignments and approvals from the past.
50#[derive(Clone)]
51pub struct PastSystemClock {
52	/// The real system clock
53	real_system_clock: SystemClock,
54	/// The difference in ticks between the real system clock and the current clock.
55	delta_ticks: Arc<AtomicU64>,
56}
57
58impl PastSystemClock {
59	/// Creates a new fake system clock  with `delta_ticks` between the real time and the fake one.
60	pub fn new(real_system_clock: SystemClock, delta_ticks: Arc<AtomicU64>) -> Self {
61		PastSystemClock { real_system_clock, delta_ticks }
62	}
63}
64
65impl Clock for PastSystemClock {
66	fn tick_now(&self) -> Tick {
67		self.real_system_clock.tick_now() -
68			self.delta_ticks.load(std::sync::atomic::Ordering::SeqCst)
69	}
70
71	fn wait(
72		&self,
73		tick: Tick,
74	) -> std::pin::Pin<Box<dyn futures::prelude::Future<Output = ()> + Send + 'static>> {
75		self.real_system_clock
76			.wait(tick + self.delta_ticks.load(std::sync::atomic::Ordering::SeqCst))
77	}
78}
79
80/// Helper function to generate a  babe epoch for this benchmark.
81/// It does not change for the duration of the test.
82pub fn generate_babe_epoch(current_slot: Slot, authorities: TestAuthorities) -> BabeEpoch {
83	let authorities = authorities
84		.validator_babe_id
85		.into_iter()
86		.enumerate()
87		.map(|(index, public)| (public, index as u64))
88		.collect_vec();
89	BabeEpoch {
90		epoch_index: 1,
91		start_slot: current_slot.saturating_sub(1u64),
92		duration: 200,
93		authorities,
94		randomness: [0xde; 32],
95		config: BabeEpochConfiguration { c: (1, 4), allowed_slots: AllowedSlots::PrimarySlots },
96	}
97}
98
99/// Generates a topology to be used for this benchmark.
100pub fn generate_topology(test_authorities: &TestAuthorities) -> SessionGridTopology {
101	let keyrings = test_authorities
102		.validator_authority_id
103		.clone()
104		.into_iter()
105		.zip(test_authorities.peer_ids.clone())
106		.collect_vec();
107
108	let topology = keyrings
109		.clone()
110		.into_iter()
111		.enumerate()
112		.map(|(index, (discovery_id, peer_id))| TopologyPeerInfo {
113			peer_ids: vec![peer_id],
114			validator_index: ValidatorIndex(index as u32),
115			discovery_id,
116		})
117		.collect_vec();
118	let shuffled = (0..keyrings.len()).collect_vec();
119
120	SessionGridTopology::new(shuffled, topology)
121}
122
123/// Generates new session topology message.
124pub fn generate_new_session_topology(
125	test_authorities: &TestAuthorities,
126	test_node: ValidatorIndex,
127	approval_voting_parallel_enabled: bool,
128) -> Vec<AllMessages> {
129	let topology = generate_topology(test_authorities);
130
131	let event = NetworkBridgeEvent::NewGossipTopology(NewGossipTopology {
132		session: 1,
133		topology,
134		local_index: Some(test_node),
135	});
136	vec![if approval_voting_parallel_enabled {
137		AllMessages::ApprovalVotingParallel(ApprovalVotingParallelMessage::NetworkBridgeUpdate(
138			event,
139		))
140	} else {
141		AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate(event))
142	}]
143}
144
145/// Generates a peer view change for the passed `block_hash`
146pub fn generate_peer_view_change_for(
147	block_hash: Hash,
148	peer_id: PeerId,
149	approval_voting_parallel_enabled: bool,
150) -> AllMessages {
151	let network = NetworkBridgeEvent::PeerViewChange(peer_id, View::new([block_hash], 0));
152	if approval_voting_parallel_enabled {
153		AllMessages::ApprovalVotingParallel(ApprovalVotingParallelMessage::NetworkBridgeUpdate(
154			network,
155		))
156	} else {
157		AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate(network))
158	}
159}
160
161/// Helper function to create a a signature for the block header.
162fn garbage_vrf_signature() -> VrfSignature {
163	let transcript = VrfTranscript::new(b"test-garbage", &[]);
164	Sr25519Keyring::Alice.pair().vrf_sign(&transcript.into())
165}
166
167/// Helper function to create a block header.
168pub fn make_header(parent_hash: Hash, slot: Slot, number: u32) -> Header {
169	let digest =
170		{
171			let mut digest = Digest::default();
172			let vrf_signature = garbage_vrf_signature();
173			digest.push(DigestItem::babe_pre_digest(PreDigest::SecondaryVRF(
174				SecondaryVRFPreDigest { authority_index: 0, slot, vrf_signature },
175			)));
176			digest
177		};
178
179	Header {
180		digest,
181		extrinsics_root: Default::default(),
182		number,
183		state_root: Default::default(),
184		parent_hash,
185	}
186}
187
188/// Helper function to create a candidate receipt.
189fn make_candidate(para_id: ParaId, hash: &Hash) -> CandidateReceiptV2 {
190	let mut r = dummy_candidate_receipt_v2_bad_sig(*hash, Some(Default::default()));
191	r.descriptor.set_para_id(para_id);
192	r
193}
194
195/// Helper function to create a list of candidates that are included in the block
196pub fn make_candidates(
197	block_hash: Hash,
198	block_number: BlockNumber,
199	num_cores: u32,
200	num_candidates: u32,
201) -> Vec<CandidateEvent> {
202	let seed = [block_number as u8; 32];
203	let mut rand_chacha = ChaCha20Rng::from_seed(seed);
204	let mut candidates = (0..num_cores)
205		.map(|core| {
206			CandidateEvent::CandidateIncluded(
207				make_candidate(ParaId::from(core), &block_hash),
208				Vec::new().into(),
209				CoreIndex(core),
210				GroupIndex(core),
211			)
212		})
213		.collect_vec();
214	let (candidates, _) = candidates.partial_shuffle(&mut rand_chacha, num_candidates as usize);
215	candidates
216		.iter_mut()
217		.map(|val| val.clone())
218		.sorted_by(|a, b| match (a, b) {
219			(
220				CandidateEvent::CandidateIncluded(_, _, core_a, _),
221				CandidateEvent::CandidateIncluded(_, _, core_b, _),
222			) => core_a.0.cmp(&core_b.0),
223			(_, _) => todo!("Should not happen"),
224		})
225		.collect_vec()
226}