1pub mod elector;
344pub mod scheme;
345pub mod types;
346
347cfg_if::cfg_if! {
348 if #[cfg(not(target_arch = "wasm32"))] {
349 use crate::types::{Round, View, ViewDelta};
350 use commonware_cryptography::PublicKey;
351 use commonware_p2p::Recipients;
352
353 mod actors;
354 pub mod config;
355 pub use config::{Config, Floor, ForwardingPolicy};
356 mod engine;
357 pub use engine::Engine;
358 mod metrics;
359
360 pub(crate) const fn min_active(activity_timeout: ViewDelta, last_finalized: View) -> View {
362 last_finalized.saturating_sub(activity_timeout)
363 }
364
365 pub(crate) fn interesting(
369 activity_timeout: ViewDelta,
370 last_finalized: View,
371 current: View,
372 pending: View,
373 allow_future: bool,
374 ) -> bool {
375 if pending.is_zero() {
377 return false;
378 }
379 if pending < min_active(activity_timeout, last_finalized) {
380 return false;
381 }
382 if !allow_future && pending > current.next() {
383 return false;
384 }
385 true
386 }
387
388 pub enum Plan<P: PublicKey> {
390 Propose {
398 round: Round,
400 },
401 Forward {
408 round: Round,
410 recipients: Recipients<P>,
412 },
413 }
414 }
415}
416
417#[cfg(any(test, feature = "mocks"))]
418pub mod mocks;
419
420#[cfg(test)]
422pub(crate) fn quorum(n: u32) -> u32 {
423 use commonware_utils::{Faults, N3f1};
424
425 N3f1::quorum(n)
426}
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431 use crate::{
432 simplex::{
433 elector::{Config as Elector, Elector as ElectorTrait, Random, RoundRobin},
434 mocks::{
435 scheme as scheme_mocks,
436 twins::{self, Elector as TwinsElector},
437 wrapped,
438 },
439 scheme::{
440 bls12381_multisig,
441 bls12381_threshold::{
442 standard as bls12381_threshold_std,
443 vrf::{self as bls12381_threshold_vrf, Seedable},
444 },
445 ed25519, secp256r1, Scheme,
446 },
447 types::{
448 Certificate, Finalization as TFinalization, Finalize as TFinalize,
449 Notarization as TNotarization, Notarize as TNotarize,
450 Nullification as TNullification, Nullify as TNullify, Proposal, Vote,
451 },
452 },
453 types::{Epoch, Participant, Round},
454 Monitor, Viewable,
455 };
456 use commonware_codec::{Decode, DecodeExt, Encode};
457 use commonware_cryptography::{
458 bls12381::primitives::variant::{MinPk, MinSig, Variant},
459 certificate::mocks::Fixture,
460 ed25519::{PrivateKey, PublicKey},
461 sha256::{Digest as Sha256Digest, Digest as D},
462 Hasher as _, Sha256, Signer as _,
463 };
464 use commonware_macros::{select, test_group, test_traced};
465 use commonware_p2p::{
466 simulated::{Config, Link, Network, Oracle, Receiver, Sender, SplitOrigin},
467 utils::mocks::inert_channel,
468 Manager as _, Recipients, Sender as _, TrackedPeers,
469 };
470 use commonware_parallel::{Sequential, Strategy};
471 use commonware_runtime::{
472 buffer::paged::CacheRef, deterministic, telemetry::metrics::count_running_tasks, Clock,
473 IoBuf, Metrics as _, Quota, Runner, Spawner, Strategizer as _, Supervisor as _,
474 };
475 use commonware_utils::{
476 ordered::Set, sync::Mutex, test_rng, Faults, N3f1, NZUsize, TestRng, NZU16,
477 };
478 use engine::Engine;
479 use futures::future::join_all;
480 use rand::{rngs::StdRng, RngExt as _, SeedableRng};
481 use rand_core::CryptoRng;
482 use std::{
483 collections::{BTreeMap, HashMap, HashSet},
484 num::{NonZeroU16, NonZeroU32, NonZeroUsize},
485 sync::Arc,
486 time::Duration,
487 };
488 use tracing::{debug, info, warn};
489 use types::Activity;
490
491 macro_rules! for_each_fixture {
494 ($cb:ident!($($args:tt)*)) => {
495 $cb!($($args)*, bls12381_threshold_vrf_min_pk, Random, bls12381_threshold_vrf::fixture::<MinPk, _>);
496 $cb!($($args)*, bls12381_threshold_vrf_min_sig, Random, bls12381_threshold_vrf::fixture::<MinSig, _>);
497 $cb!($($args)*, bls12381_threshold_std_min_pk, RoundRobin, bls12381_threshold_std::fixture::<MinPk, _>);
498 $cb!($($args)*, bls12381_threshold_std_min_sig, RoundRobin, bls12381_threshold_std::fixture::<MinSig, _>);
499 $cb!($($args)*, bls12381_multisig_min_pk, RoundRobin, bls12381_multisig::fixture::<MinPk, _>);
500 $cb!($($args)*, bls12381_multisig_min_sig, RoundRobin, bls12381_multisig::fixture::<MinSig, _>);
501 $cb!($($args)*, ed25519, RoundRobin, ed25519::fixture);
502 $cb!($($args)*, secp256r1, RoundRobin, secp256r1::fixture);
503 };
504 }
505
506 macro_rules! test_for_all_fixtures {
517 ($callee:ident) => {
518 for_each_fixture!(test_for_all_fixtures!(@emit [test_traced] $callee [] []));
519 };
520 ($callee:ident, level = $level:literal) => {
521 for_each_fixture!(test_for_all_fixtures!(@emit [test_traced($level)] $callee [] []));
522 };
523 ($callee:ident, seeds = $n:expr) => {
524 for_each_fixture!(test_for_all_fixtures!(@seeded $n, $callee));
525 };
526 ($callee:ident, $arg:expr, level = $level:literal) => {
527 for_each_fixture!(test_for_all_fixtures!(@emit [test_traced($level)] $callee [, _] [, $arg]));
528 };
529 ($callee:ident, $arg:expr) => {
530 for_each_fixture!(test_for_all_fixtures!(@emit [test_traced] $callee [, _] [, $arg]));
531 };
532 (@emit [$traced:meta] $callee:ident [$($generics:tt)*] [$($args:tt)*], $suffix:ident, $elector:ty, $fixture:expr) => {
533 paste::paste! {
534 #[test_group("slow")]
535 #[$traced]
536 fn [<test_ $callee _ $suffix>]() {
537 $callee::<_, _, $elector $($generics)*>($fixture $($args)*);
538 }
539 }
540 };
541 (@seeded $n:expr, $callee:ident, $suffix:ident, $elector:ty, $fixture:expr) => {
542 paste::paste! {
543 #[test_group("slow")]
544 #[test_traced]
545 fn [<test_ $callee _ $suffix>]() {
546 for seed in 0..$n {
547 $callee::<_, _, $elector>(seed, $fixture);
548 }
549 }
550 }
551 };
552 }
553
554 const PAGE_SIZE: NonZeroU16 = NZU16!(1024);
555 const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(10);
556 const TEST_QUOTA: Quota = Quota::per_second(NonZeroU32::MAX);
557
558 #[test]
559 fn test_interesting() {
560 let activity_timeout = ViewDelta::new(10);
561
562 assert!(!interesting(
564 activity_timeout,
565 View::zero(),
566 View::zero(),
567 View::zero(),
568 false
569 ));
570 assert!(!interesting(
571 activity_timeout,
572 View::zero(),
573 View::new(1),
574 View::zero(),
575 true
576 ));
577
578 assert!(!interesting(
580 activity_timeout,
581 View::new(20),
582 View::new(25),
583 View::new(5), false
585 ));
586
587 assert!(interesting(
589 activity_timeout,
590 View::new(20),
591 View::new(25),
592 View::new(10), false
594 ));
595
596 assert!(!interesting(
598 activity_timeout,
599 View::new(20),
600 View::new(25),
601 View::new(27),
602 false
603 ));
604
605 assert!(interesting(
607 activity_timeout,
608 View::new(20),
609 View::new(25),
610 View::new(27),
611 true
612 ));
613
614 assert!(interesting(
616 activity_timeout,
617 View::new(20),
618 View::new(25),
619 View::new(26),
620 false
621 ));
622
623 assert!(interesting(
625 activity_timeout,
626 View::new(20),
627 View::new(25),
628 View::new(22),
629 false
630 ));
631
632 assert!(interesting(
635 activity_timeout,
636 View::zero(),
637 View::new(5),
638 View::new(1),
639 false
640 ));
641 }
642
643 async fn register_validator(
645 oracle: &mut Oracle<PublicKey, deterministic::Context>,
646 validator: PublicKey,
647 ) -> (
648 (
649 Sender<PublicKey, deterministic::Context>,
650 Receiver<PublicKey>,
651 ),
652 (
653 Sender<PublicKey, deterministic::Context>,
654 Receiver<PublicKey>,
655 ),
656 (
657 Sender<PublicKey, deterministic::Context>,
658 Receiver<PublicKey>,
659 ),
660 ) {
661 let control = oracle.control(validator.clone());
662 let (vote_sender, vote_receiver) = control.register(0, TEST_QUOTA).await.unwrap();
663 let (certificate_sender, certificate_receiver) =
664 control.register(1, TEST_QUOTA).await.unwrap();
665 let (resolver_sender, resolver_receiver) = control.register(2, TEST_QUOTA).await.unwrap();
666 (
667 (vote_sender, vote_receiver),
668 (certificate_sender, certificate_receiver),
669 (resolver_sender, resolver_receiver),
670 )
671 }
672
673 async fn register_validators(
675 oracle: &mut Oracle<PublicKey, deterministic::Context>,
676 validators: &[PublicKey],
677 ) -> HashMap<
678 PublicKey,
679 (
680 (
681 Sender<PublicKey, deterministic::Context>,
682 Receiver<PublicKey>,
683 ),
684 (
685 Sender<PublicKey, deterministic::Context>,
686 Receiver<PublicKey>,
687 ),
688 (
689 Sender<PublicKey, deterministic::Context>,
690 Receiver<PublicKey>,
691 ),
692 ),
693 > {
694 let mut registrations = HashMap::new();
695 for validator in validators.iter() {
696 let registration = register_validator(oracle, validator.clone()).await;
697 registrations.insert(validator.clone(), registration);
698 }
699 registrations
700 }
701
702 async fn start_test_network_with_peers<I>(
703 context: deterministic::Context,
704 peers: I,
705 disconnect_on_block: bool,
706 ) -> Oracle<PublicKey, deterministic::Context>
707 where
708 I: IntoIterator<Item = PublicKey>,
709 {
710 let (network, oracle) = Network::new_with_peers(
711 context.child("network"),
712 Config {
713 max_size: 1024 * 1024,
714 disconnect_on_block,
715 tracked_peer_sets: NZUsize!(1),
716 },
717 peers,
718 )
719 .await;
720 network.start();
721 oracle
722 }
723
724 async fn start_test_network_with_split_peers<I, J>(
725 context: deterministic::Context,
726 primary: I,
727 secondary: J,
728 disconnect_on_block: bool,
729 ) -> Oracle<PublicKey, deterministic::Context>
730 where
731 I: IntoIterator<Item = PublicKey>,
732 J: IntoIterator<Item = PublicKey>,
733 {
734 let (network, oracle) = Network::new_with_split_peers(
735 context.child("network"),
736 Config {
737 max_size: 1024 * 1024,
738 disconnect_on_block,
739 tracked_peer_sets: NZUsize!(1),
740 },
741 primary,
742 secondary,
743 )
744 .await;
745 network.start();
746 oracle
747 }
748
749 enum Action {
751 Link(Link),
752 Update(Link), Unlink,
754 }
755
756 async fn link_validators(
762 oracle: &mut Oracle<PublicKey, deterministic::Context>,
763 validators: &[PublicKey],
764 action: Action,
765 restrict_to: Option<fn(usize, usize, usize) -> bool>,
766 ) {
767 for (i1, v1) in validators.iter().enumerate() {
768 for (i2, v2) in validators.iter().enumerate() {
769 if v2 == v1 {
771 continue;
772 }
773
774 if let Some(f) = restrict_to {
776 if !f(validators.len(), i1, i2) {
777 continue;
778 }
779 }
780
781 match action {
783 Action::Update(_) | Action::Unlink => {
784 oracle.remove_link(v1.clone(), v2.clone()).await.unwrap();
785 }
786 _ => {}
787 }
788
789 match action {
791 Action::Link(ref link) | Action::Update(ref link) => {
792 oracle
793 .add_link(v1.clone(), v2.clone(), link.clone())
794 .await
795 .unwrap();
796 }
797 _ => {}
798 }
799 }
800 }
801 }
802
803 fn count_nonzero_metric_lines(encoded: &str, patterns: &[&str]) -> u32 {
805 encoded
806 .lines()
807 .filter(|line| patterns.iter().all(|p| line.contains(p)))
808 .filter(|line| {
809 line.split_whitespace()
810 .last()
811 .and_then(|s| s.parse::<u64>().ok())
812 .is_some_and(|n| n > 0)
813 })
814 .count() as u32
815 }
816
817 fn all_online<S, F, L, T>(
818 mut fixture: F,
819 strategy: impl FnOnce(&mut deterministic::Context) -> T + Send + 'static,
820 ) where
821 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
822 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
823 L: Elector<S>,
824 T: Strategy,
825 {
826 let n = 5;
828 let quorum = quorum(n) as usize;
829 let required_containers = View::new(100);
830 let activity_timeout = ViewDelta::new(10);
831 let skip_timeout = ViewDelta::new(5);
832 let namespace = b"consensus".to_vec();
833 let executor = deterministic::Runner::timed(Duration::from_secs(300));
834 executor.start(|mut context| async move {
835 let Fixture {
837 participants,
838 schemes,
839 ..
840 } = fixture(&mut context, &namespace, n);
841 let strategy = strategy(&mut context);
842 let mut oracle =
843 start_test_network_with_peers(context.child("network"), participants.clone(), true)
844 .await;
845 let mut registrations = register_validators(&mut oracle, &participants).await;
846
847 let link = Link {
849 latency: Duration::from_millis(10),
850 jitter: Duration::from_millis(1),
851 success_rate: 1.0,
852 };
853 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
854
855 let elector = L::default();
857 let relay = Arc::new(mocks::relay::Relay::new());
858 let mut reporters = Vec::new();
859 let mut engine_handlers = Vec::new();
860 for (idx, validator) in participants.iter().enumerate() {
861 let context = context
863 .child("validator")
864 .with_attribute("public_key", validator);
865
866 let reporter_config = mocks::reporter::Config {
868 participants: participants.clone().try_into().unwrap(),
869 scheme: schemes[idx].clone(),
870 elector: elector.clone(),
871 };
872 let reporter =
873 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
874 reporters.push(reporter.clone());
875 let application_cfg = mocks::application::Config {
876 hasher: Sha256::default(),
877 relay: relay.clone(),
878 me: validator.clone(),
879 propose_latency: (10.0, 5.0),
880 verify_latency: (10.0, 5.0),
881 certify_latency: (10.0, 5.0),
882 should_certify: mocks::application::Certifier::Always,
883 };
884 let (actor, application) = mocks::application::Application::new(
885 context.child("application"),
886 application_cfg,
887 );
888 actor.start();
889 let blocker = oracle.control(validator.clone());
890 let cfg = config::Config {
891 scheme: schemes[idx].clone(),
892 elector: elector.clone(),
893 blocker,
894 automaton: application.clone(),
895 relay: application.clone(),
896 reporter: reporter.clone(),
897 strategy: strategy.clone(),
898 partition: validator.to_string(),
899 mailbox_size: NZUsize!(1024),
900 epoch: Epoch::new(333),
901 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
902 Epoch::new(333),
903 )),
904 leader_timeout: Duration::from_secs(1),
905 certification_timeout: Duration::from_secs(2),
906 timeout_retry: Duration::from_secs(10),
907 fetch_timeout: Duration::from_secs(1),
908 activity_timeout,
909 skip_timeout,
910 fetch_concurrent: NZUsize!(4),
911 replay_buffer: NZUsize!(1024 * 1024),
912 write_buffer: NZUsize!(1024 * 1024),
913 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
914 forwarding: ForwardingPolicy::Disabled,
915 };
916 let engine = Engine::new(context.child("engine"), cfg);
917
918 let (pending, recovered, resolver) = registrations
920 .remove(validator)
921 .expect("validator should be registered");
922 engine_handlers.push(engine.start(pending, recovered, resolver));
923 }
924
925 let mut finalizers = Vec::new();
927 for reporter in reporters.iter_mut() {
928 let (mut latest, mut monitor) = reporter.subscribe().await;
929 finalizers.push(context.child("finalizer").spawn(move |_| async move {
930 while latest < required_containers {
931 latest = monitor.recv().await.expect("event missing");
932 }
933 }));
934 }
935 join_all(finalizers).await;
936
937 let latest_complete = required_containers.saturating_sub(activity_timeout);
939 for reporter in reporters.iter() {
940 reporter.assert_no_faults();
942
943 reporter.assert_no_invalid();
945
946 {
948 let certified = reporter.certified.lock();
949 for view in View::range(View::new(1), latest_complete) {
950 if !certified.contains(&view) {
952 panic!("view: {view}");
953 }
954 }
955 }
956
957 let mut notarized = HashMap::new();
959 let mut finalized = HashMap::new();
960 {
961 let notarizes = reporter.notarizes.lock();
962 for view in View::range(View::new(1), latest_complete) {
963 let Some(payloads) = notarizes.get(&view) else {
965 continue;
966 };
967 if payloads.len() > 1 {
968 panic!("view: {view}");
969 }
970 let (digest, notarizers) = payloads.iter().next().unwrap();
971 notarized.insert(view, *digest);
972
973 if notarizers.len() < quorum {
974 panic!("view: {view}");
977 }
978 }
979 }
980 {
981 let notarizations = reporter.notarizations.lock();
982 for view in View::range(View::new(1), latest_complete) {
983 let Some(notarization) = notarizations.get(&view) else {
985 continue;
986 };
987 let Some(digest) = notarized.get(&view) else {
988 continue;
989 };
990 assert_eq!(¬arization.proposal.payload, digest);
991 }
992 }
993 {
994 let finalizes = reporter.finalizes.lock();
995 for view in View::range(View::new(1), latest_complete) {
996 let Some(payloads) = finalizes.get(&view) else {
998 continue;
999 };
1000 if payloads.len() > 1 {
1001 panic!("view: {view}");
1002 }
1003 let (digest, finalizers) = payloads.iter().next().unwrap();
1004 finalized.insert(view, *digest);
1005
1006 if view > latest_complete {
1008 continue;
1009 }
1010
1011 if finalizers.len() < quorum {
1013 panic!("view: {view}");
1016 }
1017
1018 let nullifies = reporter.nullifies.lock();
1020 let Some(nullifies) = nullifies.get(&view) else {
1021 continue;
1022 };
1023 for finalizers in payloads.values() {
1024 for finalizer in finalizers.iter() {
1025 if nullifies.contains(finalizer) {
1026 panic!("should not nullify and finalize at same view");
1027 }
1028 }
1029 }
1030 }
1031 }
1032 {
1033 let finalizations = reporter.finalizations.lock();
1034 for view in View::range(View::new(1), latest_complete) {
1035 let Some(finalization) = finalizations.get(&view) else {
1037 continue;
1038 };
1039 let Some(digest) = finalized.get(&view) else {
1040 continue;
1041 };
1042 assert_eq!(&finalization.proposal.payload, digest);
1043 }
1044 }
1045 }
1046
1047 let blocked = oracle.blocked().await.unwrap();
1049 assert!(blocked.is_empty());
1050 });
1051 }
1052
1053 test_for_all_fixtures!(all_online, |_| Sequential);
1054
1055 #[test_group("slow")]
1056 #[test_traced]
1057 fn test_all_online_rayon_bls12381_threshold_vrf_min_pk() {
1058 all_online::<_, _, Random, _>(bls12381_threshold_vrf::fixture::<MinPk, _>, |context| {
1059 context.strategy(NZUsize!(2))
1060 });
1061 }
1062
1063 fn non_genesis_floor_joiner_catches_tip<S, F, L>(mut fixture: F)
1064 where
1065 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
1066 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
1067 L: Elector<S>,
1068 {
1069 let n = 5;
1072 let active_count = quorum(n) as usize;
1073 let initial_tip_target = View::new(15);
1074 let activity_timeout = ViewDelta::new(10);
1075 let skip_timeout = ViewDelta::new(5);
1076 let namespace = b"consensus".to_vec();
1077 let executor = deterministic::Runner::timed(Duration::from_secs(300));
1078 executor.start(|mut context| async move {
1079 let Fixture {
1080 participants,
1081 schemes,
1082 ..
1083 } = fixture(&mut context, &namespace, n);
1084 let mut oracle =
1085 start_test_network_with_peers(context.child("network"), participants.clone(), true)
1086 .await;
1087
1088 let active = &participants[..active_count];
1089 let joiner_idx = active_count;
1090 let joiner = participants[joiner_idx].clone();
1091
1092 let link = Link {
1093 latency: Duration::from_millis(10),
1094 jitter: Duration::from_millis(1),
1095 success_rate: 1.0,
1096 };
1097 link_validators(&mut oracle, active, Action::Link(link.clone()), None).await;
1098
1099 let elector = L::default();
1100 let relay = Arc::new(mocks::relay::Relay::new());
1101 let mut reporters = Vec::new();
1102 let mut engine_handlers = Vec::new();
1103
1104 for (idx, validator) in active.iter().enumerate() {
1105 let validator_context = context
1106 .child("validator")
1107 .with_attribute("public_key", validator);
1108
1109 let reporter_config = mocks::reporter::Config {
1110 participants: participants.clone().try_into().unwrap(),
1111 scheme: schemes[idx].clone(),
1112 elector: elector.clone(),
1113 };
1114 let reporter = mocks::reporter::Reporter::new(
1115 validator_context.child("reporter"),
1116 reporter_config,
1117 );
1118 reporters.push(reporter.clone());
1119
1120 let application_cfg = mocks::application::Config {
1121 hasher: Sha256::default(),
1122 relay: relay.clone(),
1123 me: validator.clone(),
1124 propose_latency: (10.0, 5.0),
1125 verify_latency: (10.0, 5.0),
1126 certify_latency: (10.0, 5.0),
1127 should_certify: mocks::application::Certifier::Always,
1128 };
1129 let (actor, application) = mocks::application::Application::new(
1130 validator_context.child("application"),
1131 application_cfg,
1132 );
1133 actor.start();
1134
1135 let cfg = config::Config {
1136 scheme: schemes[idx].clone(),
1137 elector: elector.clone(),
1138 blocker: oracle.control(validator.clone()),
1139 automaton: application.clone(),
1140 relay: application.clone(),
1141 reporter: reporter.clone(),
1142 strategy: Sequential,
1143 partition: format!("joiner_catches_tip_{validator}"),
1144 mailbox_size: NZUsize!(1024),
1145 epoch: Epoch::new(333),
1146 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
1147 Epoch::new(333),
1148 )),
1149 leader_timeout: Duration::from_secs(1),
1150 certification_timeout: Duration::from_secs(2),
1151 timeout_retry: Duration::from_secs(10),
1152 fetch_timeout: Duration::from_secs(1),
1153 activity_timeout,
1154 skip_timeout,
1155 fetch_concurrent: NZUsize!(4),
1156 replay_buffer: NZUsize!(1024 * 1024),
1157 write_buffer: NZUsize!(1024 * 1024),
1158 page_cache: CacheRef::from_pooler(
1159 &validator_context,
1160 PAGE_SIZE,
1161 PAGE_CACHE_SIZE,
1162 ),
1163 forwarding: ForwardingPolicy::Disabled,
1164 };
1165 let engine = Engine::new(validator_context.child("engine"), cfg);
1166 let (pending, recovered, resolver) =
1167 register_validator(&mut oracle, validator.clone()).await;
1168 engine_handlers.push(engine.start(pending, recovered, resolver));
1169 }
1170
1171 let mut finalizers = Vec::new();
1172 for reporter in reporters.iter_mut() {
1173 let (mut latest, mut monitor) = reporter.subscribe().await;
1174 finalizers.push(
1175 context
1176 .child("initial_finalizer")
1177 .spawn(move |_| async move {
1178 while latest < initial_tip_target {
1179 latest = monitor.recv().await.expect("event missing");
1180 }
1181 latest
1182 }),
1183 );
1184 }
1185 let tip_at_join = join_all(finalizers)
1186 .await
1187 .into_iter()
1188 .map(|result| result.expect("initial finalizer failed"))
1189 .min()
1190 .expect("initial validators missing");
1191
1192 let (floor_view, floor_finalization) = {
1193 let finalizations = reporters[0].finalizations.lock();
1194 finalizations
1195 .iter()
1196 .filter(|(view, _)| **view > View::zero() && **view < tip_at_join)
1197 .min_by_key(|(view, _)| view.get())
1198 .map(|(view, finalization)| (*view, finalization.clone()))
1199 .expect("non-genesis floor finalization missing")
1200 };
1201 assert!(floor_view > View::zero());
1202 assert!(floor_view < tip_at_join);
1203
1204 for validator in active.iter() {
1207 oracle
1208 .add_link(joiner.clone(), validator.clone(), link.clone())
1209 .await
1210 .unwrap();
1211 oracle
1212 .add_link(validator.clone(), joiner.clone(), link.clone())
1213 .await
1214 .unwrap();
1215 }
1216
1217 let joiner_context = context
1218 .child("validator")
1219 .with_attribute("public_key", &joiner);
1220 let reporter_config = mocks::reporter::Config {
1221 participants: participants.clone().try_into().unwrap(),
1222 scheme: schemes[joiner_idx].clone(),
1223 elector: elector.clone(),
1224 };
1225 let mut joiner_reporter =
1226 mocks::reporter::Reporter::new(joiner_context.child("reporter"), reporter_config);
1227 reporters.push(joiner_reporter.clone());
1228
1229 let application_cfg = mocks::application::Config {
1230 hasher: Sha256::default(),
1231 relay: relay.clone(),
1232 me: joiner.clone(),
1233 propose_latency: (10.0, 5.0),
1234 verify_latency: (10.0, 5.0),
1235 certify_latency: (10.0, 5.0),
1236 should_certify: mocks::application::Certifier::Always,
1237 };
1238 let (actor, application) = mocks::application::Application::new(
1239 joiner_context.child("application"),
1240 application_cfg,
1241 );
1242 actor.start();
1243
1244 let cfg = config::Config {
1245 scheme: schemes[joiner_idx].clone(),
1246 elector,
1247 blocker: oracle.control(joiner.clone()),
1248 automaton: application.clone(),
1249 relay: application.clone(),
1250 reporter: joiner_reporter.clone(),
1251 strategy: Sequential,
1252 partition: format!("joiner_catches_tip_{joiner}"),
1253 mailbox_size: NZUsize!(1024),
1254 epoch: Epoch::new(333),
1255 floor: config::Floor::Finalized(floor_finalization),
1256 leader_timeout: Duration::from_secs(1),
1257 certification_timeout: Duration::from_secs(2),
1258 timeout_retry: Duration::from_secs(10),
1259 fetch_timeout: Duration::from_secs(1),
1260 activity_timeout,
1261 skip_timeout,
1262 fetch_concurrent: NZUsize!(4),
1263 replay_buffer: NZUsize!(1024 * 1024),
1264 write_buffer: NZUsize!(1024 * 1024),
1265 page_cache: CacheRef::from_pooler(&joiner_context, PAGE_SIZE, PAGE_CACHE_SIZE),
1266 forwarding: ForwardingPolicy::Disabled,
1267 };
1268 let engine = Engine::new(joiner_context.child("engine"), cfg);
1269 let (pending, recovered, resolver) = register_validator(&mut oracle, joiner).await;
1270 engine_handlers.push(engine.start(pending, recovered, resolver));
1271
1272 let (mut joiner_latest, mut joiner_monitor) = joiner_reporter.subscribe().await;
1273 while joiner_latest < tip_at_join {
1274 joiner_latest = joiner_monitor.recv().await.expect("event missing");
1275 }
1276
1277 let post_join_target = tip_at_join.saturating_add(ViewDelta::new(5));
1278 while joiner_latest < post_join_target {
1279 joiner_latest = joiner_monitor.recv().await.expect("event missing");
1280 }
1281
1282 for reporter in reporters.iter() {
1283 reporter.assert_no_faults();
1284 reporter.assert_no_invalid();
1285 }
1286
1287 let blocked = oracle.blocked().await.unwrap();
1288 assert!(blocked.is_empty());
1289 });
1290 }
1291
1292 test_for_all_fixtures!(non_genesis_floor_joiner_catches_tip);
1293
1294 fn dishonest_leader_certification_rejected<S, F>(mut fixture: F)
1309 where
1310 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
1311 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
1312 RoundRobin: Elector<S>,
1313 {
1314 let n = 5;
1315 let required_containers = View::new(50);
1316 let activity_timeout = ViewDelta::new(10);
1317 let skip_timeout = ViewDelta::new(5);
1318 let namespace = b"consensus".to_vec();
1319 let executor = deterministic::Runner::timed(Duration::from_secs(300));
1320 executor.start(|mut context| async move {
1321 let Fixture {
1322 participants,
1323 schemes,
1324 ..
1325 } = fixture(&mut context, &namespace, n);
1326 let mut oracle =
1327 start_test_network_with_peers(context.child("network"), participants.clone(), true)
1328 .await;
1329 let mut registrations = register_validators(&mut oracle, &participants).await;
1330
1331 let link = Link {
1332 latency: Duration::from_millis(10),
1333 jitter: Duration::from_millis(1),
1334 success_rate: 1.0,
1335 };
1336 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
1337
1338 let elector = RoundRobin::default();
1339 let participants_set: Set<S::PublicKey> = participants.clone().try_into().unwrap();
1340 let built_elector = elector.clone().build(&participants_set);
1341 let relay = Arc::new(mocks::relay::Relay::new());
1342 let mut reporters = Vec::new();
1343 let mut engine_handlers = Vec::new();
1344 let dishonest = Participant::new(0);
1345 for (idx, validator) in participants.iter().enumerate() {
1346 let context = context
1347 .child("validator")
1348 .with_attribute("public_key", validator);
1349 let reporter_config = mocks::reporter::Config {
1350 participants: participants.clone().try_into().unwrap(),
1351 scheme: schemes[idx].clone(),
1352 elector: elector.clone(),
1353 };
1354 let reporter =
1355 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
1356 reporters.push(reporter.clone());
1357
1358 let application_cfg = mocks::application::Config {
1359 hasher: Sha256::default(),
1360 relay: relay.clone(),
1361 me: validator.clone(),
1362 propose_latency: (10.0, 5.0),
1363 verify_latency: (10.0, 5.0),
1364 certify_latency: (10.0, 5.0),
1365 should_certify: mocks::application::Certifier::Custom(Box::new({
1366 let built_elector_clone = built_elector.clone();
1367 move |round, _| built_elector_clone.elect(round, None) != dishonest
1368 })),
1369 };
1370 let (actor, application) = mocks::application::Application::new(
1371 context.child("application"),
1372 application_cfg,
1373 );
1374 actor.start();
1375
1376 let blocker = oracle.control(validator.clone());
1377 let cfg = config::Config {
1378 scheme: schemes[idx].clone(),
1379 elector: elector.clone(),
1380 blocker,
1381 automaton: application.clone(),
1382 relay: application.clone(),
1383 reporter: reporter.clone(),
1384 strategy: Sequential,
1385 partition: validator.to_string(),
1386 mailbox_size: NZUsize!(1024),
1387 epoch: Epoch::new(333),
1388 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
1389 Epoch::new(333),
1390 )),
1391 leader_timeout: Duration::from_secs(1),
1392 certification_timeout: Duration::from_secs(2),
1393 timeout_retry: Duration::from_secs(10),
1394 fetch_timeout: Duration::from_secs(1),
1395 activity_timeout,
1396 skip_timeout,
1397 fetch_concurrent: NZUsize!(4),
1398 replay_buffer: NZUsize!(1024 * 1024),
1399 write_buffer: NZUsize!(1024 * 1024),
1400 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
1401 forwarding: ForwardingPolicy::Disabled,
1402 };
1403 let engine = Engine::new(context.child("engine"), cfg);
1404 let (pending, recovered, resolver) = registrations
1405 .remove(validator)
1406 .expect("validator should be registered");
1407 engine_handlers.push(engine.start(pending, recovered, resolver));
1408 }
1409
1410 let mut finalizers = Vec::new();
1411 for reporter in reporters.iter_mut() {
1412 let (mut latest, mut monitor) = reporter.subscribe().await;
1413 finalizers.push(context.child("finalizer").spawn(move |_| async move {
1414 while latest < required_containers {
1415 latest = monitor.recv().await.expect("event missing");
1416 }
1417 }));
1418 }
1419 join_all(finalizers).await;
1420
1421 for reporter in reporters.iter() {
1422 reporter.assert_no_faults();
1423 reporter.assert_no_invalid();
1424 }
1425 });
1426 }
1427
1428 #[test_group("slow")]
1429 #[test_traced]
1430 fn test_dishonest_leader_certification_rejected() {
1431 dishonest_leader_certification_rejected::<_, _>(
1432 bls12381_threshold_vrf::fixture::<MinPk, _>,
1433 );
1434 dishonest_leader_certification_rejected::<_, _>(
1435 bls12381_threshold_vrf::fixture::<MinSig, _>,
1436 );
1437 dishonest_leader_certification_rejected::<_, _>(
1438 bls12381_threshold_std::fixture::<MinPk, _>,
1439 );
1440 dishonest_leader_certification_rejected::<_, _>(
1441 bls12381_threshold_std::fixture::<MinSig, _>,
1442 );
1443 dishonest_leader_certification_rejected::<_, _>(bls12381_multisig::fixture::<MinPk, _>);
1444 dishonest_leader_certification_rejected::<_, _>(bls12381_multisig::fixture::<MinSig, _>);
1445 dishonest_leader_certification_rejected::<_, _>(ed25519::fixture);
1446 dishonest_leader_certification_rejected::<_, _>(secp256r1::fixture);
1447 }
1448
1449 fn observer<S, F, L>(mut fixture: F)
1450 where
1451 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
1452 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
1453 L: Elector<S>,
1454 {
1455 let n_active = 5;
1457 let required_containers = View::new(100);
1458 let activity_timeout = ViewDelta::new(10);
1459 let skip_timeout = ViewDelta::new(5);
1460 let namespace = b"consensus".to_vec();
1461 let executor = deterministic::Runner::timed(Duration::from_secs(300));
1462 executor.start(|mut context| async move {
1463 let Fixture {
1465 participants,
1466 schemes,
1467 verifier,
1468 ..
1469 } = fixture(&mut context, &namespace, n_active);
1470
1471 let private_key_observer = PrivateKey::from_seed(n_active as u64);
1473 let public_key_observer = private_key_observer.public_key();
1474
1475 let mut oracle = start_test_network_with_split_peers(
1476 context.child("network"),
1477 participants.clone(),
1478 [public_key_observer.clone()],
1479 true,
1480 )
1481 .await;
1482
1483 let mut all_validators = participants.clone();
1485 all_validators.push(public_key_observer.clone());
1486 all_validators.sort();
1487 let mut registrations = register_validators(&mut oracle, &all_validators).await;
1488
1489 let link = Link {
1491 latency: Duration::from_millis(10),
1492 jitter: Duration::from_millis(1),
1493 success_rate: 1.0,
1494 };
1495 link_validators(&mut oracle, &all_validators, Action::Link(link), None).await;
1496
1497 let elector = L::default();
1499 let relay = Arc::new(mocks::relay::Relay::new());
1500 let mut reporters = Vec::new();
1501
1502 for (idx, validator) in participants.iter().enumerate() {
1503 let is_observer = *validator == public_key_observer;
1504
1505 let context = context
1507 .child("validator")
1508 .with_attribute("public_key", validator);
1509
1510 let signing = if is_observer {
1512 verifier.clone()
1513 } else {
1514 schemes[idx].clone()
1515 };
1516 let reporter_config = mocks::reporter::Config {
1517 participants: participants.clone().try_into().unwrap(),
1518 scheme: signing.clone(),
1519 elector: elector.clone(),
1520 };
1521 let reporter =
1522 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
1523 reporters.push(reporter.clone());
1524 let application_cfg = mocks::application::Config {
1525 hasher: Sha256::default(),
1526 relay: relay.clone(),
1527 me: validator.clone(),
1528 propose_latency: (10.0, 5.0),
1529 verify_latency: (10.0, 5.0),
1530 certify_latency: (10.0, 5.0),
1531 should_certify: mocks::application::Certifier::Always,
1532 };
1533 let (actor, application) = mocks::application::Application::new(
1534 context.child("application"),
1535 application_cfg,
1536 );
1537 actor.start();
1538 let blocker = oracle.control(validator.clone());
1539 let cfg = config::Config {
1540 scheme: signing.clone(),
1541 elector: elector.clone(),
1542 blocker,
1543 automaton: application.clone(),
1544 relay: application.clone(),
1545 reporter: reporter.clone(),
1546 strategy: Sequential,
1547 partition: validator.to_string(),
1548 mailbox_size: NZUsize!(1024),
1549 epoch: Epoch::new(333),
1550 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
1551 Epoch::new(333),
1552 )),
1553 leader_timeout: Duration::from_secs(1),
1554 certification_timeout: Duration::from_secs(2),
1555 timeout_retry: Duration::from_secs(10),
1556 fetch_timeout: Duration::from_secs(1),
1557 activity_timeout,
1558 skip_timeout,
1559 fetch_concurrent: NZUsize!(4),
1560 replay_buffer: NZUsize!(1024 * 1024),
1561 write_buffer: NZUsize!(1024 * 1024),
1562 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
1563 forwarding: ForwardingPolicy::Disabled,
1564 };
1565 let engine = Engine::new(context.child("engine"), cfg);
1566
1567 let (pending, recovered, resolver) = registrations
1569 .remove(validator)
1570 .expect("validator should be registered");
1571 engine.start(pending, recovered, resolver);
1572 }
1573
1574 let mut finalizers = Vec::new();
1576 for reporter in reporters.iter_mut() {
1577 let (mut latest, mut monitor) = reporter.subscribe().await;
1578 finalizers.push(context.child("finalizer").spawn(move |_| async move {
1579 while latest < required_containers {
1580 latest = monitor.recv().await.expect("event missing");
1581 }
1582 }));
1583 }
1584 join_all(finalizers).await;
1585
1586 for reporter in reporters.iter() {
1589 reporter.assert_no_faults();
1591 reporter.assert_no_invalid();
1592
1593 let blocked = oracle.blocked().await.unwrap();
1595 assert!(blocked.is_empty());
1596 }
1597 });
1598 }
1599
1600 test_for_all_fixtures!(observer);
1601
1602 fn unclean_shutdown<S, F, L>(mut fixture: F)
1603 where
1604 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
1605 F: FnMut(&mut TestRng, &[u8], u32) -> Fixture<S>,
1606 L: Elector<S>,
1607 {
1608 let n = 5;
1610 let required_containers = View::new(100);
1611 let activity_timeout = ViewDelta::new(10);
1612 let skip_timeout = ViewDelta::new(5);
1613 let namespace = b"consensus".to_vec();
1614
1615 let shutdowns: Arc<Mutex<u64>> = Arc::new(Mutex::new(0));
1617 let supervised = Arc::new(Mutex::new(Vec::new()));
1618 let mut prev_checkpoint = None;
1619
1620 let mut rng = test_rng();
1622 let Fixture {
1623 participants,
1624 schemes,
1625 ..
1626 } = fixture(&mut rng, &namespace, n);
1627 let reporter_seed: [u8; 32] = rng.random();
1628
1629 let relay = Arc::new(mocks::relay::Relay::<Sha256Digest, S::PublicKey>::new());
1631
1632 loop {
1633 let participants = participants.clone();
1634 let schemes = schemes.clone();
1635 let shutdowns = shutdowns.clone();
1636 let supervised = supervised.clone();
1637 let relay = relay.clone();
1638 relay.deregister_all(); let f = |mut context: deterministic::Context| async move {
1641 let mut oracle = start_test_network_with_peers(
1643 context.child("network"),
1644 participants.clone(),
1645 true,
1646 )
1647 .await;
1648 let mut registrations = register_validators(&mut oracle, &participants).await;
1649
1650 let link = Link {
1652 latency: Duration::from_millis(50),
1653 jitter: Duration::from_millis(50),
1654 success_rate: 1.0,
1655 };
1656 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
1657
1658 let elector = L::default();
1660 let relay = Arc::new(mocks::relay::Relay::new());
1661 let mut reporters = HashMap::new();
1662 let mut engine_handlers = Vec::new();
1663 for (idx, validator) in participants.iter().enumerate() {
1664 let context = context
1666 .child("validator")
1667 .with_attribute("public_key", validator);
1668
1669 let reporter_config = mocks::reporter::Config {
1671 participants: participants.clone().try_into().unwrap(),
1672 scheme: schemes[idx].clone(),
1673 elector: elector.clone(),
1674 };
1675 let reporter_rng = StdRng::from_seed(reporter_seed);
1676 let reporter = mocks::reporter::Reporter::new(reporter_rng, reporter_config);
1677 reporters.insert(validator.clone(), reporter.clone());
1678 let application_cfg = mocks::application::Config {
1679 hasher: Sha256::default(),
1680 relay: relay.clone(),
1681 me: validator.clone(),
1682 propose_latency: (10.0, 5.0),
1683 verify_latency: (10.0, 5.0),
1684 certify_latency: (10.0, 5.0),
1685 should_certify: mocks::application::Certifier::Always,
1686 };
1687 let (actor, application) = mocks::application::Application::new(
1688 context.child("application"),
1689 application_cfg,
1690 );
1691 actor.start();
1692 let blocker = oracle.control(validator.clone());
1693 let cfg = config::Config {
1694 scheme: schemes[idx].clone(),
1695 elector: elector.clone(),
1696 blocker,
1697 automaton: application.clone(),
1698 relay: application.clone(),
1699 reporter: reporter.clone(),
1700 strategy: Sequential,
1701 partition: validator.to_string(),
1702 mailbox_size: NZUsize!(1024),
1703 epoch: Epoch::new(333),
1704 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
1705 Epoch::new(333),
1706 )),
1707 leader_timeout: Duration::from_secs(1),
1708 certification_timeout: Duration::from_secs(2),
1709 timeout_retry: Duration::from_secs(10),
1710 fetch_timeout: Duration::from_secs(1),
1711 activity_timeout,
1712 skip_timeout,
1713 fetch_concurrent: NZUsize!(4),
1714 replay_buffer: NZUsize!(1024 * 1024),
1715 write_buffer: NZUsize!(1024 * 1024),
1716 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
1717 forwarding: ForwardingPolicy::Disabled,
1718 };
1719 let engine = Engine::new(context.child("engine"), cfg);
1720
1721 let (pending, recovered, resolver) = registrations
1723 .remove(validator)
1724 .expect("validator should be registered");
1725 engine_handlers.push(engine.start(pending, recovered, resolver));
1726 }
1727
1728 let mut finalizers = Vec::new();
1730 for reporter in reporters.values_mut() {
1731 let (mut latest, mut monitor) = reporter.subscribe().await;
1732 finalizers.push(context.child("finalizer").spawn(move |_| async move {
1733 while latest < required_containers {
1734 latest = monitor.recv().await.expect("event missing");
1735 }
1736 }));
1737 }
1738
1739 let wait =
1741 context.random_range(Duration::from_millis(100)..Duration::from_millis(2_000));
1742 let result = select! {
1743 _ = context.sleep(wait) => {
1744 {
1746 let mut shutdowns = shutdowns.lock();
1747 debug!(shutdowns = *shutdowns, elapsed = ?wait, "restarting");
1748 *shutdowns += 1;
1749 }
1750 supervised.lock().push(reporters);
1751 false
1752 },
1753 _ = join_all(finalizers) => {
1754 let supervised = supervised.lock();
1756 for reporters in supervised.iter() {
1757 for reporter in reporters.values() {
1758 reporter.assert_no_faults();
1759 }
1760 }
1761 true
1762 },
1763 };
1764
1765 let blocked = oracle.blocked().await.unwrap();
1767 assert!(blocked.is_empty());
1768
1769 result
1770 };
1771
1772 let (complete, checkpoint) = prev_checkpoint
1773 .map_or_else(
1774 || deterministic::Runner::timed(Duration::from_secs(180)),
1775 deterministic::Runner::from,
1776 )
1777 .start_and_recover(f);
1778
1779 if complete {
1781 break;
1782 }
1783
1784 prev_checkpoint = Some(checkpoint);
1785 }
1786 }
1787
1788 test_for_all_fixtures!(unclean_shutdown);
1789
1790 fn backfill<S, F, L>(mut fixture: F)
1791 where
1792 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
1793 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
1794 L: Elector<S>,
1795 {
1796 let n = 4;
1798 let required_containers = View::new(100);
1799 let activity_timeout = ViewDelta::new(10);
1800 let skip_timeout = ViewDelta::new(5);
1801 let namespace = b"consensus".to_vec();
1802 let executor = deterministic::Runner::timed(Duration::from_secs(240));
1803 executor.start(|mut context| async move {
1804 let Fixture {
1806 participants,
1807 schemes,
1808 ..
1809 } = fixture(&mut context, &namespace, n);
1810 let mut oracle =
1811 start_test_network_with_peers(context.child("network"), participants.clone(), true)
1812 .await;
1813 let mut registrations = register_validators(&mut oracle, &participants).await;
1814
1815 let link = Link {
1817 latency: Duration::from_millis(10),
1818 jitter: Duration::from_millis(1),
1819 success_rate: 1.0,
1820 };
1821 link_validators(
1822 &mut oracle,
1823 &participants,
1824 Action::Link(link),
1825 Some(|_, i, j| ![i, j].contains(&0usize)),
1826 )
1827 .await;
1828
1829 let elector = L::default();
1831 let relay = Arc::new(mocks::relay::Relay::new());
1832 let mut reporters = Vec::new();
1833 let mut engine_handlers = Vec::new();
1834 for (idx_scheme, validator) in participants.iter().enumerate() {
1835 if idx_scheme == 0 {
1837 continue;
1838 }
1839
1840 let context = context
1842 .child("validator")
1843 .with_attribute("public_key", validator);
1844
1845 let reporter_config = mocks::reporter::Config {
1847 participants: participants.clone().try_into().unwrap(),
1848 scheme: schemes[idx_scheme].clone(),
1849 elector: elector.clone(),
1850 };
1851 let reporter =
1852 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
1853 reporters.push(reporter.clone());
1854 let application_cfg = mocks::application::Config {
1855 hasher: Sha256::default(),
1856 relay: relay.clone(),
1857 me: validator.clone(),
1858 propose_latency: (10.0, 5.0),
1859 verify_latency: (10.0, 5.0),
1860 certify_latency: (10.0, 5.0),
1861 should_certify: mocks::application::Certifier::Always,
1862 };
1863 let (actor, application) = mocks::application::Application::new(
1864 context.child("application"),
1865 application_cfg,
1866 );
1867 actor.start();
1868 let blocker = oracle.control(validator.clone());
1869 let cfg = config::Config {
1870 scheme: schemes[idx_scheme].clone(),
1871 elector: elector.clone(),
1872 blocker,
1873 automaton: application.clone(),
1874 relay: application.clone(),
1875 reporter: reporter.clone(),
1876 strategy: Sequential,
1877 partition: validator.to_string(),
1878 mailbox_size: NZUsize!(1024),
1879 epoch: Epoch::new(333),
1880 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
1881 Epoch::new(333),
1882 )),
1883 leader_timeout: Duration::from_secs(1),
1884 certification_timeout: Duration::from_secs(2),
1885 timeout_retry: Duration::from_secs(10),
1886 fetch_timeout: Duration::from_secs(1),
1887 activity_timeout,
1888 skip_timeout,
1889 fetch_concurrent: NZUsize!(4),
1890 replay_buffer: NZUsize!(1024 * 1024),
1891 write_buffer: NZUsize!(1024 * 1024),
1892 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
1893 forwarding: ForwardingPolicy::Disabled,
1894 };
1895 let engine = Engine::new(context.child("engine"), cfg);
1896
1897 let (pending, recovered, resolver) = registrations
1899 .remove(validator)
1900 .expect("validator should be registered");
1901 engine_handlers.push(engine.start(pending, recovered, resolver));
1902 }
1903
1904 let mut finalizers = Vec::new();
1906 for reporter in reporters.iter_mut() {
1907 let (mut latest, mut monitor) = reporter.subscribe().await;
1908 finalizers.push(context.child("finalizer").spawn(move |_| async move {
1909 while latest < required_containers {
1910 latest = monitor.recv().await.expect("event missing");
1911 }
1912 }));
1913 }
1914 join_all(finalizers).await;
1915
1916 let link = Link {
1918 latency: Duration::from_secs(3),
1919 jitter: Duration::from_millis(0),
1920 success_rate: 1.0,
1921 };
1922 link_validators(
1923 &mut oracle,
1924 &participants,
1925 Action::Update(link.clone()),
1926 Some(|_, i, j| ![i, j].contains(&0usize)),
1927 )
1928 .await;
1929
1930 context.sleep(Duration::from_secs(60)).await;
1932
1933 link_validators(
1935 &mut oracle,
1936 &participants,
1937 Action::Unlink,
1938 Some(|_, i, j| [i, j].contains(&1usize) && ![i, j].contains(&0usize)),
1939 )
1940 .await;
1941
1942 let me = participants[0].clone();
1944 let context = context.child("validator").with_attribute("public_key", &me);
1945
1946 link_validators(
1948 &mut oracle,
1949 &participants,
1950 Action::Link(link),
1951 Some(|_, i, j| [i, j].contains(&0usize) && ![i, j].contains(&1usize)),
1952 )
1953 .await;
1954
1955 let link = Link {
1957 latency: Duration::from_millis(10),
1958 jitter: Duration::from_millis(3),
1959 success_rate: 1.0,
1960 };
1961 link_validators(
1962 &mut oracle,
1963 &participants,
1964 Action::Update(link),
1965 Some(|_, i, j| ![i, j].contains(&1usize)),
1966 )
1967 .await;
1968
1969 let reporter_config = mocks::reporter::Config {
1971 participants: participants.clone().try_into().unwrap(),
1972 scheme: schemes[0].clone(),
1973 elector: elector.clone(),
1974 };
1975 let mut reporter =
1976 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
1977 reporters.push(reporter.clone());
1978 let application_cfg = mocks::application::Config {
1979 hasher: Sha256::default(),
1980 relay: relay.clone(),
1981 me: me.clone(),
1982 propose_latency: (10.0, 5.0),
1983 verify_latency: (10.0, 5.0),
1984 certify_latency: (10.0, 5.0),
1985 should_certify: mocks::application::Certifier::Always,
1986 };
1987 let (actor, application) =
1988 mocks::application::Application::new(context.child("application"), application_cfg);
1989 actor.start();
1990 let blocker = oracle.control(me.clone());
1991 let cfg = config::Config {
1992 scheme: schemes[0].clone(),
1993 elector: elector.clone(),
1994 blocker,
1995 automaton: application.clone(),
1996 relay: application.clone(),
1997 reporter: reporter.clone(),
1998 strategy: Sequential,
1999 partition: me.to_string(),
2000 mailbox_size: NZUsize!(1024),
2001 epoch: Epoch::new(333),
2002 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(Epoch::new(
2003 333,
2004 ))),
2005 leader_timeout: Duration::from_secs(1),
2006 certification_timeout: Duration::from_secs(2),
2007 timeout_retry: Duration::from_secs(10),
2008 fetch_timeout: Duration::from_secs(1),
2009 activity_timeout,
2010 skip_timeout,
2011 fetch_concurrent: NZUsize!(4),
2012 replay_buffer: NZUsize!(1024 * 1024),
2013 write_buffer: NZUsize!(1024 * 1024),
2014 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
2015 forwarding: ForwardingPolicy::Disabled,
2016 };
2017 let engine = Engine::new(context.child("engine"), cfg);
2018
2019 let (pending, recovered, resolver) = registrations
2021 .remove(&me)
2022 .expect("validator should be registered");
2023 engine_handlers.push(engine.start(pending, recovered, resolver));
2024
2025 let (mut latest, mut monitor) = reporter.subscribe().await;
2027 while latest < required_containers {
2028 latest = monitor.recv().await.expect("event missing");
2029 }
2030
2031 let blocked = oracle.blocked().await.unwrap();
2033 assert!(blocked.is_empty());
2034 });
2035 }
2036
2037 test_for_all_fixtures!(backfill);
2038
2039 fn one_offline<S, F, L>(mut fixture: F)
2040 where
2041 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
2042 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
2043 L: Elector<S>,
2044 {
2045 let n = 5;
2047 let quorum = quorum(n) as usize;
2048 let required_containers = View::new(100);
2049 let activity_timeout = ViewDelta::new(10);
2050 let skip_timeout = ViewDelta::new(5);
2051 let max_exceptions = 10;
2052 let namespace = b"consensus".to_vec();
2053 let executor = deterministic::Runner::timed(Duration::from_secs(300));
2054 executor.start(|mut context| async move {
2055 let Fixture {
2057 participants,
2058 schemes,
2059 ..
2060 } = fixture(&mut context, &namespace, n);
2061 let mut oracle =
2062 start_test_network_with_peers(context.child("network"), participants.clone(), true)
2063 .await;
2064 let mut registrations = register_validators(&mut oracle, &participants).await;
2065
2066 let link = Link {
2068 latency: Duration::from_millis(10),
2069 jitter: Duration::from_millis(1),
2070 success_rate: 1.0,
2071 };
2072 link_validators(
2073 &mut oracle,
2074 &participants,
2075 Action::Link(link),
2076 Some(|_, i, j| ![i, j].contains(&0usize)),
2077 )
2078 .await;
2079
2080 let elector = L::default();
2082 let relay = Arc::new(mocks::relay::Relay::new());
2083 let mut reporters = Vec::new();
2084 let mut engine_handlers = Vec::new();
2085 for (idx_scheme, validator) in participants.iter().enumerate() {
2086 if idx_scheme == 0 {
2088 continue;
2089 }
2090
2091 let context = context
2093 .child("validator")
2094 .with_attribute("public_key", validator);
2095
2096 let reporter_config = mocks::reporter::Config {
2098 participants: participants.clone().try_into().unwrap(),
2099 scheme: schemes[idx_scheme].clone(),
2100 elector: elector.clone(),
2101 };
2102 let reporter =
2103 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
2104 reporters.push(reporter.clone());
2105 let application_cfg = mocks::application::Config {
2106 hasher: Sha256::default(),
2107 relay: relay.clone(),
2108 me: validator.clone(),
2109 propose_latency: (10.0, 5.0),
2110 verify_latency: (10.0, 5.0),
2111 certify_latency: (10.0, 5.0),
2112 should_certify: mocks::application::Certifier::Always,
2113 };
2114 let (actor, application) = mocks::application::Application::new(
2115 context.child("application"),
2116 application_cfg,
2117 );
2118 actor.start();
2119 let blocker = oracle.control(validator.clone());
2120 let cfg = config::Config {
2121 scheme: schemes[idx_scheme].clone(),
2122 elector: elector.clone(),
2123 blocker,
2124 automaton: application.clone(),
2125 relay: application.clone(),
2126 reporter: reporter.clone(),
2127 strategy: Sequential,
2128 partition: validator.to_string(),
2129 mailbox_size: NZUsize!(1024),
2130 epoch: Epoch::new(333),
2131 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
2132 Epoch::new(333),
2133 )),
2134 leader_timeout: Duration::from_secs(1),
2135 certification_timeout: Duration::from_secs(2),
2136 timeout_retry: Duration::from_secs(10),
2137 fetch_timeout: Duration::from_secs(1),
2138 activity_timeout,
2139 skip_timeout,
2140 fetch_concurrent: NZUsize!(4),
2141 replay_buffer: NZUsize!(1024 * 1024),
2142 write_buffer: NZUsize!(1024 * 1024),
2143 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
2144 forwarding: ForwardingPolicy::Disabled,
2145 };
2146 let engine = Engine::new(context.child("engine"), cfg);
2147
2148 let (pending, recovered, resolver) = registrations
2150 .remove(validator)
2151 .expect("validator should be registered");
2152 engine_handlers.push(engine.start(pending, recovered, resolver));
2153 }
2154
2155 let mut finalizers = Vec::new();
2157 for reporter in reporters.iter_mut() {
2158 let (mut latest, mut monitor) = reporter.subscribe().await;
2159 finalizers.push(context.child("finalizer").spawn(move |_| async move {
2160 while latest < required_containers {
2161 latest = monitor.recv().await.expect("event missing");
2162 }
2163 }));
2164 }
2165 join_all(finalizers).await;
2166
2167 let exceptions = 0;
2169 let offline = &participants[0];
2170 for reporter in reporters.iter() {
2171 reporter.assert_no_faults();
2173
2174 reporter.assert_no_invalid();
2176
2177 let mut exceptions = 0;
2179 {
2180 let notarizes = reporter.notarizes.lock();
2181 for (view, payloads) in notarizes.iter() {
2182 for participants in payloads.values() {
2183 if participants.contains(offline) {
2184 panic!("view: {view}");
2185 }
2186 }
2187 }
2188 }
2189 {
2190 let nullifies = reporter.nullifies.lock();
2191 for (view, participants) in nullifies.iter() {
2192 if participants.contains(offline) {
2193 panic!("view: {view}");
2194 }
2195 }
2196 }
2197 {
2198 let finalizes = reporter.finalizes.lock();
2199 for (view, payloads) in finalizes.iter() {
2200 for finalizers in payloads.values() {
2201 if finalizers.contains(offline) {
2202 panic!("view: {view}");
2203 }
2204 }
2205 }
2206 }
2207
2208 let mut offline_views = Vec::new();
2210 {
2211 let leaders = reporter.leaders.lock();
2212 for (view, leader) in leaders.iter() {
2213 if leader == offline {
2214 offline_views.push(*view);
2215 }
2216 }
2217 }
2218 assert!(!offline_views.is_empty());
2219
2220 {
2222 let nullifies = reporter.nullifies.lock();
2223 for view in offline_views.iter() {
2224 let nullifies = nullifies.get(view).map_or(0, |n| n.len());
2225 if nullifies < quorum {
2226 warn!("missing expected view nullifies: {}", view);
2227 exceptions += 1;
2228 }
2229 }
2230 }
2231 {
2232 let nullifications = reporter.nullifications.lock();
2233 for view in offline_views.iter() {
2234 if !nullifications.contains_key(view) {
2235 warn!("missing expected view nullifies: {}", view);
2236 exceptions += 1;
2237 }
2238 }
2239 }
2240
2241 assert!(exceptions <= max_exceptions);
2243 }
2244 assert!(exceptions <= max_exceptions);
2245
2246 let blocked = oracle.blocked().await.unwrap();
2248 assert!(blocked.is_empty());
2249
2250 let encoded = context.encode();
2252 let leader_label = format!("leader=\"{}\"", offline);
2253 assert!(
2254 count_nonzero_metric_lines(&encoded, &["_timeouts", &leader_label]) >= n - 1,
2255 "expected timeout metrics for offline leader"
2256 );
2257 assert_eq!(
2258 count_nonzero_metric_lines(&encoded, &["_nullifications", &leader_label]),
2259 n - 1,
2260 "expected all online nodes to record _nullifications for offline leader"
2261 );
2262 });
2263 }
2264
2265 test_for_all_fixtures!(one_offline);
2266
2267 fn slow_validator<S, F, L>(mut fixture: F)
2268 where
2269 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
2270 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
2271 L: Elector<S>,
2272 {
2273 let n = 5;
2275 let required_containers = View::new(50);
2276 let activity_timeout = ViewDelta::new(10);
2277 let skip_timeout = ViewDelta::new(5);
2278 let namespace = b"consensus".to_vec();
2279 let executor = deterministic::Runner::timed(Duration::from_secs(300));
2280 executor.start(|mut context| async move {
2281 let Fixture {
2283 participants,
2284 schemes,
2285 ..
2286 } = fixture(&mut context, &namespace, n);
2287 let mut oracle =
2288 start_test_network_with_peers(context.child("network"), participants.clone(), true)
2289 .await;
2290 let mut registrations = register_validators(&mut oracle, &participants).await;
2291
2292 let link = Link {
2294 latency: Duration::from_millis(10),
2295 jitter: Duration::from_millis(1),
2296 success_rate: 1.0,
2297 };
2298 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
2299
2300 let elector = L::default();
2302 let relay = Arc::new(mocks::relay::Relay::new());
2303 let mut reporters = Vec::new();
2304 let mut engine_handlers = Vec::new();
2305 for (idx_scheme, validator) in participants.iter().enumerate() {
2306 let context = context
2308 .child("validator")
2309 .with_attribute("public_key", validator);
2310
2311 let reporter_config = mocks::reporter::Config {
2313 participants: participants.clone().try_into().unwrap(),
2314 scheme: schemes[idx_scheme].clone(),
2315 elector: elector.clone(),
2316 };
2317 let reporter =
2318 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
2319 reporters.push(reporter.clone());
2320 let application_cfg = if idx_scheme == 0 {
2321 mocks::application::Config {
2322 hasher: Sha256::default(),
2323 relay: relay.clone(),
2324 me: validator.clone(),
2325 propose_latency: (10_000.0, 0.0),
2326 verify_latency: (10_000.0, 5.0),
2327 certify_latency: (10_000.0, 5.0),
2328 should_certify: mocks::application::Certifier::Always,
2329 }
2330 } else {
2331 mocks::application::Config {
2332 hasher: Sha256::default(),
2333 relay: relay.clone(),
2334 me: validator.clone(),
2335 propose_latency: (10.0, 5.0),
2336 verify_latency: (10.0, 5.0),
2337 certify_latency: (10.0, 5.0),
2338 should_certify: mocks::application::Certifier::Always,
2339 }
2340 };
2341 let (actor, application) = mocks::application::Application::new(
2342 context.child("application"),
2343 application_cfg,
2344 );
2345 actor.start();
2346 let blocker = oracle.control(validator.clone());
2347 let cfg = config::Config {
2348 scheme: schemes[idx_scheme].clone(),
2349 elector: elector.clone(),
2350 blocker,
2351 automaton: application.clone(),
2352 relay: application.clone(),
2353 reporter: reporter.clone(),
2354 strategy: Sequential,
2355 partition: validator.to_string(),
2356 mailbox_size: NZUsize!(1024),
2357 epoch: Epoch::new(333),
2358 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
2359 Epoch::new(333),
2360 )),
2361 leader_timeout: Duration::from_secs(1),
2362 certification_timeout: Duration::from_secs(2),
2363 timeout_retry: Duration::from_secs(10),
2364 fetch_timeout: Duration::from_secs(1),
2365 activity_timeout,
2366 skip_timeout,
2367 fetch_concurrent: NZUsize!(4),
2368 replay_buffer: NZUsize!(1024 * 1024),
2369 write_buffer: NZUsize!(1024 * 1024),
2370 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
2371 forwarding: ForwardingPolicy::Disabled,
2372 };
2373 let engine = Engine::new(context.child("engine"), cfg);
2374
2375 let (pending, recovered, resolver) = registrations
2377 .remove(validator)
2378 .expect("validator should be registered");
2379 engine_handlers.push(engine.start(pending, recovered, resolver));
2380 }
2381
2382 let mut finalizers = Vec::new();
2384 for reporter in reporters.iter_mut() {
2385 let (mut latest, mut monitor) = reporter.subscribe().await;
2386 finalizers.push(context.child("finalizer").spawn(move |_| async move {
2387 while latest < required_containers {
2388 latest = monitor.recv().await.expect("event missing");
2389 }
2390 }));
2391 }
2392 join_all(finalizers).await;
2393
2394 let slow = &participants[0];
2396 for reporter in reporters.iter() {
2397 reporter.assert_no_faults();
2399
2400 reporter.assert_no_invalid();
2402
2403 {
2406 let notarizes = reporter.notarizes.lock();
2407 assert!(notarizes.values().all(|payloads| {
2408 payloads
2409 .values()
2410 .all(|participants| !participants.contains(slow))
2411 }));
2412 }
2413 {
2414 let finalizes = reporter.finalizes.lock();
2415 assert!(finalizes.values().all(|payloads| {
2416 payloads
2417 .values()
2418 .all(|participants| !participants.contains(slow))
2419 }));
2420 }
2421
2422 {
2424 let finalizations = reporter.finalizations.lock();
2425 assert!(finalizations
2426 .keys()
2427 .any(|view| *view >= required_containers));
2428 }
2429 }
2430
2431 let blocked = oracle.blocked().await.unwrap();
2433 assert!(blocked.is_empty());
2434 });
2435 }
2436
2437 test_for_all_fixtures!(slow_validator);
2438
2439 fn all_recovery<S, F, L>(mut fixture: F)
2440 where
2441 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
2442 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
2443 L: Elector<S>,
2444 {
2445 let n = 5;
2447 let required_containers = View::new(100);
2448 let activity_timeout = ViewDelta::new(10);
2449 let skip_timeout = ViewDelta::new(2);
2450 let namespace = b"consensus".to_vec();
2451 let executor = deterministic::Runner::timed(Duration::from_secs(1800));
2452 executor.start(|mut context| async move {
2453 let Fixture {
2455 participants,
2456 schemes,
2457 ..
2458 } = fixture(&mut context, &namespace, n);
2459 let mut oracle =
2460 start_test_network_with_peers(context.child("network"), participants.clone(), true)
2461 .await;
2462 let mut registrations = register_validators(&mut oracle, &participants).await;
2463
2464 let link = Link {
2466 latency: Duration::from_secs(3),
2467 jitter: Duration::from_millis(0),
2468 success_rate: 1.0,
2469 };
2470 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
2471
2472 let elector = L::default();
2474 let relay = Arc::new(mocks::relay::Relay::new());
2475 let mut reporters = Vec::new();
2476 let mut engine_handlers = Vec::new();
2477 for (idx, validator) in participants.iter().enumerate() {
2478 let context = context
2480 .child("validator")
2481 .with_attribute("public_key", validator);
2482
2483 let reporter_config = mocks::reporter::Config {
2485 participants: participants.clone().try_into().unwrap(),
2486 scheme: schemes[idx].clone(),
2487 elector: elector.clone(),
2488 };
2489 let reporter =
2490 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
2491 reporters.push(reporter.clone());
2492 let application_cfg = mocks::application::Config {
2493 hasher: Sha256::default(),
2494 relay: relay.clone(),
2495 me: validator.clone(),
2496 propose_latency: (10.0, 5.0),
2497 verify_latency: (10.0, 5.0),
2498 certify_latency: (10.0, 5.0),
2499 should_certify: mocks::application::Certifier::Always,
2500 };
2501 let (actor, application) = mocks::application::Application::new(
2502 context.child("application"),
2503 application_cfg,
2504 );
2505 actor.start();
2506 let blocker = oracle.control(validator.clone());
2507 let cfg = config::Config {
2508 scheme: schemes[idx].clone(),
2509 elector: elector.clone(),
2510 blocker,
2511 automaton: application.clone(),
2512 relay: application.clone(),
2513 reporter: reporter.clone(),
2514 strategy: Sequential,
2515 partition: validator.to_string(),
2516 mailbox_size: NZUsize!(1024),
2517 epoch: Epoch::new(333),
2518 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
2519 Epoch::new(333),
2520 )),
2521 leader_timeout: Duration::from_secs(1),
2522 certification_timeout: Duration::from_secs(2),
2523 timeout_retry: Duration::from_secs(10),
2524 fetch_timeout: Duration::from_secs(1),
2525 activity_timeout,
2526 skip_timeout,
2527 fetch_concurrent: NZUsize!(4),
2528 replay_buffer: NZUsize!(1024 * 1024),
2529 write_buffer: NZUsize!(1024 * 1024),
2530 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
2531 forwarding: ForwardingPolicy::Disabled,
2532 };
2533 let engine = Engine::new(context.child("engine"), cfg);
2534
2535 let (pending, recovered, resolver) = registrations
2537 .remove(validator)
2538 .expect("validator should be registered");
2539 engine_handlers.push(engine.start(pending, recovered, resolver));
2540 }
2541
2542 let mut finalizers = Vec::new();
2544 for reporter in reporters.iter_mut() {
2545 let (_, mut monitor) = reporter.subscribe().await;
2546 finalizers.push(context.child("finalizer").spawn(move |context| async move {
2547 select! {
2548 _timeout = context.sleep(Duration::from_secs(60)) => {},
2549 _done = monitor.recv() => {
2550 panic!("engine should not notarize or finalize anything");
2551 },
2552 }
2553 }));
2554 }
2555 join_all(finalizers).await;
2556
2557 link_validators(&mut oracle, &participants, Action::Unlink, None).await;
2559
2560 context.sleep(Duration::from_secs(60)).await;
2562
2563 let mut latest = View::zero();
2565 for reporter in reporters.iter() {
2566 let nullifies = reporter.nullifies.lock();
2567 let max = nullifies.keys().max().unwrap();
2568 if *max > latest {
2569 latest = *max;
2570 }
2571 }
2572
2573 let link = Link {
2575 latency: Duration::from_millis(10),
2576 jitter: Duration::from_millis(1),
2577 success_rate: 1.0,
2578 };
2579 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
2580
2581 let mut finalizers = Vec::new();
2583 for reporter in reporters.iter_mut() {
2584 let (mut latest, mut monitor) = reporter.subscribe().await;
2585 finalizers.push(context.child("finalizer").spawn(move |_| async move {
2586 while latest < required_containers {
2587 latest = monitor.recv().await.expect("event missing");
2588 }
2589 }));
2590 }
2591 join_all(finalizers).await;
2592
2593 for reporter in reporters.iter() {
2595 reporter.assert_no_faults();
2597
2598 reporter.assert_no_invalid();
2600
2601 {
2606 let mut found = 0;
2610 let notarizations = reporter.notarizations.lock();
2611 for view in View::range(latest, latest.saturating_add(activity_timeout)) {
2612 if notarizations.contains_key(&view) {
2613 found += 1;
2614 }
2615 }
2616 let tolerated_missing = skip_timeout.get().saturating_add(1);
2617 assert!(
2618 found >= activity_timeout.get().saturating_sub(tolerated_missing),
2619 "found: {found}"
2620 );
2621 }
2622 }
2623
2624 let blocked = oracle.blocked().await.unwrap();
2626 assert!(blocked.is_empty());
2627 });
2628 }
2629
2630 test_for_all_fixtures!(all_recovery);
2631
2632 fn partition<S, F, L>(mut fixture: F)
2633 where
2634 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
2635 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
2636 L: Elector<S>,
2637 {
2638 let n = 10;
2640 let required_containers = View::new(50);
2641 let activity_timeout = ViewDelta::new(10);
2642 let skip_timeout = ViewDelta::new(5);
2643 let namespace = b"consensus".to_vec();
2644 let executor = deterministic::Runner::timed(Duration::from_secs(900));
2645 executor.start(|mut context| async move {
2646 let Fixture {
2648 participants,
2649 schemes,
2650 ..
2651 } = fixture(&mut context, &namespace, n);
2652 let mut oracle =
2653 start_test_network_with_peers(context.child("network"), participants.clone(), true)
2654 .await;
2655 let mut registrations = register_validators(&mut oracle, &participants).await;
2656
2657 let link = Link {
2659 latency: Duration::from_millis(10),
2660 jitter: Duration::from_millis(1),
2661 success_rate: 1.0,
2662 };
2663 link_validators(&mut oracle, &participants, Action::Link(link.clone()), None).await;
2664
2665 let elector = L::default();
2667 let relay = Arc::new(mocks::relay::Relay::new());
2668 let mut reporters = Vec::new();
2669 let mut engine_handlers = Vec::new();
2670 for (idx, validator) in participants.iter().enumerate() {
2671 let context = context
2673 .child("validator")
2674 .with_attribute("public_key", validator);
2675
2676 let reporter_config = mocks::reporter::Config {
2678 participants: participants.clone().try_into().unwrap(),
2679 scheme: schemes[idx].clone(),
2680 elector: elector.clone(),
2681 };
2682 let reporter =
2683 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
2684 reporters.push(reporter.clone());
2685 let application_cfg = mocks::application::Config {
2686 hasher: Sha256::default(),
2687 relay: relay.clone(),
2688 me: validator.clone(),
2689 propose_latency: (10.0, 5.0),
2690 verify_latency: (10.0, 5.0),
2691 certify_latency: (10.0, 5.0),
2692 should_certify: mocks::application::Certifier::Always,
2693 };
2694 let (actor, application) = mocks::application::Application::new(
2695 context.child("application"),
2696 application_cfg,
2697 );
2698 actor.start();
2699 let blocker = oracle.control(validator.clone());
2700 let cfg = config::Config {
2701 scheme: schemes[idx].clone(),
2702 elector: elector.clone(),
2703 blocker,
2704 automaton: application.clone(),
2705 relay: application.clone(),
2706 reporter: reporter.clone(),
2707 strategy: Sequential,
2708 partition: validator.to_string(),
2709 mailbox_size: NZUsize!(1024),
2710 epoch: Epoch::new(333),
2711 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
2712 Epoch::new(333),
2713 )),
2714 leader_timeout: Duration::from_secs(1),
2715 certification_timeout: Duration::from_secs(2),
2716 timeout_retry: Duration::from_secs(10),
2717 fetch_timeout: Duration::from_secs(1),
2718 activity_timeout,
2719 skip_timeout,
2720 fetch_concurrent: NZUsize!(4),
2721 replay_buffer: NZUsize!(1024 * 1024),
2722 write_buffer: NZUsize!(1024 * 1024),
2723 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
2724 forwarding: ForwardingPolicy::Disabled,
2725 };
2726 let engine = Engine::new(context.child("engine"), cfg);
2727
2728 let (pending, recovered, resolver) = registrations
2730 .remove(validator)
2731 .expect("validator should be registered");
2732 engine_handlers.push(engine.start(pending, recovered, resolver));
2733 }
2734
2735 let mut finalizers = Vec::new();
2737 for reporter in reporters.iter_mut() {
2738 let (mut latest, mut monitor) = reporter.subscribe().await;
2739 finalizers.push(context.child("finalizer").spawn(move |_| async move {
2740 while latest < required_containers {
2741 latest = monitor.recv().await.expect("event missing");
2742 }
2743 }));
2744 }
2745 join_all(finalizers).await;
2746
2747 fn separated(n: usize, a: usize, b: usize) -> bool {
2749 let m = n / 2;
2750 (a < m && b >= m) || (a >= m && b < m)
2751 }
2752 link_validators(&mut oracle, &participants, Action::Unlink, Some(separated)).await;
2753
2754 context.sleep(Duration::from_secs(10)).await;
2756
2757 let mut finalizers = Vec::new();
2759 for reporter in reporters.iter_mut() {
2760 let (_, mut monitor) = reporter.subscribe().await;
2761 finalizers.push(context.child("finalizer").spawn(move |context| async move {
2762 select! {
2763 _timeout = context.sleep(Duration::from_secs(60)) => {},
2764 _done = monitor.recv() => {
2765 panic!("engine should not notarize or finalize anything");
2766 },
2767 }
2768 }));
2769 }
2770 join_all(finalizers).await;
2771
2772 link_validators(
2774 &mut oracle,
2775 &participants,
2776 Action::Link(link),
2777 Some(separated),
2778 )
2779 .await;
2780
2781 let mut finalizers = Vec::new();
2783 for reporter in reporters.iter_mut() {
2784 let (mut latest, mut monitor) = reporter.subscribe().await;
2785 let required = latest.saturating_add(ViewDelta::new(required_containers.get()));
2786 finalizers.push(context.child("finalizer").spawn(move |_| async move {
2787 while latest < required {
2788 latest = monitor.recv().await.expect("event missing");
2789 }
2790 }));
2791 }
2792 join_all(finalizers).await;
2793
2794 for reporter in reporters.iter() {
2796 reporter.assert_no_faults();
2798
2799 reporter.assert_no_invalid();
2801 }
2802
2803 let blocked = oracle.blocked().await.unwrap();
2805 assert!(blocked.is_empty());
2806 });
2807 }
2808
2809 test_for_all_fixtures!(partition);
2810
2811 fn slow_and_lossy_links_seeded<S, F, L>(seed: u64, mut fixture: F) -> String
2812 where
2813 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
2814 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
2815 L: Elector<S>,
2816 {
2817 let n = 5;
2819 let required_containers = View::new(50);
2820 let activity_timeout = ViewDelta::new(10);
2821 let skip_timeout = ViewDelta::new(5);
2822 let namespace = b"consensus".to_vec();
2823 let cfg = deterministic::Config::new()
2824 .with_seed(seed)
2825 .with_timeout(Some(Duration::from_secs(5_000)));
2826 let executor = deterministic::Runner::new(cfg);
2827 executor.start(|mut context| async move {
2828 let Fixture {
2830 participants,
2831 schemes,
2832 ..
2833 } = fixture(&mut context, &namespace, n);
2834 let mut oracle =
2835 start_test_network_with_peers(context.child("network"), participants.clone(), true)
2836 .await;
2837 let mut registrations = register_validators(&mut oracle, &participants).await;
2838
2839 let degraded_link = Link {
2841 latency: Duration::from_millis(200),
2842 jitter: Duration::from_millis(150),
2843 success_rate: 0.5,
2844 };
2845 link_validators(
2846 &mut oracle,
2847 &participants,
2848 Action::Link(degraded_link),
2849 None,
2850 )
2851 .await;
2852
2853 let elector = L::default();
2855 let relay = Arc::new(mocks::relay::Relay::new());
2856 let mut reporters = Vec::new();
2857 let mut engine_handlers = Vec::new();
2858 for (idx, validator) in participants.iter().enumerate() {
2859 let context = context
2861 .child("validator")
2862 .with_attribute("public_key", validator);
2863
2864 let reporter_config = mocks::reporter::Config {
2866 participants: participants.clone().try_into().unwrap(),
2867 scheme: schemes[idx].clone(),
2868 elector: elector.clone(),
2869 };
2870 let reporter =
2871 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
2872 reporters.push(reporter.clone());
2873 let application_cfg = mocks::application::Config {
2874 hasher: Sha256::default(),
2875 relay: relay.clone(),
2876 me: validator.clone(),
2877 propose_latency: (10.0, 5.0),
2878 verify_latency: (10.0, 5.0),
2879 certify_latency: (10.0, 5.0),
2880 should_certify: mocks::application::Certifier::Always,
2881 };
2882 let (actor, application) = mocks::application::Application::new(
2883 context.child("application"),
2884 application_cfg,
2885 );
2886 actor.start();
2887 let blocker = oracle.control(validator.clone());
2888 let cfg = config::Config {
2889 scheme: schemes[idx].clone(),
2890 elector: elector.clone(),
2891 blocker,
2892 automaton: application.clone(),
2893 relay: application.clone(),
2894 reporter: reporter.clone(),
2895 strategy: Sequential,
2896 partition: validator.to_string(),
2897 mailbox_size: NZUsize!(1024),
2898 epoch: Epoch::new(333),
2899 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
2900 Epoch::new(333),
2901 )),
2902 leader_timeout: Duration::from_secs(1),
2903 certification_timeout: Duration::from_secs(2),
2904 timeout_retry: Duration::from_secs(10),
2905 fetch_timeout: Duration::from_secs(1),
2906 activity_timeout,
2907 skip_timeout,
2908 fetch_concurrent: NZUsize!(4),
2909 replay_buffer: NZUsize!(1024 * 1024),
2910 write_buffer: NZUsize!(1024 * 1024),
2911 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
2912 forwarding: ForwardingPolicy::Disabled,
2913 };
2914 let engine = Engine::new(context.child("engine"), cfg);
2915
2916 let (pending, recovered, resolver) = registrations
2918 .remove(validator)
2919 .expect("validator should be registered");
2920 engine_handlers.push(engine.start(pending, recovered, resolver));
2921 }
2922
2923 let mut finalizers = Vec::new();
2925 for reporter in reporters.iter_mut() {
2926 let (mut latest, mut monitor) = reporter.subscribe().await;
2927 finalizers.push(context.child("finalizer").spawn(move |_| async move {
2928 while latest < required_containers {
2929 latest = monitor.recv().await.expect("event missing");
2930 }
2931 }));
2932 }
2933 join_all(finalizers).await;
2934
2935 for reporter in reporters.iter() {
2937 reporter.assert_no_faults();
2939
2940 reporter.assert_no_invalid();
2942 }
2943
2944 let blocked = oracle.blocked().await.unwrap();
2946 assert!(blocked.is_empty());
2947
2948 context.auditor().state()
2949 })
2950 }
2951
2952 fn slow_and_lossy_links<S, F, L>(fixture: F) -> String
2953 where
2954 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
2955 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
2956 L: Elector<S>,
2957 {
2958 slow_and_lossy_links_seeded::<_, _, L>(6, fixture)
2959 }
2960
2961 test_for_all_fixtures!(slow_and_lossy_links);
2962
2963 fn determinism<S, F, L>(seed: u64, fixture: F)
2964 where
2965 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
2966 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S> + Copy,
2967 L: Elector<S>,
2968 {
2969 assert_eq!(
2972 slow_and_lossy_links_seeded::<_, _, L>(seed, fixture),
2973 slow_and_lossy_links_seeded::<_, _, L>(seed, fixture),
2974 );
2975 }
2976
2977 test_for_all_fixtures!(determinism, seeds = 5);
2978
2979 #[test_group("slow")]
2980 #[test_traced]
2981 fn test_distinct_states() {
2982 macro_rules! collect {
2984 ($vec:ident, $suffix:ident, $elector:ty, $fixture:expr) => {
2985 $vec.push((
2986 stringify!($suffix),
2987 slow_and_lossy_links_seeded::<_, _, $elector>(7, $fixture),
2988 ));
2989 };
2990 }
2991 let mut states = Vec::new();
2992 for_each_fixture!(collect!(states));
2993 for pair in states.windows(2) {
2994 assert_ne!(
2995 pair[0].1, pair[1].1,
2996 "state {} equals state {}",
2997 pair[0].0, pair[1].0
2998 );
2999 }
3000 }
3001
3002 fn conflicter<S, F, L>(seed: u64, mut fixture: F)
3003 where
3004 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3005 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3006 L: Elector<S>,
3007 {
3008 let n = 4;
3010 let required_containers = View::new(50);
3011 let activity_timeout = ViewDelta::new(10);
3012 let skip_timeout = ViewDelta::new(5);
3013 let namespace = b"consensus".to_vec();
3014 let cfg = deterministic::Config::new()
3015 .with_seed(seed)
3016 .with_timeout(Some(Duration::from_secs(30)));
3017 let executor = deterministic::Runner::new(cfg);
3018 executor.start(|mut context| async move {
3019 let Fixture {
3021 participants,
3022 schemes,
3023 ..
3024 } = fixture(&mut context, &namespace, n);
3025 let mut oracle =
3026 start_test_network_with_peers(context.child("network"), participants.clone(), true)
3027 .await;
3028 let mut registrations = register_validators(&mut oracle, &participants).await;
3029
3030 let link = Link {
3032 latency: Duration::from_millis(10),
3033 jitter: Duration::from_millis(1),
3034 success_rate: 1.0,
3035 };
3036 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
3037
3038 let elector = L::default();
3040 let relay = Arc::new(mocks::relay::Relay::new());
3041 let mut reporters = Vec::new();
3042 for (idx_scheme, validator) in participants.iter().enumerate() {
3043 let context = context
3045 .child("validator")
3046 .with_attribute("public_key", validator);
3047
3048 let reporter_config = mocks::reporter::Config {
3050 participants: participants.clone().try_into().unwrap(),
3051 scheme: schemes[idx_scheme].clone(),
3052 elector: elector.clone(),
3053 };
3054 let reporter =
3055 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3056 let (pending, recovered, resolver) = registrations
3057 .remove(validator)
3058 .expect("validator should be registered");
3059 if idx_scheme == 0 {
3060 let cfg = mocks::conflicter::Config {
3061 scheme: schemes[idx_scheme].clone(),
3062 };
3063
3064 let engine: mocks::conflicter::Conflicter<_, _, Sha256> =
3065 mocks::conflicter::Conflicter::new(context.child("byzantine_engine"), cfg);
3066 engine.start(pending);
3067 } else {
3068 reporters.push(reporter.clone());
3069 let application_cfg = mocks::application::Config {
3070 hasher: Sha256::default(),
3071 relay: relay.clone(),
3072 me: validator.clone(),
3073 propose_latency: (10.0, 5.0),
3074 verify_latency: (10.0, 5.0),
3075 certify_latency: (10.0, 5.0),
3076 should_certify: mocks::application::Certifier::Always,
3077 };
3078 let (actor, application) = mocks::application::Application::new(
3079 context.child("application"),
3080 application_cfg,
3081 );
3082 actor.start();
3083 let blocker = oracle.control(validator.clone());
3084 let cfg = config::Config {
3085 scheme: schemes[idx_scheme].clone(),
3086 elector: elector.clone(),
3087 blocker,
3088 automaton: application.clone(),
3089 relay: application.clone(),
3090 reporter: reporter.clone(),
3091 strategy: Sequential,
3092 partition: validator.to_string(),
3093 mailbox_size: NZUsize!(1024),
3094 epoch: Epoch::new(333),
3095 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
3096 Epoch::new(333),
3097 )),
3098 leader_timeout: Duration::from_secs(1),
3099 certification_timeout: Duration::from_secs(2),
3100 timeout_retry: Duration::from_secs(10),
3101 fetch_timeout: Duration::from_secs(1),
3102 activity_timeout,
3103 skip_timeout,
3104 fetch_concurrent: NZUsize!(4),
3105 replay_buffer: NZUsize!(1024 * 1024),
3106 write_buffer: NZUsize!(1024 * 1024),
3107 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3108 forwarding: ForwardingPolicy::Disabled,
3109 };
3110 let engine = Engine::new(context.child("engine"), cfg);
3111 engine.start(pending, recovered, resolver);
3112 }
3113 }
3114
3115 let mut finalizers = Vec::new();
3117 for reporter in reporters.iter_mut() {
3118 let (mut latest, mut monitor) = reporter.subscribe().await;
3119 finalizers.push(context.child("finalizer").spawn(move |_| async move {
3120 while latest < required_containers {
3121 latest = monitor.recv().await.expect("event missing");
3122 }
3123 }));
3124 }
3125 join_all(finalizers).await;
3126
3127 let byz = &participants[0];
3129 let mut count_conflicting = 0;
3130 for reporter in reporters.iter() {
3131 {
3133 let faults = reporter.faults.lock();
3134 assert_eq!(faults.len(), 1);
3135 let faulter = faults.get(byz).expect("byzantine party is not faulter");
3136 for faults in faulter.values() {
3137 for fault in faults.iter() {
3138 match fault {
3139 Activity::ConflictingNotarize(_) => {
3140 count_conflicting += 1;
3141 }
3142 Activity::ConflictingFinalize(_) => {
3143 count_conflicting += 1;
3144 }
3145 _ => panic!("unexpected fault: {fault:?}"),
3146 }
3147 }
3148 }
3149 }
3150
3151 reporter.assert_no_invalid();
3153 }
3154 assert!(count_conflicting > 0);
3155
3156 let blocked = oracle.blocked().await.unwrap();
3158 assert!(!blocked.is_empty());
3159 for (a, b) in blocked {
3160 assert_ne!(&a, byz);
3161 assert_eq!(&b, byz);
3162 }
3163 });
3164 }
3165
3166 test_for_all_fixtures!(conflicter, seeds = 5);
3167
3168 fn invalid<S, F, L>(seed: u64, mut fixture: F)
3169 where
3170 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3171 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3172 L: Elector<S>,
3173 {
3174 let n = 4;
3176 let required_containers = View::new(50);
3177 let activity_timeout = ViewDelta::new(10);
3178 let skip_timeout = ViewDelta::new(5);
3179 let namespace = b"consensus".to_vec();
3180 let cfg = deterministic::Config::new()
3181 .with_seed(seed)
3182 .with_timeout(Some(Duration::from_secs(30)));
3183 let executor = deterministic::Runner::new(cfg);
3184 executor.start(|mut context| async move {
3185 let Fixture {
3187 participants,
3188 schemes,
3189 ..
3190 } = fixture(&mut context, &namespace, n);
3191
3192 let schemes: Vec<_> = schemes
3193 .into_iter()
3194 .enumerate()
3195 .map(|(idx, scheme)| {
3196 let is_byzantine = idx == 0;
3197 let behavior = if is_byzantine {
3198 wrapped::Behavior::CorruptSignature
3199 } else {
3200 wrapped::Behavior::Honest
3201 };
3202 wrapped::Scheme::new(scheme, behavior)
3203 })
3204 .collect();
3205
3206 let mut oracle =
3207 start_test_network_with_peers(context.child("network"), participants.clone(), true)
3208 .await;
3209 let mut registrations = register_validators(&mut oracle, &participants).await;
3210
3211 let link = Link {
3213 latency: Duration::from_millis(10),
3214 jitter: Duration::from_millis(1),
3215 success_rate: 1.0,
3216 };
3217 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
3218
3219 let elector = wrapped::Config(L::default());
3221 let relay = Arc::new(mocks::relay::Relay::new());
3222 let mut reporters = Vec::new();
3223 for (idx_scheme, validator) in participants.iter().enumerate() {
3224 let context = context
3226 .child("validator")
3227 .with_attribute("public_key", validator);
3228
3229 let reporter_config = mocks::reporter::Config {
3230 participants: participants.clone().try_into().unwrap(),
3231 scheme: schemes[idx_scheme].clone(),
3232 elector: elector.clone(),
3233 };
3234 let reporter =
3235 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3236 reporters.push(reporter.clone());
3237
3238 let application_cfg = mocks::application::Config {
3239 hasher: Sha256::default(),
3240 relay: relay.clone(),
3241 me: validator.clone(),
3242 propose_latency: (10.0, 5.0),
3243 verify_latency: (10.0, 5.0),
3244 certify_latency: (10.0, 5.0),
3245 should_certify: mocks::application::Certifier::Always,
3246 };
3247 let (actor, application) = mocks::application::Application::new(
3248 context.child("application"),
3249 application_cfg,
3250 );
3251 actor.start();
3252 let blocker = oracle.control(validator.clone());
3253 let cfg = config::Config {
3254 scheme: schemes[idx_scheme].clone(),
3255 elector: elector.clone(),
3256 blocker,
3257 automaton: application.clone(),
3258 relay: application.clone(),
3259 reporter: reporter.clone(),
3260 strategy: Sequential,
3261 partition: validator.clone().to_string(),
3262 mailbox_size: NZUsize!(1024),
3263 epoch: Epoch::new(333),
3264 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
3265 Epoch::new(333),
3266 )),
3267 leader_timeout: Duration::from_secs(1),
3268 certification_timeout: Duration::from_secs(2),
3269 timeout_retry: Duration::from_secs(10),
3270 fetch_timeout: Duration::from_secs(1),
3271 activity_timeout,
3272 skip_timeout,
3273 fetch_concurrent: NZUsize!(4),
3274 replay_buffer: NZUsize!(1024 * 1024),
3275 write_buffer: NZUsize!(1024 * 1024),
3276 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3277 forwarding: ForwardingPolicy::Disabled,
3278 };
3279 let engine = Engine::new(context.child("engine"), cfg);
3280 let (pending, recovered, resolver) = registrations
3281 .remove(validator)
3282 .expect("validator should be registered");
3283 engine.start(pending, recovered, resolver);
3284 }
3285
3286 let mut finalizers = Vec::new();
3290 for reporter in reporters.iter_mut().skip(1) {
3291 let (mut latest, mut monitor) = reporter.subscribe().await;
3292 finalizers.push(context.child("finalizer").spawn(move |_| async move {
3293 while latest < required_containers {
3294 latest = monitor.recv().await.expect("event missing");
3295 }
3296 }));
3297 }
3298 join_all(finalizers).await;
3299
3300 for (i, reporter) in reporters.iter().enumerate() {
3302 reporter.assert_no_faults();
3304
3305 assert!(*reporter.invalid_votes.lock() > 0);
3308
3309 let is_byzantine = i == 0;
3312 if is_byzantine {
3313 assert!(*reporter.invalid_certificates.lock() > 0);
3314 } else {
3315 assert_eq!(*reporter.invalid_certificates.lock(), 0);
3316 }
3317 }
3318
3319 let blocked = oracle.blocked().await.unwrap();
3322 assert!(blocked.len() >= participants.len() - 1);
3323 let byz = &participants[0];
3324 for (_, b) in blocked {
3325 assert_eq!(&b, byz);
3327 }
3328 });
3329 }
3330
3331 test_for_all_fixtures!(invalid, seeds = 5);
3332
3333 fn received_certificates_are_reported<S, F, L>(mut fixture: F)
3335 where
3336 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3337 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3338 L: Elector<S>,
3339 {
3340 let n = 4;
3341 let required_containers = View::new(10);
3342 let activity_timeout = ViewDelta::new(10);
3343 let skip_timeout = ViewDelta::new(5);
3344 let namespace = b"consensus".to_vec();
3345 let cfg = deterministic::Config::new()
3346 .with_seed(0)
3347 .with_timeout(Some(Duration::from_secs(30)));
3348 let executor = deterministic::Runner::new(cfg);
3349 executor.start(|mut context| async move {
3350 let Fixture {
3351 participants,
3352 schemes,
3353 ..
3354 } = fixture(&mut context, &namespace, n);
3355
3356 let mut oracle = start_test_network_with_peers(
3357 context.child("network"),
3358 participants.clone(),
3359 false,
3360 )
3361 .await;
3362 let mut registrations = register_validators(&mut oracle, &participants).await;
3363
3364 let link = Link {
3369 latency: Duration::from_millis(100),
3370 jitter: Duration::from_millis(1),
3371 success_rate: 1.0,
3372 };
3373 fn link_graph(_: usize, i: usize, j: usize) -> bool {
3374 if i == 0 || j == 0 {
3375 return i == 1 || j == 1;
3376 }
3377 true
3378 }
3379 link_validators(
3380 &mut oracle,
3381 &participants,
3382 Action::Link(link),
3383 Some(link_graph),
3384 )
3385 .await;
3386
3387 let elector = L::default();
3388 let relay = Arc::new(mocks::relay::Relay::new());
3389 let mut reporters = Vec::new();
3390 for (idx_scheme, validator) in participants.iter().enumerate() {
3391 let context = context
3392 .child("validator")
3393 .with_attribute("public_key", validator);
3394 let reporter_config = mocks::reporter::Config {
3395 participants: participants.clone().try_into().unwrap(),
3396 scheme: schemes[idx_scheme].clone(),
3397 elector: elector.clone(),
3398 };
3399 let reporter =
3400 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3401 reporters.push(reporter.clone());
3402
3403 let application_cfg = mocks::application::Config {
3404 hasher: Sha256::default(),
3405 relay: relay.clone(),
3406 me: validator.clone(),
3407 propose_latency: (10.0, 5.0),
3408 verify_latency: (10.0, 5.0),
3409 certify_latency: (10.0, 5.0),
3410 should_certify: mocks::application::Certifier::Always,
3411 };
3412 let (actor, application) = mocks::application::Application::new(
3413 context.child("application"),
3414 application_cfg,
3415 );
3416 actor.start();
3417 let blocker = oracle.control(validator.clone());
3418 let cfg = config::Config {
3419 scheme: schemes[idx_scheme].clone(),
3420 elector: elector.clone(),
3421 blocker,
3422 automaton: application.clone(),
3423 relay: application.clone(),
3424 reporter: reporter.clone(),
3425 strategy: Sequential,
3426 partition: validator.clone().to_string(),
3427 mailbox_size: NZUsize!(1024),
3428 epoch: Epoch::new(333),
3429 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
3430 Epoch::new(333),
3431 )),
3432 leader_timeout: Duration::from_secs(1),
3433 certification_timeout: Duration::from_secs(2),
3434 timeout_retry: Duration::from_secs(10),
3435 fetch_timeout: Duration::from_secs(1),
3436 activity_timeout,
3437 skip_timeout,
3438 fetch_concurrent: NZUsize!(4),
3439 replay_buffer: NZUsize!(1024 * 1024),
3440 write_buffer: NZUsize!(1024 * 1024),
3441 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3442 forwarding: ForwardingPolicy::Disabled,
3443 };
3444 let engine = Engine::new(context.child("engine"), cfg);
3445 let (pending, recovered, resolver) = registrations
3446 .remove(validator)
3447 .expect("validator should be registered");
3448 engine.start(pending, recovered, resolver);
3449 }
3450 let excluded_reporter = reporters[0].clone();
3452 let mut honest_reporter = reporters[1].clone();
3453 let (mut honest_latest, mut honest_monitor) = honest_reporter.subscribe().await;
3454 while honest_latest < required_containers {
3455 honest_latest = honest_monitor.recv().await.expect("event missing");
3456 }
3457
3458 context.sleep(Duration::from_secs(1)).await;
3460
3461 let honest_notarized = {
3464 let notarizations = honest_reporter.notarizations.lock();
3465 View::range(View::new(1), required_containers.next())
3466 .filter(|view| notarizations.contains_key(view))
3467 .count()
3468 };
3469 let excluded_notarized = {
3470 let notarizations = excluded_reporter.notarizations.lock();
3471 View::range(View::new(1), required_containers.next())
3472 .filter(|view| notarizations.contains_key(view))
3473 .count()
3474 };
3475 assert!(
3476 excluded_notarized >= honest_notarized.saturating_sub(2),
3477 "honest_notarized: {honest_notarized}, excluded_notarized: {excluded_notarized}"
3478 );
3479
3480 let honest_finalized = {
3481 let finalizations = honest_reporter.finalizations.lock();
3482 View::range(View::new(1), required_containers.next())
3483 .filter(|view| finalizations.contains_key(view))
3484 .count()
3485 };
3486 let excluded_finalized = {
3487 let finalizations = excluded_reporter.finalizations.lock();
3488 View::range(View::new(1), required_containers.next())
3489 .filter(|view| finalizations.contains_key(view))
3490 .count()
3491 };
3492 assert!(
3493 excluded_finalized >= honest_finalized.saturating_sub(2),
3494 "honest_finalized: {honest_finalized}, excluded_finalized: {excluded_finalized}"
3495 );
3496 });
3497 }
3498
3499 test_for_all_fixtures!(received_certificates_are_reported);
3500
3501 fn survives_burst<S, F, L>(mut fixture: F)
3502 where
3503 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3504 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3505 L: Elector<S>,
3506 {
3507 let n = 4;
3508 let epoch = Epoch::new(333);
3509 let namespace = b"mailbox_size_one_certificate_burst".to_vec();
3510 let executor = deterministic::Runner::default();
3511 executor.start(|mut context| async move {
3512 let Fixture {
3513 participants,
3514 schemes,
3515 ..
3516 } = fixture(&mut context, &namespace, n);
3517 let me = participants[0].clone();
3518 let mut oracle =
3519 start_test_network_with_peers(context.child("network"), participants.clone(), true)
3520 .await;
3521 let (pending, recovered, resolver) = register_validator(&mut oracle, me.clone()).await;
3522
3523 let injector_pk = PrivateKey::from_seed(9_000_000).public_key();
3524 let (mut injector_sender, _injector_receiver) = oracle
3525 .control(injector_pk.clone())
3526 .register(1, TEST_QUOTA)
3527 .await
3528 .unwrap();
3529 let link = Link {
3530 latency: Duration::from_millis(0),
3531 jitter: Duration::from_millis(0),
3532 success_rate: 1.0,
3533 };
3534 oracle
3535 .add_link(injector_pk.clone(), me.clone(), link)
3536 .await
3537 .unwrap();
3538 oracle.manager().track(
3539 1,
3540 TrackedPeers::new(
3541 Set::from_iter_dedup(std::iter::once(me.clone())),
3542 Set::from_iter_dedup(std::iter::once(injector_pk.clone())),
3543 ),
3544 );
3545 context.sleep(Duration::from_millis(1)).await;
3546
3547 let quorum = quorum(n) as usize;
3548 let notarization = |view: View, parent: View, payload: &[u8]| {
3549 let proposal =
3550 Proposal::new(Round::new(epoch, view), parent, Sha256::hash(payload));
3551 let votes: Vec<_> = schemes
3552 .iter()
3553 .take(quorum)
3554 .map(|scheme| TNotarize::sign(scheme, proposal.clone()).unwrap())
3555 .collect();
3556 TNotarization::from_notarizes(&schemes[0], &votes, &Sequential)
3557 .expect("notarization requires quorum")
3558 };
3559 let finalization = |view: View, parent: View, payload: &[u8]| {
3560 let proposal =
3561 Proposal::new(Round::new(epoch, view), parent, Sha256::hash(payload));
3562 let votes: Vec<_> = schemes
3563 .iter()
3564 .take(quorum)
3565 .map(|scheme| TFinalize::sign(scheme, proposal.clone()).unwrap())
3566 .collect();
3567 TFinalization::from_finalizes(&schemes[0], &votes, &Sequential)
3568 .expect("finalization requires quorum")
3569 };
3570
3571 for certificate in [
3573 Certificate::Notarization(notarization(View::new(1), View::zero(), b"payload-1")),
3574 Certificate::Notarization(notarization(View::new(2), View::new(1), b"payload-2")),
3575 Certificate::Notarization(notarization(View::new(3), View::new(2), b"payload-3")),
3576 Certificate::Finalization(finalization(View::new(3), View::new(2), b"payload-3")),
3577 ] {
3578 injector_sender.send(Recipients::One(me.clone()), certificate.encode(), true);
3579 }
3580
3581 let elector = L::default();
3582 let reporter_config = mocks::reporter::Config {
3583 participants: participants.clone().try_into().unwrap(),
3584 scheme: schemes[0].clone(),
3585 elector: elector.clone(),
3586 };
3587 let reporter =
3588 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3589 let mut monitor_reporter = reporter.clone();
3590 let (mut latest, mut monitor) = monitor_reporter.subscribe().await;
3591
3592 let relay = Arc::new(mocks::relay::Relay::new());
3593 let application_cfg = mocks::application::Config {
3594 hasher: Sha256::default(),
3595 relay: relay.clone(),
3596 me: me.clone(),
3597 propose_latency: (0.0, 0.0),
3598 verify_latency: (0.0, 0.0),
3599 certify_latency: (0.0, 0.0),
3600 should_certify: mocks::application::Certifier::Always,
3601 };
3602 let (mut application_actor, application) =
3603 mocks::application::Application::new(context.child("application"), application_cfg);
3604 application_actor.set_stall_proposals(true);
3605 application_actor.start();
3606
3607 let cfg = config::Config {
3608 scheme: schemes[0].clone(),
3609 elector,
3610 blocker: oracle.control(me.clone()),
3611 automaton: application.clone(),
3612 relay: application,
3613 reporter,
3614 strategy: Sequential,
3615 partition: me.to_string(),
3616 mailbox_size: NZUsize!(1),
3617 epoch,
3618 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(epoch)),
3619 leader_timeout: Duration::from_secs(1),
3620 certification_timeout: Duration::from_secs(2),
3621 timeout_retry: Duration::from_secs(10),
3622 fetch_timeout: Duration::from_secs(1),
3623 activity_timeout: ViewDelta::new(10),
3624 skip_timeout: ViewDelta::new(5),
3625 fetch_concurrent: NZUsize!(4),
3626 replay_buffer: NZUsize!(1024 * 1024),
3627 write_buffer: NZUsize!(1024 * 1024),
3628 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3629 forwarding: ForwardingPolicy::Disabled,
3630 };
3631 let engine = Engine::new(context.child("engine"), cfg);
3632 engine.start(pending, recovered, resolver);
3633
3634 while latest < View::new(3) {
3635 latest = monitor.recv().await.expect("finalization event missing");
3636 }
3637 });
3638 }
3639
3640 test_for_all_fixtures!(survives_burst);
3641
3642 fn impersonator<S, F, L>(seed: u64, mut fixture: F)
3643 where
3644 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3645 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3646 L: Elector<S>,
3647 {
3648 let n = 4;
3650 let required_containers = View::new(50);
3651 let activity_timeout = ViewDelta::new(10);
3652 let skip_timeout = ViewDelta::new(5);
3653 let namespace = b"consensus".to_vec();
3654 let cfg = deterministic::Config::new()
3655 .with_seed(seed)
3656 .with_timeout(Some(Duration::from_secs(30)));
3657 let executor = deterministic::Runner::new(cfg);
3658 executor.start(|mut context| async move {
3659 let Fixture {
3661 participants,
3662 schemes,
3663 ..
3664 } = fixture(&mut context, &namespace, n);
3665 let mut oracle =
3666 start_test_network_with_peers(context.child("network"), participants.clone(), true)
3667 .await;
3668 let mut registrations = register_validators(&mut oracle, &participants).await;
3669
3670 let link = Link {
3672 latency: Duration::from_millis(10),
3673 jitter: Duration::from_millis(1),
3674 success_rate: 1.0,
3675 };
3676 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
3677
3678 let elector = L::default();
3680 let relay = Arc::new(mocks::relay::Relay::new());
3681 let mut reporters = Vec::new();
3682 for (idx_scheme, validator) in participants.iter().enumerate() {
3683 let context = context
3685 .child("validator")
3686 .with_attribute("public_key", validator);
3687
3688 let reporter_config = mocks::reporter::Config {
3690 participants: participants.clone().try_into().unwrap(),
3691 scheme: schemes[idx_scheme].clone(),
3692 elector: elector.clone(),
3693 };
3694 let reporter =
3695 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3696 let (pending, recovered, resolver) = registrations
3697 .remove(validator)
3698 .expect("validator should be registered");
3699 if idx_scheme == 0 {
3700 let cfg = mocks::impersonator::Config {
3701 scheme: schemes[idx_scheme].clone(),
3702 };
3703
3704 let engine: mocks::impersonator::Impersonator<_, _, Sha256> =
3705 mocks::impersonator::Impersonator::new(
3706 context.child("byzantine_engine"),
3707 cfg,
3708 );
3709 engine.start(pending);
3710 } else {
3711 reporters.push(reporter.clone());
3712 let application_cfg = mocks::application::Config {
3713 hasher: Sha256::default(),
3714 relay: relay.clone(),
3715 me: validator.clone(),
3716 propose_latency: (10.0, 5.0),
3717 verify_latency: (10.0, 5.0),
3718 certify_latency: (10.0, 5.0),
3719 should_certify: mocks::application::Certifier::Always,
3720 };
3721 let (actor, application) = mocks::application::Application::new(
3722 context.child("application"),
3723 application_cfg,
3724 );
3725 actor.start();
3726 let blocker = oracle.control(validator.clone());
3727 let cfg = config::Config {
3728 scheme: schemes[idx_scheme].clone(),
3729 elector: elector.clone(),
3730 blocker,
3731 automaton: application.clone(),
3732 relay: application.clone(),
3733 reporter: reporter.clone(),
3734 strategy: Sequential,
3735 partition: validator.clone().to_string(),
3736 mailbox_size: NZUsize!(1024),
3737 epoch: Epoch::new(333),
3738 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
3739 Epoch::new(333),
3740 )),
3741 leader_timeout: Duration::from_secs(1),
3742 certification_timeout: Duration::from_secs(2),
3743 timeout_retry: Duration::from_secs(10),
3744 fetch_timeout: Duration::from_secs(1),
3745 activity_timeout,
3746 skip_timeout,
3747 fetch_concurrent: NZUsize!(4),
3748 replay_buffer: NZUsize!(1024 * 1024),
3749 write_buffer: NZUsize!(1024 * 1024),
3750 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3751 forwarding: ForwardingPolicy::Disabled,
3752 };
3753 let engine = Engine::new(context.child("engine"), cfg);
3754 engine.start(pending, recovered, resolver);
3755 }
3756 }
3757
3758 let mut finalizers = Vec::new();
3760 for reporter in reporters.iter_mut() {
3761 let (mut latest, mut monitor) = reporter.subscribe().await;
3762 finalizers.push(context.child("finalizer").spawn(move |_| async move {
3763 while latest < required_containers {
3764 latest = monitor.recv().await.expect("event missing");
3765 }
3766 }));
3767 }
3768 join_all(finalizers).await;
3769
3770 let byz = &participants[0];
3772 for reporter in reporters.iter() {
3773 reporter.assert_no_faults();
3775
3776 reporter.assert_no_invalid();
3778 }
3779
3780 let blocked = oracle.blocked().await.unwrap();
3782 assert!(!blocked.is_empty());
3783 for (a, b) in blocked {
3784 assert_ne!(&a, byz);
3785 assert_eq!(&b, byz);
3786 }
3787 });
3788 }
3789
3790 test_for_all_fixtures!(impersonator, seeds = 5);
3791
3792 fn equivocator_seeded<S, F, L>(seed: u64, mut fixture: F) -> bool
3793 where
3794 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3795 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3796 L: Elector<S>,
3797 {
3798 let n = 7;
3800 let required_containers = View::new(50);
3801 let activity_timeout = ViewDelta::new(10);
3802 let skip_timeout = ViewDelta::new(5);
3803 let namespace = b"consensus".to_vec();
3804 let cfg = deterministic::Config::new()
3805 .with_seed(seed)
3806 .with_timeout(Some(Duration::from_secs(60)));
3807 let executor = deterministic::Runner::new(cfg);
3808 executor.start(|mut context| async move {
3809 let Fixture {
3811 participants,
3812 schemes,
3813 ..
3814 } = fixture(&mut context, &namespace, n);
3815 let mut oracle =
3816 start_test_network_with_peers(context.child("network"), participants.clone(), true)
3817 .await;
3818 let mut registrations = register_validators(&mut oracle, &participants).await;
3819
3820 let link = Link {
3822 latency: Duration::from_millis(10),
3823 jitter: Duration::from_millis(1),
3824 success_rate: 1.0,
3825 };
3826 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
3827
3828 let elector = L::default();
3830 let mut engines = Vec::new();
3831 let relay = Arc::new(mocks::relay::Relay::new());
3832 let mut reporters = Vec::new();
3833 for (idx_scheme, validator) in participants.iter().enumerate() {
3834 let context = context
3836 .child("validator")
3837 .with_attribute("public_key", validator);
3838
3839 let reporter_config = mocks::reporter::Config {
3841 participants: participants.clone().try_into().unwrap(),
3842 scheme: schemes[idx_scheme].clone(),
3843 elector: elector.clone(),
3844 };
3845 let reporter =
3846 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3847 reporters.push(reporter.clone());
3848 let (pending, recovered, resolver) = registrations
3849 .remove(validator)
3850 .expect("validator should be registered");
3851 if idx_scheme == 0 {
3852 let cfg = mocks::equivocator::Config {
3853 scheme: schemes[idx_scheme].clone(),
3854 epoch: Epoch::new(333),
3855 relay: relay.clone(),
3856 hasher: Sha256::default(),
3857 elector: elector.clone(),
3858 };
3859
3860 let engine = mocks::equivocator::Equivocator::new(
3861 context.child("byzantine_engine"),
3862 cfg,
3863 );
3864 engines.push(engine.start(pending, recovered));
3865 } else {
3866 let application_cfg = mocks::application::Config {
3867 hasher: Sha256::default(),
3868 relay: relay.clone(),
3869 me: validator.clone(),
3870 propose_latency: (10.0, 5.0),
3871 verify_latency: (10.0, 5.0),
3872 certify_latency: (10.0, 5.0),
3873 should_certify: mocks::application::Certifier::Always,
3874 };
3875 let (actor, application) = mocks::application::Application::new(
3876 context.child("application"),
3877 application_cfg,
3878 );
3879 actor.start();
3880 let blocker = oracle.control(validator.clone());
3881 let cfg = config::Config {
3882 scheme: schemes[idx_scheme].clone(),
3883 elector: elector.clone(),
3884 blocker,
3885 automaton: application.clone(),
3886 relay: application.clone(),
3887 reporter: reporter.clone(),
3888 strategy: Sequential,
3889 partition: validator.to_string(),
3890 mailbox_size: NZUsize!(1024),
3891 epoch: Epoch::new(333),
3892 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
3893 Epoch::new(333),
3894 )),
3895 leader_timeout: Duration::from_secs(1),
3896 certification_timeout: Duration::from_secs(2),
3897 timeout_retry: Duration::from_secs(10),
3898 fetch_timeout: Duration::from_secs(1),
3899 activity_timeout,
3900 skip_timeout,
3901 fetch_concurrent: NZUsize!(4),
3902 replay_buffer: NZUsize!(1024 * 1024),
3903 write_buffer: NZUsize!(1024 * 1024),
3904 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3905 forwarding: ForwardingPolicy::Disabled,
3906 };
3907 let engine = Engine::new(context.child("engine"), cfg);
3908 engines.push(engine.start(pending, recovered, resolver));
3909 }
3910 }
3911
3912 let mut finalizers = Vec::new();
3914 for reporter in reporters.iter_mut().skip(1) {
3915 let (mut latest, mut monitor) = reporter.subscribe().await;
3916 finalizers.push(context.child("finalizer").spawn(move |_| async move {
3917 while latest < required_containers {
3918 latest = monitor.recv().await.expect("event missing");
3919 }
3920 }));
3921 }
3922 join_all(finalizers).await;
3923
3924 let idx = context.random_range(1..engines.len()); let validator = &participants[idx];
3927 let handle = engines.remove(idx);
3928 handle.abort();
3929 let _ = handle.await;
3930 reporters.remove(idx);
3931 info!(idx, ?validator, "aborted validator");
3932
3933 let mut finalizers = Vec::new();
3935 for reporter in reporters.iter_mut().skip(1) {
3936 let (mut latest, mut monitor) = reporter.subscribe().await;
3937 finalizers.push(context.child("finalizer").spawn(move |_| async move {
3938 while latest < View::new(required_containers.get() * 2) {
3939 latest = monitor.recv().await.expect("event missing");
3940 }
3941 }));
3942 }
3943 join_all(finalizers).await;
3944
3945 info!(idx, ?validator, "restarting validator");
3947 let context = context
3948 .child("validator_restarted")
3949 .with_attribute("public_key", validator);
3950
3951 let reporter_config = mocks::reporter::Config {
3953 participants: participants.clone().try_into().unwrap(),
3954 scheme: schemes[idx].clone(),
3955 elector: elector.clone(),
3956 };
3957 let reporter =
3958 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3959 let (pending, recovered, resolver) =
3960 register_validator(&mut oracle, validator.clone()).await;
3961 reporters.push(reporter.clone());
3962 let application_cfg = mocks::application::Config {
3963 hasher: Sha256::default(),
3964 relay: relay.clone(),
3965 me: validator.clone(),
3966 propose_latency: (10.0, 5.0),
3967 verify_latency: (10.0, 5.0),
3968 certify_latency: (10.0, 5.0),
3969 should_certify: mocks::application::Certifier::Always,
3970 };
3971 let (actor, application) =
3972 mocks::application::Application::new(context.child("application"), application_cfg);
3973 actor.start();
3974 let blocker = oracle.control(validator.clone());
3975 let cfg = config::Config {
3976 scheme: schemes[idx].clone(),
3977 elector: elector.clone(),
3978 blocker,
3979 automaton: application.clone(),
3980 relay: application.clone(),
3981 reporter: reporter.clone(),
3982 strategy: Sequential,
3983 partition: validator.to_string(),
3984 mailbox_size: NZUsize!(1024),
3985 epoch: Epoch::new(333),
3986 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(Epoch::new(
3987 333,
3988 ))),
3989 leader_timeout: Duration::from_secs(1),
3990 certification_timeout: Duration::from_secs(2),
3991 timeout_retry: Duration::from_secs(10),
3992 fetch_timeout: Duration::from_secs(1),
3993 activity_timeout,
3994 skip_timeout,
3995 fetch_concurrent: NZUsize!(4),
3996 replay_buffer: NZUsize!(1024 * 1024),
3997 write_buffer: NZUsize!(1024 * 1024),
3998 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3999 forwarding: ForwardingPolicy::Disabled,
4000 };
4001 let engine = Engine::new(context.child("engine"), cfg);
4002 engine.start(pending, recovered, resolver);
4003
4004 let mut finalizers = Vec::new();
4006 for reporter in reporters.iter_mut().skip(1) {
4007 let (mut latest, mut monitor) = reporter.subscribe().await;
4008 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4009 while latest < View::new(required_containers.get() * 3) {
4010 latest = monitor.recv().await.expect("event missing");
4011 }
4012 }));
4013 }
4014 join_all(finalizers).await;
4015
4016 let byz = &participants[0];
4020 let blocked = oracle.blocked().await.unwrap();
4021 for (a, b) in &blocked {
4022 assert_ne!(a, byz);
4023 assert_eq!(b, byz);
4024 }
4025 !blocked.is_empty()
4026 })
4027 }
4028
4029 fn equivocator<S, F, L>(fixture: F)
4030 where
4031 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4032 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S> + Copy,
4033 L: Elector<S>,
4034 {
4035 let detected = (0..5).any(|seed| equivocator_seeded::<_, _, L>(seed, fixture));
4036 assert!(
4037 detected,
4038 "expected at least one seed to detect equivocation"
4039 );
4040 }
4041
4042 test_for_all_fixtures!(equivocator);
4043
4044 fn reconfigurer<S, F, L>(seed: u64, mut fixture: F)
4045 where
4046 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4047 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4048 L: Elector<S>,
4049 {
4050 let n = 4;
4052 let required_containers = View::new(50);
4053 let activity_timeout = ViewDelta::new(10);
4054 let skip_timeout = ViewDelta::new(5);
4055 let namespace = b"consensus".to_vec();
4056 let cfg = deterministic::Config::new()
4057 .with_seed(seed)
4058 .with_timeout(Some(Duration::from_secs(30)));
4059 let executor = deterministic::Runner::new(cfg);
4060 executor.start(|mut context| async move {
4061 let Fixture {
4063 participants,
4064 schemes,
4065 ..
4066 } = fixture(&mut context, &namespace, n);
4067 let mut oracle =
4068 start_test_network_with_peers(context.child("network"), participants.clone(), true)
4069 .await;
4070 let mut registrations = register_validators(&mut oracle, &participants).await;
4071
4072 let link = Link {
4074 latency: Duration::from_millis(10),
4075 jitter: Duration::from_millis(1),
4076 success_rate: 1.0,
4077 };
4078 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4079
4080 let elector = L::default();
4082 let relay = Arc::new(mocks::relay::Relay::new());
4083 let mut reporters = Vec::new();
4084 for (idx_scheme, validator) in participants.iter().enumerate() {
4085 let context = context
4087 .child("validator")
4088 .with_attribute("public_key", validator);
4089
4090 let reporter_config = mocks::reporter::Config {
4092 participants: participants.clone().try_into().unwrap(),
4093 scheme: schemes[idx_scheme].clone(),
4094 elector: elector.clone(),
4095 };
4096 let reporter =
4097 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4098 let (pending, recovered, resolver) = registrations
4099 .remove(validator)
4100 .expect("validator should be registered");
4101 if idx_scheme == 0 {
4102 let cfg = mocks::reconfigurer::Config {
4103 scheme: schemes[idx_scheme].clone(),
4104 };
4105 let engine: mocks::reconfigurer::Reconfigurer<_, _, Sha256> =
4106 mocks::reconfigurer::Reconfigurer::new(
4107 context.child("byzantine_engine"),
4108 cfg,
4109 );
4110 engine.start(pending);
4111 } else {
4112 reporters.push(reporter.clone());
4113 let application_cfg = mocks::application::Config {
4114 hasher: Sha256::default(),
4115 relay: relay.clone(),
4116 me: validator.clone(),
4117 propose_latency: (10.0, 5.0),
4118 verify_latency: (10.0, 5.0),
4119 certify_latency: (10.0, 5.0),
4120 should_certify: mocks::application::Certifier::Always,
4121 };
4122 let (actor, application) = mocks::application::Application::new(
4123 context.child("application"),
4124 application_cfg,
4125 );
4126 actor.start();
4127 let blocker = oracle.control(validator.clone());
4128 let cfg = config::Config {
4129 scheme: schemes[idx_scheme].clone(),
4130 elector: elector.clone(),
4131 blocker,
4132 automaton: application.clone(),
4133 relay: application.clone(),
4134 reporter: reporter.clone(),
4135 strategy: Sequential,
4136 partition: validator.to_string(),
4137 mailbox_size: NZUsize!(1024),
4138 epoch: Epoch::new(333),
4139 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
4140 Epoch::new(333),
4141 )),
4142 leader_timeout: Duration::from_secs(1),
4143 certification_timeout: Duration::from_secs(2),
4144 timeout_retry: Duration::from_secs(10),
4145 fetch_timeout: Duration::from_secs(1),
4146 activity_timeout,
4147 skip_timeout,
4148 fetch_concurrent: NZUsize!(4),
4149 replay_buffer: NZUsize!(1024 * 1024),
4150 write_buffer: NZUsize!(1024 * 1024),
4151 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4152 forwarding: ForwardingPolicy::Disabled,
4153 };
4154 let engine = Engine::new(context.child("engine"), cfg);
4155 engine.start(pending, recovered, resolver);
4156 }
4157 }
4158
4159 let mut finalizers = Vec::new();
4161 for reporter in reporters.iter_mut() {
4162 let (mut latest, mut monitor) = reporter.subscribe().await;
4163 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4164 while latest < required_containers {
4165 latest = monitor.recv().await.expect("event missing");
4166 }
4167 }));
4168 }
4169 join_all(finalizers).await;
4170
4171 let byz = &participants[0];
4173 for reporter in reporters.iter() {
4174 reporter.assert_no_faults();
4176
4177 reporter.assert_no_invalid();
4179 }
4180
4181 let blocked = oracle.blocked().await.unwrap();
4183 assert!(!blocked.is_empty());
4184 for (a, b) in blocked {
4185 assert_ne!(&a, byz);
4186 assert_eq!(&b, byz);
4187 }
4188 });
4189 }
4190
4191 test_for_all_fixtures!(reconfigurer, seeds = 5);
4192
4193 fn nuller<S, F, L>(seed: u64, mut fixture: F)
4194 where
4195 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4196 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4197 L: Elector<S>,
4198 {
4199 let n = 4;
4201 let required_containers = View::new(50);
4202 let activity_timeout = ViewDelta::new(10);
4203 let skip_timeout = ViewDelta::new(5);
4204 let namespace = b"consensus".to_vec();
4205 let cfg = deterministic::Config::new()
4206 .with_seed(seed)
4207 .with_timeout(Some(Duration::from_secs(30)));
4208 let executor = deterministic::Runner::new(cfg);
4209 executor.start(|mut context| async move {
4210 let Fixture {
4212 participants,
4213 schemes,
4214 ..
4215 } = fixture(&mut context, &namespace, n);
4216 let mut oracle =
4217 start_test_network_with_peers(context.child("network"), participants.clone(), true)
4218 .await;
4219 let mut registrations = register_validators(&mut oracle, &participants).await;
4220
4221 let link = Link {
4223 latency: Duration::from_millis(10),
4224 jitter: Duration::from_millis(1),
4225 success_rate: 1.0,
4226 };
4227 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4228
4229 let elector = L::default();
4231 let relay = Arc::new(mocks::relay::Relay::new());
4232 let mut reporters = Vec::new();
4233 for (idx_scheme, validator) in participants.iter().enumerate() {
4234 let context = context
4236 .child("validator")
4237 .with_attribute("public_key", validator);
4238
4239 let reporter_config = mocks::reporter::Config {
4241 participants: participants.clone().try_into().unwrap(),
4242 scheme: schemes[idx_scheme].clone(),
4243 elector: elector.clone(),
4244 };
4245 let reporter =
4246 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4247 let (pending, recovered, resolver) = registrations
4248 .remove(validator)
4249 .expect("validator should be registered");
4250 if idx_scheme == 0 {
4251 let cfg = mocks::nuller::Config {
4252 scheme: schemes[idx_scheme].clone(),
4253 };
4254 let engine: mocks::nuller::Nuller<_, _, Sha256> =
4255 mocks::nuller::Nuller::new(context.child("byzantine_engine"), cfg);
4256 engine.start(pending);
4257 } else {
4258 reporters.push(reporter.clone());
4259 let application_cfg = mocks::application::Config {
4260 hasher: Sha256::default(),
4261 relay: relay.clone(),
4262 me: validator.clone(),
4263 propose_latency: (10.0, 5.0),
4264 verify_latency: (10.0, 5.0),
4265 certify_latency: (10.0, 5.0),
4266 should_certify: mocks::application::Certifier::Always,
4267 };
4268 let (actor, application) = mocks::application::Application::new(
4269 context.child("application"),
4270 application_cfg,
4271 );
4272 actor.start();
4273 let blocker = oracle.control(validator.clone());
4274 let cfg = config::Config {
4275 scheme: schemes[idx_scheme].clone(),
4276 elector: elector.clone(),
4277 blocker,
4278 automaton: application.clone(),
4279 relay: application.clone(),
4280 reporter: reporter.clone(),
4281 strategy: Sequential,
4282 partition: validator.clone().to_string(),
4283 mailbox_size: NZUsize!(1024),
4284 epoch: Epoch::new(333),
4285 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
4286 Epoch::new(333),
4287 )),
4288 leader_timeout: Duration::from_secs(1),
4289 certification_timeout: Duration::from_secs(2),
4290 timeout_retry: Duration::from_secs(10),
4291 fetch_timeout: Duration::from_secs(1),
4292 activity_timeout,
4293 skip_timeout,
4294 fetch_concurrent: NZUsize!(4),
4295 replay_buffer: NZUsize!(1024 * 1024),
4296 write_buffer: NZUsize!(1024 * 1024),
4297 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4298 forwarding: ForwardingPolicy::Disabled,
4299 };
4300 let engine = Engine::new(context.child("engine"), cfg);
4301 engine.start(pending, recovered, resolver);
4302 }
4303 }
4304
4305 let mut finalizers = Vec::new();
4307 for reporter in reporters.iter_mut() {
4308 let (mut latest, mut monitor) = reporter.subscribe().await;
4309 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4310 while latest < required_containers {
4311 latest = monitor.recv().await.expect("event missing");
4312 }
4313 }));
4314 }
4315 join_all(finalizers).await;
4316
4317 let byz = &participants[0];
4319 let mut count_nullify_and_finalize = 0;
4320 for reporter in reporters.iter() {
4321 {
4323 let faults = reporter.faults.lock();
4324 assert_eq!(faults.len(), 1);
4325 let faulter = faults.get(byz).expect("byzantine party is not faulter");
4326 for faults in faulter.values() {
4327 for fault in faults.iter() {
4328 match fault {
4329 Activity::NullifyFinalize(_) => {
4330 count_nullify_and_finalize += 1;
4331 }
4332 _ => panic!("unexpected fault: {fault:?}"),
4333 }
4334 }
4335 }
4336 }
4337
4338 reporter.assert_no_invalid();
4340 }
4341 assert!(count_nullify_and_finalize > 0);
4342
4343 let blocked = oracle.blocked().await.unwrap();
4345 assert!(!blocked.is_empty());
4346 for (a, b) in blocked {
4347 assert_ne!(&a, byz);
4348 assert_eq!(&b, byz);
4349 }
4350 });
4351 }
4352
4353 test_for_all_fixtures!(nuller, seeds = 5);
4354
4355 fn outdated<S, F, L>(seed: u64, mut fixture: F)
4356 where
4357 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4358 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4359 L: Elector<S>,
4360 {
4361 let n = 4;
4363 let required_containers = View::new(100);
4364 let activity_timeout = ViewDelta::new(10);
4365 let skip_timeout = ViewDelta::new(5);
4366 let namespace = b"consensus".to_vec();
4367 let cfg = deterministic::Config::new()
4368 .with_seed(seed)
4369 .with_timeout(Some(Duration::from_secs(30)));
4370 let executor = deterministic::Runner::new(cfg);
4371 executor.start(|mut context| async move {
4372 let Fixture {
4374 participants,
4375 schemes,
4376 ..
4377 } = fixture(&mut context, &namespace, n);
4378 let mut oracle =
4379 start_test_network_with_peers(context.child("network"), participants.clone(), true)
4380 .await;
4381 let mut registrations = register_validators(&mut oracle, &participants).await;
4382
4383 let link = Link {
4385 latency: Duration::from_millis(10),
4386 jitter: Duration::from_millis(1),
4387 success_rate: 1.0,
4388 };
4389 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4390
4391 let elector = L::default();
4393 let relay = Arc::new(mocks::relay::Relay::new());
4394 let mut reporters = Vec::new();
4395 for (idx_scheme, validator) in participants.iter().enumerate() {
4396 let context = context
4398 .child("validator")
4399 .with_attribute("public_key", validator);
4400
4401 let reporter_config = mocks::reporter::Config {
4403 participants: participants.clone().try_into().unwrap(),
4404 scheme: schemes[idx_scheme].clone(),
4405 elector: elector.clone(),
4406 };
4407 let reporter =
4408 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4409 let (pending, recovered, resolver) = registrations
4410 .remove(validator)
4411 .expect("validator should be registered");
4412 if idx_scheme == 0 {
4413 let cfg = mocks::outdated::Config {
4414 scheme: schemes[idx_scheme].clone(),
4415 view_delta: ViewDelta::new(activity_timeout.get().saturating_mul(4)),
4416 };
4417 let engine: mocks::outdated::Outdated<_, _, Sha256> =
4418 mocks::outdated::Outdated::new(context.child("byzantine_engine"), cfg);
4419 engine.start(pending);
4420 } else {
4421 reporters.push(reporter.clone());
4422 let application_cfg = mocks::application::Config {
4423 hasher: Sha256::default(),
4424 relay: relay.clone(),
4425 me: validator.clone(),
4426 propose_latency: (10.0, 5.0),
4427 verify_latency: (10.0, 5.0),
4428 certify_latency: (10.0, 5.0),
4429 should_certify: mocks::application::Certifier::Always,
4430 };
4431 let (actor, application) = mocks::application::Application::new(
4432 context.child("application"),
4433 application_cfg,
4434 );
4435 actor.start();
4436 let blocker = oracle.control(validator.clone());
4437 let cfg = config::Config {
4438 scheme: schemes[idx_scheme].clone(),
4439 elector: elector.clone(),
4440 blocker,
4441 automaton: application.clone(),
4442 relay: application.clone(),
4443 reporter: reporter.clone(),
4444 strategy: Sequential,
4445 partition: validator.clone().to_string(),
4446 mailbox_size: NZUsize!(1024),
4447 epoch: Epoch::new(333),
4448 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
4449 Epoch::new(333),
4450 )),
4451 leader_timeout: Duration::from_secs(1),
4452 certification_timeout: Duration::from_secs(2),
4453 timeout_retry: Duration::from_secs(10),
4454 fetch_timeout: Duration::from_secs(1),
4455 activity_timeout,
4456 skip_timeout,
4457 fetch_concurrent: NZUsize!(4),
4458 replay_buffer: NZUsize!(1024 * 1024),
4459 write_buffer: NZUsize!(1024 * 1024),
4460 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4461 forwarding: ForwardingPolicy::Disabled,
4462 };
4463 let engine = Engine::new(context.child("engine"), cfg);
4464 engine.start(pending, recovered, resolver);
4465 }
4466 }
4467
4468 let mut finalizers = Vec::new();
4470 for reporter in reporters.iter_mut() {
4471 let (mut latest, mut monitor) = reporter.subscribe().await;
4472 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4473 while latest < required_containers {
4474 latest = monitor.recv().await.expect("event missing");
4475 }
4476 }));
4477 }
4478 join_all(finalizers).await;
4479
4480 for reporter in reporters.iter() {
4482 reporter.assert_no_faults();
4484
4485 reporter.assert_no_invalid();
4487 }
4488
4489 let blocked = oracle.blocked().await.unwrap();
4491 assert!(blocked.is_empty());
4492 });
4493 }
4494
4495 test_for_all_fixtures!(outdated, seeds = 5);
4496
4497 fn run_1k<S, F, L>(mut fixture: F)
4498 where
4499 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4500 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4501 L: Elector<S>,
4502 {
4503 let n = 10;
4505 let required_containers = View::new(1_000);
4506 let activity_timeout = ViewDelta::new(10);
4507 let skip_timeout = ViewDelta::new(5);
4508 let namespace = b"consensus".to_vec();
4509 let cfg = deterministic::Config::new();
4510 let executor = deterministic::Runner::new(cfg);
4511 executor.start(|mut context| async move {
4512 let Fixture {
4514 participants,
4515 schemes,
4516 ..
4517 } = fixture(&mut context, &namespace, n);
4518 let mut oracle =
4519 start_test_network_with_peers(context.child("network"), participants.clone(), true)
4520 .await;
4521 let mut registrations = register_validators(&mut oracle, &participants).await;
4522
4523 let link = Link {
4525 latency: Duration::from_millis(80),
4526 jitter: Duration::from_millis(10),
4527 success_rate: 0.98,
4528 };
4529 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4530
4531 let elector = L::default();
4533 let relay = Arc::new(mocks::relay::Relay::new());
4534 let mut reporters = Vec::new();
4535 let mut engine_handlers = Vec::new();
4536 for (idx, validator) in participants.iter().enumerate() {
4537 let context = context
4539 .child("validator")
4540 .with_attribute("public_key", validator);
4541
4542 let reporter_config = mocks::reporter::Config {
4544 participants: participants.clone().try_into().unwrap(),
4545 scheme: schemes[idx].clone(),
4546 elector: elector.clone(),
4547 };
4548 let reporter =
4549 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4550 reporters.push(reporter.clone());
4551 let application_cfg = mocks::application::Config {
4552 hasher: Sha256::default(),
4553 relay: relay.clone(),
4554 me: validator.clone(),
4555 propose_latency: (100.0, 50.0),
4556 verify_latency: (50.0, 40.0),
4557 certify_latency: (50.0, 40.0),
4558 should_certify: mocks::application::Certifier::Always,
4559 };
4560 let (actor, application) = mocks::application::Application::new(
4561 context.child("application"),
4562 application_cfg,
4563 );
4564 actor.start();
4565 let blocker = oracle.control(validator.clone());
4566 let cfg = config::Config {
4567 scheme: schemes[idx].clone(),
4568 elector: elector.clone(),
4569 blocker,
4570 automaton: application.clone(),
4571 relay: application.clone(),
4572 reporter: reporter.clone(),
4573 strategy: Sequential,
4574 partition: validator.to_string(),
4575 mailbox_size: NZUsize!(1024),
4576 epoch: Epoch::new(333),
4577 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
4578 Epoch::new(333),
4579 )),
4580 leader_timeout: Duration::from_secs(1),
4581 certification_timeout: Duration::from_secs(2),
4582 timeout_retry: Duration::from_secs(10),
4583 fetch_timeout: Duration::from_secs(1),
4584 activity_timeout,
4585 skip_timeout,
4586 fetch_concurrent: NZUsize!(4),
4587 replay_buffer: NZUsize!(1024 * 1024),
4588 write_buffer: NZUsize!(1024 * 1024),
4589 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4590 forwarding: ForwardingPolicy::Disabled,
4591 };
4592 let engine = Engine::new(context.child("engine"), cfg);
4593
4594 let (pending, recovered, resolver) = registrations
4596 .remove(validator)
4597 .expect("validator should be registered");
4598 engine_handlers.push(engine.start(pending, recovered, resolver));
4599 }
4600
4601 let mut finalizers = Vec::new();
4603 for reporter in reporters.iter_mut() {
4604 let (mut latest, mut monitor) = reporter.subscribe().await;
4605 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4606 while latest < required_containers {
4607 latest = monitor.recv().await.expect("event missing");
4608 }
4609 }));
4610 }
4611 join_all(finalizers).await;
4612
4613 for reporter in reporters.iter() {
4615 reporter.assert_no_faults();
4617
4618 reporter.assert_no_invalid();
4620 }
4621
4622 let blocked = oracle.blocked().await.unwrap();
4624 assert!(blocked.is_empty());
4625 })
4626 }
4627
4628 #[test_group("slow")]
4629 #[test_traced]
4630 fn test_1k() {
4631 run_1k::<_, _, RoundRobin>(scheme_mocks::fixture);
4632 }
4633
4634 fn engine_shutdown<S, F, L>(seed: u64, mut fixture: F, graceful: bool)
4635 where
4636 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4637 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4638 L: Elector<S>,
4639 {
4640 let n = 1;
4641 let namespace = b"consensus".to_vec();
4642 let cfg = deterministic::Config::default()
4643 .with_seed(seed)
4644 .with_timeout(Some(Duration::from_secs(10)));
4645 let executor = deterministic::Runner::new(cfg);
4646 executor.start(|mut context| async move {
4647 let Fixture {
4649 participants,
4650 schemes,
4651 ..
4652 } = fixture(&mut context, &namespace, n);
4653 let mut oracle =
4654 start_test_network_with_peers(context.child("network"), participants.clone(), true)
4655 .await;
4656 let mut registrations = register_validators(&mut oracle, &participants).await;
4657
4658 let link = Link {
4660 latency: Duration::from_millis(1),
4661 jitter: Duration::from_millis(0),
4662 success_rate: 1.0,
4663 };
4664 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4665
4666 let elector = L::default();
4668 let reporter_config = mocks::reporter::Config {
4669 participants: participants.clone().try_into().unwrap(),
4670 scheme: schemes[0].clone(),
4671 elector: elector.clone(),
4672 };
4673 let reporter =
4674 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4675 let relay = Arc::new(mocks::relay::Relay::new());
4676 let application_cfg = mocks::application::Config {
4677 hasher: Sha256::default(),
4678 relay: relay.clone(),
4679 me: participants[0].clone(),
4680 propose_latency: (1.0, 0.0),
4681 verify_latency: (1.0, 0.0),
4682 certify_latency: (1.0, 0.0),
4683 should_certify: mocks::application::Certifier::Always,
4684 };
4685 let (actor, application) =
4686 mocks::application::Application::new(context.child("application"), application_cfg);
4687 actor.start();
4688 let blocker = oracle.control(participants[0].clone());
4689 let cfg = config::Config {
4690 scheme: schemes[0].clone(),
4691 elector: elector.clone(),
4692 blocker,
4693 automaton: application.clone(),
4694 relay: application.clone(),
4695 reporter: reporter.clone(),
4696 strategy: Sequential,
4697 partition: participants[0].clone().to_string(),
4698 mailbox_size: NZUsize!(64),
4699 epoch: Epoch::new(333),
4700 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(Epoch::new(
4701 333,
4702 ))),
4703 leader_timeout: Duration::from_millis(50),
4704 certification_timeout: Duration::from_millis(100),
4705 timeout_retry: Duration::from_millis(250),
4706 fetch_timeout: Duration::from_millis(50),
4707 activity_timeout: ViewDelta::new(4),
4708 skip_timeout: ViewDelta::new(2),
4709 fetch_concurrent: NZUsize!(4),
4710 replay_buffer: NZUsize!(1024 * 16),
4711 write_buffer: NZUsize!(1024 * 16),
4712 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4713 forwarding: ForwardingPolicy::Disabled,
4714 };
4715 let engine = Engine::new(context.child("engine"), cfg);
4716
4717 let (pending, recovered, resolver) = registrations
4719 .remove(&participants[0])
4720 .expect("validator should be registered");
4721 let handle = engine.start(pending, recovered, resolver);
4722
4723 context.sleep(Duration::from_millis(1000)).await;
4725
4726 let running_before = count_running_tasks(&context, "engine");
4728 assert!(
4729 running_before > 0,
4730 "at least one engine task should be running"
4731 );
4732
4733 context.sleep(Duration::from_millis(1500)).await;
4735 assert!(
4736 count_running_tasks(&context, "engine") > 0,
4737 "engine tasks should still be running"
4738 );
4739
4740 let running_after = if graceful {
4742 let result = context
4743 .child("stop")
4744 .stop(0, Some(Duration::from_secs(5)))
4745 .await;
4746 assert!(
4747 result.is_ok(),
4748 "graceful shutdown should complete: {result:?}"
4749 );
4750 count_running_tasks(&context, "engine")
4751 } else {
4752 handle.abort();
4753 let _ = handle.await; context.sleep(Duration::from_millis(1000)).await;
4757 count_running_tasks(&context, "engine")
4758 };
4759 assert_eq!(
4760 running_after, 0,
4761 "all engine tasks should be stopped, but {running_after} still running"
4762 );
4763 });
4764 }
4765
4766 fn children_shutdown_on_engine_abort<S, F, L>(seed: u64, fixture: F)
4767 where
4768 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4769 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4770 L: Elector<S>,
4771 {
4772 engine_shutdown::<S, F, L>(seed, fixture, false);
4773 }
4774
4775 test_for_all_fixtures!(children_shutdown_on_engine_abort, seeds = 10);
4776
4777 fn graceful_shutdown<S, F, L>(seed: u64, fixture: F)
4778 where
4779 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4780 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4781 L: Elector<S>,
4782 {
4783 engine_shutdown::<S, F, L>(seed, fixture, true);
4784 }
4785
4786 test_for_all_fixtures!(graceful_shutdown, seeds = 10);
4787
4788 fn attributable_reporter_filtering<S, F, L>(mut fixture: F)
4789 where
4790 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4791 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4792 L: Elector<S>,
4793 {
4794 let n = 3;
4795 let required_containers = View::new(10);
4796 let activity_timeout = ViewDelta::new(10);
4797 let skip_timeout = ViewDelta::new(5);
4798 let namespace = b"consensus".to_vec();
4799 let executor = deterministic::Runner::timed(Duration::from_secs(30));
4800 executor.start(|mut context| async move {
4801 let Fixture {
4803 participants,
4804 schemes,
4805 ..
4806 } = fixture(&mut context, &namespace, n);
4807 let mut oracle = start_test_network_with_peers(
4808 context.child("network"),
4809 participants.clone(),
4810 false,
4811 )
4812 .await;
4813 let mut registrations = register_validators(&mut oracle, &participants).await;
4814
4815 let link = Link {
4817 latency: Duration::from_millis(10),
4818 jitter: Duration::from_millis(1),
4819 success_rate: 1.0,
4820 };
4821 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4822
4823 let elector = L::default();
4825 let relay = Arc::new(mocks::relay::Relay::new());
4826 let mut reporters = Vec::new();
4827 for (idx, validator) in participants.iter().enumerate() {
4828 let context = context
4829 .child("validator")
4830 .with_attribute("public_key", validator);
4831
4832 let reporter_config = mocks::reporter::Config {
4833 participants: participants.clone().try_into().unwrap(),
4834 scheme: schemes[idx].clone(),
4835 elector: elector.clone(),
4836 };
4837 let mock_reporter =
4838 mocks::reporter::Reporter::new(context.child("mock_reporter"), reporter_config);
4839
4840 let attributable_reporter = scheme::reporter::AttributableReporter::new(
4842 context.child("rng"),
4843 schemes[idx].clone(),
4844 mock_reporter.clone(),
4845 Sequential,
4846 true, );
4848 reporters.push(mock_reporter.clone());
4849
4850 let application_cfg = mocks::application::Config {
4851 hasher: Sha256::default(),
4852 relay: relay.clone(),
4853 me: validator.clone(),
4854 propose_latency: (10.0, 5.0),
4855 verify_latency: (10.0, 5.0),
4856 certify_latency: (10.0, 5.0),
4857 should_certify: mocks::application::Certifier::Always,
4858 };
4859 let (actor, application) = mocks::application::Application::new(
4860 context.child("application"),
4861 application_cfg,
4862 );
4863 actor.start();
4864 let blocker = oracle.control(validator.clone());
4865 let cfg = config::Config {
4866 scheme: schemes[idx].clone(),
4867 elector: elector.clone(),
4868 blocker,
4869 automaton: application.clone(),
4870 relay: application.clone(),
4871 reporter: attributable_reporter,
4872 strategy: Sequential,
4873 partition: validator.to_string(),
4874 mailbox_size: NZUsize!(1024),
4875 epoch: Epoch::new(333),
4876 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
4877 Epoch::new(333),
4878 )),
4879 leader_timeout: Duration::from_secs(1),
4880 certification_timeout: Duration::from_secs(2),
4881 timeout_retry: Duration::from_secs(10),
4882 fetch_timeout: Duration::from_secs(1),
4883 activity_timeout,
4884 skip_timeout,
4885 fetch_concurrent: NZUsize!(4),
4886 replay_buffer: NZUsize!(1024 * 1024),
4887 write_buffer: NZUsize!(1024 * 1024),
4888 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4889 forwarding: ForwardingPolicy::Disabled,
4890 };
4891 let engine = Engine::new(context.child("engine"), cfg);
4892
4893 let (pending, recovered, resolver) = registrations
4895 .remove(validator)
4896 .expect("validator should be registered");
4897 engine.start(pending, recovered, resolver);
4898 }
4899
4900 let mut finalizers = Vec::new();
4902 for reporter in reporters.iter_mut() {
4903 let (mut latest, mut monitor) = reporter.subscribe().await;
4904 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4905 while latest < required_containers {
4906 latest = monitor.recv().await.expect("event missing");
4907 }
4908 }));
4909 }
4910 join_all(finalizers).await;
4911
4912 for reporter in reporters.iter() {
4914 reporter.assert_no_faults();
4916
4917 reporter.assert_no_invalid();
4919
4920 {
4922 let notarizations = reporter.notarizations.lock();
4923 let finalizations = reporter.finalizations.lock();
4924 assert!(
4925 !notarizations.is_empty() || !finalizations.is_empty(),
4926 "Certificates should be reported"
4927 );
4928 }
4929
4930 let notarizes = reporter.notarizes.lock();
4932 let last_view = notarizes.keys().max().cloned().unwrap_or_default();
4933 for (view, payloads) in notarizes.iter() {
4934 if *view == last_view {
4935 continue; }
4937
4938 let signers: usize = payloads.values().map(|signers| signers.len()).sum();
4939
4940 if S::is_attributable() {
4942 assert!(signers > 1, "view {view}: {signers}");
4943 } else {
4944 assert_eq!(signers, 0);
4946 }
4947 }
4948
4949 let finalizes = reporter.finalizes.lock();
4951 for payloads in finalizes.values() {
4952 let signers: usize = payloads.values().map(|signers| signers.len()).sum();
4953
4954 if S::is_attributable() {
4956 assert!(signers > 1);
4957 } else {
4958 assert_eq!(signers, 0);
4960 }
4961 }
4962 }
4963
4964 let blocked = oracle.blocked().await.unwrap();
4966 assert!(blocked.is_empty());
4967 });
4968 }
4969
4970 test_for_all_fixtures!(attributable_reporter_filtering);
4971
4972 fn split_views_no_lockup<S, F, L>(mut fixture: F)
4973 where
4974 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4975 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4976 L: Elector<S>,
4977 {
4978 enum ParticipantType {
4991 Group1, Group2, Ignorant, Byzantine, }
4996 let get_type = |idx: usize| -> ParticipantType {
4997 match idx {
4998 0..3 => ParticipantType::Group1,
4999 3..6 => ParticipantType::Group2,
5000 6 => ParticipantType::Ignorant,
5001 7..10 => ParticipantType::Byzantine,
5002 _ => unreachable!(),
5003 }
5004 };
5005
5006 let n = 10;
5008 let quorum = quorum(n) as usize;
5009 assert_eq!(quorum, 7);
5010 let activity_timeout = ViewDelta::new(10);
5011 let skip_timeout = ViewDelta::new(5);
5012 let namespace = b"consensus".to_vec();
5013 let executor = deterministic::Runner::timed(Duration::from_secs(300));
5014 executor.start(|mut context| async move {
5015 let Fixture {
5017 participants,
5018 schemes,
5019 ..
5020 } = fixture(&mut context, &namespace, n);
5021 let mut oracle = start_test_network_with_peers(
5022 context.child("network"),
5023 participants.clone(),
5024 false,
5025 )
5026 .await;
5027 let mut registrations = register_validators(&mut oracle, &participants).await;
5028
5029 let build_finalization = |proposal: &Proposal<D>| -> TFinalization<_, D> {
5033 let votes: Vec<_> = (0..=quorum)
5034 .map(|i| TFinalize::sign(&schemes[i], proposal.clone()).unwrap())
5035 .collect();
5036 TFinalization::from_finalizes(&schemes[0], &votes, &Sequential)
5037 .expect("finalization quorum")
5038 };
5039 let build_notarization = |proposal: &Proposal<D>| -> TNotarization<_, D> {
5041 let votes: Vec<_> = (0..=quorum)
5042 .map(|i| TNotarize::sign(&schemes[i], proposal.clone()).unwrap())
5043 .collect();
5044 TNotarization::from_notarizes(&schemes[0], &votes, &Sequential)
5045 .expect("notarization quorum")
5046 };
5047 let build_nullification = |round: Round| -> TNullification<_> {
5048 let votes: Vec<_> = (0..=quorum)
5049 .map(|i| TNullify::sign::<D>(&schemes[i], round).unwrap())
5050 .collect();
5051 TNullification::from_nullifies(&schemes[0], &votes, &Sequential)
5052 .expect("nullification quorum")
5053 };
5054 let f_view = 1;
5056 let round_f = Round::new(Epoch::new(333), View::new(f_view));
5057 let payload_b0 = Sha256::hash(b"B_F");
5058 let proposal_b0 = Proposal::new(round_f, View::new(f_view - 1), payload_b0);
5059 let payload_b1a = Sha256::hash(b"B_G1");
5060 let proposal_b1a = Proposal::new(
5061 Round::new(Epoch::new(333), View::new(f_view + 1)),
5062 View::new(f_view),
5063 payload_b1a,
5064 );
5065 let payload_b1b = Sha256::hash(b"B_G2");
5066 let proposal_b1b = Proposal::new(
5067 Round::new(Epoch::new(333), View::new(f_view + 2)),
5068 View::new(f_view),
5069 payload_b1b,
5070 );
5071
5072 let b0_notarization = build_notarization(&proposal_b0);
5074 let b0_finalization = build_finalization(&proposal_b0);
5075 let b1a_notarization = build_notarization(&proposal_b1a);
5077 let b1b_notarization = build_notarization(&proposal_b1b);
5078 let null_a = build_nullification(Round::new(Epoch::new(333), View::new(f_view + 1)));
5080 let null_b = build_nullification(Round::new(Epoch::new(333), View::new(f_view + 2)));
5081
5082 let injector_pk = PrivateKey::from_seed(1_000_000).public_key();
5084 let (mut injector_sender, _inj_certificate_receiver) = oracle
5085 .control(injector_pk.clone())
5086 .register(1, TEST_QUOTA)
5087 .await
5088 .unwrap();
5089
5090 let link = Link {
5092 latency: Duration::from_millis(10),
5093 jitter: Duration::from_millis(0),
5094 success_rate: 1.0,
5095 };
5096 for p in participants.iter() {
5097 oracle
5098 .add_link(injector_pk.clone(), p.clone(), link.clone())
5099 .await
5100 .unwrap();
5101 }
5102 oracle.manager().track(
5103 1,
5104 TrackedPeers::new(
5105 Set::from_iter_dedup(participants.iter().cloned()),
5106 Set::from_iter_dedup(std::slice::from_ref(&injector_pk).iter().cloned()),
5107 ),
5108 );
5109 context.sleep(Duration::from_millis(10)).await;
5110
5111 let msg = Certificate::<_, D>::Notarization(b0_notarization).encode();
5115 injector_sender.send(Recipients::All, msg, true);
5116 let msg = Certificate::<_, D>::Finalization(b0_finalization).encode();
5117 injector_sender.send(Recipients::All, msg, true);
5118 let notarization_msg = Certificate::<_, D>::Notarization(b1a_notarization);
5120 let nullification_msg = Certificate::<_, D>::Nullification(null_a.clone());
5121 for (i, participant) in participants.iter().enumerate() {
5122 let recipient = Recipients::One(participant.clone());
5123 let msg = match get_type(i) {
5124 ParticipantType::Group1 => notarization_msg.encode(),
5125 _ => nullification_msg.encode(),
5126 };
5127 injector_sender.send(recipient, msg, true);
5128 }
5129 let notarization_msg = Certificate::<_, D>::Notarization(b1b_notarization);
5131 let nullification_msg = Certificate::<_, D>::Nullification(null_b.clone());
5132 for (i, participant) in participants.iter().enumerate() {
5133 let recipient = Recipients::One(participant.clone());
5134 let msg = match get_type(i) {
5135 ParticipantType::Group2 => notarization_msg.encode(),
5136 _ => nullification_msg.encode(),
5137 };
5138 injector_sender.send(recipient, msg, true);
5139 }
5140
5141 let elector = L::default();
5147 let relay = Arc::new(mocks::relay::Relay::new());
5148 let mut honest_reporters = Vec::new();
5149 for (idx, validator) in participants.iter().enumerate() {
5150 let (pending, recovered, resolver) = registrations
5151 .remove(validator)
5152 .expect("validator should be registered");
5153 let participant_type = get_type(idx);
5154 if matches!(participant_type, ParticipantType::Byzantine) {
5155 let cfg = mocks::nullify_only::Config {
5157 scheme: schemes[idx].clone(),
5158 };
5159 let engine: mocks::nullify_only::NullifyOnly<_, _, Sha256> =
5160 mocks::nullify_only::NullifyOnly::new(
5161 context
5162 .child("byzantine")
5163 .with_attribute("public_key", validator),
5164 cfg,
5165 );
5166 engine.start(pending);
5167 drop(recovered);
5169 drop(resolver);
5170 } else {
5171 let reporter_config = mocks::reporter::Config {
5173 participants: participants.clone().try_into().unwrap(),
5174 scheme: schemes[idx].clone(),
5175 elector: elector.clone(),
5176 };
5177 let reporter = mocks::reporter::Reporter::new(
5178 context
5179 .child("reporter")
5180 .with_attribute("public_key", validator),
5181 reporter_config,
5182 );
5183 honest_reporters.push(reporter.clone());
5184
5185 let application_cfg = mocks::application::Config {
5186 hasher: Sha256::default(),
5187 relay: relay.clone(),
5188 me: validator.clone(),
5189 propose_latency: (250.0, 50.0), verify_latency: (10.0, 5.0),
5191 certify_latency: (10.0, 5.0),
5192 should_certify: mocks::application::Certifier::Always,
5193 };
5194 let (actor, application) = mocks::application::Application::new(
5195 context
5196 .child("application")
5197 .with_attribute("public_key", validator),
5198 application_cfg,
5199 );
5200 actor.start();
5201 let blocker = oracle.control(validator.clone());
5202 let cfg = config::Config {
5203 scheme: schemes[idx].clone(),
5204 elector: elector.clone(),
5205 blocker,
5206 automaton: application.clone(),
5207 relay: application.clone(),
5208 reporter: reporter.clone(),
5209 strategy: Sequential,
5210 partition: validator.to_string(),
5211 mailbox_size: NZUsize!(1024),
5212 epoch: Epoch::new(333),
5213 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
5214 Epoch::new(333),
5215 )),
5216 leader_timeout: Duration::from_secs(10),
5217 certification_timeout: Duration::from_secs(10),
5218 timeout_retry: Duration::from_secs(10),
5219 fetch_timeout: Duration::from_secs(1),
5220 activity_timeout,
5221 skip_timeout,
5222 fetch_concurrent: NZUsize!(4),
5223 replay_buffer: NZUsize!(1024 * 1024),
5224 write_buffer: NZUsize!(1024 * 1024),
5225 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
5226 forwarding: ForwardingPolicy::Disabled,
5227 };
5228 let engine = Engine::new(
5229 context
5230 .child("engine")
5231 .with_attribute("public_key", validator),
5232 cfg,
5233 );
5234 engine.start(pending, recovered, resolver);
5235 }
5236 }
5237
5238 context.sleep(Duration::from_secs(2)).await;
5240
5241 let view = View::new(f_view);
5246 for reporter in honest_reporters.iter() {
5247 let finalizations = reporter.finalizations.lock();
5248 assert!(finalizations.contains_key(&view));
5249 }
5250
5251 let view = View::new(f_view + 1);
5255 for (i, reporter) in honest_reporters.iter().enumerate() {
5256 let finalizations = reporter.finalizations.lock();
5257 assert!(!finalizations.contains_key(&view));
5258 let nullifications = reporter.nullifications.lock();
5259 let notarizations = reporter.notarizations.lock();
5260 match get_type(i) {
5261 ParticipantType::Group1 => {
5262 assert!(notarizations.contains_key(&view));
5263 assert!(!nullifications.contains_key(&view));
5264 }
5265 _ => {
5266 assert!(nullifications.contains_key(&view));
5267 assert!(!notarizations.contains_key(&view));
5268 }
5269 }
5270 }
5271
5272 let view = View::new(f_view + 2);
5276 for (i, reporter) in honest_reporters.iter().enumerate() {
5277 let finalizations = reporter.finalizations.lock();
5278 assert!(!finalizations.contains_key(&view));
5279 let nullifications = reporter.nullifications.lock();
5280 let notarizations = reporter.notarizations.lock();
5281 match get_type(i) {
5282 ParticipantType::Group2 => {
5283 assert!(notarizations.contains_key(&view));
5284 assert!(!nullifications.contains_key(&view));
5285 }
5286 _ => {
5287 assert!(nullifications.contains_key(&view));
5288 assert!(!notarizations.contains_key(&view));
5289 }
5290 }
5291 }
5292
5293 let next_view = View::new(f_view + 3);
5295 for (i, reporter) in honest_reporters.iter().enumerate() {
5296 let nullifies = reporter.nullifies.lock();
5297 assert!(!nullifies.contains_key(&next_view), "reporter {i}");
5298 }
5299
5300 link_validators(&mut oracle, &participants, Action::Link(link.clone()), None).await;
5304
5305 {
5307 let target = View::new(f_view + 3);
5308 let mut finalizers = Vec::new();
5309 for reporter in honest_reporters.iter_mut() {
5310 let (mut latest, mut monitor) = reporter.subscribe().await;
5311 finalizers.push(
5312 context
5313 .child("resume_finalizer")
5314 .spawn(move |_| async move {
5315 while latest < target {
5316 latest = monitor.recv().await.expect("event missing");
5317 }
5318 }),
5319 );
5320 }
5321 join_all(finalizers).await;
5322 }
5323
5324 for reporter in honest_reporters.iter() {
5326 reporter.assert_no_faults();
5327 reporter.assert_no_invalid();
5328 }
5329 let blocked = oracle.blocked().await.unwrap();
5330 assert!(blocked.is_empty(), "blocked peers: {blocked:?}");
5331 });
5332 }
5333
5334 test_for_all_fixtures!(split_views_no_lockup);
5335
5336 fn tle<V, L>()
5337 where
5338 V: Variant,
5339 L: Elector<bls12381_threshold_vrf::Scheme<PublicKey, V>>,
5340 {
5341 let n = 4;
5343 let namespace = b"consensus".to_vec();
5344 let activity_timeout = ViewDelta::new(100);
5345 let skip_timeout = ViewDelta::new(50);
5346 let executor = deterministic::Runner::timed(Duration::from_secs(30));
5347 executor.start(|mut context| async move {
5348 let Fixture {
5350 participants,
5351 schemes,
5352 ..
5353 } = bls12381_threshold_vrf::fixture::<V, _>(&mut context, &namespace, n);
5354 let mut oracle =
5355 start_test_network_with_peers(context.child("network"), participants.clone(), true)
5356 .await;
5357 let mut registrations = register_validators(&mut oracle, &participants).await;
5358
5359 let link = Link {
5361 latency: Duration::from_millis(10),
5362 jitter: Duration::from_millis(5),
5363 success_rate: 1.0,
5364 };
5365 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
5366
5367 let elector = L::default();
5369 let relay = Arc::new(mocks::relay::Relay::new());
5370 let mut reporters = Vec::new();
5371 let mut engine_handlers = Vec::new();
5372 let monitor_reporter = Arc::new(Mutex::new(None));
5373 for (idx, validator) in participants.iter().enumerate() {
5374 let context = context
5376 .child("validator")
5377 .with_attribute("public_key", validator);
5378
5379 let reporter_config = mocks::reporter::Config {
5381 participants: participants.clone().try_into().unwrap(),
5382 scheme: schemes[idx].clone(),
5383 elector: elector.clone(),
5384 };
5385 let reporter =
5386 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
5387 reporters.push(reporter.clone());
5388 if idx == 0 {
5389 *monitor_reporter.lock() = Some(reporter.clone());
5390 }
5391
5392 let application_cfg = mocks::application::Config {
5394 hasher: Sha256::default(),
5395 relay: relay.clone(),
5396 me: validator.clone(),
5397 propose_latency: (10.0, 5.0),
5398 verify_latency: (10.0, 5.0),
5399 certify_latency: (10.0, 5.0),
5400 should_certify: mocks::application::Certifier::Always,
5401 };
5402 let (actor, application) = mocks::application::Application::new(
5403 context.child("application"),
5404 application_cfg,
5405 );
5406 actor.start();
5407 let blocker = oracle.control(validator.clone());
5408 let cfg = config::Config {
5409 scheme: schemes[idx].clone(),
5410 elector: elector.clone(),
5411 blocker,
5412 automaton: application.clone(),
5413 relay: application.clone(),
5414 reporter: reporter.clone(),
5415 strategy: Sequential,
5416 partition: validator.to_string(),
5417 mailbox_size: NZUsize!(1024),
5418 epoch: Epoch::new(333),
5419 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
5420 Epoch::new(333),
5421 )),
5422 leader_timeout: Duration::from_millis(100),
5423 certification_timeout: Duration::from_millis(200),
5424 timeout_retry: Duration::from_millis(500),
5425 fetch_timeout: Duration::from_millis(100),
5426 activity_timeout,
5427 skip_timeout,
5428 fetch_concurrent: NZUsize!(4),
5429 replay_buffer: NZUsize!(1024 * 1024),
5430 write_buffer: NZUsize!(1024 * 1024),
5431 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
5432 forwarding: ForwardingPolicy::Disabled,
5433 };
5434 let engine = Engine::new(context.child("engine"), cfg);
5435
5436 let (pending, recovered, resolver) = registrations
5438 .remove(validator)
5439 .expect("validator should be registered");
5440 engine_handlers.push(engine.start(pending, recovered, resolver));
5441 }
5442
5443 let target = Round::new(Epoch::new(333), View::new(10)); let message = b"Secret message for future view10"; let ciphertext = schemes[0].encrypt(&mut context, target, *message);
5449
5450 let reporter = monitor_reporter.lock().clone().unwrap();
5452 loop {
5453 context.sleep(Duration::from_millis(100)).await;
5455 let notarizations = reporter.notarizations.lock();
5456 let Some(notarization) = notarizations.get(&target.view()) else {
5457 continue;
5458 };
5459
5460 let seed = notarization.seed();
5462 let decrypted = seed
5463 .decrypt(&ciphertext)
5464 .expect("Decryption should succeed with valid seed signature");
5465 assert_eq!(
5466 message,
5467 decrypted.as_ref(),
5468 "Decrypted message should match original message"
5469 );
5470 break;
5471 }
5472 });
5473 }
5474
5475 #[test_traced]
5476 fn test_tle() {
5477 tle::<MinPk, Random>();
5478 tle::<MinSig, Random>();
5479 }
5480
5481 fn run_hailstorm<S, F, L>(
5482 seed: u64,
5483 shutdowns: usize,
5484 interval: ViewDelta,
5485 mut fixture: F,
5486 ) -> String
5487 where
5488 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
5489 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
5490 L: Elector<S>,
5491 {
5492 let n = 5;
5494 let activity_timeout = ViewDelta::new(10);
5495 let skip_timeout = ViewDelta::new(5);
5496 let namespace = b"consensus".to_vec();
5497 let cfg = deterministic::Config::new().with_seed(seed);
5498 let executor = deterministic::Runner::new(cfg);
5499 executor.start(|mut context| async move {
5500 let Fixture {
5502 participants,
5503 schemes,
5504 ..
5505 } = fixture(&mut context, &namespace, n);
5506 let mut oracle =
5507 start_test_network_with_peers(context.child("network"), participants.clone(), true)
5508 .await;
5509 let mut registrations = register_validators(&mut oracle, &participants).await;
5510
5511 let link = Link {
5513 latency: Duration::from_millis(10),
5514 jitter: Duration::from_millis(1),
5515 success_rate: 1.0,
5516 };
5517 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
5518
5519 let elector = L::default();
5521 let relay = Arc::new(mocks::relay::Relay::new());
5522 let mut reporters = BTreeMap::new();
5523 let mut engine_handlers = BTreeMap::new();
5524 for (idx, validator) in participants.iter().enumerate() {
5525 let context = context
5527 .child("validator")
5528 .with_attribute("public_key", validator);
5529
5530 let reporter_config = mocks::reporter::Config {
5532 participants: participants.clone().try_into().unwrap(),
5533 scheme: schemes[idx].clone(),
5534 elector: elector.clone(),
5535 };
5536 let reporter =
5537 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
5538 reporters.insert(idx, reporter.clone());
5539 let application_cfg = mocks::application::Config {
5540 hasher: Sha256::default(),
5541 relay: relay.clone(),
5542 me: validator.clone(),
5543 propose_latency: (10.0, 5.0),
5544 verify_latency: (10.0, 5.0),
5545 certify_latency: (10.0, 5.0),
5546 should_certify: mocks::application::Certifier::Always,
5547 };
5548 let (actor, application) = mocks::application::Application::new(
5549 context.child("application"),
5550 application_cfg,
5551 );
5552 actor.start();
5553 let blocker = oracle.control(validator.clone());
5554 let cfg = config::Config {
5555 scheme: schemes[idx].clone(),
5556 elector: elector.clone(),
5557 blocker,
5558 automaton: application.clone(),
5559 relay: application.clone(),
5560 reporter: reporter.clone(),
5561 strategy: Sequential,
5562 partition: validator.to_string(),
5563 mailbox_size: NZUsize!(1024),
5564 epoch: Epoch::new(333),
5565 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
5566 Epoch::new(333),
5567 )),
5568 leader_timeout: Duration::from_secs(1),
5569 certification_timeout: Duration::from_secs(2),
5570 timeout_retry: Duration::from_secs(10),
5571 fetch_timeout: Duration::from_secs(1),
5572 activity_timeout,
5573 skip_timeout,
5574 fetch_concurrent: NZUsize!(4),
5575 replay_buffer: NZUsize!(1024 * 1024),
5576 write_buffer: NZUsize!(1024 * 1024),
5577 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
5578 forwarding: ForwardingPolicy::Disabled,
5579 };
5580 let engine = Engine::new(context.child("engine"), cfg);
5581
5582 let (pending, recovered, resolver) = registrations
5584 .remove(validator)
5585 .expect("validator should be registered");
5586 engine_handlers.insert(idx, engine.start(pending, recovered, resolver));
5587 }
5588
5589 let mut target = View::zero();
5591 for i in 0..shutdowns {
5592 target = target.saturating_add(interval);
5594
5595 let mut finalizers = Vec::new();
5597 for reporter in reporters.values_mut() {
5598 let (mut latest, mut monitor) = reporter.subscribe().await;
5599 finalizers.push(context.child("finalizer").spawn(move |_| async move {
5600 while latest < target {
5601 latest = monitor.recv().await.expect("event missing");
5602 }
5603 }));
5604 }
5605 join_all(finalizers).await;
5606 target = target.saturating_add(interval);
5607
5608 let idx = context.random_range(0..engine_handlers.len());
5610 let validator = &participants[idx];
5611 let handle = engine_handlers.remove(&idx).unwrap();
5612 handle.abort();
5613 let _ = handle.await;
5614 let selected_reporter = reporters.remove(&idx).unwrap();
5615 info!(idx, ?validator, "shutdown validator");
5616
5617 let mut finalizers = Vec::new();
5619 for reporter in reporters.values_mut() {
5620 let (mut latest, mut monitor) = reporter.subscribe().await;
5621 finalizers.push(context.child("finalizer").spawn(move |_| async move {
5622 while latest < target {
5623 latest = monitor.recv().await.expect("event missing");
5624 }
5625 }));
5626 }
5627 join_all(finalizers).await;
5628 target = target.saturating_add(interval);
5629
5630 info!(idx, ?validator, "restarting validator");
5632 let context = context
5633 .child("validator_restarted")
5634 .with_attribute("public_key", validator)
5635 .with_attribute("restart", i);
5636
5637 let (pending, recovered, resolver) =
5639 register_validator(&mut oracle, validator.clone()).await;
5640 let application_cfg = mocks::application::Config {
5641 hasher: Sha256::default(),
5642 relay: relay.clone(),
5643 me: validator.clone(),
5644 propose_latency: (10.0, 5.0),
5645 verify_latency: (10.0, 5.0),
5646 certify_latency: (10.0, 5.0),
5647 should_certify: mocks::application::Certifier::Always,
5648 };
5649 let (actor, application) = mocks::application::Application::new(
5650 context.child("application"),
5651 application_cfg,
5652 );
5653 actor.start();
5654 reporters.insert(idx, selected_reporter.clone());
5655 let blocker = oracle.control(validator.clone());
5656 let cfg = config::Config {
5657 scheme: schemes[idx].clone(),
5658 elector: elector.clone(),
5659 blocker,
5660 automaton: application.clone(),
5661 relay: application.clone(),
5662 reporter: selected_reporter,
5663 strategy: Sequential,
5664 partition: validator.to_string(),
5665 mailbox_size: NZUsize!(1024),
5666 epoch: Epoch::new(333),
5667 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
5668 Epoch::new(333),
5669 )),
5670 leader_timeout: Duration::from_secs(1),
5671 certification_timeout: Duration::from_secs(2),
5672 timeout_retry: Duration::from_secs(10),
5673 fetch_timeout: Duration::from_secs(1),
5674 activity_timeout,
5675 skip_timeout,
5676 fetch_concurrent: NZUsize!(4),
5677 replay_buffer: NZUsize!(1024 * 1024),
5678 write_buffer: NZUsize!(1024 * 1024),
5679 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
5680 forwarding: ForwardingPolicy::Disabled,
5681 };
5682 let engine = Engine::new(context.child("engine"), cfg);
5683 engine_handlers.insert(idx, engine.start(pending, recovered, resolver));
5684
5685 let mut finalizers = Vec::new();
5687 for reporter in reporters.values_mut() {
5688 let (mut latest, mut monitor) = reporter.subscribe().await;
5689 finalizers.push(context.child("finalizer").spawn(move |_| async move {
5690 while latest < target {
5691 latest = monitor.recv().await.expect("event missing");
5692 }
5693 }));
5694 }
5695 join_all(finalizers).await;
5696 info!(idx, ?validator, "validator recovered");
5697 }
5698
5699 let latest_complete = target.saturating_sub(activity_timeout);
5701 for reporter in reporters.values() {
5702 reporter.assert_no_faults();
5704
5705 reporter.assert_no_invalid();
5707
5708 let mut notarized = HashMap::new();
5710 let mut finalized = HashMap::new();
5711 {
5712 let notarizes = reporter.notarizes.lock();
5713 for view in View::range(View::new(1), latest_complete) {
5714 let Some(payloads) = notarizes.get(&view) else {
5716 continue;
5717 };
5718 if payloads.len() > 1 {
5719 panic!("view: {view}");
5720 }
5721 let (digest, _) = payloads.iter().next().unwrap();
5722 notarized.insert(view, *digest);
5723 }
5724 }
5725 {
5726 let notarizations = reporter.notarizations.lock();
5727 for view in View::range(View::new(1), latest_complete) {
5728 let Some(notarization) = notarizations.get(&view) else {
5730 continue;
5731 };
5732 let Some(digest) = notarized.get(&view) else {
5733 continue;
5734 };
5735 assert_eq!(¬arization.proposal.payload, digest);
5736 }
5737 }
5738 {
5739 let finalizes = reporter.finalizes.lock();
5740 for view in View::range(View::new(1), latest_complete) {
5741 let Some(payloads) = finalizes.get(&view) else {
5743 continue;
5744 };
5745 if payloads.len() > 1 {
5746 panic!("view: {view}");
5747 }
5748 let (digest, _) = payloads.iter().next().unwrap();
5749 finalized.insert(view, *digest);
5750
5751 if view > latest_complete {
5753 continue;
5754 }
5755
5756 let nullifies = reporter.nullifies.lock();
5758 let Some(nullifies) = nullifies.get(&view) else {
5759 continue;
5760 };
5761 for finalizers in payloads.values() {
5762 for finalizer in finalizers.iter() {
5763 if nullifies.contains(finalizer) {
5764 panic!("should not nullify and finalize at same view");
5765 }
5766 }
5767 }
5768 }
5769 }
5770 {
5771 let finalizations = reporter.finalizations.lock();
5772 for view in View::range(View::new(1), latest_complete) {
5773 let Some(finalization) = finalizations.get(&view) else {
5775 continue;
5776 };
5777 let Some(digest) = finalized.get(&view) else {
5778 continue;
5779 };
5780 assert_eq!(&finalization.proposal.payload, digest);
5781 }
5782 }
5783 }
5784
5785 let blocked = oracle.blocked().await.unwrap();
5787 assert!(blocked.is_empty());
5788
5789 context.auditor().state()
5791 })
5792 }
5793
5794 fn hailstorm<S, F, L>(fixture: F)
5797 where
5798 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
5799 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S> + Copy,
5800 L: Elector<S>,
5801 {
5802 assert_eq!(
5803 run_hailstorm::<_, _, L>(0, 10, ViewDelta::new(15), fixture),
5804 run_hailstorm::<_, _, L>(0, 10, ViewDelta::new(15), fixture),
5805 );
5806 }
5807
5808 test_for_all_fixtures!(hailstorm);
5809
5810 #[derive(Clone, Copy, Debug)]
5847 struct TwinsCampaign {
5848 n: u32,
5849 rounds: usize,
5850 mode: twins::Mode,
5851 max_cases: usize,
5852 trailing_finalizations: usize,
5853 }
5854
5855 fn twins_campaign<S, F, L>(
5856 rng: &mut impl CryptoRng,
5857 campaign: TwinsCampaign,
5858 link: Link,
5859 mut fixture: F,
5860 ) where
5861 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
5862 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
5863 L: Elector<S>,
5864 {
5865 let n = campaign.n;
5866 let faults = N3f1::max_faults(n) as usize;
5867 let cases = twins::cases(
5868 rng,
5869 twins::Framework {
5870 participants: n as usize,
5871 faults,
5872 rounds: campaign.rounds,
5873 mode: campaign.mode,
5874 max_cases: campaign.max_cases,
5875 },
5876 );
5877 assert!(
5878 !cases.is_empty(),
5879 "twins campaign should generate at least one case"
5880 );
5881 for case in cases {
5882 let scenario = case.scenario.clone();
5883 let twin_indices = case.compromised.clone();
5884 assert_eq!(
5885 twin_indices.len(),
5886 faults,
5887 "unexpected twins count for n={n} (expected f={faults})",
5888 );
5889
5890 let activity_timeout = ViewDelta::new(10);
5891 let skip_timeout = ViewDelta::new(5);
5892 let namespace = b"consensus".to_vec();
5893 let link = link.clone();
5894 let trailing_finalizations = campaign.trailing_finalizations;
5895 let mut case_fixture =
5896 |ctx: &mut deterministic::Context, ns: &[u8], n: u32| fixture(ctx, ns, n);
5897 let cfg = deterministic::Config::new().with_rng(Box::new(StdRng::from_rng(&mut *rng)));
5898 let executor = deterministic::Runner::new(cfg);
5899 executor.start(|mut context| async move {
5900 let Fixture {
5901 participants,
5902 schemes,
5903 ..
5904 } = case_fixture(&mut context, &namespace, n);
5905 let participants: Arc<[_]> = participants.into();
5906 let mut oracle = start_test_network_with_peers(
5907 context.child("network"),
5908 participants.iter().cloned(),
5909 false,
5910 )
5911 .await;
5912 let mut registrations = register_validators(&mut oracle, &participants).await;
5913 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
5914
5915 let elector = TwinsElector::new(L::default(), &scenario, n as usize);
5916 let relay = Arc::new(mocks::relay::Relay::new());
5917 let mut reporters = Vec::new();
5918 let mut engine_handlers = Vec::new();
5919 let twin_index_set: HashSet<usize> = twin_indices.iter().copied().collect();
5920
5921 for idx in twin_indices.iter().copied() {
5923 let validator = &participants[idx];
5924 let (
5925 (vote_sender, vote_receiver),
5926 (certificate_sender, certificate_receiver),
5927 (_resolver_sender, _resolver_receiver),
5928 ) = registrations
5929 .remove(validator)
5930 .expect("validator should be registered");
5931
5932 let make_vote_forwarder = || {
5933 let participants = participants.clone();
5934 let scenario = scenario.clone();
5935 move |origin: SplitOrigin, _: &Recipients<_>, message: &IoBuf| {
5936 let msg: Vote<S, D> = Vote::decode(message.clone()).unwrap();
5937 let (primary, secondary) =
5938 scenario.partitions(msg.view(), participants.as_ref());
5939 match origin {
5940 SplitOrigin::Primary => Some(Recipients::Some(primary)),
5941 SplitOrigin::Secondary => Some(Recipients::Some(secondary)),
5942 }
5943 }
5944 };
5945 let make_certificate_forwarder = || {
5946 let codec = schemes[idx].certificate_codec_config();
5947 let participants = participants.clone();
5948 let scenario = scenario.clone();
5949 move |origin: SplitOrigin, _: &Recipients<_>, message: &IoBuf| {
5950 let msg: Certificate<S, D> =
5951 Certificate::decode_cfg(&mut message.as_ref(), &codec).unwrap();
5952 let (primary, secondary) =
5953 scenario.partitions(msg.view(), participants.as_ref());
5954 match origin {
5955 SplitOrigin::Primary => Some(Recipients::Some(primary)),
5956 SplitOrigin::Secondary => Some(Recipients::Some(secondary)),
5957 }
5958 }
5959 };
5960 let make_vote_router = || {
5961 let participants = participants.clone();
5962 let scenario = scenario.clone();
5963 move |(sender, message): &(_, IoBuf)| {
5964 let msg: Vote<S, D> = Vote::decode(message.clone()).unwrap();
5965 scenario.route(msg.view(), sender, participants.as_ref())
5966 }
5967 };
5968 let make_certificate_router = || {
5969 let codec = schemes[idx].certificate_codec_config();
5970 let participants = participants.clone();
5971 let scenario = scenario.clone();
5972 move |(sender, message): &(_, IoBuf)| {
5973 let msg: Certificate<S, D> =
5974 Certificate::decode_cfg(&mut message.as_ref(), &codec).unwrap();
5975 scenario.route(msg.view(), sender, participants.as_ref())
5976 }
5977 };
5978 let (vote_sender_primary, vote_sender_secondary) =
5979 vote_sender.split_with(make_vote_forwarder());
5980 let (vote_receiver_primary, vote_receiver_secondary) = vote_receiver
5981 .split_with(
5982 context.child("pending_split").with_attribute("index", idx),
5983 make_vote_router(),
5984 );
5985 let (certificate_sender_primary, certificate_sender_secondary) =
5986 certificate_sender.split_with(make_certificate_forwarder());
5987 let (certificate_receiver_primary, certificate_receiver_secondary) =
5988 certificate_receiver.split_with(
5989 context
5990 .child("recovered_split")
5991 .with_attribute("index", idx),
5992 make_certificate_router(),
5993 );
5994
5995 for (twin_label, pending, recovered) in [
5996 (
5997 "primary",
5998 (vote_sender_primary, vote_receiver_primary),
5999 (certificate_sender_primary, certificate_receiver_primary),
6000 ),
6001 (
6002 "secondary",
6003 (vote_sender_secondary, vote_receiver_secondary),
6004 (certificate_sender_secondary, certificate_receiver_secondary),
6005 ),
6006 ] {
6007 let partition = format!("twin_{idx}_{twin_label}");
6008 let context = context
6009 .child("twin")
6010 .with_attribute("index", idx)
6011 .with_attribute("side", twin_label);
6012
6013 let reporter_config = mocks::reporter::Config {
6014 participants: participants.as_ref().try_into().unwrap(),
6015 scheme: schemes[idx].clone(),
6016 elector: elector.clone(),
6017 };
6018 let reporter = mocks::reporter::Reporter::new(
6019 context.child("reporter"),
6020 reporter_config,
6021 );
6022 reporters.push(reporter.clone());
6023
6024 let application_cfg = mocks::application::Config {
6025 hasher: Sha256::default(),
6026 relay: relay.clone(),
6027 me: validator.clone(),
6028 propose_latency: (10.0, 5.0),
6029 verify_latency: (10.0, 5.0),
6030 certify_latency: (10.0, 5.0),
6031 should_certify: mocks::application::Certifier::Always,
6032 };
6033 let (actor, application) = mocks::application::Application::new(
6034 context.child("application"),
6035 application_cfg,
6036 );
6037 actor.start();
6038
6039 let blocker = oracle.control(validator.clone());
6040 let cfg = config::Config {
6041 scheme: schemes[idx].clone(),
6042 elector: elector.clone(),
6043 blocker,
6044 automaton: application.clone(),
6045 relay: application.clone(),
6046 reporter: reporter.clone(),
6047 strategy: Sequential,
6048 partition,
6049 mailbox_size: NZUsize!(1024),
6050 epoch: Epoch::new(333),
6051 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
6052 Epoch::new(333),
6053 )),
6054 leader_timeout: Duration::from_secs(1),
6055 certification_timeout: Duration::from_millis(1_500),
6056 timeout_retry: Duration::from_secs(10),
6057 fetch_timeout: Duration::from_secs(1),
6058 activity_timeout,
6059 skip_timeout,
6060 fetch_concurrent: NZUsize!(4),
6061 replay_buffer: NZUsize!(1024 * 1024),
6062 write_buffer: NZUsize!(1024 * 1024),
6063 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
6064 forwarding: ForwardingPolicy::Disabled,
6065 };
6066 let engine = Engine::new(context.child("engine"), cfg);
6067 engine_handlers.push(engine.start(
6068 pending,
6069 recovered,
6070 inert_channel(participants.as_ref()),
6071 ));
6072 }
6073 }
6074
6075 let honest_start = reporters.len();
6077 for (idx, validator) in participants.iter().enumerate() {
6078 if twin_index_set.contains(&idx) {
6079 continue;
6080 }
6081
6082 let partition = format!("honest_{idx}");
6083 let context = context.child("honest").with_attribute("index", idx);
6084
6085 let reporter_config = mocks::reporter::Config {
6086 participants: participants.as_ref().try_into().unwrap(),
6087 scheme: schemes[idx].clone(),
6088 elector: elector.clone(),
6089 };
6090 let reporter =
6091 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
6092 reporters.push(reporter.clone());
6093
6094 let application_cfg = mocks::application::Config {
6095 hasher: Sha256::default(),
6096 relay: relay.clone(),
6097 me: validator.clone(),
6098 propose_latency: (10.0, 5.0),
6099 verify_latency: (10.0, 5.0),
6100 certify_latency: (10.0, 5.0),
6101 should_certify: mocks::application::Certifier::Always,
6102 };
6103 let (actor, application) = mocks::application::Application::new(
6104 context.child("application"),
6105 application_cfg,
6106 );
6107 actor.start();
6108
6109 let blocker = oracle.control(validator.clone());
6110 let cfg = config::Config {
6111 scheme: schemes[idx].clone(),
6112 elector: elector.clone(),
6113 blocker,
6114 automaton: application.clone(),
6115 relay: application.clone(),
6116 reporter: reporter.clone(),
6117 strategy: Sequential,
6118 partition,
6119 mailbox_size: NZUsize!(1024),
6120 epoch: Epoch::new(333),
6121 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
6122 Epoch::new(333),
6123 )),
6124 leader_timeout: Duration::from_secs(1),
6125 certification_timeout: Duration::from_millis(1_500),
6126 timeout_retry: Duration::from_secs(10),
6127 fetch_timeout: Duration::from_secs(1),
6128 activity_timeout,
6129 skip_timeout,
6130 fetch_concurrent: NZUsize!(4),
6131 replay_buffer: NZUsize!(1024 * 1024),
6132 write_buffer: NZUsize!(1024 * 1024),
6133 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
6134 forwarding: ForwardingPolicy::Disabled,
6135 };
6136 let engine = Engine::new(context.child("engine"), cfg);
6137
6138 let (
6139 (pending_sender, pending_receiver),
6140 (recovered_sender, recovered_receiver),
6141 _,
6142 ) = registrations
6143 .remove(validator)
6144 .expect("validator should be registered");
6145 engine_handlers.push(engine.start(
6146 (pending_sender, pending_receiver),
6147 (recovered_sender, recovered_receiver),
6148 inert_channel(participants.as_ref()),
6149 ));
6150 }
6151
6152 let prefix_end = View::new(scenario.rounds().len() as u64);
6162 let mut finalizers = Vec::new();
6163 for (i, reporter) in reporters.iter_mut().skip(honest_start).enumerate() {
6164 let (_latest, mut monitor) = reporter.subscribe().await;
6165 let required = trailing_finalizations;
6166 finalizers.push(context.child("finalizer").with_attribute("index", i).spawn(
6167 move |_| async move {
6168 let mut count = 0usize;
6169 while count < required {
6170 let view = monitor.recv().await.expect("event missing");
6171 if view > prefix_end {
6172 count += 1;
6173 }
6174 }
6175 },
6176 ));
6177 }
6178 join_all(finalizers).await;
6179
6180 let mut finalized_at_view: BTreeMap<View, D> = BTreeMap::new();
6182 for reporter in reporters.iter().skip(honest_start) {
6183 let finalizations = reporter.finalizations.lock();
6184 for (view, finalization) in finalizations.iter() {
6185 let digest = finalization.proposal.payload;
6186 if let Some(existing) = finalized_at_view.get(view) {
6187 assert_eq!(
6188 existing, &digest,
6189 "safety violation: conflicting finalizations at view {view}"
6190 );
6191 } else {
6192 finalized_at_view.insert(*view, digest);
6193 }
6194 }
6195 }
6196
6197 for reporter in reporters.iter().skip(honest_start) {
6199 reporter.assert_no_invalid();
6200 }
6201
6202 let twin_identities: HashSet<_> = twin_indices
6204 .iter()
6205 .map(|idx| participants[*idx].clone())
6206 .collect();
6207 let mut notarized_by_honest_signer: BTreeMap<View, HashMap<PublicKey, D>> =
6208 BTreeMap::new();
6209 let mut finalized_by_honest_signer: BTreeMap<View, HashMap<PublicKey, D>> =
6210 BTreeMap::new();
6211 for reporter in reporters.iter().skip(honest_start) {
6212 let notarizes = reporter.notarizes.lock();
6213 for (view, payloads) in notarizes.iter() {
6214 let signers = notarized_by_honest_signer.entry(*view).or_default();
6215 for (digest, payload_signers) in payloads.iter() {
6216 for signer in payload_signers.iter() {
6217 if twin_identities.contains(signer) {
6218 continue;
6219 }
6220 if let Some(existing) = signers.insert(signer.clone(), *digest) {
6221 assert_eq!(
6222 existing, *digest,
6223 "honest signer produced conflicting notarizes at view {view}"
6224 );
6225 }
6226 }
6227 }
6228 }
6229
6230 let finalizes = reporter.finalizes.lock();
6231 for (view, payloads) in finalizes.iter() {
6232 let signers = finalized_by_honest_signer.entry(*view).or_default();
6233 for (digest, payload_signers) in payloads.iter() {
6234 for signer in payload_signers.iter() {
6235 if twin_identities.contains(signer) {
6236 continue;
6237 }
6238 if let Some(existing) = signers.insert(signer.clone(), *digest) {
6239 assert_eq!(
6240 existing, *digest,
6241 "honest signer produced conflicting finalizes at view {view}"
6242 );
6243 }
6244 }
6245 }
6246 }
6247 }
6248
6249 for reporter in reporters.iter().skip(honest_start) {
6251 let faults = reporter.faults.lock();
6252 for faulter in faults.keys() {
6253 assert!(
6254 twin_identities.contains(faulter),
6255 "fault from non-twin participant"
6256 );
6257 }
6258 }
6259
6260 let blocked = oracle.blocked().await.unwrap();
6261 for (_, faulter) in blocked {
6262 assert!(
6263 twin_identities.contains(&faulter),
6264 "blocked peer attributed to non-twin participant"
6265 );
6266 }
6267 });
6268 }
6269 }
6270
6271 const TWINS_CAMPAIGN: TwinsCampaign = TwinsCampaign {
6272 n: 5,
6273 rounds: 3,
6274 mode: twins::Mode::Sampled,
6275 max_cases: 20,
6276 trailing_finalizations: 10,
6277 };
6278
6279 const TWINS_LINK: Link = Link {
6280 latency: Duration::from_millis(500),
6281 jitter: Duration::from_millis(500),
6282 success_rate: 1.0,
6283 };
6284
6285 #[test_group("slow")]
6286 #[test_traced("INFO")]
6287 fn test_twins_sampled() {
6288 for link in [
6289 Link {
6290 latency: Duration::from_millis(10),
6291 jitter: Duration::from_millis(10),
6292 success_rate: 1.0,
6293 },
6294 TWINS_LINK,
6295 ] {
6296 twins_campaign::<_, _, RoundRobin>(
6297 &mut test_rng(),
6298 TWINS_CAMPAIGN,
6299 link,
6300 scheme_mocks::fixture,
6301 );
6302 }
6303 }
6304
6305 #[test_group("slow")]
6306 #[test_traced("INFO")]
6307 fn test_twins_sustained() {
6308 let campaign = TwinsCampaign {
6309 mode: twins::Mode::Sustained,
6310 ..TWINS_CAMPAIGN
6311 };
6312 for link in [
6313 Link {
6314 latency: Duration::from_millis(10),
6315 jitter: Duration::from_millis(10),
6316 success_rate: 1.0,
6317 },
6318 TWINS_LINK,
6319 ] {
6320 twins_campaign::<_, _, RoundRobin>(
6321 &mut test_rng(),
6322 campaign,
6323 link,
6324 scheme_mocks::fixture,
6325 );
6326 }
6327 }
6328
6329 #[test_group("slow")]
6330 #[test_traced("INFO")]
6331 fn test_twins_large_sampled() {
6332 let campaign = TwinsCampaign {
6333 n: 10,
6334 rounds: 5,
6335 ..TWINS_CAMPAIGN
6336 };
6337 twins_campaign::<_, _, RoundRobin>(
6338 &mut test_rng(),
6339 campaign,
6340 TWINS_LINK,
6341 scheme_mocks::fixture,
6342 );
6343 }
6344
6345 #[test_group("slow")]
6346 #[test_traced("INFO")]
6347 fn test_twins_large_sustained() {
6348 let campaign = TwinsCampaign {
6349 n: 10,
6350 rounds: 5,
6351 mode: twins::Mode::Sustained,
6352 ..TWINS_CAMPAIGN
6353 };
6354 twins_campaign::<_, _, RoundRobin>(
6355 &mut test_rng(),
6356 campaign,
6357 TWINS_LINK,
6358 scheme_mocks::fixture,
6359 );
6360 }
6361
6362 fn twins<S, F, L>(fixture: F)
6363 where
6364 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
6365 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
6366 L: Elector<S>,
6367 {
6368 twins_campaign::<_, _, L>(&mut test_rng(), TWINS_CAMPAIGN, TWINS_LINK, fixture);
6369 }
6370
6371 test_for_all_fixtures!(twins, level = "INFO");
6372}