1use 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#[derive(Debug, PartialEq, Clone)]
31pub struct TranchesToApproveResult {
32 pub required_tranches: RequiredTranches,
34 pub total_observed_no_shows: usize,
36 pub no_show_validators: Vec<ValidatorIndex>,
37}
38
39#[derive(Debug, PartialEq, Clone)]
41pub enum RequiredTranches {
42 All,
45 Pending {
47 considered: DelayTranche,
49 next_no_show: Option<Tick>,
51 maximum_broadcast: DelayTranche,
55 clock_drift: Tick,
59 },
60 Exact {
64 needed: DelayTranche,
66 tolerated_missing: usize,
68 next_no_show: Option<Tick>,
72 last_assignment_tick: Option<Tick>,
74 },
75}
76
77#[derive(Debug, Clone, Copy, PartialEq)]
79pub enum Check {
80 Unapproved,
82 Approved(usize, Option<Tick>),
86 ApprovedOneThird,
88}
89
90impl Check {
91 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 pub fn known_no_shows(&self) -> usize {
104 match *self {
105 Check::Approved(n, _) => n,
106 _ => 0,
107 }
108 }
109}
110
111pub fn check_approval(
113 candidate: &CandidateEntry,
114 approval: &ApprovalEntry,
115 required: RequiredTranches,
116) -> Check {
117 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 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 assigned_mask &= approvals;
144 let n_approved = assigned_mask.count_ones();
145
146 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#[derive(Debug)]
173struct State {
174 assignments: usize,
176 depth: usize,
178 covered: usize,
180 covering: usize,
185 uncovered: usize,
188 next_no_show: Option<Tick>,
190 last_assignment_tick: Option<Tick>,
192 total_observed_no_shows: usize,
193 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 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 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 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 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 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 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
327fn 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 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
358fn 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 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
414pub 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 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 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 if tranche > drifted_tranche_now {
456 return None;
457 }
458
459 let n_assignments = assignments.iter()
461 .filter(|(v_index, _)| v_index.0 < n_validators as u32)
462 .count();
463
464 let last_assignment_tick = assignments.iter()
466 .map(|(_, t)| *t)
467 .max();
468
469 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 None
492 }
493 RequiredTranches::Pending { .. } => {
494 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 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 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, 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, 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 approvals.set(3, true);
942 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 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 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, 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 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 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 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![], vec![0], vec![0, 3], vec![2], vec![2, 4], vec![0, 1, 2], vec![2, 3, 4, 8], vec![0, 1, 2, 5, 6, 7], ];
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 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 let tranches: Vec<DelayTranche> =
1208 filled_tranches.take(PREFIX as usize).map(|e| e.0).collect();
1209
1210 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}