1use crate::{
28 simplex::scheme::bls12381_threshold::vrf as bls12381_threshold_vrf,
29 types::{Participant, Round, TermLength, View, ViewDelta},
30};
31use commonware_codec::Encode;
32use commonware_cryptography::{
33 Hasher, PublicKey, Sha256, bls12381::primitives::variant::Variant, certificate::Scheme,
34};
35use commonware_utils::{modulo, ordered::Set};
36use std::{fmt, marker::PhantomData, time::Duration};
37
38pub trait Config<S: Scheme>: Clone + Send + 'static {
52 type Elector: Elector<S>;
54
55 fn build(self, participants: &Set<S::PublicKey>) -> Self::Elector;
63}
64
65#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub struct Terms {
68 length: TermLength,
70 stall_timeout: Option<Duration>,
72 optimistic_views: ViewDelta,
74}
75
76impl Terms {
77 pub const fn rotating() -> Self {
80 Self {
81 length: TermLength::ONE,
82 stall_timeout: None,
83 optimistic_views: ViewDelta::zero(),
84 }
85 }
86
87 pub const fn stable(
123 length: TermLength,
124 stall_timeout: Duration,
125 optimistic_views: ViewDelta,
126 ) -> Self {
127 assert!(
128 length.get() > 1,
129 "stable leaders require a term length greater than 1"
130 );
131 assert!(
132 !stall_timeout.is_zero(),
133 "stable leaders require a stall timeout greater than zero"
134 );
135 Self {
136 length,
137 stall_timeout: Some(stall_timeout),
138 optimistic_views,
139 }
140 }
141
142 pub const fn length(&self) -> TermLength {
152 self.length
153 }
154
155 pub const fn stall_timeout(&self) -> Option<Duration> {
159 self.stall_timeout
160 }
161
162 pub const fn optimistic_views(&self) -> ViewDelta {
166 self.optimistic_views
167 }
168}
169
170impl Default for Terms {
171 fn default() -> Self {
172 Self::rotating()
173 }
174}
175
176pub trait Elector<S: Scheme>: Clone + Send + 'static {
208 fn terms(&self) -> Terms;
213
214 fn elect(&self, round: Round, certificate: Option<&S::Certificate>) -> Participant;
230}
231
232#[derive(Debug, Default)]
240pub struct RoundRobin<H: Hasher = Sha256> {
241 seed: Option<Vec<u8>>,
242 terms: Terms,
243 _phantom: PhantomData<H>,
244}
245
246impl<H: Hasher> Clone for RoundRobin<H> {
247 fn clone(&self) -> Self {
248 Self {
249 seed: self.seed.clone(),
250 terms: self.terms,
251 _phantom: PhantomData,
252 }
253 }
254}
255
256impl<H: Hasher> RoundRobin<H> {
257 pub fn shuffled(seed: &[u8]) -> Self {
262 Self {
263 seed: Some(seed.to_vec()),
264 terms: Terms::rotating(),
265 _phantom: PhantomData,
266 }
267 }
268
269 pub const fn with_term(
283 mut self,
284 term_length: TermLength,
285 stall_timeout: Duration,
286 optimistic_views: ViewDelta,
287 ) -> Self {
288 self.terms = Terms::stable(term_length, stall_timeout, optimistic_views);
289 self
290 }
291}
292
293impl<S: Scheme, H: Hasher> Config<S> for RoundRobin<H> {
294 type Elector = RoundRobinElector<S>;
295
296 fn build(self, participants: &Set<S::PublicKey>) -> RoundRobinElector<S> {
297 assert!(!participants.is_empty(), "no participants");
298
299 let mut permutation: Vec<Participant> = (0..participants.len())
300 .map(Participant::from_usize)
301 .collect();
302
303 if let Some(seed) = &self.seed {
304 permutation.sort_by_key(|&index| H::hash(&[seed, &index.get().encode()]));
305 }
306
307 RoundRobinElector {
308 permutation,
309 terms: self.terms,
310 _phantom: PhantomData,
311 }
312 }
313}
314
315#[derive(Clone, Debug)]
319pub struct RoundRobinElector<S: Scheme> {
320 permutation: Vec<Participant>,
321 terms: Terms,
322 _phantom: PhantomData<S>,
323}
324
325impl<S: Scheme> Elector<S> for RoundRobinElector<S> {
326 fn terms(&self) -> Terms {
327 self.terms
328 }
329
330 fn elect(&self, round: Round, _certificate: Option<&S::Certificate>) -> Participant {
331 let term_idx = round.view().term_index(self.terms.length());
333
334 let n = self.permutation.len();
336 let idx = round.epoch().get().wrapping_add(term_idx)
337 % u64::try_from(n).expect("permutation length fits in u64");
338 let idx = usize::try_from(idx).expect("leader index fits in usize");
339 self.permutation[idx]
340 }
341}
342
343#[derive(Clone, Copy, Debug, PartialEq, Eq)]
345pub enum RandomVersion {
346 #[deprecated(
348 note = "mapping encoded threshold signature directly to participants can bias selection"
349 )]
350 V0,
351 V1,
355}
356
357pub struct Random<H: Hasher = Sha256> {
369 version: RandomVersion,
370 _hasher: PhantomData<H>,
371}
372
373impl<H: Hasher> Random<H> {
374 pub const fn new(version: RandomVersion) -> Self {
376 Self {
377 version,
378 _hasher: PhantomData,
379 }
380 }
381
382 #[allow(deprecated)]
388 pub fn select_leader<V: Variant>(
389 &self,
390 round: Round,
391 n: u32,
392 seed_signature: Option<V::Signature>,
393 ) -> Participant {
394 assert_ne!(n, 0, "no participants");
395 assert!(seed_signature.is_some() || round.view() == View::new(1));
396
397 let Some(seed_signature) = seed_signature else {
398 let idx = round.epoch().get().wrapping_add(round.view().get()) % u64::from(n);
400 return Participant::new(u32::try_from(idx).expect("leader index fits in u32"));
401 };
402
403 let encoded = seed_signature.encode();
405 let index = match self.version {
406 RandomVersion::V0 => modulo(encoded.as_ref(), u64::from(n)),
407 RandomVersion::V1 => modulo(H::hash(&[encoded.as_ref()]).as_ref(), u64::from(n)),
408 };
409 Participant::new(u32::try_from(index).expect("leader index must fit in u32"))
410 }
411}
412
413impl<H: Hasher> Clone for Random<H> {
414 fn clone(&self) -> Self {
415 Self::new(self.version)
416 }
417}
418
419impl<H: Hasher> fmt::Debug for Random<H> {
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 self.version.fmt(f)
422 }
423}
424
425impl<P, V, H> Config<bls12381_threshold_vrf::Scheme<P, V>> for Random<H>
426where
427 P: PublicKey,
428 V: Variant,
429 H: Hasher,
430{
431 type Elector = RandomElector<bls12381_threshold_vrf::Scheme<P, V>, H>;
432
433 fn build(
434 self,
435 participants: &Set<P>,
436 ) -> RandomElector<bls12381_threshold_vrf::Scheme<P, V>, H> {
437 assert!(!participants.is_empty(), "no participants");
438 RandomElector {
439 n: participants.len() as u32,
440 version: self,
441 _phantom: PhantomData,
442 }
443 }
444}
445
446pub struct RandomElector<S: Scheme, H: Hasher = Sha256> {
450 n: u32,
451 version: Random<H>,
452 _phantom: PhantomData<S>,
453}
454
455impl<S: Scheme, H: Hasher> Clone for RandomElector<S, H> {
456 fn clone(&self) -> Self {
457 Self {
458 n: self.n,
459 version: self.version.clone(),
460 _phantom: PhantomData,
461 }
462 }
463}
464
465impl<S: Scheme, H: Hasher> fmt::Debug for RandomElector<S, H> {
466 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467 f.debug_struct("RandomElector")
468 .field("n", &self.n)
469 .field("version", &self.version)
470 .finish()
471 }
472}
473
474impl<P, V, H> Elector<bls12381_threshold_vrf::Scheme<P, V>>
475 for RandomElector<bls12381_threshold_vrf::Scheme<P, V>, H>
476where
477 P: PublicKey,
478 V: Variant,
479 H: Hasher,
480{
481 fn terms(&self) -> Terms {
482 Terms::rotating()
483 }
484
485 fn elect(
486 &self,
487 round: Round,
488 certificate: Option<&bls12381_threshold_vrf::Certificate<V>>,
489 ) -> Participant {
490 self.version.select_leader::<V>(
491 round,
492 self.n,
493 certificate.map(|c| {
494 c.get()
495 .expect("verified certificate must decode")
496 .seed_signature
497 }),
498 )
499 }
500}
501
502#[cfg(test)]
503mod tests {
504 use super::*;
505 use crate::{
506 simplex::{
507 scheme::{bls12381_threshold::vrf as bls12381_threshold_vrf, ed25519},
508 types::Subject,
509 },
510 types::{Epoch, View},
511 };
512 use commonware_cryptography::{
513 Sha256, bls12381::primitives::variant::MinPk, certificate::mocks::Fixture,
514 sha256::Digest as Sha256Digest,
515 };
516 use commonware_parallel::Sequential;
517 use commonware_utils::{Faults, N3f1, NZU32, TryFromIterator, non_empty, test_rng};
518
519 const NAMESPACE: &[u8] = b"test";
520
521 type ThresholdScheme =
522 bls12381_threshold_vrf::Scheme<commonware_cryptography::ed25519::PublicKey, MinPk>;
523
524 #[test]
525 fn stable_terms_preserve_optimistic_views() {
526 let stall = Duration::from_secs(1);
527 let length = TermLength::new(NZU32!(5));
528
529 for requested in [0, 3, 4, 5, 6, u64::MAX] {
533 let terms = Terms::stable(length, stall, ViewDelta::new(requested));
534 assert_eq!(terms.optimistic_views(), ViewDelta::new(requested));
535 }
536 }
537
538 #[test]
539 fn round_robin_rotates_through_participants() {
540 let mut rng = test_rng();
541 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 4);
542 let participants = Set::try_from_iter(participants).unwrap();
543 let n = participants.len() as u32;
544 let elector: RoundRobinElector<ed25519::Scheme> =
545 RoundRobin::<Sha256>::default().build(&participants);
546 let epoch = Epoch::new(0);
547
548 let mut leaders = Vec::new();
550 for view in 1..=(3 * n as u64) {
551 let round = Round::new(epoch, View::new(view));
552 leaders.push(elector.elect(round, None));
553 }
554
555 for i in 0..leaders.len() - 1 {
557 assert_eq!(Participant::new((leaders[i].get() + 1) % n), leaders[i + 1]);
558 }
559 }
560
561 #[test]
562 fn round_robin_cycles_through_epochs() {
563 let mut rng = test_rng();
564 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 5);
565 let participants = Set::try_from_iter(participants).unwrap();
566 let n = participants.len();
567 let elector: RoundRobinElector<ed25519::Scheme> =
568 RoundRobin::<Sha256>::default().build(&participants);
569
570 let leaders: Vec<_> = (0..n as u64)
572 .map(|e| {
573 let round = Round::new(Epoch::new(e), View::new(1));
574 elector.elect(round, None)
575 })
576 .collect();
577
578 let mut seen = vec![false; n];
580 for leader in &leaders {
581 assert!(!seen[usize::from(*leader)]);
582 seen[usize::from(*leader)] = true;
583 }
584 assert!(seen.iter().all(|x| *x));
585 }
586
587 #[test]
588 fn round_robin_handles_wrapping_epoch_plus_term_index() {
589 let mut rng = test_rng();
590 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 5);
591 let participants = Set::try_from_iter(participants).unwrap();
592 let elector: RoundRobinElector<ed25519::Scheme> = RoundRobin::<Sha256>::default()
593 .with_term(
594 TermLength::new(NZU32!(5)),
595 Duration::from_secs(10),
596 ViewDelta::new(0),
597 )
598 .build(&participants);
599
600 let round = Round::new(Epoch::new(u64::MAX - 1), View::new(6));
601 let term_idx = round.view().term_index(TermLength::new(NZU32!(5)));
602 let expected = round.epoch().get().wrapping_add(term_idx) % 5;
603
604 assert_eq!(
605 elector.elect(round, None),
606 Participant::new(expected as u32)
607 );
608 }
609
610 #[test]
611 fn round_robin_uses_stable_leaders_within_terms() {
612 let mut rng = test_rng();
613 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 4);
614 let participants = Set::try_from_iter(participants).unwrap();
615 let elector: RoundRobinElector<ed25519::Scheme> = RoundRobin::<Sha256>::default()
616 .with_term(
617 TermLength::new(NZU32!(3)),
618 Duration::from_secs(10),
619 ViewDelta::new(0),
620 )
621 .build(&participants);
622 let epoch = Epoch::new(0);
623
624 let leader_v1 = elector.elect(Round::new(epoch, View::new(1)), None);
625 let leader_v2 = elector.elect(Round::new(epoch, View::new(2)), None);
626 let leader_v3 = elector.elect(Round::new(epoch, View::new(3)), None);
627 let leader_v4 = elector.elect(Round::new(epoch, View::new(4)), None);
628 let leader_v5 = elector.elect(Round::new(epoch, View::new(5)), None);
629 let leader_v6 = elector.elect(Round::new(epoch, View::new(6)), None);
630
631 assert_eq!(leader_v1, leader_v2);
632 assert_eq!(leader_v1, leader_v3);
633 assert_eq!(leader_v4, leader_v5);
634 assert_eq!(leader_v4, leader_v6);
635 assert_ne!(leader_v1, leader_v4);
636 }
637
638 #[test]
639 fn round_robin_epoch_transition_shifts_stable_term_leader() {
640 let mut rng = test_rng();
641 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 4);
642 let participants = Set::try_from_iter(participants).unwrap();
643 let elector: RoundRobinElector<ed25519::Scheme> = RoundRobin::<Sha256>::default()
644 .with_term(
645 TermLength::new(NZU32!(3)),
646 Duration::from_secs(10),
647 ViewDelta::new(0),
648 )
649 .build(&participants);
650
651 let leader_epoch_0 = elector.elect(Round::new(Epoch::new(0), View::new(1)), None);
652 let leader_epoch_0_v2 = elector.elect(Round::new(Epoch::new(0), View::new(2)), None);
653 let leader_epoch_1 = elector.elect(Round::new(Epoch::new(1), View::new(1)), None);
654 let leader_epoch_1_v3 = elector.elect(Round::new(Epoch::new(1), View::new(3)), None);
655 let leader_epoch_2 = elector.elect(Round::new(Epoch::new(2), View::new(1)), None);
656 let leader_epoch_2_v2 = elector.elect(Round::new(Epoch::new(2), View::new(2)), None);
657
658 assert_eq!(leader_epoch_0, Participant::new(1));
659 assert_eq!(leader_epoch_0_v2, leader_epoch_0);
660 assert_eq!(leader_epoch_1, Participant::new(2));
661 assert_eq!(leader_epoch_1_v3, leader_epoch_1);
662 assert_eq!(leader_epoch_2, Participant::new(3));
663 assert_eq!(leader_epoch_2_v2, leader_epoch_2);
664 }
665
666 #[test]
667 fn round_robin_shuffled_changes_order() {
668 let mut rng = test_rng();
669 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 5);
670 let participants = Set::try_from_iter(participants).unwrap();
671
672 let elector_no_seed: RoundRobinElector<ed25519::Scheme> =
673 RoundRobin::<Sha256>::default().build(&participants);
674 let elector_seed_1: RoundRobinElector<ed25519::Scheme> =
675 RoundRobin::<Sha256>::shuffled(b"seed1").build(&participants);
676 let elector_seed_2: RoundRobinElector<ed25519::Scheme> =
677 RoundRobin::<Sha256>::shuffled(b"seed2").build(&participants);
678
679 let epoch = Epoch::new(0);
681 let leaders_no_seed: Vec<_> = (1..=5)
682 .map(|v| elector_no_seed.elect(Round::new(epoch, View::new(v)), None))
683 .collect();
684 let leaders_seed_1: Vec<_> = (1..=5)
685 .map(|v| elector_seed_1.elect(Round::new(epoch, View::new(v)), None))
686 .collect();
687 let leaders_seed_2: Vec<_> = (1..=5)
688 .map(|v| elector_seed_2.elect(Round::new(epoch, View::new(v)), None))
689 .collect();
690
691 assert_eq!(
693 leaders_no_seed,
694 vec![
695 Participant::new(1),
696 Participant::new(2),
697 Participant::new(3),
698 Participant::new(4),
699 Participant::new(0)
700 ]
701 );
702
703 assert_ne!(leaders_seed_1, leaders_no_seed);
705 assert_ne!(leaders_seed_2, leaders_no_seed);
706 assert_ne!(leaders_seed_1, leaders_seed_2);
707
708 for leaders in [&leaders_seed_1, &leaders_seed_2] {
710 let mut sorted = leaders.clone();
711 sorted.sort();
712 assert_eq!(
713 sorted,
714 vec![
715 Participant::new(0),
716 Participant::new(1),
717 Participant::new(2),
718 Participant::new(3),
719 Participant::new(4)
720 ]
721 );
722 }
723 }
724
725 #[test]
726 fn round_robin_same_seed_is_deterministic() {
727 let mut rng = test_rng();
728 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 5);
729 let participants = Set::try_from_iter(participants).unwrap();
730
731 let elector1: RoundRobinElector<ed25519::Scheme> =
732 RoundRobin::<Sha256>::shuffled(b"same_seed").build(&participants);
733 let elector2: RoundRobinElector<ed25519::Scheme> =
734 RoundRobin::<Sha256>::shuffled(b"same_seed").build(&participants);
735
736 let epoch = Epoch::new(0);
737 for view in 1..=10 {
738 let round = Round::new(epoch, View::new(view));
739 assert_eq!(elector1.elect(round, None), elector2.elect(round, None));
740 }
741 }
742
743 #[test]
744 #[should_panic(expected = "no participants")]
745 fn round_robin_build_panics_on_empty_participants() {
746 let participants: Set<commonware_cryptography::ed25519::PublicKey> = Set::default();
747 let _: RoundRobinElector<ed25519::Scheme> =
748 RoundRobin::<Sha256>::default().build(&participants);
749 }
750
751 #[test]
752 fn random_falls_back_to_round_robin_for_view_1() {
753 let mut rng = test_rng();
754 let Fixture { participants, .. } =
755 bls12381_threshold_vrf::fixture::<MinPk, _>(&mut rng, NAMESPACE, 5);
756 let participants = Set::try_from_iter(participants).unwrap();
757 let n = participants.len();
758 let elector: RandomElector<ThresholdScheme> =
759 Random::new(RandomVersion::V1).build(&participants);
760
761 let leaders: Vec<_> = (0..n as u64)
763 .map(|e| {
764 let round = Round::new(Epoch::new(e), View::new(1));
765 elector.elect(round, None)
766 })
767 .collect();
768
769 let mut seen = vec![false; n];
771 for leader in &leaders {
772 assert!(!seen[usize::from(*leader)]);
773 seen[usize::from(*leader)] = true;
774 }
775 assert!(seen.iter().all(|x| *x));
776 }
777
778 #[test]
779 fn random_fallback_does_not_truncate_before_modulo() {
780 let mut rng = test_rng();
783 let Fixture { participants, .. } =
784 bls12381_threshold_vrf::fixture::<MinPk, _>(&mut rng, NAMESPACE, 5);
785 let participants = Set::try_from_iter(participants).unwrap();
786 let random: RandomElector<ThresholdScheme> =
787 Random::new(RandomVersion::V1).build(&participants);
788 let round_robin: RoundRobinElector<ThresholdScheme> =
789 RoundRobin::<Sha256>::default().build(&participants);
790
791 let round = Round::new(Epoch::new(u64::from(u32::MAX)), View::new(1));
793
794 assert_eq!(round_robin.elect(round, None), Participant::new(1));
796 assert_eq!(random.elect(round, None), Participant::new(1));
797 }
798
799 #[test]
800 fn random_uses_certificate_randomness() {
801 let mut rng = test_rng();
802 let Fixture {
803 participants,
804 schemes,
805 ..
806 } = bls12381_threshold_vrf::fixture::<MinPk, _>(&mut rng, NAMESPACE, 5);
807 let participants = Set::try_from_iter(participants).unwrap();
808 let elector: RandomElector<ThresholdScheme> =
809 Random::new(RandomVersion::V1).build(&participants);
810 let quorum = N3f1::quorum(schemes.len()) as usize;
811
812 let round1 = Round::new(Epoch::new(1), View::new(2));
814 let attestations1: Vec<_> = schemes
815 .iter()
816 .take(quorum)
817 .map(|s| {
818 s.sign::<Sha256Digest>(Subject::Nullify { round: round1 })
819 .unwrap()
820 })
821 .collect();
822 let cert1 = schemes[0]
823 .assemble(non_empty![@attestations1], &Sequential)
824 .unwrap();
825
826 let round2 = Round::new(Epoch::new(1), View::new(3));
828 let attestations2: Vec<_> = schemes
829 .iter()
830 .take(quorum)
831 .map(|s| {
832 s.sign::<Sha256Digest>(Subject::Nullify { round: round2 })
833 .unwrap()
834 })
835 .collect();
836 let cert2 = schemes[0]
837 .assemble(non_empty![@attestations2], &Sequential)
838 .unwrap();
839
840 let leader1a = elector.elect(round1, Some(&cert1));
842 let leader1b = elector.elect(round1, Some(&cert1));
843 assert_eq!(leader1a, leader1b);
844
845 let leader2 = elector.elect(round1, Some(&cert2));
851 assert_ne!(leader1a, leader2);
852 }
853
854 #[test]
855 #[should_panic(expected = "no participants")]
856 fn random_build_panics_on_empty_participants() {
857 let participants: Set<commonware_cryptography::ed25519::PublicKey> = Set::default();
858 let _: RandomElector<ThresholdScheme> = Random::new(RandomVersion::V1).build(&participants);
859 }
860
861 #[test]
862 #[should_panic]
863 fn random_panics_on_none_certificate_after_view_1() {
864 let mut rng = test_rng();
865 let Fixture { participants, .. } =
866 bls12381_threshold_vrf::fixture::<MinPk, _>(&mut rng, NAMESPACE, 5);
867 let participants = Set::try_from_iter(participants).unwrap();
868 let elector: RandomElector<ThresholdScheme> =
869 Random::new(RandomVersion::V1).build(&participants);
870
871 let round = Round::new(Epoch::new(1), View::new(2));
873 elector.elect(round, None);
874 }
875
876 mod conformance {
877 use super::*;
878 use commonware_codec::{Encode, Write};
879 use commonware_conformance::Conformance;
880 use commonware_cryptography::Sha256;
881 use rand::{RngExt as _, SeedableRng};
882 use rand_chacha::ChaCha8Rng;
883
884 struct RoundRobinShuffleConformance;
890
891 impl Conformance for RoundRobinShuffleConformance {
892 async fn commit(seed: u64) -> Vec<u8> {
893 let mut rng = ChaCha8Rng::seed_from_u64(seed);
894
895 let n = rng.random_range(1..=100);
897 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, n);
898 let participants = Set::try_from_iter(participants).unwrap();
899
900 let shuffle_seed: [u8; 32] = rng.random();
902
903 let elector: RoundRobinElector<ed25519::Scheme> =
905 RoundRobin::<Sha256>::shuffled(&shuffle_seed).build(&participants);
906
907 elector.permutation.encode().to_vec()
909 }
910 }
911
912 struct RandomV0SelectLeaderConformance;
917
918 struct RandomV1SelectLeaderConformance;
923
924 fn random_select_leader_commit(seed: u64, version: Random) -> Vec<u8> {
925 let mut rng = ChaCha8Rng::seed_from_u64(seed);
926
927 let n = rng.random_range(4..=10);
929 let Fixture {
930 participants,
931 schemes,
932 ..
933 } = bls12381_threshold_vrf::fixture::<MinPk, _>(&mut rng, NAMESPACE, n);
934 let participants = Set::try_from_iter(participants).unwrap();
935 let elector: RandomElector<ThresholdScheme> = version.build(&participants);
936 let quorum =
937 usize::try_from(N3f1::quorum(schemes.len())).expect("quorum exceeds usize::MAX");
938
939 let epoch = rng.random_range(0..1000);
941 let view = rng.random_range(2..=101);
942 let round = Round::new(Epoch::new(epoch), View::new(view));
943
944 let attestations: Vec<_> = schemes
946 .iter()
947 .take(quorum)
948 .map(|s| s.sign::<Sha256Digest>(Subject::Nullify { round }).unwrap())
949 .collect();
950 let cert = schemes[0]
951 .assemble(non_empty![@attestations], &Sequential)
952 .unwrap();
953
954 let leader = elector.elect(round, Some(&cert));
956
957 let round_v1 = Round::new(Epoch::new(epoch), View::new(1));
959 let leader_v1 = elector.elect(round_v1, None);
960
961 let mut result = leader.encode_mut();
963 leader_v1.write(&mut result);
964 result.to_vec()
965 }
966
967 #[allow(deprecated)]
968 impl Conformance for RandomV0SelectLeaderConformance {
969 async fn commit(seed: u64) -> Vec<u8> {
970 random_select_leader_commit(seed, Random::new(RandomVersion::V0))
971 }
972 }
973
974 impl Conformance for RandomV1SelectLeaderConformance {
975 async fn commit(seed: u64) -> Vec<u8> {
976 random_select_leader_commit(seed, Random::new(RandomVersion::V1))
977 }
978 }
979
980 commonware_conformance::conformance_tests! {
981 RoundRobinShuffleConformance => 512,
982 RandomV0SelectLeaderConformance => 512,
983 RandomV1SelectLeaderConformance => 512,
984 }
985 }
986}