1use crate::{
38 simplex::scheme::bls12381_threshold::vrf as bls12381_threshold_vrf,
39 types::{Participant, Round, TermLength, View, ViewDelta},
40};
41use commonware_codec::Encode;
42use commonware_cryptography::{
43 Hasher, PublicKey, Sha256,
44 bls12381::primitives::variant::Variant,
45 certificate::{Scheme, Verifier},
46};
47use commonware_utils::{Widen, modulo, ordered::Set};
48use std::{fmt, marker::PhantomData, time::Duration};
49
50pub trait Config<S: Scheme>: Clone + Send + 'static {
64 type Elector: Elector<S>;
66
67 fn build(self, participants: &Set<S::PublicKey>) -> Self::Elector;
75}
76
77#[derive(Clone, Copy, Debug, PartialEq, Eq)]
79pub struct Terms {
80 length: TermLength,
82 stall_timeout: Option<Duration>,
84 optimistic_views: ViewDelta,
86}
87
88impl Terms {
89 pub const fn rotating() -> Self {
92 Self {
93 length: TermLength::ONE,
94 stall_timeout: None,
95 optimistic_views: ViewDelta::zero(),
96 }
97 }
98
99 pub const fn stable(
135 length: TermLength,
136 stall_timeout: Duration,
137 optimistic_views: ViewDelta,
138 ) -> Self {
139 assert!(
140 length.get() > 1,
141 "stable leaders require a term length greater than 1"
142 );
143 assert!(
144 !stall_timeout.is_zero(),
145 "stable leaders require a stall timeout greater than zero"
146 );
147 Self {
148 length,
149 stall_timeout: Some(stall_timeout),
150 optimistic_views,
151 }
152 }
153
154 pub const fn length(&self) -> TermLength {
164 self.length
165 }
166
167 pub const fn stall_timeout(&self) -> Option<Duration> {
171 self.stall_timeout
172 }
173
174 pub const fn optimistic_views(&self) -> ViewDelta {
178 self.optimistic_views
179 }
180}
181
182impl Default for Terms {
183 fn default() -> Self {
184 Self::rotating()
185 }
186}
187
188mod private {
189 pub trait Sealed {}
190}
191
192pub trait Mode: private::Sealed {
197 type Input<'a, C: 'a>;
199
200 fn input<C>(certificate: Option<&C>) -> Self::Input<'_, C>;
202
203 fn early<'a, C: 'a>() -> Option<Self::Input<'a, C>>;
206}
207
208pub struct Scheduled;
214
215pub struct Dynamic;
220
221impl private::Sealed for Scheduled {}
222impl private::Sealed for Dynamic {}
223
224impl Mode for Scheduled {
225 type Input<'a, C: 'a> = ();
226
227 fn input<C>(_: Option<&C>) {}
228
229 fn early<'a, C: 'a>() -> Option<()> {
230 Some(())
231 }
232}
233
234impl Mode for Dynamic {
235 type Input<'a, C: 'a> = Option<&'a C>;
236
237 fn input<C>(certificate: Option<&C>) -> Option<&C> {
238 certificate
239 }
240
241 fn early<'a, C: 'a>() -> Option<Option<&'a C>> {
242 None
243 }
244}
245
246pub type Input<'a, S, E> =
248 <<E as Elector<S>>::Mode as Mode>::Input<'a, <S as Verifier>::Certificate>;
249
250pub trait Elector<S: Scheme>: Clone + Send + 'static {
281 type Mode: Mode;
284
285 fn terms(&self) -> Terms;
290
291 fn elect(&self, round: Round, input: Input<'_, S, Self>) -> Participant;
308}
309
310#[derive(Debug, Default)]
318pub struct RoundRobin<H: Hasher = Sha256> {
319 seed: Option<Vec<u8>>,
320 terms: Terms,
321 _phantom: PhantomData<H>,
322}
323
324impl<H: Hasher> Clone for RoundRobin<H> {
325 fn clone(&self) -> Self {
326 Self {
327 seed: self.seed.clone(),
328 terms: self.terms,
329 _phantom: PhantomData,
330 }
331 }
332}
333
334impl<H: Hasher> RoundRobin<H> {
335 pub fn shuffled(seed: &[u8]) -> Self {
340 Self {
341 seed: Some(seed.to_vec()),
342 terms: Terms::rotating(),
343 _phantom: PhantomData,
344 }
345 }
346
347 pub const fn with_term(
361 mut self,
362 term_length: TermLength,
363 stall_timeout: Duration,
364 optimistic_views: ViewDelta,
365 ) -> Self {
366 self.terms = Terms::stable(term_length, stall_timeout, optimistic_views);
367 self
368 }
369}
370
371impl<S: Scheme, H: Hasher> Config<S> for RoundRobin<H> {
372 type Elector = RoundRobinElector<S>;
373
374 fn build(self, participants: &Set<S::PublicKey>) -> RoundRobinElector<S> {
375 assert!(!participants.is_empty(), "no participants");
376
377 let mut permutation: Vec<Participant> = (0..participants.len())
378 .map(Participant::from_usize)
379 .collect();
380
381 if let Some(seed) = &self.seed {
382 permutation.sort_by_cached_key(|&index| H::hash(&[seed, &index.get().encode()]));
383 }
384
385 RoundRobinElector {
386 permutation,
387 terms: self.terms,
388 _phantom: PhantomData,
389 }
390 }
391}
392
393#[derive(Clone, Debug)]
397pub struct RoundRobinElector<S: Scheme> {
398 permutation: Vec<Participant>,
399 terms: Terms,
400 _phantom: PhantomData<S>,
401}
402
403impl<S: Scheme> Elector<S> for RoundRobinElector<S> {
404 type Mode = Scheduled;
405
406 fn terms(&self) -> Terms {
407 self.terms
408 }
409
410 fn elect(&self, round: Round, _: ()) -> Participant {
411 let term_idx = round.view().term_index(self.terms.length());
413
414 let n: u64 = Widen::widen(self.permutation.len());
416 let idx = round.epoch().get().wrapping_add(term_idx) % n;
417 let idx = usize::try_from(idx).expect("leader index fits in usize");
418 self.permutation[idx]
419 }
420}
421
422#[derive(Clone, Copy, Debug, PartialEq, Eq)]
424pub enum RandomVersion {
425 #[deprecated(
427 note = "mapping encoded threshold signature directly to participants can bias selection"
428 )]
429 V0,
430 V1,
434}
435
436pub struct Random<H: Hasher = Sha256> {
448 version: RandomVersion,
449 _hasher: PhantomData<H>,
450}
451
452impl<H: Hasher> Random<H> {
453 pub const fn new(version: RandomVersion) -> Self {
455 Self {
456 version,
457 _hasher: PhantomData,
458 }
459 }
460
461 #[allow(deprecated)]
467 pub fn select_leader<V: Variant>(
468 &self,
469 round: Round,
470 n: u32,
471 seed_signature: Option<V::Signature>,
472 ) -> Participant {
473 assert_ne!(n, 0, "no participants");
474 assert!(seed_signature.is_some() || round.view() == View::new(1));
475
476 let Some(seed_signature) = seed_signature else {
477 let idx = round.epoch().get().wrapping_add(round.view().get()) % u64::from(n);
479 return Participant::new(u32::try_from(idx).expect("leader index fits in u32"));
480 };
481
482 let encoded = seed_signature.encode();
484 let index = match self.version {
485 RandomVersion::V0 => modulo(encoded.as_ref(), u64::from(n)),
486 RandomVersion::V1 => modulo(H::hash(&[encoded.as_ref()]).as_ref(), u64::from(n)),
487 };
488 Participant::new(u32::try_from(index).expect("leader index must fit in u32"))
489 }
490}
491
492impl<H: Hasher> Clone for Random<H> {
493 fn clone(&self) -> Self {
494 Self::new(self.version)
495 }
496}
497
498impl<H: Hasher> fmt::Debug for Random<H> {
499 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 self.version.fmt(f)
501 }
502}
503
504impl<P, V, H> Config<bls12381_threshold_vrf::Scheme<P, V>> for Random<H>
505where
506 P: PublicKey,
507 V: Variant,
508 H: Hasher,
509{
510 type Elector = RandomElector<bls12381_threshold_vrf::Scheme<P, V>, H>;
511
512 fn build(
513 self,
514 participants: &Set<P>,
515 ) -> RandomElector<bls12381_threshold_vrf::Scheme<P, V>, H> {
516 assert!(!participants.is_empty(), "no participants");
517 RandomElector {
518 n: participants.len() as u32,
519 version: self,
520 _phantom: PhantomData,
521 }
522 }
523}
524
525pub struct RandomElector<S: Scheme, H: Hasher = Sha256> {
529 n: u32,
530 version: Random<H>,
531 _phantom: PhantomData<S>,
532}
533
534impl<S: Scheme, H: Hasher> Clone for RandomElector<S, H> {
535 fn clone(&self) -> Self {
536 Self {
537 n: self.n,
538 version: self.version.clone(),
539 _phantom: PhantomData,
540 }
541 }
542}
543
544impl<S: Scheme, H: Hasher> fmt::Debug for RandomElector<S, H> {
545 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
546 f.debug_struct("RandomElector")
547 .field("n", &self.n)
548 .field("version", &self.version)
549 .finish()
550 }
551}
552
553impl<P, V, H> Elector<bls12381_threshold_vrf::Scheme<P, V>>
554 for RandomElector<bls12381_threshold_vrf::Scheme<P, V>, H>
555where
556 P: PublicKey,
557 V: Variant,
558 H: Hasher,
559{
560 type Mode = Dynamic;
561
562 fn terms(&self) -> Terms {
563 Terms::rotating()
564 }
565
566 fn elect(
574 &self,
575 round: Round,
576 certificate: Option<&bls12381_threshold_vrf::Certificate<V>>,
577 ) -> Participant {
578 self.version.select_leader::<V>(
579 round,
580 self.n,
581 certificate.map(|c| {
582 c.get()
583 .expect("verified certificate must decode")
584 .seed_signature
585 }),
586 )
587 }
588}
589
590#[cfg(test)]
591mod tests {
592 use super::*;
593 use crate::{
594 simplex::{
595 scheme::{bls12381_threshold::vrf as bls12381_threshold_vrf, ed25519},
596 types::Subject,
597 },
598 types::{Epoch, View},
599 };
600 use commonware_cryptography::{
601 Sha256, bls12381::primitives::variant::MinPk, certificate::mocks::Fixture,
602 sha256::Digest as Sha256Digest,
603 };
604 use commonware_parallel::Sequential;
605 use commonware_utils::{Faults, N3f1, NZU32, TryFromIterator, Widen, non_empty, test_rng};
606
607 const NAMESPACE: &[u8] = b"test";
608
609 type ThresholdScheme =
610 bls12381_threshold_vrf::Scheme<commonware_cryptography::ed25519::PublicKey, MinPk>;
611
612 #[test]
613 fn stable_terms_preserve_optimistic_views() {
614 let stall = Duration::from_secs(1);
615 let length = TermLength::new(NZU32!(5));
616
617 for requested in [0, 3, 4, 5, 6, u64::MAX] {
621 let terms = Terms::stable(length, stall, ViewDelta::new(requested));
622 assert_eq!(terms.optimistic_views(), ViewDelta::new(requested));
623 }
624 }
625
626 #[test]
627 fn round_robin_rotates_through_participants() {
628 let mut rng = test_rng();
629 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 4);
630 let participants = Set::try_from_iter(participants).unwrap();
631 let n = participants.len() as u32;
632 let elector: RoundRobinElector<ed25519::Scheme> =
633 RoundRobin::<Sha256>::default().build(&participants);
634 let epoch = Epoch::new(0);
635
636 let mut leaders = Vec::new();
638 for view in 1..=(3 * u64::from(n)) {
639 let round = Round::new(epoch, View::new(view));
640 leaders.push(elector.elect(round, ()));
641 }
642
643 for i in 0..leaders.len() - 1 {
645 assert_eq!(Participant::new((leaders[i].get() + 1) % n), leaders[i + 1]);
646 }
647 }
648
649 #[test]
650 fn round_robin_cycles_through_epochs() {
651 let mut rng = test_rng();
652 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 5);
653 let participants = Set::try_from_iter(participants).unwrap();
654 let n = participants.len();
655 let elector: RoundRobinElector<ed25519::Scheme> =
656 RoundRobin::<Sha256>::default().build(&participants);
657
658 let leaders: Vec<_> = (0..Widen::widen(n))
660 .map(|e| {
661 let round = Round::new(Epoch::new(e), View::new(1));
662 elector.elect(round, ())
663 })
664 .collect();
665
666 let mut seen = vec![false; n];
668 for leader in &leaders {
669 assert!(!seen[usize::from(*leader)]);
670 seen[usize::from(*leader)] = true;
671 }
672 assert!(seen.iter().all(|x| *x));
673 }
674
675 #[test]
676 fn round_robin_handles_wrapping_epoch_plus_term_index() {
677 let mut rng = test_rng();
678 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 5);
679 let participants = Set::try_from_iter(participants).unwrap();
680 let elector: RoundRobinElector<ed25519::Scheme> = RoundRobin::<Sha256>::default()
681 .with_term(
682 TermLength::new(NZU32!(5)),
683 Duration::from_secs(10),
684 ViewDelta::new(0),
685 )
686 .build(&participants);
687
688 let round = Round::new(Epoch::new(u64::MAX - 1), View::new(6));
689 let term_idx = round.view().term_index(TermLength::new(NZU32!(5)));
690 let expected = round.epoch().get().wrapping_add(term_idx) % 5;
691
692 assert_eq!(elector.elect(round, ()), Participant::new(expected as u32));
693 }
694
695 #[test]
696 fn round_robin_uses_stable_leaders_within_terms() {
697 let mut rng = test_rng();
698 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 4);
699 let participants = Set::try_from_iter(participants).unwrap();
700 let elector: RoundRobinElector<ed25519::Scheme> = RoundRobin::<Sha256>::default()
701 .with_term(
702 TermLength::new(NZU32!(3)),
703 Duration::from_secs(10),
704 ViewDelta::new(0),
705 )
706 .build(&participants);
707 let epoch = Epoch::new(0);
708
709 let leader_v1 = elector.elect(Round::new(epoch, View::new(1)), ());
710 let leader_v2 = elector.elect(Round::new(epoch, View::new(2)), ());
711 let leader_v3 = elector.elect(Round::new(epoch, View::new(3)), ());
712 let leader_v4 = elector.elect(Round::new(epoch, View::new(4)), ());
713 let leader_v5 = elector.elect(Round::new(epoch, View::new(5)), ());
714 let leader_v6 = elector.elect(Round::new(epoch, View::new(6)), ());
715
716 assert_eq!(leader_v1, leader_v2);
717 assert_eq!(leader_v1, leader_v3);
718 assert_eq!(leader_v4, leader_v5);
719 assert_eq!(leader_v4, leader_v6);
720 assert_ne!(leader_v1, leader_v4);
721 }
722
723 #[test]
724 fn round_robin_epoch_transition_shifts_stable_term_leader() {
725 let mut rng = test_rng();
726 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 4);
727 let participants = Set::try_from_iter(participants).unwrap();
728 let elector: RoundRobinElector<ed25519::Scheme> = RoundRobin::<Sha256>::default()
729 .with_term(
730 TermLength::new(NZU32!(3)),
731 Duration::from_secs(10),
732 ViewDelta::new(0),
733 )
734 .build(&participants);
735
736 let leader_epoch_0 = elector.elect(Round::new(Epoch::new(0), View::new(1)), ());
737 let leader_epoch_0_v2 = elector.elect(Round::new(Epoch::new(0), View::new(2)), ());
738 let leader_epoch_1 = elector.elect(Round::new(Epoch::new(1), View::new(1)), ());
739 let leader_epoch_1_v3 = elector.elect(Round::new(Epoch::new(1), View::new(3)), ());
740 let leader_epoch_2 = elector.elect(Round::new(Epoch::new(2), View::new(1)), ());
741 let leader_epoch_2_v2 = elector.elect(Round::new(Epoch::new(2), View::new(2)), ());
742
743 assert_eq!(leader_epoch_0, Participant::new(1));
744 assert_eq!(leader_epoch_0_v2, leader_epoch_0);
745 assert_eq!(leader_epoch_1, Participant::new(2));
746 assert_eq!(leader_epoch_1_v3, leader_epoch_1);
747 assert_eq!(leader_epoch_2, Participant::new(3));
748 assert_eq!(leader_epoch_2_v2, leader_epoch_2);
749 }
750
751 #[test]
752 fn round_robin_shuffled_changes_order() {
753 let mut rng = test_rng();
754 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 5);
755 let participants = Set::try_from_iter(participants).unwrap();
756
757 let elector_no_seed: RoundRobinElector<ed25519::Scheme> =
758 RoundRobin::<Sha256>::default().build(&participants);
759 let elector_seed_1: RoundRobinElector<ed25519::Scheme> =
760 RoundRobin::<Sha256>::shuffled(b"seed1").build(&participants);
761 let elector_seed_2: RoundRobinElector<ed25519::Scheme> =
762 RoundRobin::<Sha256>::shuffled(b"seed2").build(&participants);
763
764 let epoch = Epoch::new(0);
766 let leaders_no_seed: Vec<_> = (1..=5)
767 .map(|v| elector_no_seed.elect(Round::new(epoch, View::new(v)), ()))
768 .collect();
769 let leaders_seed_1: Vec<_> = (1..=5)
770 .map(|v| elector_seed_1.elect(Round::new(epoch, View::new(v)), ()))
771 .collect();
772 let leaders_seed_2: Vec<_> = (1..=5)
773 .map(|v| elector_seed_2.elect(Round::new(epoch, View::new(v)), ()))
774 .collect();
775
776 assert_eq!(
778 leaders_no_seed,
779 vec![
780 Participant::new(1),
781 Participant::new(2),
782 Participant::new(3),
783 Participant::new(4),
784 Participant::new(0)
785 ]
786 );
787
788 assert_ne!(leaders_seed_1, leaders_no_seed);
790 assert_ne!(leaders_seed_2, leaders_no_seed);
791 assert_ne!(leaders_seed_1, leaders_seed_2);
792
793 for leaders in [&leaders_seed_1, &leaders_seed_2] {
795 let mut sorted = leaders.clone();
796 sorted.sort();
797 assert_eq!(
798 sorted,
799 vec![
800 Participant::new(0),
801 Participant::new(1),
802 Participant::new(2),
803 Participant::new(3),
804 Participant::new(4)
805 ]
806 );
807 }
808 }
809
810 #[test]
811 fn round_robin_same_seed_is_deterministic() {
812 let mut rng = test_rng();
813 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 5);
814 let participants = Set::try_from_iter(participants).unwrap();
815
816 let elector1: RoundRobinElector<ed25519::Scheme> =
817 RoundRobin::<Sha256>::shuffled(b"same_seed").build(&participants);
818 let elector2: RoundRobinElector<ed25519::Scheme> =
819 RoundRobin::<Sha256>::shuffled(b"same_seed").build(&participants);
820
821 let epoch = Epoch::new(0);
822 for view in 1..=10 {
823 let round = Round::new(epoch, View::new(view));
824 assert_eq!(elector1.elect(round, ()), elector2.elect(round, ()));
825 }
826 }
827
828 #[test]
829 #[should_panic(expected = "no participants")]
830 fn round_robin_build_panics_on_empty_participants() {
831 let participants: Set<commonware_cryptography::ed25519::PublicKey> = Set::default();
832 let _: RoundRobinElector<ed25519::Scheme> =
833 RoundRobin::<Sha256>::default().build(&participants);
834 }
835
836 #[test]
837 fn random_falls_back_to_round_robin_for_view_1() {
838 let mut rng = test_rng();
839 let Fixture { participants, .. } =
840 bls12381_threshold_vrf::fixture::<MinPk, _>(&mut rng, NAMESPACE, 5);
841 let participants = Set::try_from_iter(participants).unwrap();
842 let n = participants.len();
843 let elector: RandomElector<ThresholdScheme> =
844 Random::new(RandomVersion::V1).build(&participants);
845
846 let leaders: Vec<_> = (0..n as u64)
848 .map(|e| {
849 let round = Round::new(Epoch::new(e), View::new(1));
850 elector.elect(round, None)
851 })
852 .collect();
853
854 let mut seen = vec![false; n];
856 for leader in &leaders {
857 assert!(!seen[usize::from(*leader)]);
858 seen[usize::from(*leader)] = true;
859 }
860 assert!(seen.iter().all(|x| *x));
861 }
862
863 #[test]
864 fn random_fallback_does_not_truncate_before_modulo() {
865 let mut rng = test_rng();
868 let Fixture { participants, .. } =
869 bls12381_threshold_vrf::fixture::<MinPk, _>(&mut rng, NAMESPACE, 5);
870 let participants = Set::try_from_iter(participants).unwrap();
871 let random: RandomElector<ThresholdScheme> =
872 Random::new(RandomVersion::V1).build(&participants);
873 let round_robin: RoundRobinElector<ThresholdScheme> =
874 RoundRobin::<Sha256>::default().build(&participants);
875
876 let round = Round::new(Epoch::new(u64::from(u32::MAX)), View::new(1));
878
879 assert_eq!(round_robin.elect(round, ()), Participant::new(1));
881 assert_eq!(random.elect(round, None), Participant::new(1));
882 }
883
884 #[test]
885 fn random_uses_certificate_randomness() {
886 let mut rng = test_rng();
887 let Fixture {
888 participants,
889 schemes,
890 ..
891 } = bls12381_threshold_vrf::fixture::<MinPk, _>(&mut rng, NAMESPACE, 5);
892 let participants = Set::try_from_iter(participants).unwrap();
893 let elector: RandomElector<ThresholdScheme> =
894 Random::new(RandomVersion::V1).build(&participants);
895 let quorum = N3f1::quorum(schemes.len()) as usize;
896
897 let round1 = Round::new(Epoch::new(1), View::new(2));
899 let attestations1: Vec<_> = schemes
900 .iter()
901 .take(quorum)
902 .map(|s| {
903 s.sign::<Sha256Digest>(Subject::Nullify { round: round1 })
904 .unwrap()
905 })
906 .collect();
907 let cert1 = schemes[0]
908 .assemble(non_empty![@attestations1], &Sequential)
909 .unwrap();
910
911 let round2 = Round::new(Epoch::new(1), View::new(3));
913 let attestations2: Vec<_> = schemes
914 .iter()
915 .take(quorum)
916 .map(|s| {
917 s.sign::<Sha256Digest>(Subject::Nullify { round: round2 })
918 .unwrap()
919 })
920 .collect();
921 let cert2 = schemes[0]
922 .assemble(non_empty![@attestations2], &Sequential)
923 .unwrap();
924
925 let leader1a = elector.elect(round1, Some(&cert1));
927 let leader1b = elector.elect(round1, Some(&cert1));
928 assert_eq!(leader1a, leader1b);
929
930 let leader2 = elector.elect(round1, Some(&cert2));
936 assert_ne!(leader1a, leader2);
937 }
938
939 #[test]
940 #[should_panic(expected = "no participants")]
941 fn random_build_panics_on_empty_participants() {
942 let participants: Set<commonware_cryptography::ed25519::PublicKey> = Set::default();
943 let _: RandomElector<ThresholdScheme> = Random::new(RandomVersion::V1).build(&participants);
944 }
945
946 #[test]
947 #[should_panic]
948 fn random_panics_on_none_certificate_after_view_1() {
949 let mut rng = test_rng();
950 let Fixture { participants, .. } =
951 bls12381_threshold_vrf::fixture::<MinPk, _>(&mut rng, NAMESPACE, 5);
952 let participants = Set::try_from_iter(participants).unwrap();
953 let elector: RandomElector<ThresholdScheme> =
954 Random::new(RandomVersion::V1).build(&participants);
955
956 let round = Round::new(Epoch::new(1), View::new(2));
958 elector.elect(round, None);
959 }
960
961 mod conformance {
962 use super::*;
963 use commonware_codec::{Encode, Write};
964 use commonware_conformance::Conformance;
965 use commonware_cryptography::Sha256;
966 use rand::{RngExt as _, SeedableRng};
967 use rand_chacha::ChaCha8Rng;
968
969 struct RoundRobinShuffleConformance;
975
976 impl Conformance for RoundRobinShuffleConformance {
977 async fn commit(seed: u64) -> Vec<u8> {
978 let mut rng = ChaCha8Rng::seed_from_u64(seed);
979
980 let n = rng.random_range(1..=100);
982 let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, n);
983 let participants = Set::try_from_iter(participants).unwrap();
984
985 let shuffle_seed: [u8; 32] = rng.random();
987
988 let elector: RoundRobinElector<ed25519::Scheme> =
990 RoundRobin::<Sha256>::shuffled(&shuffle_seed).build(&participants);
991
992 elector.permutation.encode().to_vec()
994 }
995 }
996
997 struct RandomV0SelectLeaderConformance;
1002
1003 struct RandomV1SelectLeaderConformance;
1008
1009 fn random_select_leader_commit(seed: u64, version: Random) -> Vec<u8> {
1010 let mut rng = ChaCha8Rng::seed_from_u64(seed);
1011
1012 let n = rng.random_range(4..=10);
1014 let Fixture {
1015 participants,
1016 schemes,
1017 ..
1018 } = bls12381_threshold_vrf::fixture::<MinPk, _>(&mut rng, NAMESPACE, n);
1019 let participants = Set::try_from_iter(participants).unwrap();
1020 let elector: RandomElector<ThresholdScheme> = version.build(&participants);
1021 let quorum =
1022 usize::try_from(N3f1::quorum(schemes.len())).expect("quorum exceeds usize::MAX");
1023
1024 let epoch = rng.random_range(0..1000);
1026 let view = rng.random_range(2..=101);
1027 let round = Round::new(Epoch::new(epoch), View::new(view));
1028
1029 let attestations: Vec<_> = schemes
1031 .iter()
1032 .take(quorum)
1033 .map(|s| s.sign::<Sha256Digest>(Subject::Nullify { round }).unwrap())
1034 .collect();
1035 let cert = schemes[0]
1036 .assemble(non_empty![@attestations], &Sequential)
1037 .unwrap();
1038
1039 let leader = elector.elect(round, Some(&cert));
1041
1042 let round_v1 = Round::new(Epoch::new(epoch), View::new(1));
1044 let leader_v1 = elector.elect(round_v1, None);
1045
1046 let mut result = leader.encode_mut();
1048 leader_v1.write(&mut result);
1049 result.to_vec()
1050 }
1051
1052 #[allow(deprecated)]
1053 impl Conformance for RandomV0SelectLeaderConformance {
1054 async fn commit(seed: u64) -> Vec<u8> {
1055 random_select_leader_commit(seed, Random::new(RandomVersion::V0))
1056 }
1057 }
1058
1059 impl Conformance for RandomV1SelectLeaderConformance {
1060 async fn commit(seed: u64) -> Vec<u8> {
1061 random_select_leader_commit(seed, Random::new(RandomVersion::V1))
1062 }
1063 }
1064
1065 commonware_conformance::conformance_tests! {
1066 RoundRobinShuffleConformance => 512,
1067 RandomV0SelectLeaderConformance => 512,
1068 RandomV1SelectLeaderConformance => 512,
1069 }
1070 }
1071}