referrerpolicy=no-referrer-when-downgrade

polkadot_node_core_approval_voting/
approval_checking.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//! Utilities for checking whether a candidate has been approved under a given block.
18
19use bitvec::{order::Lsb0 as BitOrderLsb0, slice::BitSlice};
20use polkadot_node_primitives::approval::v1::DelayTranche;
21use polkadot_primitives::ValidatorIndex;
22
23use crate::{
24	persisted_entries::{ApprovalEntry, CandidateEntry, TrancheEntry},
25	MAX_RECORDED_NO_SHOW_VALIDATORS_PER_CANDIDATE,
26};
27use polkadot_node_primitives::approval::time::Tick;
28
29/// Result of counting the necessary tranches needed for approving a block.
30#[derive(Debug, PartialEq, Clone)]
31pub struct TranchesToApproveResult {
32	/// The required tranches for approving this block
33	pub required_tranches: RequiredTranches,
34	/// The total number of no_shows at the moment we are doing the counting.
35	pub total_observed_no_shows: usize,
36	pub no_show_validators: Vec<ValidatorIndex>,
37}
38
39/// The required tranches of assignments needed to determine whether a candidate is approved.
40#[derive(Debug, PartialEq, Clone)]
41pub enum RequiredTranches {
42	/// All validators appear to be required, based on tranches already taken and remaining
43	/// no-shows.
44	All,
45	/// More tranches required - We're awaiting more assignments.
46	Pending {
47		/// The highest considered delay tranche when counting assignments.
48		considered: DelayTranche,
49		/// The tick at which the next no-show, of the assignments counted, would occur.
50		next_no_show: Option<Tick>,
51		/// The highest tranche to consider when looking to broadcast own assignment.
52		/// This should be considered along with the clock drift to avoid broadcasting
53		/// assignments that are before the local time.
54		maximum_broadcast: DelayTranche,
55		/// The clock drift, in ticks, to apply to the local clock when determining whether
56		/// to broadcast an assignment or when to schedule a wakeup. The local clock should be
57		/// treated as though it is `clock_drift` ticks earlier.
58		clock_drift: Tick,
59	},
60	/// An exact number of required tranches and a number of no-shows. This indicates that
61	/// at least the amount of `needed_approvals` are assigned and additionally all no-shows
62	/// are covered.
63	Exact {
64		/// The tranche to inspect up to.
65		needed: DelayTranche,
66		/// The amount of missing votes that should be tolerated.
67		tolerated_missing: usize,
68		/// When the next no-show would be, if any. This is used to schedule the next wakeup in the
69		/// event that there are some assignments that don't have corresponding approval votes. If
70		/// this is `None`, all assignments have approvals.
71		next_no_show: Option<Tick>,
72		/// The last tick at which a needed assignment was received.
73		last_assignment_tick: Option<Tick>,
74	},
75}
76
77/// The result of a check.
78#[derive(Debug, Clone, Copy, PartialEq)]
79pub enum Check {
80	/// The candidate is unapproved.
81	Unapproved,
82	/// The candidate is approved, with the given amount of no-shows,
83	/// with the last counted assignment being received at the given
84	/// tick.
85	Approved(usize, Option<Tick>),
86	/// The candidate is approved by one third of all validators.
87	ApprovedOneThird,
88}
89
90impl Check {
91	/// Whether the candidate is approved and all relevant assignments
92	/// have at most the given assignment tick.
93	pub fn is_approved(&self, max_assignment_tick: Tick) -> bool {
94		match *self {
95			Check::Unapproved => false,
96			Check::Approved(_, last_assignment_tick) =>
97				last_assignment_tick.map_or(true, |t| t <= max_assignment_tick),
98			Check::ApprovedOneThird => true,
99		}
100	}
101
102	/// The number of known no-shows in this computation.
103	pub fn known_no_shows(&self) -> usize {
104		match *self {
105			Check::Approved(n, _) => n,
106			_ => 0,
107		}
108	}
109}
110
111/// Check the approval of a candidate.
112pub fn check_approval(
113	candidate: &CandidateEntry,
114	approval: &ApprovalEntry,
115	required: RequiredTranches,
116) -> Check {
117	// any set of size f+1 contains at least one honest node. If at least one
118	// honest node approves, the candidate should be approved.
119	let approvals = candidate.approvals();
120	if 3 * approvals.count_ones() > approvals.len() {
121		return Check::ApprovedOneThird
122	}
123
124	match required {
125		RequiredTranches::Pending { .. } => Check::Unapproved,
126		RequiredTranches::All => Check::Unapproved,
127		RequiredTranches::Exact { needed, tolerated_missing, last_assignment_tick, .. } => {
128			// whether all assigned validators up to `needed` less no_shows have approved.
129			// e.g. if we had 5 tranches and 1 no-show, we would accept all validators in
130			// tranches 0..=5 except for 1 approving. In that example, we also accept all
131			// validators in tranches 0..=5 approving, but that would indicate that the
132			// RequiredTranches value was incorrectly constructed, so it is not realistic.
133			// If there are more missing approvals than there are no-shows, that indicates
134			// that there are some assignments which are not yet no-shows, but may become
135			// no-shows.
136
137			let mut assigned_mask = approval.assignments_up_to(needed);
138			let approvals = candidate.approvals();
139
140			let n_assigned = assigned_mask.count_ones();
141
142			// Filter the amount of assigned validators by those which have approved.
143			assigned_mask &= approvals;
144			let n_approved = assigned_mask.count_ones();
145
146			// note: the process of computing `required` only chooses `exact` if
147			// that will surpass a minimum amount of checks.
148			// shouldn't typically go above, since all no-shows are supposed to be covered.
149			if n_approved + tolerated_missing >= n_assigned {
150				Check::Approved(tolerated_missing, last_assignment_tick)
151			} else {
152				Check::Unapproved
153			}
154		},
155	}
156}
157
158// Determining the amount of tranches required for approval or which assignments are pending
159// involves moving through a series of states while looping over the tranches
160//
161// that we are aware of. First, we perform an initial count of the number of assignments
162// until we reach the number of needed assignments for approval. As we progress, we count the
163// number of no-shows in each tranche.
164//
165// Then, if there are any no-shows, we proceed into a series of subsequent states for covering
166// no-shows.
167//
168// We cover each no-show by a non-empty tranche, keeping track of the amount of further
169// no-shows encountered along the way. Once all of the no-shows we were previously aware
170// of are covered, we then progress to cover the no-shows we encountered while covering those,
171// and so on.
172#[derive(Debug)]
173struct State {
174	/// The total number of assignments obtained.
175	assignments: usize,
176	/// The depth of no-shows we are currently covering.
177	depth: usize,
178	/// The amount of no-shows that have been covered at the previous or current depths.
179	covered: usize,
180	/// The amount of assignments that we are attempting to cover at this depth.
181	///
182	/// At depth 0, these are the initial needed approvals, and at other depths these
183	/// are no-shows.
184	covering: usize,
185	/// The number of uncovered no-shows encountered at this depth. These will be the
186	/// `covering` of the next depth.
187	uncovered: usize,
188	/// The next tick at which a no-show would occur, if any.
189	next_no_show: Option<Tick>,
190	/// The last tick at which a considered assignment was received.
191	last_assignment_tick: Option<Tick>,
192	total_observed_no_shows: usize,
193	// The validator's index that are no-shows.
194	no_show_validators: Vec<ValidatorIndex>,
195}
196
197impl State {
198	fn output(
199		&self,
200		tranche: DelayTranche,
201		needed_approvals: usize,
202		n_validators: usize,
203		no_show_duration: Tick,
204	) -> TranchesToApproveResult {
205		let covering = if self.depth == 0 { 0 } else { self.covering };
206		if self.depth != 0 && self.assignments + covering + self.uncovered >= n_validators {
207			return TranchesToApproveResult {
208				required_tranches: RequiredTranches::All,
209				total_observed_no_shows: self.total_observed_no_shows,
210				no_show_validators: self.no_show_validators.clone(),
211			}
212		}
213
214		// If we have enough assignments and all no-shows are covered, we have reached the number
215		// of tranches that we need to have.
216		if self.assignments >= needed_approvals && (covering + self.uncovered) == 0 {
217			return TranchesToApproveResult {
218				required_tranches: RequiredTranches::Exact {
219					needed: tranche,
220					tolerated_missing: self.covered,
221					next_no_show: self.next_no_show,
222					last_assignment_tick: self.last_assignment_tick,
223				},
224				total_observed_no_shows: self.total_observed_no_shows,
225				no_show_validators: self.no_show_validators.clone(),
226			}
227		}
228
229		// We're pending more assignments and should look at more tranches.
230		let clock_drift = self.clock_drift(no_show_duration);
231		if self.depth == 0 {
232			TranchesToApproveResult {
233				required_tranches: RequiredTranches::Pending {
234					considered: tranche,
235					next_no_show: self.next_no_show,
236					// during the initial assignment-gathering phase, we want to accept assignments
237					// from any tranche. Note that honest validators will still not broadcast their
238					// assignment until it is time to do so, regardless of this value.
239					maximum_broadcast: DelayTranche::max_value(),
240					clock_drift,
241				},
242				total_observed_no_shows: self.total_observed_no_shows,
243				no_show_validators: self.no_show_validators.clone(),
244			}
245		} else {
246			TranchesToApproveResult {
247				required_tranches: RequiredTranches::Pending {
248					considered: tranche,
249					next_no_show: self.next_no_show,
250					maximum_broadcast: tranche + (covering + self.uncovered) as DelayTranche,
251					clock_drift,
252				},
253				total_observed_no_shows: self.total_observed_no_shows,
254				no_show_validators: self.no_show_validators.clone(),
255			}
256		}
257	}
258
259	fn clock_drift(&self, no_show_duration: Tick) -> Tick {
260		self.depth as Tick * no_show_duration
261	}
262
263	fn advance(
264		mut self,
265		new_assignments: usize,
266		new_no_shows: usize,
267		next_no_show: Option<Tick>,
268		last_assignment_tick: Option<Tick>,
269		no_show_validators: Vec<ValidatorIndex>,
270	) -> State {
271		let new_covered = if self.depth == 0 {
272			new_assignments
273		} else {
274			// When covering no-shows, we treat each non-empty tranche as covering 1 assignment,
275			// regardless of how many assignments are within the tranche.
276			new_assignments.min(1)
277		};
278
279		let assignments = self.assignments + new_assignments;
280		let covering = self.covering.saturating_sub(new_covered);
281		let covered = if self.depth == 0 {
282			// If we're at depth 0, we're not actually covering no-shows,
283			// so we don't need to count them as such.
284			0
285		} else {
286			self.covered + new_covered
287		};
288		let uncovered = self.uncovered + new_no_shows;
289		let next_no_show = super::min_prefer_some(self.next_no_show, next_no_show);
290		let last_assignment_tick = std::cmp::max(self.last_assignment_tick, last_assignment_tick);
291
292		let (depth, covering, uncovered) = if covering == 0 {
293			if uncovered == 0 {
294				(self.depth, 0, uncovered)
295			} else {
296				(self.depth + 1, uncovered, 0)
297			}
298		} else {
299			(self.depth, covering, uncovered)
300		};
301
302		// Make sure we don't store too many no-show validators, since this metric
303		// is valuable if there are just a few of them to identify the problematic
304		// validators.
305		// If there are a lot then we've got bigger problems and no need to make this
306		// array unnecessarily large.
307		if self.no_show_validators.len() + no_show_validators.len() <
308			MAX_RECORDED_NO_SHOW_VALIDATORS_PER_CANDIDATE
309		{
310			self.no_show_validators.extend(no_show_validators);
311		}
312
313		State {
314			assignments,
315			depth,
316			covered,
317			covering,
318			uncovered,
319			next_no_show,
320			last_assignment_tick,
321			total_observed_no_shows: self.total_observed_no_shows + new_no_shows,
322			no_show_validators: self.no_show_validators,
323		}
324	}
325}
326
327/// Constructs an infinite iterator from an array of `TrancheEntry` values. Any missing tranches
328/// are filled with empty assignments, as they are needed to compute the approved tranches.
329fn filled_tranche_iterator(
330	tranches: &[TrancheEntry],
331) -> impl Iterator<Item = (DelayTranche, &[(ValidatorIndex, Tick)])> {
332	let mut gap_end = None;
333
334	let approval_entries_filled = tranches.iter().flat_map(move |tranche_entry| {
335		let tranche = tranche_entry.tranche();
336		let assignments = tranche_entry.assignments();
337
338		// The new gap_start immediately follows the prior gap_end, if one exists.
339		// Otherwise, on the first pass, the new gap_start is set to the first
340		// tranche so that the range below will be empty.
341		let gap_start = gap_end.map(|end| end + 1).unwrap_or(tranche);
342		gap_end = Some(tranche);
343
344		(gap_start..tranche)
345			.map(|i| (i, &[] as &[_]))
346			.chain(std::iter::once((tranche, assignments)))
347	});
348
349	let pre_end = tranches.first().map(|t| t.tranche());
350	let post_start = tranches.last().map_or(0, |t| t.tranche() + 1);
351
352	let pre = pre_end.into_iter().flat_map(|pre_end| (0..pre_end).map(|i| (i, &[] as &[_])));
353	let post = (post_start..).map(|i| (i, &[] as &[_]));
354
355	pre.chain(approval_entries_filled).chain(post)
356}
357
358/// Computes the number of `no_show` validators in a set of assignments given the relevant approvals
359/// and tick parameters. This method also returns the next tick at which a `no_show` will occur
360/// amongst the set of validators that have not submitted an approval.
361///
362/// This also bounds the earliest tick of all assignments to be equal to the
363/// block tick for the purposes of the calculation, so no assignment can be treated
364/// as being received before the block itself. This is unlikely if not impossible
365/// in practice, but can occur during test code.
366///
367/// If the returned `next_no_show` is not None, there are two possible cases for the value of
368/// based on the earliest assignment `tick` of a non-approving, yet-to-be-no-show validator:
369///  - if `tick` <= `clock_drift`: the value will always be `clock_drift` + `no_show_duration`.
370///  - if `tick` >  `clock_drift`: the value is equal to `tick` + `no_show_duration`.
371fn count_no_shows(
372	assignments: &[(ValidatorIndex, Tick)],
373	approvals: &BitSlice<u8, BitOrderLsb0>,
374	clock_drift: Tick,
375	block_tick: Tick,
376	no_show_duration: Tick,
377	drifted_tick_now: Tick,
378) -> (usize, Option<u64>, Vec<ValidatorIndex>) {
379	let mut next_no_show = None;
380	let mut no_show_validators = Vec::new();
381	let no_shows = assignments
382		.iter()
383		.map(|(v_index, tick)| {
384			(v_index, tick.max(&block_tick).saturating_sub(clock_drift) + no_show_duration)
385		})
386		.filter(|&(v_index, no_show_at)| {
387			let has_approved = if let Some(approved) = approvals.get(v_index.0 as usize) {
388				*approved
389			} else {
390				return false
391			};
392
393			let is_no_show = !has_approved && no_show_at <= drifted_tick_now;
394
395			if !is_no_show && !has_approved {
396				// When doing the comparison above, no_show_at and drifted_tick_now are calculated
397				// with the clock_drift removed. The reason for adding back the clock_drift in
398				// computing next_no_show is so that the scheduler knows the deadline at which
399				// *this node* should observe whether or not the validator is a no show. Recall
400				// that when the when drifted_tick_now is computed during that subsequent wake up,
401				// the clock drift will be removed again to do the comparison above.
402				next_no_show = super::min_prefer_some(next_no_show, Some(no_show_at + clock_drift));
403			}
404			if is_no_show {
405				no_show_validators.push(*v_index);
406			}
407			is_no_show
408		})
409		.count();
410
411	(no_shows, next_no_show, no_show_validators)
412}
413
414/// Determine the amount of tranches of assignments needed to determine approval of a candidate.
415pub fn tranches_to_approve(
416	approval_entry: &ApprovalEntry,
417	approvals: &BitSlice<u8, BitOrderLsb0>,
418	tranche_now: DelayTranche,
419	block_tick: Tick,
420	no_show_duration: Tick,
421	needed_approvals: usize,
422) -> TranchesToApproveResult {
423	let tick_now = tranche_now as Tick + block_tick;
424	let n_validators = approval_entry.n_validators();
425
426	let initial_state = State {
427		assignments: 0,
428		depth: 0,
429		covered: 0,
430		covering: needed_approvals,
431		uncovered: 0,
432		next_no_show: None,
433		last_assignment_tick: None,
434		total_observed_no_shows: 0,
435		no_show_validators: Vec::new(),
436	};
437
438	// The `ApprovalEntry` doesn't have any data for empty tranches. We still want to iterate over
439	// these empty tranches, so we create an iterator to fill the gaps.
440	//
441	// This iterator has an infinitely long amount of non-empty tranches appended to the end.
442	let tranches_with_gaps_filled = filled_tranche_iterator(approval_entry.tranches());
443
444	tranches_with_gaps_filled
445		.scan(Some(initial_state), |state, (tranche, assignments)| {
446			// The `Option` here is used for early exit.
447			let s = state.take()?;
448
449			let clock_drift = s.clock_drift(no_show_duration);
450			let drifted_tick_now = tick_now.saturating_sub(clock_drift);
451			let drifted_tranche_now = drifted_tick_now.saturating_sub(block_tick) as DelayTranche;
452
453			// Break the loop once we've taken enough tranches.
454			// Note that we always take tranche 0 as `drifted_tranche_now` cannot be less than 0.
455			if tranche > drifted_tranche_now {
456				return None;
457			}
458
459			// Count the number of valid validator assignments.
460			let n_assignments = assignments.iter()
461				.filter(|(v_index, _)| v_index.0 < n_validators as u32)
462				.count();
463
464			// Get the latest tick of valid validator assignments.
465			let last_assignment_tick = assignments.iter()
466				.map(|(_, t)| *t)
467				.max();
468
469			// count no-shows. An assignment is a no-show if there is no corresponding approval vote
470			// after a fixed duration.
471			//
472			// While we count the no-shows, we also determine the next possible no-show we might
473			// see within this tranche.
474			let (no_shows, next_no_show, no_show_validators) = count_no_shows(
475				assignments,
476				approvals,
477				clock_drift,
478				block_tick,
479				no_show_duration,
480				drifted_tick_now,
481			);
482
483			let s = s.advance(n_assignments, no_shows, next_no_show, last_assignment_tick, no_show_validators);
484			let output = s.output(tranche, needed_approvals, n_validators, no_show_duration);
485
486			*state = match output.required_tranches {
487				RequiredTranches::Exact { .. } | RequiredTranches::All => {
488					// Wipe the state clean so the next iteration of this closure will terminate
489					// the iterator. This guarantees that we can call `last` further down to see
490					// either a `Finished` or `Pending` result
491					None
492				}
493				RequiredTranches::Pending { .. } => {
494					// Pending results are only interesting when they are the last result of the iterator
495					// i.e. we never achieve a satisfactory level of assignment.
496					Some(s)
497				}
498			};
499
500			Some(output)
501		})
502		.last()
503		.expect("the underlying iterator is infinite, starts at 0, and never exits early before tranche 1; qed")
504}
505
506#[cfg(test)]
507mod tests {
508	use super::*;
509	use crate::{approval_db, BTreeMap};
510	use bitvec::{bitvec, order::Lsb0 as BitOrderLsb0, vec::BitVec};
511	use polkadot_primitives::GroupIndex;
512	use polkadot_primitives_test_helpers::{dummy_candidate_receipt_v2, dummy_hash};
513
514	#[test]
515	fn pending_is_not_approved() {
516		let candidate = CandidateEntry::from_v1(
517			approval_db::v1::CandidateEntry {
518				candidate: dummy_candidate_receipt_v2(dummy_hash()),
519				session: 0,
520				block_assignments: BTreeMap::default(),
521				approvals: BitVec::default(),
522			},
523			0,
524		);
525
526		let approval_entry = approval_db::v3::ApprovalEntry {
527			tranches: Vec::new(),
528			assigned_validators: BitVec::default(),
529			our_assignment: None,
530			our_approval_sig: None,
531			backing_group: GroupIndex(0),
532			approved: false,
533		}
534		.into();
535
536		assert!(!check_approval(
537			&candidate,
538			&approval_entry,
539			RequiredTranches::Pending {
540				considered: 0,
541				next_no_show: None,
542				maximum_broadcast: 0,
543				clock_drift: 0,
544			},
545		)
546		.is_approved(Tick::max_value()));
547	}
548
549	#[test]
550	fn exact_takes_only_assignments_up_to() {
551		let mut candidate: CandidateEntry = CandidateEntry::from_v1(
552			approval_db::v1::CandidateEntry {
553				candidate: dummy_candidate_receipt_v2(dummy_hash()),
554				session: 0,
555				block_assignments: BTreeMap::default(),
556				approvals: bitvec![u8, BitOrderLsb0; 0; 10],
557			},
558			0,
559		);
560
561		for i in 0..3 {
562			candidate.mark_approval(ValidatorIndex(i));
563		}
564
565		let approval_entry = approval_db::v3::ApprovalEntry {
566			tranches: vec![
567				approval_db::v3::TrancheEntry {
568					tranche: 0,
569					assignments: (0..2).map(|i| (ValidatorIndex(i), 0.into())).collect(),
570				},
571				approval_db::v3::TrancheEntry {
572					tranche: 1,
573					assignments: (2..5).map(|i| (ValidatorIndex(i), 1.into())).collect(),
574				},
575				approval_db::v3::TrancheEntry {
576					tranche: 2,
577					assignments: (5..10).map(|i| (ValidatorIndex(i), 0.into())).collect(),
578				},
579			],
580			assigned_validators: bitvec![u8, BitOrderLsb0; 1; 10],
581			our_assignment: None,
582			our_approval_sig: None,
583			backing_group: GroupIndex(0),
584			approved: false,
585		}
586		.into();
587
588		assert!(check_approval(
589			&candidate,
590			&approval_entry,
591			RequiredTranches::Exact {
592				needed: 0,
593				tolerated_missing: 0,
594				next_no_show: None,
595				last_assignment_tick: None
596			},
597		)
598		.is_approved(Tick::max_value()));
599		assert!(!check_approval(
600			&candidate,
601			&approval_entry,
602			RequiredTranches::Exact {
603				needed: 1,
604				tolerated_missing: 0,
605				next_no_show: None,
606				last_assignment_tick: None
607			},
608		)
609		.is_approved(Tick::max_value()));
610		assert!(check_approval(
611			&candidate,
612			&approval_entry,
613			RequiredTranches::Exact {
614				needed: 1,
615				tolerated_missing: 2,
616				next_no_show: None,
617				last_assignment_tick: None
618			},
619		)
620		.is_approved(Tick::max_value()));
621	}
622
623	#[test]
624	fn one_honest_node_always_approves() {
625		let mut candidate: CandidateEntry = CandidateEntry::from_v1(
626			approval_db::v1::CandidateEntry {
627				candidate: dummy_candidate_receipt_v2(dummy_hash()),
628				session: 0,
629				block_assignments: BTreeMap::default(),
630				approvals: bitvec![u8, BitOrderLsb0; 0; 10],
631			},
632			0,
633		);
634
635		for i in 0..3 {
636			candidate.mark_approval(ValidatorIndex(i));
637		}
638
639		let approval_entry = approval_db::v3::ApprovalEntry {
640			tranches: vec![
641				approval_db::v3::TrancheEntry {
642					tranche: 0,
643					assignments: (0..4).map(|i| (ValidatorIndex(i), 0.into())).collect(),
644				},
645				approval_db::v3::TrancheEntry {
646					tranche: 1,
647					assignments: (4..6).map(|i| (ValidatorIndex(i), 1.into())).collect(),
648				},
649				approval_db::v3::TrancheEntry {
650					tranche: 2,
651					assignments: (6..10).map(|i| (ValidatorIndex(i), 0.into())).collect(),
652				},
653			],
654			assigned_validators: bitvec![u8, BitOrderLsb0; 1; 10],
655			our_assignment: None,
656			our_approval_sig: None,
657			backing_group: GroupIndex(0),
658			approved: false,
659		}
660		.into();
661
662		let exact_all = RequiredTranches::Exact {
663			needed: 10,
664			tolerated_missing: 0,
665			next_no_show: None,
666			last_assignment_tick: None,
667		};
668
669		let pending_all = RequiredTranches::Pending {
670			considered: 5,
671			next_no_show: None,
672			maximum_broadcast: 8,
673			clock_drift: 12,
674		};
675
676		assert!(!check_approval(&candidate, &approval_entry, RequiredTranches::All,)
677			.is_approved(Tick::max_value()));
678
679		assert!(!check_approval(&candidate, &approval_entry, exact_all.clone(),)
680			.is_approved(Tick::max_value()));
681
682		assert!(!check_approval(&candidate, &approval_entry, pending_all.clone(),)
683			.is_approved(Tick::max_value()));
684
685		// This creates a set of 4/10 approvals, which is always an approval.
686		candidate.mark_approval(ValidatorIndex(3));
687
688		assert!(check_approval(&candidate, &approval_entry, RequiredTranches::All,)
689			.is_approved(Tick::max_value()));
690
691		assert!(
692			check_approval(&candidate, &approval_entry, exact_all,).is_approved(Tick::max_value())
693		);
694
695		assert!(check_approval(&candidate, &approval_entry, pending_all,)
696			.is_approved(Tick::max_value()));
697	}
698
699	#[test]
700	fn tranches_to_approve_everyone_present() {
701		let block_tick = 20;
702		let no_show_duration = 10;
703		let needed_approvals = 4;
704
705		let mut approval_entry: ApprovalEntry = approval_db::v3::ApprovalEntry {
706			tranches: Vec::new(),
707			assigned_validators: bitvec![u8, BitOrderLsb0; 0; 5],
708			our_assignment: None,
709			our_approval_sig: None,
710			backing_group: GroupIndex(0),
711			approved: false,
712		}
713		.into();
714
715		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
716		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick, false);
717
718		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick + 1, false);
719		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick + 1, false);
720
721		approval_entry.import_assignment(2, ValidatorIndex(4), block_tick + 2, false);
722
723		let approvals = bitvec![u8, BitOrderLsb0; 1; 5];
724
725		assert_eq!(
726			tranches_to_approve(
727				&approval_entry,
728				&approvals,
729				2,
730				block_tick,
731				no_show_duration,
732				needed_approvals,
733			)
734			.required_tranches,
735			RequiredTranches::Exact {
736				needed: 1,
737				tolerated_missing: 0,
738				next_no_show: None,
739				last_assignment_tick: Some(21)
740			},
741		);
742	}
743
744	#[test]
745	fn tranches_to_approve_not_enough_initial_count() {
746		let block_tick = 20;
747		let no_show_duration = 10;
748		let needed_approvals = 4;
749
750		let mut approval_entry: ApprovalEntry = approval_db::v3::ApprovalEntry {
751			tranches: Vec::new(),
752			assigned_validators: bitvec![u8, BitOrderLsb0; 0; 10],
753			our_assignment: None,
754			our_approval_sig: None,
755			backing_group: GroupIndex(0),
756			approved: false,
757		}
758		.into();
759
760		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
761		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, true);
762		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick, false);
763		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick, true);
764
765		let approvals = bitvec![u8, BitOrderLsb0; 0; 10];
766
767		let tranche_now = 2;
768		assert_eq!(
769			tranches_to_approve(
770				&approval_entry,
771				&approvals,
772				tranche_now,
773				block_tick,
774				no_show_duration,
775				needed_approvals,
776			)
777			.required_tranches,
778			RequiredTranches::Pending {
779				considered: 2,
780				next_no_show: Some(block_tick + no_show_duration),
781				maximum_broadcast: DelayTranche::max_value(),
782				clock_drift: 0,
783			},
784		);
785	}
786
787	#[test]
788	fn tranches_to_approve_no_shows_before_initial_count_treated_same_as_not_initial() {
789		let block_tick = 20;
790		let no_show_duration = 10;
791		let needed_approvals = 4;
792
793		let mut approval_entry: ApprovalEntry = approval_db::v3::ApprovalEntry {
794			tranches: Vec::new(),
795			assigned_validators: bitvec![u8, BitOrderLsb0; 0; 10],
796			our_assignment: None,
797			our_approval_sig: None,
798			backing_group: GroupIndex(0),
799			approved: false,
800		}
801		.into();
802
803		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
804		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick, false);
805
806		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick, false);
807
808		let mut approvals = bitvec![u8, BitOrderLsb0; 0; 10];
809		approvals.set(0, true);
810		approvals.set(1, true);
811
812		let tranche_now = no_show_duration as DelayTranche + 1;
813		assert_eq!(
814			tranches_to_approve(
815				&approval_entry,
816				&approvals,
817				tranche_now,
818				block_tick,
819				no_show_duration,
820				needed_approvals,
821			)
822			.required_tranches,
823			RequiredTranches::Pending {
824				considered: 11,
825				next_no_show: None,
826				maximum_broadcast: DelayTranche::max_value(),
827				clock_drift: 0,
828			},
829		);
830	}
831
832	#[test]
833	fn tranches_to_approve_cover_no_show_not_enough() {
834		let block_tick = 20;
835		let no_show_duration = 10;
836		let needed_approvals = 4;
837		let n_validators = 8;
838
839		let mut approval_entry: ApprovalEntry = approval_db::v3::ApprovalEntry {
840			tranches: Vec::new(),
841			assigned_validators: bitvec![u8, BitOrderLsb0; 0; n_validators],
842			our_assignment: None,
843			our_approval_sig: None,
844			backing_group: GroupIndex(0),
845			approved: false,
846		}
847		.into();
848
849		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
850		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick, false);
851
852		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick, false);
853		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick, false);
854
855		let mut approvals = bitvec![u8, BitOrderLsb0; 0; n_validators];
856		approvals.set(0, true);
857		approvals.set(1, true);
858		// skip 2
859		approvals.set(3, true);
860
861		let tranche_now = no_show_duration as DelayTranche + 1;
862		assert_eq!(
863			tranches_to_approve(
864				&approval_entry,
865				&approvals,
866				tranche_now,
867				block_tick,
868				no_show_duration,
869				needed_approvals,
870			)
871			.required_tranches,
872			RequiredTranches::Pending {
873				considered: 1,
874				next_no_show: None,
875				maximum_broadcast: 2, // tranche 1 + 1 no-show
876				clock_drift: 1 * no_show_duration,
877			}
878		);
879
880		approvals.set(0, false);
881
882		assert_eq!(
883			tranches_to_approve(
884				&approval_entry,
885				&approvals,
886				tranche_now,
887				block_tick,
888				no_show_duration,
889				needed_approvals,
890			)
891			.required_tranches,
892			RequiredTranches::Pending {
893				considered: 1,
894				next_no_show: None,
895				maximum_broadcast: 3, // tranche 1 + 2 no-shows
896				clock_drift: 1 * no_show_duration,
897			}
898		);
899	}
900
901	#[test]
902	fn tranches_to_approve_multi_cover_not_enough() {
903		let block_tick = 20;
904		let no_show_duration = 10;
905		let needed_approvals = 4;
906		let n_validators = 8;
907
908		let mut approval_entry: ApprovalEntry = approval_db::v3::ApprovalEntry {
909			tranches: Vec::new(),
910			assigned_validators: bitvec![u8, BitOrderLsb0; 0; n_validators],
911			our_assignment: None,
912			our_approval_sig: None,
913			backing_group: GroupIndex(0),
914			approved: false,
915		}
916		.into();
917
918		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
919		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick, false);
920
921		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick + 1, false);
922		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick + 1, false);
923
924		approval_entry.import_assignment(
925			2,
926			ValidatorIndex(4),
927			block_tick + no_show_duration + 2,
928			false,
929		);
930		approval_entry.import_assignment(
931			2,
932			ValidatorIndex(5),
933			block_tick + no_show_duration + 2,
934			false,
935		);
936
937		let mut approvals = bitvec![u8, BitOrderLsb0; 0; n_validators];
938		approvals.set(0, true);
939		approvals.set(1, true);
940		// skip 2
941		approvals.set(3, true);
942		// skip 4
943		approvals.set(5, true);
944
945		let tranche_now = 1;
946		assert_eq!(
947			tranches_to_approve(
948				&approval_entry,
949				&approvals,
950				tranche_now,
951				block_tick,
952				no_show_duration,
953				needed_approvals,
954			)
955			.required_tranches,
956			RequiredTranches::Exact {
957				needed: 1,
958				tolerated_missing: 0,
959				next_no_show: Some(block_tick + no_show_duration + 1),
960				last_assignment_tick: Some(block_tick + 1),
961			},
962		);
963
964		// first no-show covered.
965		let tranche_now = no_show_duration as DelayTranche + 2;
966		assert_eq!(
967			tranches_to_approve(
968				&approval_entry,
969				&approvals,
970				tranche_now,
971				block_tick,
972				no_show_duration,
973				needed_approvals,
974			)
975			.required_tranches,
976			RequiredTranches::Exact {
977				needed: 2,
978				tolerated_missing: 1,
979				next_no_show: Some(block_tick + 2 * no_show_duration + 2),
980				last_assignment_tick: Some(block_tick + no_show_duration + 2),
981			},
982		);
983
984		// another no-show in tranche 2.
985		let tranche_now = (no_show_duration * 2) as DelayTranche + 2;
986		assert_eq!(
987			tranches_to_approve(
988				&approval_entry,
989				&approvals,
990				tranche_now,
991				block_tick,
992				no_show_duration,
993				needed_approvals,
994			)
995			.required_tranches,
996			RequiredTranches::Pending {
997				considered: 2,
998				next_no_show: None,
999				maximum_broadcast: 3, // tranche 2 + 1 uncovered no-show.
1000				clock_drift: 2 * no_show_duration,
1001			},
1002		);
1003	}
1004
1005	#[test]
1006	fn tranches_to_approve_cover_no_show() {
1007		let block_tick = 20;
1008		let no_show_duration = 10;
1009		let needed_approvals = 4;
1010		let n_validators = 8;
1011
1012		let mut approval_entry: ApprovalEntry = approval_db::v3::ApprovalEntry {
1013			tranches: Vec::new(),
1014			assigned_validators: bitvec![u8, BitOrderLsb0; 0; n_validators],
1015			our_assignment: None,
1016			our_approval_sig: None,
1017			backing_group: GroupIndex(0),
1018			approved: false,
1019		}
1020		.into();
1021
1022		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
1023		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick, false);
1024
1025		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick + 1, false);
1026		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick + 1, false);
1027
1028		approval_entry.import_assignment(
1029			2,
1030			ValidatorIndex(4),
1031			block_tick + no_show_duration + 2,
1032			false,
1033		);
1034		approval_entry.import_assignment(
1035			2,
1036			ValidatorIndex(5),
1037			block_tick + no_show_duration + 2,
1038			false,
1039		);
1040
1041		let mut approvals = bitvec![u8, BitOrderLsb0; 0; n_validators];
1042		approvals.set(0, true);
1043		approvals.set(1, true);
1044		// skip 2
1045		approvals.set(3, true);
1046		approvals.set(4, true);
1047		approvals.set(5, true);
1048
1049		let tranche_now = no_show_duration as DelayTranche + 2;
1050		assert_eq!(
1051			tranches_to_approve(
1052				&approval_entry,
1053				&approvals,
1054				tranche_now,
1055				block_tick,
1056				no_show_duration,
1057				needed_approvals,
1058			)
1059			.required_tranches,
1060			RequiredTranches::Exact {
1061				needed: 2,
1062				tolerated_missing: 1,
1063				next_no_show: None,
1064				last_assignment_tick: Some(block_tick + no_show_duration + 2)
1065			},
1066		);
1067
1068		// Even though tranche 2 has 2 validators, it only covers 1 no-show.
1069		// to cover a second no-show, we need to take another non-empty tranche.
1070
1071		approvals.set(0, false);
1072
1073		assert_eq!(
1074			tranches_to_approve(
1075				&approval_entry,
1076				&approvals,
1077				tranche_now,
1078				block_tick,
1079				no_show_duration,
1080				needed_approvals,
1081			)
1082			.required_tranches,
1083			RequiredTranches::Pending {
1084				considered: 2,
1085				next_no_show: None,
1086				maximum_broadcast: 3,
1087				clock_drift: no_show_duration,
1088			},
1089		);
1090
1091		approval_entry.import_assignment(3, ValidatorIndex(6), block_tick, false);
1092		approvals.set(6, true);
1093
1094		let tranche_now = no_show_duration as DelayTranche + 3;
1095		assert_eq!(
1096			tranches_to_approve(
1097				&approval_entry,
1098				&approvals,
1099				tranche_now,
1100				block_tick,
1101				no_show_duration,
1102				needed_approvals,
1103			)
1104			.required_tranches,
1105			RequiredTranches::Exact {
1106				needed: 3,
1107				tolerated_missing: 2,
1108				next_no_show: None,
1109				last_assignment_tick: Some(block_tick + no_show_duration + 2),
1110			},
1111		);
1112	}
1113
1114	#[test]
1115	fn validator_indexes_out_of_range_are_ignored_in_assignments() {
1116		let block_tick = 20;
1117		let no_show_duration = 10;
1118		let needed_approvals = 3;
1119
1120		let mut candidate: CandidateEntry = CandidateEntry::from_v1(
1121			approval_db::v1::CandidateEntry {
1122				candidate: dummy_candidate_receipt_v2(dummy_hash()),
1123				session: 0,
1124				block_assignments: BTreeMap::default(),
1125				approvals: bitvec![u8, BitOrderLsb0; 0; 3],
1126			},
1127			0,
1128		);
1129
1130		for i in 0..3 {
1131			candidate.mark_approval(ValidatorIndex(i));
1132		}
1133
1134		let approval_entry = approval_db::v3::ApprovalEntry {
1135			tranches: vec![
1136				// Assignments with invalid validator indexes.
1137				approval_db::v3::TrancheEntry {
1138					tranche: 1,
1139					assignments: (2..5).map(|i| (ValidatorIndex(i), 1.into())).collect(),
1140				},
1141			],
1142			assigned_validators: bitvec![u8, BitOrderLsb0; 1; 3],
1143			our_assignment: None,
1144			our_approval_sig: None,
1145			backing_group: GroupIndex(0),
1146			approved: false,
1147		}
1148		.into();
1149
1150		let approvals = bitvec![u8, BitOrderLsb0; 0; 3];
1151
1152		let tranche_now = 10;
1153		assert_eq!(
1154			tranches_to_approve(
1155				&approval_entry,
1156				&approvals,
1157				tranche_now,
1158				block_tick,
1159				no_show_duration,
1160				needed_approvals,
1161			)
1162			.required_tranches,
1163			RequiredTranches::Pending {
1164				considered: 10,
1165				next_no_show: None,
1166				maximum_broadcast: DelayTranche::max_value(),
1167				clock_drift: 0,
1168			},
1169		);
1170	}
1171
1172	#[test]
1173	fn filled_tranche_iterator_yields_sequential_tranches() {
1174		const PREFIX: u32 = 10;
1175
1176		let test_tranches = vec![
1177			vec![],                 // empty set
1178			vec![0],                // zero start
1179			vec![0, 3],             // zero start with gap
1180			vec![2],                // non-zero start
1181			vec![2, 4],             // non-zero start with gap
1182			vec![0, 1, 2],          // zero start with run and no gap
1183			vec![2, 3, 4, 8],       // non-zero start with run and gap
1184			vec![0, 1, 2, 5, 6, 7], // zero start with runs and gap
1185		];
1186
1187		for test_tranche in test_tranches {
1188			let mut approval_entry: ApprovalEntry = approval_db::v3::ApprovalEntry {
1189				tranches: Vec::new(),
1190				backing_group: GroupIndex(0),
1191				our_assignment: None,
1192				our_approval_sig: None,
1193				assigned_validators: bitvec![u8, BitOrderLsb0; 0; 3],
1194				approved: false,
1195			}
1196			.into();
1197
1198			// Populate the requested tranches. The assignments aren't inspected in
1199			// this test.
1200			for &t in &test_tranche {
1201				approval_entry.import_assignment(t, ValidatorIndex(0), 0, false);
1202			}
1203
1204			let filled_tranches = filled_tranche_iterator(approval_entry.tranches());
1205
1206			// Take the first PREFIX entries and map them to their tranche.
1207			let tranches: Vec<DelayTranche> =
1208				filled_tranches.take(PREFIX as usize).map(|e| e.0).collect();
1209
1210			// We expect this sequence to be sequential.
1211			let exp_tranches: Vec<DelayTranche> = (0..PREFIX).collect();
1212			assert_eq!(tranches, exp_tranches, "for test tranches: {:?}", test_tranche);
1213		}
1214	}
1215
1216	#[derive(Debug)]
1217	struct NoShowTest {
1218		assignments: Vec<(ValidatorIndex, Tick)>,
1219		approvals: Vec<usize>,
1220		clock_drift: Tick,
1221		no_show_duration: Tick,
1222		drifted_tick_now: Tick,
1223		exp_no_shows: usize,
1224		exp_next_no_show: Option<u64>,
1225	}
1226
1227	fn test_count_no_shows(test: NoShowTest) {
1228		let n_validators = 4;
1229		let block_tick = 20;
1230
1231		let mut approvals = bitvec![u8, BitOrderLsb0; 0; n_validators];
1232		for &v_index in &test.approvals {
1233			approvals.set(v_index, true);
1234		}
1235
1236		let (no_shows, next_no_show, _) = count_no_shows(
1237			&test.assignments,
1238			&approvals,
1239			test.clock_drift,
1240			block_tick,
1241			test.no_show_duration,
1242			test.drifted_tick_now,
1243		);
1244		assert_eq!(no_shows, test.exp_no_shows, "for test: {:?}", test);
1245		assert_eq!(next_no_show, test.exp_next_no_show, "for test {:?}", test);
1246	}
1247
1248	#[test]
1249	fn count_no_shows_empty_assignments() {
1250		test_count_no_shows(NoShowTest {
1251			assignments: vec![],
1252			approvals: vec![],
1253			clock_drift: 0,
1254			no_show_duration: 0,
1255			drifted_tick_now: 0,
1256			exp_no_shows: 0,
1257			exp_next_no_show: None,
1258		})
1259	}
1260
1261	#[test]
1262	fn count_no_shows_single_validator_is_next_no_show() {
1263		test_count_no_shows(NoShowTest {
1264			assignments: vec![(ValidatorIndex(1), 31)],
1265			approvals: vec![],
1266			clock_drift: 10,
1267			no_show_duration: 10,
1268			drifted_tick_now: 20,
1269			exp_no_shows: 0,
1270			exp_next_no_show: Some(41),
1271		})
1272	}
1273
1274	#[test]
1275	fn count_no_shows_single_validator_approval_at_drifted_tick_now() {
1276		test_count_no_shows(NoShowTest {
1277			assignments: vec![(ValidatorIndex(1), 20)],
1278			approvals: vec![1],
1279			clock_drift: 10,
1280			no_show_duration: 10,
1281			drifted_tick_now: 20,
1282			exp_no_shows: 0,
1283			exp_next_no_show: None,
1284		})
1285	}
1286
1287	#[test]
1288	fn count_no_shows_single_validator_approval_after_drifted_tick_now() {
1289		test_count_no_shows(NoShowTest {
1290			assignments: vec![(ValidatorIndex(1), 21)],
1291			approvals: vec![1],
1292			clock_drift: 10,
1293			no_show_duration: 10,
1294			drifted_tick_now: 20,
1295			exp_no_shows: 0,
1296			exp_next_no_show: None,
1297		})
1298	}
1299
1300	#[test]
1301	fn count_no_shows_two_validators_next_no_show_ordered_first() {
1302		test_count_no_shows(NoShowTest {
1303			assignments: vec![(ValidatorIndex(1), 31), (ValidatorIndex(2), 32)],
1304			approvals: vec![],
1305			clock_drift: 10,
1306			no_show_duration: 10,
1307			drifted_tick_now: 20,
1308			exp_no_shows: 0,
1309			exp_next_no_show: Some(41),
1310		})
1311	}
1312
1313	#[test]
1314	fn count_no_shows_two_validators_next_no_show_ordered_last() {
1315		test_count_no_shows(NoShowTest {
1316			assignments: vec![(ValidatorIndex(1), 32), (ValidatorIndex(2), 31)],
1317			approvals: vec![],
1318			clock_drift: 10,
1319			no_show_duration: 10,
1320			drifted_tick_now: 20,
1321			exp_no_shows: 0,
1322			exp_next_no_show: Some(41),
1323		})
1324	}
1325
1326	#[test]
1327	fn count_no_shows_three_validators_one_almost_late_one_no_show_one_approving() {
1328		test_count_no_shows(NoShowTest {
1329			assignments: vec![
1330				(ValidatorIndex(1), 31),
1331				(ValidatorIndex(2), 19),
1332				(ValidatorIndex(3), 19),
1333			],
1334			approvals: vec![3],
1335			clock_drift: 10,
1336			no_show_duration: 10,
1337			drifted_tick_now: 20,
1338			exp_no_shows: 1,
1339			exp_next_no_show: Some(41),
1340		})
1341	}
1342
1343	#[test]
1344	fn count_no_shows_three_no_show_validators() {
1345		test_count_no_shows(NoShowTest {
1346			assignments: vec![
1347				(ValidatorIndex(1), 20),
1348				(ValidatorIndex(2), 20),
1349				(ValidatorIndex(3), 20),
1350			],
1351			approvals: vec![],
1352			clock_drift: 10,
1353			no_show_duration: 10,
1354			drifted_tick_now: 20,
1355			exp_no_shows: 3,
1356			exp_next_no_show: None,
1357		})
1358	}
1359
1360	#[test]
1361	fn count_no_shows_three_approving_validators() {
1362		test_count_no_shows(NoShowTest {
1363			assignments: vec![
1364				(ValidatorIndex(1), 20),
1365				(ValidatorIndex(2), 20),
1366				(ValidatorIndex(3), 20),
1367			],
1368			approvals: vec![1, 2, 3],
1369			clock_drift: 10,
1370			no_show_duration: 10,
1371			drifted_tick_now: 20,
1372			exp_no_shows: 0,
1373			exp_next_no_show: None,
1374		})
1375	}
1376
1377	#[test]
1378	fn count_no_shows_earliest_possible_next_no_show_is_clock_drift_plus_no_show_duration() {
1379		test_count_no_shows(NoShowTest {
1380			assignments: vec![(ValidatorIndex(1), 0)],
1381			approvals: vec![],
1382			clock_drift: 10,
1383			no_show_duration: 20,
1384			drifted_tick_now: 0,
1385			exp_no_shows: 0,
1386			exp_next_no_show: Some(40),
1387		})
1388	}
1389
1390	#[test]
1391	fn count_no_shows_assignment_tick_equal_to_clock_drift_yields_earliest_possible_next_no_show() {
1392		test_count_no_shows(NoShowTest {
1393			assignments: vec![(ValidatorIndex(1), 10)],
1394			approvals: vec![],
1395			clock_drift: 10,
1396			no_show_duration: 20,
1397			drifted_tick_now: 0,
1398			exp_no_shows: 0,
1399			exp_next_no_show: Some(40),
1400		})
1401	}
1402
1403	#[test]
1404	fn count_no_shows_validator_index_out_of_approvals_range_is_ignored_as_no_show() {
1405		test_count_no_shows(NoShowTest {
1406			assignments: vec![(ValidatorIndex(1000), 20)],
1407			approvals: vec![],
1408			clock_drift: 10,
1409			no_show_duration: 10,
1410			drifted_tick_now: 20,
1411			exp_no_shows: 0,
1412			exp_next_no_show: None,
1413		})
1414	}
1415
1416	#[test]
1417	fn count_no_shows_validator_index_out_of_approvals_range_is_ignored_as_next_no_show() {
1418		test_count_no_shows(NoShowTest {
1419			assignments: vec![(ValidatorIndex(1000), 21)],
1420			approvals: vec![],
1421			clock_drift: 10,
1422			no_show_duration: 10,
1423			drifted_tick_now: 20,
1424			exp_no_shows: 0,
1425			exp_next_no_show: None,
1426		})
1427	}
1428
1429	#[test]
1430	fn depth_0_covering_not_treated_as_such() {
1431		let state = State {
1432			assignments: 0,
1433			depth: 0,
1434			covered: 0,
1435			covering: 10,
1436			uncovered: 0,
1437			next_no_show: None,
1438			last_assignment_tick: None,
1439			total_observed_no_shows: 0,
1440			no_show_validators: Default::default(),
1441		};
1442
1443		assert_eq!(
1444			state.output(0, 10, 10, 20).required_tranches,
1445			RequiredTranches::Pending {
1446				considered: 0,
1447				next_no_show: None,
1448				maximum_broadcast: DelayTranche::max_value(),
1449				clock_drift: 0,
1450			},
1451		);
1452	}
1453
1454	#[test]
1455	fn depth_0_issued_as_exact_even_when_all() {
1456		let state = State {
1457			assignments: 10,
1458			depth: 0,
1459			covered: 0,
1460			covering: 0,
1461			uncovered: 0,
1462			next_no_show: None,
1463			last_assignment_tick: None,
1464			total_observed_no_shows: 0,
1465			no_show_validators: Default::default(),
1466		};
1467
1468		assert_eq!(
1469			state.output(0, 10, 10, 20).required_tranches,
1470			RequiredTranches::Exact {
1471				needed: 0,
1472				tolerated_missing: 0,
1473				next_no_show: None,
1474				last_assignment_tick: None
1475			},
1476		);
1477	}
1478}