1use polkadot_primitives::{CandidateHash, CompactStatement, Hash, ValidatorIndex};
59
60use crate::LOG_TARGET;
61use std::collections::{HashMap, HashSet};
62
63#[derive(Hash, Clone, PartialEq, Eq)]
65enum Knowledge {
66 General(CandidateHash),
68 Specific(CompactStatement, ValidatorIndex),
70}
71
72#[derive(Hash, Clone, PartialEq, Eq)]
74enum TaggedKnowledge {
75 IncomingP2P(Knowledge),
77 OutgoingP2P(Knowledge),
79 Seconded(CandidateHash),
83}
84
85pub struct ClusterTracker {
89 validators: Vec<ValidatorIndex>,
90 seconding_limit: usize,
91 knowledge: HashMap<ValidatorIndex, HashSet<TaggedKnowledge>>,
92 pending: HashMap<ValidatorIndex, HashSet<(ValidatorIndex, CompactStatement)>>,
95}
96
97impl ClusterTracker {
98 pub fn new(cluster_validators: Vec<ValidatorIndex>, seconding_limit: usize) -> Option<Self> {
100 if cluster_validators.is_empty() {
101 return None
102 }
103 Some(ClusterTracker {
104 validators: cluster_validators,
105 seconding_limit,
106 knowledge: HashMap::new(),
107 pending: HashMap::new(),
108 })
109 }
110
111 pub fn can_receive(
115 &self,
116 sender: ValidatorIndex,
117 originator: ValidatorIndex,
118 statement: CompactStatement,
119 ) -> Result<Accept, RejectIncoming> {
120 if !self.is_in_group(sender) || !self.is_in_group(originator) {
121 return Err(RejectIncoming::NotInGroup)
122 }
123
124 if self.they_sent(sender, Knowledge::Specific(statement.clone(), originator)) {
125 return Err(RejectIncoming::Duplicate)
126 }
127
128 match statement {
129 CompactStatement::Seconded(candidate_hash) => {
130 let other_seconded_for_orig_from_remote = self
134 .knowledge
135 .get(&sender)
136 .into_iter()
137 .flat_map(|v_knowledge| v_knowledge.iter())
138 .filter(|k| match k {
139 TaggedKnowledge::IncomingP2P(Knowledge::Specific(
140 CompactStatement::Seconded(_),
141 orig,
142 )) if orig == &originator => true,
143 _ => false,
144 })
145 .count();
146
147 if other_seconded_for_orig_from_remote == self.seconding_limit {
148 return Err(RejectIncoming::ExcessiveSeconded)
149 }
150
151 if self.seconded_already_or_within_limit(originator, candidate_hash) {
153 Ok(Accept::Ok)
154 } else {
155 Ok(Accept::WithPrejudice)
156 }
157 },
158 CompactStatement::Valid(candidate_hash) => {
159 if !self.knows_candidate(sender, candidate_hash) {
160 return Err(RejectIncoming::CandidateUnknown)
161 }
162
163 Ok(Accept::Ok)
164 },
165 }
166 }
167
168 pub fn note_issued(&mut self, originator: ValidatorIndex, statement: CompactStatement) {
170 for cluster_member in &self.validators {
171 if !self.they_know_statement(*cluster_member, originator, statement.clone()) {
172 self.pending
175 .entry(*cluster_member)
176 .or_default()
177 .insert((originator, statement.clone()));
178 }
179 }
180 }
181
182 pub fn note_received(
186 &mut self,
187 sender: ValidatorIndex,
188 originator: ValidatorIndex,
189 statement: CompactStatement,
190 ) {
191 for cluster_member in &self.validators {
192 if cluster_member == &sender {
193 if let Some(pending) = self.pending.get_mut(&sender) {
194 pending.remove(&(originator, statement.clone()));
195 }
196 } else if !self.they_know_statement(*cluster_member, originator, statement.clone()) {
197 self.pending
200 .entry(*cluster_member)
201 .or_default()
202 .insert((originator, statement.clone()));
203 }
204 }
205
206 {
207 let sender_knowledge = self.knowledge.entry(sender).or_default();
208 sender_knowledge.insert(TaggedKnowledge::IncomingP2P(Knowledge::Specific(
209 statement.clone(),
210 originator,
211 )));
212
213 if let CompactStatement::Seconded(candidate_hash) = statement.clone() {
214 sender_knowledge
215 .insert(TaggedKnowledge::IncomingP2P(Knowledge::General(candidate_hash)));
216 }
217 }
218
219 if let CompactStatement::Seconded(candidate_hash) = statement {
220 if self.seconded_already_or_within_limit(originator, candidate_hash) {
223 let originator_knowledge = self.knowledge.entry(originator).or_default();
224 originator_knowledge.insert(TaggedKnowledge::Seconded(candidate_hash));
225 }
226 }
227 }
228
229 pub fn can_send(
231 &self,
232 target: ValidatorIndex,
233 originator: ValidatorIndex,
234 statement: CompactStatement,
235 ) -> Result<(), RejectOutgoing> {
236 if !self.is_in_group(target) || !self.is_in_group(originator) {
237 return Err(RejectOutgoing::NotInGroup)
238 }
239
240 if self.they_know_statement(target, originator, statement.clone()) {
241 return Err(RejectOutgoing::Known)
242 }
243
244 match statement {
245 CompactStatement::Seconded(candidate_hash) => {
246 if !self.seconded_already_or_within_limit(originator, candidate_hash) {
249 return Err(RejectOutgoing::ExcessiveSeconded)
250 }
251
252 Ok(())
253 },
254 CompactStatement::Valid(candidate_hash) => {
255 if !self.knows_candidate(target, candidate_hash) {
256 return Err(RejectOutgoing::CandidateUnknown)
257 }
258
259 Ok(())
260 },
261 }
262 }
263
264 pub fn note_sent(
267 &mut self,
268 target: ValidatorIndex,
269 originator: ValidatorIndex,
270 statement: CompactStatement,
271 ) {
272 {
273 let target_knowledge = self.knowledge.entry(target).or_default();
274 target_knowledge.insert(TaggedKnowledge::OutgoingP2P(Knowledge::Specific(
275 statement.clone(),
276 originator,
277 )));
278
279 if let CompactStatement::Seconded(candidate_hash) = statement.clone() {
280 target_knowledge
281 .insert(TaggedKnowledge::OutgoingP2P(Knowledge::General(candidate_hash)));
282 }
283 }
284
285 if let CompactStatement::Seconded(candidate_hash) = statement {
286 let originator_knowledge = self.knowledge.entry(originator).or_default();
287 originator_knowledge.insert(TaggedKnowledge::Seconded(candidate_hash));
288 }
289
290 if let Some(pending) = self.pending.get_mut(&target) {
291 pending.remove(&(originator, statement));
292 }
293 }
294
295 pub fn targets(&self) -> &[ValidatorIndex] {
298 &self.validators
299 }
300
301 pub fn senders_for_originator(&self, originator: ValidatorIndex) -> &[ValidatorIndex] {
308 if self.validators.contains(&originator) {
309 &self.validators[..]
310 } else {
311 &[]
312 }
313 }
314
315 pub fn knows_candidate(
317 &self,
318 validator: ValidatorIndex,
319 candidate_hash: CandidateHash,
320 ) -> bool {
321 self.we_sent_seconded(validator, candidate_hash) ||
324 self.they_sent_seconded(validator, candidate_hash) ||
325 self.validator_seconded(validator, candidate_hash)
326 }
327
328 pub fn can_request(&self, target: ValidatorIndex, candidate_hash: CandidateHash) -> bool {
330 self.validators.contains(&target) &&
331 self.we_sent_seconded(target, candidate_hash) &&
332 !self.they_sent_seconded(target, candidate_hash)
333 }
334
335 pub fn pending_statements_for(
340 &self,
341 target: ValidatorIndex,
342 ) -> Vec<(ValidatorIndex, CompactStatement)> {
343 let mut v = self
344 .pending
345 .get(&target)
346 .map(|x| x.iter().cloned().collect::<Vec<_>>())
347 .unwrap_or_default();
348
349 v.sort_by_key(|(_, s)| match s {
350 CompactStatement::Seconded(_) => 0u8,
351 CompactStatement::Valid(_) => 1u8,
352 });
353
354 v
355 }
356
357 fn seconded_already_or_within_limit(
362 &self,
363 validator: ValidatorIndex,
364 candidate_hash: CandidateHash,
365 ) -> bool {
366 let seconded_other_candidates = self
367 .knowledge
368 .get(&validator)
369 .into_iter()
370 .flat_map(|v_knowledge| v_knowledge.iter())
371 .filter(|k| match k {
372 TaggedKnowledge::Seconded(c) if c != &candidate_hash => true,
373 _ => false,
374 })
375 .count();
376
377 seconded_other_candidates < self.seconding_limit
380 }
381
382 fn they_know_statement(
383 &self,
384 validator: ValidatorIndex,
385 originator: ValidatorIndex,
386 statement: CompactStatement,
387 ) -> bool {
388 let knowledge = Knowledge::Specific(statement, originator);
389 self.we_sent(validator, knowledge.clone()) || self.they_sent(validator, knowledge)
390 }
391
392 fn they_sent(&self, validator: ValidatorIndex, knowledge: Knowledge) -> bool {
393 self.knowledge
394 .get(&validator)
395 .map_or(false, |k| k.contains(&TaggedKnowledge::IncomingP2P(knowledge)))
396 }
397
398 fn we_sent(&self, validator: ValidatorIndex, knowledge: Knowledge) -> bool {
399 self.knowledge
400 .get(&validator)
401 .map_or(false, |k| k.contains(&TaggedKnowledge::OutgoingP2P(knowledge)))
402 }
403
404 fn we_sent_seconded(&self, validator: ValidatorIndex, candidate_hash: CandidateHash) -> bool {
405 self.we_sent(validator, Knowledge::General(candidate_hash))
406 }
407
408 fn they_sent_seconded(&self, validator: ValidatorIndex, candidate_hash: CandidateHash) -> bool {
409 self.they_sent(validator, Knowledge::General(candidate_hash))
410 }
411
412 fn validator_seconded(&self, validator: ValidatorIndex, candidate_hash: CandidateHash) -> bool {
413 self.knowledge
414 .get(&validator)
415 .map_or(false, |k| k.contains(&TaggedKnowledge::Seconded(candidate_hash)))
416 }
417
418 fn is_in_group(&self, validator: ValidatorIndex) -> bool {
419 self.validators.contains(&validator)
420 }
421
422 pub fn warn_if_too_many_pending_statements(&self, parent_hash: Hash) {
431 if self.pending.iter().filter(|pending| !pending.1.is_empty()).count() >=
432 self.validators.len() &&
433 self.validators.len() > 1
435 {
436 gum::warn!(
437 target: LOG_TARGET,
438 pending_statements = ?self.pending,
439 ?parent_hash,
440 "Cluster has too many pending statements, something wrong with our connection to our group peers
441 Restart might be needed if validator gets 0 backing rewards for more than 3-4 consecutive sessions"
442 );
443 }
444 }
445}
446
447#[derive(Debug, PartialEq)]
449pub enum Accept {
450 Ok,
453 WithPrejudice,
455}
456
457#[derive(Debug, PartialEq)]
459pub enum RejectIncoming {
460 ExcessiveSeconded,
462 NotInGroup,
464 CandidateUnknown,
466 Duplicate,
468}
469
470#[derive(Debug, PartialEq)]
472pub enum RejectOutgoing {
473 CandidateUnknown,
475 ExcessiveSeconded,
478 Known,
480 NotInGroup,
482}
483
484#[cfg(test)]
485mod tests {
486 use super::*;
487 use polkadot_primitives::Hash;
488
489 #[test]
490 fn rejects_incoming_outside_of_group() {
491 let group =
492 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
493
494 let seconding_limit = 2;
495
496 let tracker = ClusterTracker::new(group.clone(), seconding_limit).expect("not empty");
497
498 assert_eq!(
499 tracker.can_receive(
500 ValidatorIndex(100),
501 ValidatorIndex(5),
502 CompactStatement::Seconded(CandidateHash(Hash::repeat_byte(1))),
503 ),
504 Err(RejectIncoming::NotInGroup),
505 );
506
507 assert_eq!(
508 tracker.can_receive(
509 ValidatorIndex(5),
510 ValidatorIndex(100),
511 CompactStatement::Seconded(CandidateHash(Hash::repeat_byte(1))),
512 ),
513 Err(RejectIncoming::NotInGroup),
514 );
515 }
516
517 #[test]
518 fn begrudgingly_accepts_too_many_seconded_from_multiple_peers() {
519 let group =
520 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
521
522 let seconding_limit = 2;
523 let hash_a = CandidateHash(Hash::repeat_byte(1));
524 let hash_b = CandidateHash(Hash::repeat_byte(2));
525 let hash_c = CandidateHash(Hash::repeat_byte(3));
526
527 let mut tracker = ClusterTracker::new(group.clone(), seconding_limit).expect("not empty");
528
529 assert_eq!(
530 tracker.can_receive(
531 ValidatorIndex(5),
532 ValidatorIndex(5),
533 CompactStatement::Seconded(hash_a),
534 ),
535 Ok(Accept::Ok),
536 );
537 tracker.note_received(
538 ValidatorIndex(5),
539 ValidatorIndex(5),
540 CompactStatement::Seconded(hash_a),
541 );
542
543 assert_eq!(
544 tracker.can_receive(
545 ValidatorIndex(5),
546 ValidatorIndex(5),
547 CompactStatement::Seconded(hash_b),
548 ),
549 Ok(Accept::Ok),
550 );
551 tracker.note_received(
552 ValidatorIndex(5),
553 ValidatorIndex(5),
554 CompactStatement::Seconded(hash_b),
555 );
556
557 assert_eq!(
558 tracker.can_receive(
559 ValidatorIndex(5),
560 ValidatorIndex(5),
561 CompactStatement::Seconded(hash_c),
562 ),
563 Err(RejectIncoming::ExcessiveSeconded),
564 );
565 }
566
567 #[test]
568 fn rejects_too_many_seconded_from_sender() {
569 let group =
570 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
571
572 let seconding_limit = 2;
573 let hash_a = CandidateHash(Hash::repeat_byte(1));
574 let hash_b = CandidateHash(Hash::repeat_byte(2));
575 let hash_c = CandidateHash(Hash::repeat_byte(3));
576
577 let mut tracker = ClusterTracker::new(group.clone(), seconding_limit).expect("not empty");
578
579 assert_eq!(
580 tracker.can_receive(
581 ValidatorIndex(5),
582 ValidatorIndex(5),
583 CompactStatement::Seconded(hash_a),
584 ),
585 Ok(Accept::Ok),
586 );
587 tracker.note_received(
588 ValidatorIndex(5),
589 ValidatorIndex(5),
590 CompactStatement::Seconded(hash_a),
591 );
592
593 assert_eq!(
594 tracker.can_receive(
595 ValidatorIndex(5),
596 ValidatorIndex(5),
597 CompactStatement::Seconded(hash_b),
598 ),
599 Ok(Accept::Ok),
600 );
601 tracker.note_received(
602 ValidatorIndex(5),
603 ValidatorIndex(5),
604 CompactStatement::Seconded(hash_b),
605 );
606
607 assert_eq!(
608 tracker.can_receive(
609 ValidatorIndex(200),
610 ValidatorIndex(5),
611 CompactStatement::Seconded(hash_c),
612 ),
613 Ok(Accept::WithPrejudice),
614 );
615 }
616
617 #[test]
618 fn rejects_duplicates() {
619 let group =
620 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
621
622 let seconding_limit = 2;
623 let hash_a = CandidateHash(Hash::repeat_byte(1));
624
625 let mut tracker = ClusterTracker::new(group, seconding_limit).expect("not empty");
626
627 tracker.note_received(
628 ValidatorIndex(5),
629 ValidatorIndex(5),
630 CompactStatement::Seconded(hash_a),
631 );
632
633 tracker.note_received(
634 ValidatorIndex(5),
635 ValidatorIndex(200),
636 CompactStatement::Valid(hash_a),
637 );
638
639 assert_eq!(
640 tracker.can_receive(
641 ValidatorIndex(5),
642 ValidatorIndex(5),
643 CompactStatement::Seconded(hash_a),
644 ),
645 Err(RejectIncoming::Duplicate),
646 );
647
648 assert_eq!(
649 tracker.can_receive(
650 ValidatorIndex(5),
651 ValidatorIndex(200),
652 CompactStatement::Valid(hash_a),
653 ),
654 Err(RejectIncoming::Duplicate),
655 );
656 }
657
658 #[test]
659 fn rejects_incoming_valid_without_seconded() {
660 let group =
661 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
662
663 let seconding_limit = 2;
664
665 let tracker = ClusterTracker::new(group, seconding_limit).expect("not empty");
666
667 let hash_a = CandidateHash(Hash::repeat_byte(1));
668
669 assert_eq!(
670 tracker.can_receive(
671 ValidatorIndex(5),
672 ValidatorIndex(5),
673 CompactStatement::Valid(hash_a),
674 ),
675 Err(RejectIncoming::CandidateUnknown),
676 );
677 }
678
679 #[test]
680 fn accepts_incoming_valid_after_receiving_seconded() {
681 let group =
682 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
683
684 let seconding_limit = 2;
685
686 let mut tracker = ClusterTracker::new(group.clone(), seconding_limit).expect("not empty");
687 let hash_a = CandidateHash(Hash::repeat_byte(1));
688
689 tracker.note_received(
690 ValidatorIndex(5),
691 ValidatorIndex(200),
692 CompactStatement::Seconded(hash_a),
693 );
694
695 assert_eq!(
696 tracker.can_receive(
697 ValidatorIndex(5),
698 ValidatorIndex(5),
699 CompactStatement::Valid(hash_a),
700 ),
701 Ok(Accept::Ok)
702 );
703 }
704
705 #[test]
706 fn accepts_incoming_valid_after_outgoing_seconded() {
707 let group =
708 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
709
710 let seconding_limit = 2;
711
712 let mut tracker = ClusterTracker::new(group.clone(), seconding_limit).expect("not empty");
713 let hash_a = CandidateHash(Hash::repeat_byte(1));
714
715 tracker.note_sent(
716 ValidatorIndex(5),
717 ValidatorIndex(200),
718 CompactStatement::Seconded(hash_a),
719 );
720
721 assert_eq!(
722 tracker.can_receive(
723 ValidatorIndex(5),
724 ValidatorIndex(5),
725 CompactStatement::Valid(hash_a),
726 ),
727 Ok(Accept::Ok)
728 );
729 }
730
731 #[test]
732 fn cannot_send_too_many_seconded_even_to_multiple_peers() {
733 let group =
734 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
735
736 let seconding_limit = 2;
737
738 let mut tracker = ClusterTracker::new(group.clone(), seconding_limit).expect("not empty");
739 let hash_a = CandidateHash(Hash::repeat_byte(1));
740 let hash_b = CandidateHash(Hash::repeat_byte(2));
741 let hash_c = CandidateHash(Hash::repeat_byte(3));
742
743 tracker.note_sent(
744 ValidatorIndex(200),
745 ValidatorIndex(5),
746 CompactStatement::Seconded(hash_a),
747 );
748
749 tracker.note_sent(
750 ValidatorIndex(200),
751 ValidatorIndex(5),
752 CompactStatement::Seconded(hash_b),
753 );
754
755 assert_eq!(
756 tracker.can_send(
757 ValidatorIndex(200),
758 ValidatorIndex(5),
759 CompactStatement::Seconded(hash_c),
760 ),
761 Err(RejectOutgoing::ExcessiveSeconded),
762 );
763
764 assert_eq!(
765 tracker.can_send(
766 ValidatorIndex(24),
767 ValidatorIndex(5),
768 CompactStatement::Seconded(hash_c),
769 ),
770 Err(RejectOutgoing::ExcessiveSeconded),
771 );
772 }
773
774 #[test]
775 fn cannot_send_duplicate() {
776 let group =
777 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
778
779 let seconding_limit = 2;
780
781 let mut tracker = ClusterTracker::new(group.clone(), seconding_limit).expect("not empty");
782 let hash_a = CandidateHash(Hash::repeat_byte(1));
783
784 tracker.note_sent(
785 ValidatorIndex(200),
786 ValidatorIndex(5),
787 CompactStatement::Seconded(hash_a),
788 );
789
790 assert_eq!(
791 tracker.can_send(
792 ValidatorIndex(200),
793 ValidatorIndex(5),
794 CompactStatement::Seconded(hash_a),
795 ),
796 Err(RejectOutgoing::Known),
797 );
798 }
799
800 #[test]
801 fn cannot_send_what_was_received() {
802 let group =
803 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
804
805 let seconding_limit = 2;
806
807 let mut tracker = ClusterTracker::new(group.clone(), seconding_limit).expect("not empty");
808 let hash_a = CandidateHash(Hash::repeat_byte(1));
809
810 tracker.note_received(
811 ValidatorIndex(200),
812 ValidatorIndex(5),
813 CompactStatement::Seconded(hash_a),
814 );
815
816 assert_eq!(
817 tracker.can_send(
818 ValidatorIndex(200),
819 ValidatorIndex(5),
820 CompactStatement::Seconded(hash_a),
821 ),
822 Err(RejectOutgoing::Known),
823 );
824 }
825
826 #[test]
828 fn can_send_statements_received_with_prejudice() {
829 let group =
830 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
831
832 let seconding_limit = 1;
833
834 let mut tracker = ClusterTracker::new(group.clone(), seconding_limit).expect("not empty");
835 let hash_a = CandidateHash(Hash::repeat_byte(1));
836 let hash_b = CandidateHash(Hash::repeat_byte(2));
837
838 assert_eq!(
839 tracker.can_receive(
840 ValidatorIndex(200),
841 ValidatorIndex(5),
842 CompactStatement::Seconded(hash_a),
843 ),
844 Ok(Accept::Ok),
845 );
846
847 tracker.note_received(
848 ValidatorIndex(200),
849 ValidatorIndex(5),
850 CompactStatement::Seconded(hash_a),
851 );
852
853 assert_eq!(
854 tracker.can_receive(
855 ValidatorIndex(24),
856 ValidatorIndex(5),
857 CompactStatement::Seconded(hash_b),
858 ),
859 Ok(Accept::WithPrejudice),
860 );
861
862 tracker.note_received(
863 ValidatorIndex(24),
864 ValidatorIndex(5),
865 CompactStatement::Seconded(hash_b),
866 );
867
868 assert_eq!(
869 tracker.can_send(
870 ValidatorIndex(24),
871 ValidatorIndex(5),
872 CompactStatement::Seconded(hash_a),
873 ),
874 Ok(()),
875 );
876 }
877
878 #[test]
882 fn pending_statements_set_when_receiving_fresh_statements() {
883 let group =
884 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
885
886 let seconding_limit = 1;
887
888 let mut tracker = ClusterTracker::new(group.clone(), seconding_limit).expect("not empty");
889 let hash_a = CandidateHash(Hash::repeat_byte(1));
890 let hash_b = CandidateHash(Hash::repeat_byte(2));
891
892 {
894 assert_eq!(
895 tracker.can_receive(
896 ValidatorIndex(200),
897 ValidatorIndex(5),
898 CompactStatement::Seconded(hash_a),
899 ),
900 Ok(Accept::Ok),
901 );
902 tracker.note_received(
903 ValidatorIndex(200),
904 ValidatorIndex(5),
905 CompactStatement::Seconded(hash_a),
906 );
907
908 assert_eq!(
909 tracker.pending_statements_for(ValidatorIndex(5)),
910 vec![(ValidatorIndex(5), CompactStatement::Seconded(hash_a))]
911 );
912 assert_eq!(tracker.pending_statements_for(ValidatorIndex(200)), vec![]);
913 assert_eq!(
914 tracker.pending_statements_for(ValidatorIndex(24)),
915 vec![(ValidatorIndex(5), CompactStatement::Seconded(hash_a))]
916 );
917 assert_eq!(
918 tracker.pending_statements_for(ValidatorIndex(146)),
919 vec![(ValidatorIndex(5), CompactStatement::Seconded(hash_a))]
920 );
921 }
922
923 {
925 assert_eq!(
927 tracker.can_send(
928 ValidatorIndex(24),
929 ValidatorIndex(200),
930 CompactStatement::Seconded(hash_a)
931 ),
932 Ok(())
933 );
934 tracker.note_sent(
935 ValidatorIndex(24),
936 ValidatorIndex(200),
937 CompactStatement::Seconded(hash_a),
938 );
939
940 assert_eq!(
943 tracker.can_receive(
944 ValidatorIndex(24),
945 ValidatorIndex(200),
946 CompactStatement::Valid(hash_a),
947 ),
948 Ok(Accept::Ok),
949 );
950 tracker.note_received(
951 ValidatorIndex(24),
952 ValidatorIndex(200),
953 CompactStatement::Valid(hash_a),
954 );
955
956 assert_eq!(
957 tracker.pending_statements_for(ValidatorIndex(5)),
958 vec![
959 (ValidatorIndex(5), CompactStatement::Seconded(hash_a)),
960 (ValidatorIndex(200), CompactStatement::Valid(hash_a))
961 ]
962 );
963 assert_eq!(
964 tracker.pending_statements_for(ValidatorIndex(200)),
965 vec![(ValidatorIndex(200), CompactStatement::Valid(hash_a))]
966 );
967 assert_eq!(
968 tracker.pending_statements_for(ValidatorIndex(24)),
969 vec![(ValidatorIndex(5), CompactStatement::Seconded(hash_a))]
970 );
971 assert_eq!(
972 tracker.pending_statements_for(ValidatorIndex(146)),
973 vec![
974 (ValidatorIndex(5), CompactStatement::Seconded(hash_a)),
975 (ValidatorIndex(200), CompactStatement::Valid(hash_a))
976 ]
977 );
978 }
979
980 {
982 assert_eq!(
983 tracker.can_receive(
984 ValidatorIndex(5),
985 ValidatorIndex(146),
986 CompactStatement::Seconded(hash_b),
987 ),
988 Ok(Accept::Ok),
989 );
990 tracker.note_received(
991 ValidatorIndex(5),
992 ValidatorIndex(146),
993 CompactStatement::Seconded(hash_b),
994 );
995
996 assert_eq!(
997 tracker.pending_statements_for(ValidatorIndex(5)),
998 vec![
999 (ValidatorIndex(5), CompactStatement::Seconded(hash_a)),
1000 (ValidatorIndex(200), CompactStatement::Valid(hash_a))
1001 ]
1002 );
1003 assert_eq!(
1004 tracker.pending_statements_for(ValidatorIndex(200)),
1005 vec![
1006 (ValidatorIndex(146), CompactStatement::Seconded(hash_b)),
1007 (ValidatorIndex(200), CompactStatement::Valid(hash_a)),
1008 ]
1009 );
1010 {
1011 let mut pending_statements = tracker.pending_statements_for(ValidatorIndex(24));
1012 pending_statements.sort();
1013 assert_eq!(
1014 pending_statements,
1015 vec![
1016 (ValidatorIndex(5), CompactStatement::Seconded(hash_a)),
1017 (ValidatorIndex(146), CompactStatement::Seconded(hash_b))
1018 ],
1019 );
1020 }
1021 {
1022 let mut pending_statements = tracker.pending_statements_for(ValidatorIndex(146));
1023 pending_statements.sort();
1024 assert_eq!(
1025 pending_statements,
1026 vec![
1027 (ValidatorIndex(5), CompactStatement::Seconded(hash_a)),
1028 (ValidatorIndex(146), CompactStatement::Seconded(hash_b)),
1029 (ValidatorIndex(200), CompactStatement::Valid(hash_a)),
1030 ]
1031 );
1032 }
1033 }
1034 }
1035
1036 #[test]
1039 fn pending_statements_updated_when_sending_statements() {
1040 let group =
1041 vec![ValidatorIndex(5), ValidatorIndex(200), ValidatorIndex(24), ValidatorIndex(146)];
1042
1043 let seconding_limit = 1;
1044
1045 let mut tracker = ClusterTracker::new(group.clone(), seconding_limit).expect("not empty");
1046 let hash_a = CandidateHash(Hash::repeat_byte(1));
1047 let hash_b = CandidateHash(Hash::repeat_byte(2));
1048
1049 {
1051 assert_eq!(
1052 tracker.can_receive(
1053 ValidatorIndex(200),
1054 ValidatorIndex(5),
1055 CompactStatement::Seconded(hash_a),
1056 ),
1057 Ok(Accept::Ok),
1058 );
1059 tracker.note_received(
1060 ValidatorIndex(200),
1061 ValidatorIndex(5),
1062 CompactStatement::Seconded(hash_a),
1063 );
1064
1065 assert_eq!(
1067 tracker.pending_statements_for(ValidatorIndex(5)),
1068 vec![(ValidatorIndex(5), CompactStatement::Seconded(hash_a))]
1069 );
1070 assert_eq!(tracker.pending_statements_for(ValidatorIndex(200)), vec![]);
1071 assert_eq!(
1072 tracker.pending_statements_for(ValidatorIndex(24)),
1073 vec![(ValidatorIndex(5), CompactStatement::Seconded(hash_a))]
1074 );
1075 assert_eq!(
1076 tracker.pending_statements_for(ValidatorIndex(146)),
1077 vec![(ValidatorIndex(5), CompactStatement::Seconded(hash_a))]
1078 );
1079 }
1080
1081 {
1083 assert_eq!(
1085 tracker.can_send(
1086 ValidatorIndex(24),
1087 ValidatorIndex(200),
1088 CompactStatement::Seconded(hash_b)
1089 ),
1090 Ok(())
1091 );
1092 tracker.note_sent(
1093 ValidatorIndex(24),
1094 ValidatorIndex(200),
1095 CompactStatement::Seconded(hash_b),
1096 );
1097
1098 assert_eq!(
1100 tracker.can_receive(
1101 ValidatorIndex(24),
1102 ValidatorIndex(200),
1103 CompactStatement::Valid(hash_b),
1104 ),
1105 Ok(Accept::Ok),
1106 );
1107 tracker.note_received(
1108 ValidatorIndex(24),
1109 ValidatorIndex(200),
1110 CompactStatement::Valid(hash_b),
1111 );
1112
1113 assert_eq!(
1115 tracker.pending_statements_for(ValidatorIndex(5)),
1116 vec![
1117 (ValidatorIndex(5), CompactStatement::Seconded(hash_a)),
1118 (ValidatorIndex(200), CompactStatement::Valid(hash_b))
1119 ]
1120 );
1121 assert_eq!(
1122 tracker.pending_statements_for(ValidatorIndex(200)),
1123 vec![(ValidatorIndex(200), CompactStatement::Valid(hash_b))]
1124 );
1125 assert_eq!(
1126 tracker.pending_statements_for(ValidatorIndex(24)),
1127 vec![(ValidatorIndex(5), CompactStatement::Seconded(hash_a))]
1128 );
1129 assert_eq!(
1130 tracker.pending_statements_for(ValidatorIndex(146)),
1131 vec![
1132 (ValidatorIndex(5), CompactStatement::Seconded(hash_a)),
1133 (ValidatorIndex(200), CompactStatement::Valid(hash_b))
1134 ]
1135 );
1136 }
1137
1138 {
1140 assert_eq!(
1141 tracker.can_send(
1142 ValidatorIndex(5),
1143 ValidatorIndex(5),
1144 CompactStatement::Seconded(hash_a)
1145 ),
1146 Ok(())
1147 );
1148 tracker.note_sent(
1149 ValidatorIndex(5),
1150 ValidatorIndex(5),
1151 CompactStatement::Seconded(hash_a),
1152 );
1153
1154 assert_eq!(
1156 tracker.pending_statements_for(ValidatorIndex(5)),
1157 vec![(ValidatorIndex(200), CompactStatement::Valid(hash_b))]
1158 );
1159 assert_eq!(
1160 tracker.pending_statements_for(ValidatorIndex(200)),
1161 vec![(ValidatorIndex(200), CompactStatement::Valid(hash_b))]
1162 );
1163 assert_eq!(
1164 tracker.pending_statements_for(ValidatorIndex(24)),
1165 vec![(ValidatorIndex(5), CompactStatement::Seconded(hash_a))]
1166 );
1167 assert_eq!(
1168 tracker.pending_statements_for(ValidatorIndex(146)),
1169 vec![
1170 (ValidatorIndex(5), CompactStatement::Seconded(hash_a)),
1171 (ValidatorIndex(200), CompactStatement::Valid(hash_b))
1172 ]
1173 );
1174 }
1175
1176 {
1178 assert_eq!(
1180 tracker.can_send(
1181 ValidatorIndex(5),
1182 ValidatorIndex(200),
1183 CompactStatement::Seconded(hash_b)
1184 ),
1185 Ok(())
1186 );
1187 tracker.note_sent(
1188 ValidatorIndex(5),
1189 ValidatorIndex(200),
1190 CompactStatement::Seconded(hash_b),
1191 );
1192
1193 assert_eq!(
1196 tracker.can_send(
1197 ValidatorIndex(5),
1198 ValidatorIndex(200),
1199 CompactStatement::Valid(hash_b)
1200 ),
1201 Ok(())
1202 );
1203 tracker.note_sent(
1204 ValidatorIndex(5),
1205 ValidatorIndex(200),
1206 CompactStatement::Valid(hash_b),
1207 );
1208
1209 assert_eq!(tracker.pending_statements_for(ValidatorIndex(5)), vec![]);
1211 assert_eq!(
1212 tracker.pending_statements_for(ValidatorIndex(200)),
1213 vec![(ValidatorIndex(200), CompactStatement::Valid(hash_b))]
1214 );
1215 assert_eq!(
1216 tracker.pending_statements_for(ValidatorIndex(24)),
1217 vec![(ValidatorIndex(5), CompactStatement::Seconded(hash_a))]
1218 );
1219 assert_eq!(
1220 tracker.pending_statements_for(ValidatorIndex(146)),
1221 vec![
1222 (ValidatorIndex(5), CompactStatement::Seconded(hash_a)),
1223 (ValidatorIndex(200), CompactStatement::Valid(hash_b))
1224 ]
1225 );
1226 }
1227 }
1228}