referrerpolicy=no-referrer-when-downgrade

polkadot_subsystem_bench/approval/
test_message.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::{
18	approval::{ApprovalsOptions, BlockTestData, CandidateTestData},
19	configuration::TestAuthorities,
20};
21use codec::{Decode, Encode};
22use itertools::Itertools;
23use polkadot_node_network_protocol::v3 as protocol_v3;
24use polkadot_primitives::{CandidateIndex, Hash, ValidatorIndex};
25use sc_network_types::PeerId;
26use std::collections::{HashMap, HashSet};
27
28#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
29pub struct TestMessageInfo {
30	/// The actual message
31	pub msg: protocol_v3::ApprovalDistributionMessage,
32	/// The list of peers that would sends this message in a real topology.
33	/// It includes both the peers that would send the message because of the topology
34	/// or because of randomly choosing so.
35	pub sent_by: Vec<ValidatorIndex>,
36	/// The tranche at which this message should be sent.
37	pub tranche: u32,
38	/// The block hash this message refers to.
39	pub block_hash: Hash,
40}
41
42impl std::hash::Hash for TestMessageInfo {
43	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
44		match &self.msg {
45			protocol_v3::ApprovalDistributionMessage::Assignments(assignments) => {
46				for (assignment, candidates) in assignments {
47					(assignment.block_hash, assignment.validator).hash(state);
48					candidates.hash(state);
49				}
50			},
51			protocol_v3::ApprovalDistributionMessage::Approvals(approvals) => {
52				for approval in approvals {
53					(approval.block_hash, approval.validator).hash(state);
54					approval.candidate_indices.hash(state);
55				}
56			},
57		};
58	}
59}
60
61#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
62/// A list of messages that depend of each-other, approvals cover one of the assignments and
63/// vice-versa.
64pub struct MessagesBundle {
65	pub assignments: Vec<TestMessageInfo>,
66	pub approvals: Vec<TestMessageInfo>,
67}
68
69impl MessagesBundle {
70	/// The tranche when this bundle can be sent correctly, so no assignments or approvals will be
71	/// from the future.
72	pub fn tranche_to_send(&self) -> u32 {
73		self.assignments
74			.iter()
75			.chain(self.approvals.iter())
76			.max_by(|a, b| a.tranche.cmp(&b.tranche))
77			.unwrap()
78			.tranche
79	}
80
81	/// The min tranche in the bundle.
82	pub fn min_tranche(&self) -> u32 {
83		self.assignments
84			.iter()
85			.chain(self.approvals.iter())
86			.min_by(|a, b| a.tranche.cmp(&b.tranche))
87			.unwrap()
88			.tranche
89	}
90
91	/// Tells if the bundle is needed for sending.
92	/// We either send it because we need more assignments and approvals to approve the candidates
93	/// or because we configured the test to send messages until a given tranche.
94	pub fn should_send(
95		&self,
96		candidates_test_data: &HashMap<(Hash, CandidateIndex), CandidateTestData>,
97		options: &ApprovalsOptions,
98	) -> bool {
99		self.needed_for_approval(candidates_test_data) ||
100			(!options.stop_when_approved &&
101				self.min_tranche() <= options.last_considered_tranche)
102	}
103
104	/// Tells if the bundle is needed because we need more messages to approve the candidates.
105	pub fn needed_for_approval(
106		&self,
107		candidates_test_data: &HashMap<(Hash, CandidateIndex), CandidateTestData>,
108	) -> bool {
109		self.assignments
110			.iter()
111			.any(|message| message.needed_for_approval(candidates_test_data))
112	}
113
114	/// Mark the assignments in the bundle as sent.
115	pub fn record_sent_assignment(
116		&self,
117		candidates_test_data: &mut HashMap<(Hash, CandidateIndex), CandidateTestData>,
118	) {
119		self.assignments
120			.iter()
121			.for_each(|assignment| assignment.record_sent_assignment(candidates_test_data));
122	}
123}
124
125impl TestMessageInfo {
126	/// Tells if the message is an approval.
127	fn is_approval(&self) -> bool {
128		match self.msg {
129			protocol_v3::ApprovalDistributionMessage::Assignments(_) => false,
130			protocol_v3::ApprovalDistributionMessage::Approvals(_) => true,
131		}
132	}
133
134	/// Records an approval.
135	/// We use this to check after all messages have been processed that we didn't loose any
136	/// message.
137	pub fn record_vote(&self, state: &BlockTestData) {
138		if self.is_approval() {
139			match &self.msg {
140				protocol_v3::ApprovalDistributionMessage::Assignments(_) => todo!(),
141				protocol_v3::ApprovalDistributionMessage::Approvals(approvals) =>
142					for approval in approvals {
143						for candidate_index in approval.candidate_indices.iter_ones() {
144							state
145								.votes
146								.get(approval.validator.0 as usize)
147								.unwrap()
148								.get(candidate_index)
149								.unwrap()
150								.store(true, std::sync::atomic::Ordering::SeqCst);
151						}
152					},
153			}
154		}
155	}
156
157	/// Mark the assignments in the message as sent.
158	pub fn record_sent_assignment(
159		&self,
160		candidates_test_data: &mut HashMap<(Hash, CandidateIndex), CandidateTestData>,
161	) {
162		match &self.msg {
163			protocol_v3::ApprovalDistributionMessage::Assignments(assignments) => {
164				for (assignment, candidate_indices) in assignments {
165					for candidate_index in candidate_indices.iter_ones() {
166						let candidate_test_data = candidates_test_data
167							.get_mut(&(assignment.block_hash, candidate_index as CandidateIndex))
168							.unwrap();
169						candidate_test_data.mark_sent_assignment(self.tranche)
170					}
171				}
172			},
173			protocol_v3::ApprovalDistributionMessage::Approvals(_approvals) => todo!(),
174		}
175	}
176
177	/// Returns a list of candidates indices in this message
178	pub fn candidate_indices(&self) -> HashSet<usize> {
179		let mut unique_candidate_indices = HashSet::new();
180		match &self.msg {
181			protocol_v3::ApprovalDistributionMessage::Assignments(assignments) =>
182				for (_assignment, candidate_indices) in assignments {
183					for candidate_index in candidate_indices.iter_ones() {
184						unique_candidate_indices.insert(candidate_index);
185					}
186				},
187			protocol_v3::ApprovalDistributionMessage::Approvals(approvals) =>
188				for approval in approvals {
189					for candidate_index in approval.candidate_indices.iter_ones() {
190						unique_candidate_indices.insert(candidate_index);
191					}
192				},
193		}
194		unique_candidate_indices
195	}
196
197	/// Marks this message as no-shows if the number of configured no-shows is above the registered
198	/// no-shows.
199	/// Returns true if the message is a no-show.
200	pub fn no_show_if_required(
201		&self,
202		assignments: &[TestMessageInfo],
203		candidates_test_data: &mut HashMap<(Hash, CandidateIndex), CandidateTestData>,
204	) -> bool {
205		let mut should_no_show = false;
206		if self.is_approval() {
207			let covered_candidates = assignments
208				.iter()
209				.map(|assignment| (assignment, assignment.candidate_indices()))
210				.collect_vec();
211
212			match &self.msg {
213				protocol_v3::ApprovalDistributionMessage::Assignments(_) => todo!(),
214				protocol_v3::ApprovalDistributionMessage::Approvals(approvals) => {
215					assert_eq!(approvals.len(), 1);
216
217					for approval in approvals {
218						should_no_show = should_no_show ||
219							approval.candidate_indices.iter_ones().all(|candidate_index| {
220								let candidate_test_data = candidates_test_data
221									.get_mut(&(
222										approval.block_hash,
223										candidate_index as CandidateIndex,
224									))
225									.unwrap();
226								let assignment = covered_candidates
227									.iter()
228									.find(|(_assignment, candidates)| {
229										candidates.contains(&candidate_index)
230									})
231									.unwrap();
232								candidate_test_data.should_no_show(assignment.0.tranche)
233							});
234
235						if should_no_show {
236							for candidate_index in approval.candidate_indices.iter_ones() {
237								let candidate_test_data = candidates_test_data
238									.get_mut(&(
239										approval.block_hash,
240										candidate_index as CandidateIndex,
241									))
242									.unwrap();
243								let assignment = covered_candidates
244									.iter()
245									.find(|(_assignment, candidates)| {
246										candidates.contains(&candidate_index)
247									})
248									.unwrap();
249								candidate_test_data.record_no_show(assignment.0.tranche)
250							}
251						}
252					}
253				},
254			}
255		}
256		should_no_show
257	}
258
259	/// Tells if a message is needed for approval
260	pub fn needed_for_approval(
261		&self,
262		candidates_test_data: &HashMap<(Hash, CandidateIndex), CandidateTestData>,
263	) -> bool {
264		match &self.msg {
265			protocol_v3::ApprovalDistributionMessage::Assignments(assignments) =>
266				assignments.iter().any(|(assignment, candidate_indices)| {
267					candidate_indices.iter_ones().any(|candidate_index| {
268						candidates_test_data
269							.get(&(assignment.block_hash, candidate_index as CandidateIndex))
270							.map(|data| data.should_send_tranche(self.tranche))
271							.unwrap_or_default()
272					})
273				}),
274			protocol_v3::ApprovalDistributionMessage::Approvals(approvals) =>
275				approvals.iter().any(|approval| {
276					approval.candidate_indices.iter_ones().any(|candidate_index| {
277						candidates_test_data
278							.get(&(approval.block_hash, candidate_index as CandidateIndex))
279							.map(|data| data.should_send_tranche(self.tranche))
280							.unwrap_or_default()
281					})
282				}),
283		}
284	}
285
286	/// Splits a message into multiple messages based on what peers should send this message.
287	/// It build a HashMap of messages that should be sent by each peer.
288	pub fn split_by_peer_id(
289		self,
290		authorities: &TestAuthorities,
291	) -> HashMap<(ValidatorIndex, PeerId), Vec<TestMessageInfo>> {
292		let mut result: HashMap<(ValidatorIndex, PeerId), Vec<TestMessageInfo>> = HashMap::new();
293
294		for validator_index in &self.sent_by {
295			let peer = authorities.peer_ids.get(validator_index.0 as usize).unwrap();
296			result.entry((*validator_index, *peer)).or_default().push(TestMessageInfo {
297				msg: self.msg.clone(),
298				sent_by: Default::default(),
299				tranche: self.tranche,
300				block_hash: self.block_hash,
301			});
302		}
303		result
304	}
305}