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 all_crash_after_nullify<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 = 4;
2640 let required_containers = View::new(10);
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(3600));
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 elector = L::default();
2660 let relay = Arc::new(mocks::relay::Relay::new());
2661 let mut reporters = Vec::new();
2662 let mut engine_handlers = Vec::new();
2663 for (idx_scheme, validator) in participants.iter().enumerate() {
2664 if idx_scheme == 0 {
2666 continue;
2667 }
2668
2669 let context = context
2671 .child("validator")
2672 .with_attribute("public_key", validator);
2673
2674 let reporter_config = mocks::reporter::Config {
2676 participants: participants.clone().try_into().unwrap(),
2677 scheme: schemes[idx_scheme].clone(),
2678 elector: elector.clone(),
2679 };
2680 let reporter =
2681 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
2682 reporters.push(reporter.clone());
2683 let application_cfg = mocks::application::Config {
2684 hasher: Sha256::default(),
2685 relay: relay.clone(),
2686 me: validator.clone(),
2687 propose_latency: (10.0, 5.0),
2688 verify_latency: (10.0, 5.0),
2689 certify_latency: (10.0, 5.0),
2690 should_certify: mocks::application::Certifier::Always,
2691 };
2692 let (actor, application) = mocks::application::Application::new(
2693 context.child("application"),
2694 application_cfg,
2695 );
2696 actor.start();
2697 let blocker = oracle.control(validator.clone());
2698 let cfg = config::Config {
2699 scheme: schemes[idx_scheme].clone(),
2700 elector: elector.clone(),
2701 blocker,
2702 automaton: application.clone(),
2703 relay: application.clone(),
2704 reporter: reporter.clone(),
2705 strategy: Sequential,
2706 partition: validator.to_string(),
2707 mailbox_size: NZUsize!(1024),
2708 epoch: Epoch::new(333),
2709 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
2710 Epoch::new(333),
2711 )),
2712 leader_timeout: Duration::from_secs(1),
2713 certification_timeout: Duration::from_secs(2),
2714 timeout_retry: Duration::from_secs(10),
2715 fetch_timeout: Duration::from_secs(1),
2716 activity_timeout,
2717 skip_timeout,
2718 fetch_concurrent: NZUsize!(4),
2719 replay_buffer: NZUsize!(1024 * 1024),
2720 write_buffer: NZUsize!(1024 * 1024),
2721 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
2722 forwarding: ForwardingPolicy::Disabled,
2723 };
2724 let engine = Engine::new(context.child("engine"), cfg);
2725
2726 let (pending, recovered, resolver) = registrations
2728 .remove(validator)
2729 .expect("validator should be registered");
2730 engine_handlers.push(engine.start(pending, recovered, resolver));
2731 }
2732
2733 let stalled = View::new(1);
2736 loop {
2737 let nullified = reporters.iter().zip(participants.iter().skip(1)).all(
2738 |(reporter, validator)| {
2739 reporter
2740 .nullifies
2741 .lock()
2742 .get(&stalled)
2743 .is_some_and(|nullifiers| nullifiers.contains(validator))
2744 },
2745 );
2746 if nullified {
2747 break;
2748 }
2749 context.sleep(Duration::from_millis(100)).await;
2750 }
2751
2752 context.sleep(Duration::from_secs(1)).await;
2756
2757 for handle in engine_handlers.drain(..) {
2760 handle.abort();
2761 let _ = handle.await;
2762 }
2763 relay.deregister_all();
2764
2765 let link = Link {
2767 latency: Duration::from_millis(10),
2768 jitter: Duration::from_millis(1),
2769 success_rate: 1.0,
2770 };
2771 link_validators(
2772 &mut oracle,
2773 &participants,
2774 Action::Link(link),
2775 Some(|_, i, j| ![i, j].contains(&0usize)),
2776 )
2777 .await;
2778
2779 let mut reporters = Vec::new();
2781 for (idx_scheme, validator) in participants.iter().enumerate() {
2782 if idx_scheme == 0 {
2784 continue;
2785 }
2786
2787 let context = context
2789 .child("validator_restarted")
2790 .with_attribute("public_key", validator);
2791
2792 let reporter_config = mocks::reporter::Config {
2794 participants: participants.clone().try_into().unwrap(),
2795 scheme: schemes[idx_scheme].clone(),
2796 elector: elector.clone(),
2797 };
2798 let reporter =
2799 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
2800 reporters.push(reporter.clone());
2801 let application_cfg = mocks::application::Config {
2802 hasher: Sha256::default(),
2803 relay: relay.clone(),
2804 me: validator.clone(),
2805 propose_latency: (10.0, 5.0),
2806 verify_latency: (10.0, 5.0),
2807 certify_latency: (10.0, 5.0),
2808 should_certify: mocks::application::Certifier::Always,
2809 };
2810 let (actor, application) = mocks::application::Application::new(
2811 context.child("application"),
2812 application_cfg,
2813 );
2814 actor.start();
2815 let blocker = oracle.control(validator.clone());
2816 let cfg = config::Config {
2817 scheme: schemes[idx_scheme].clone(),
2818 elector: elector.clone(),
2819 blocker,
2820 automaton: application.clone(),
2821 relay: application.clone(),
2822 reporter: reporter.clone(),
2823 strategy: Sequential,
2824 partition: validator.to_string(),
2825 mailbox_size: NZUsize!(1024),
2826 epoch: Epoch::new(333),
2827 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
2828 Epoch::new(333),
2829 )),
2830 leader_timeout: Duration::from_secs(1),
2831 certification_timeout: Duration::from_secs(2),
2832 timeout_retry: Duration::from_secs(10),
2833 fetch_timeout: Duration::from_secs(1),
2834 activity_timeout,
2835 skip_timeout,
2836 fetch_concurrent: NZUsize!(4),
2837 replay_buffer: NZUsize!(1024 * 1024),
2838 write_buffer: NZUsize!(1024 * 1024),
2839 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
2840 forwarding: ForwardingPolicy::Disabled,
2841 };
2842 let engine = Engine::new(context.child("engine"), cfg);
2843
2844 let (pending, recovered, resolver) =
2846 register_validator(&mut oracle, validator.clone()).await;
2847 engine.start(pending, recovered, resolver);
2848 }
2849
2850 let mut finalizers = Vec::new();
2854 for reporter in reporters.iter_mut() {
2855 let (mut latest, mut monitor) = reporter.subscribe().await;
2856 finalizers.push(context.child("finalizer").spawn(move |_| async move {
2857 while latest < required_containers {
2858 latest = monitor.recv().await.expect("event missing");
2859 }
2860 }));
2861 }
2862 join_all(finalizers).await;
2863 });
2864 }
2865
2866 test_for_all_fixtures!(all_crash_after_nullify);
2867
2868 fn partition<S, F, L>(mut fixture: F)
2869 where
2870 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
2871 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
2872 L: Elector<S>,
2873 {
2874 let n = 10;
2876 let required_containers = View::new(50);
2877 let activity_timeout = ViewDelta::new(10);
2878 let skip_timeout = ViewDelta::new(5);
2879 let namespace = b"consensus".to_vec();
2880 let executor = deterministic::Runner::timed(Duration::from_secs(900));
2881 executor.start(|mut context| async move {
2882 let Fixture {
2884 participants,
2885 schemes,
2886 ..
2887 } = fixture(&mut context, &namespace, n);
2888 let mut oracle =
2889 start_test_network_with_peers(context.child("network"), participants.clone(), true)
2890 .await;
2891 let mut registrations = register_validators(&mut oracle, &participants).await;
2892
2893 let link = Link {
2895 latency: Duration::from_millis(10),
2896 jitter: Duration::from_millis(1),
2897 success_rate: 1.0,
2898 };
2899 link_validators(&mut oracle, &participants, Action::Link(link.clone()), None).await;
2900
2901 let elector = L::default();
2903 let relay = Arc::new(mocks::relay::Relay::new());
2904 let mut reporters = Vec::new();
2905 let mut engine_handlers = Vec::new();
2906 for (idx, validator) in participants.iter().enumerate() {
2907 let context = context
2909 .child("validator")
2910 .with_attribute("public_key", validator);
2911
2912 let reporter_config = mocks::reporter::Config {
2914 participants: participants.clone().try_into().unwrap(),
2915 scheme: schemes[idx].clone(),
2916 elector: elector.clone(),
2917 };
2918 let reporter =
2919 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
2920 reporters.push(reporter.clone());
2921 let application_cfg = mocks::application::Config {
2922 hasher: Sha256::default(),
2923 relay: relay.clone(),
2924 me: validator.clone(),
2925 propose_latency: (10.0, 5.0),
2926 verify_latency: (10.0, 5.0),
2927 certify_latency: (10.0, 5.0),
2928 should_certify: mocks::application::Certifier::Always,
2929 };
2930 let (actor, application) = mocks::application::Application::new(
2931 context.child("application"),
2932 application_cfg,
2933 );
2934 actor.start();
2935 let blocker = oracle.control(validator.clone());
2936 let cfg = config::Config {
2937 scheme: schemes[idx].clone(),
2938 elector: elector.clone(),
2939 blocker,
2940 automaton: application.clone(),
2941 relay: application.clone(),
2942 reporter: reporter.clone(),
2943 strategy: Sequential,
2944 partition: validator.to_string(),
2945 mailbox_size: NZUsize!(1024),
2946 epoch: Epoch::new(333),
2947 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
2948 Epoch::new(333),
2949 )),
2950 leader_timeout: Duration::from_secs(1),
2951 certification_timeout: Duration::from_secs(2),
2952 timeout_retry: Duration::from_secs(10),
2953 fetch_timeout: Duration::from_secs(1),
2954 activity_timeout,
2955 skip_timeout,
2956 fetch_concurrent: NZUsize!(4),
2957 replay_buffer: NZUsize!(1024 * 1024),
2958 write_buffer: NZUsize!(1024 * 1024),
2959 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
2960 forwarding: ForwardingPolicy::Disabled,
2961 };
2962 let engine = Engine::new(context.child("engine"), cfg);
2963
2964 let (pending, recovered, resolver) = registrations
2966 .remove(validator)
2967 .expect("validator should be registered");
2968 engine_handlers.push(engine.start(pending, recovered, resolver));
2969 }
2970
2971 let mut finalizers = Vec::new();
2973 for reporter in reporters.iter_mut() {
2974 let (mut latest, mut monitor) = reporter.subscribe().await;
2975 finalizers.push(context.child("finalizer").spawn(move |_| async move {
2976 while latest < required_containers {
2977 latest = monitor.recv().await.expect("event missing");
2978 }
2979 }));
2980 }
2981 join_all(finalizers).await;
2982
2983 fn separated(n: usize, a: usize, b: usize) -> bool {
2985 let m = n / 2;
2986 (a < m && b >= m) || (a >= m && b < m)
2987 }
2988 link_validators(&mut oracle, &participants, Action::Unlink, Some(separated)).await;
2989
2990 context.sleep(Duration::from_secs(10)).await;
2992
2993 let mut finalizers = Vec::new();
2995 for reporter in reporters.iter_mut() {
2996 let (_, mut monitor) = reporter.subscribe().await;
2997 finalizers.push(context.child("finalizer").spawn(move |context| async move {
2998 select! {
2999 _timeout = context.sleep(Duration::from_secs(60)) => {},
3000 _done = monitor.recv() => {
3001 panic!("engine should not notarize or finalize anything");
3002 },
3003 }
3004 }));
3005 }
3006 join_all(finalizers).await;
3007
3008 link_validators(
3010 &mut oracle,
3011 &participants,
3012 Action::Link(link),
3013 Some(separated),
3014 )
3015 .await;
3016
3017 let mut finalizers = Vec::new();
3019 for reporter in reporters.iter_mut() {
3020 let (mut latest, mut monitor) = reporter.subscribe().await;
3021 let required = latest.saturating_add(ViewDelta::new(required_containers.get()));
3022 finalizers.push(context.child("finalizer").spawn(move |_| async move {
3023 while latest < required {
3024 latest = monitor.recv().await.expect("event missing");
3025 }
3026 }));
3027 }
3028 join_all(finalizers).await;
3029
3030 for reporter in reporters.iter() {
3032 reporter.assert_no_faults();
3034
3035 reporter.assert_no_invalid();
3037 }
3038
3039 let blocked = oracle.blocked().await.unwrap();
3041 assert!(blocked.is_empty());
3042 });
3043 }
3044
3045 test_for_all_fixtures!(partition);
3046
3047 fn slow_and_lossy_links_seeded<S, F, L>(seed: u64, mut fixture: F) -> String
3048 where
3049 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3050 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3051 L: Elector<S>,
3052 {
3053 let n = 5;
3055 let required_containers = View::new(50);
3056 let activity_timeout = ViewDelta::new(10);
3057 let skip_timeout = ViewDelta::new(5);
3058 let namespace = b"consensus".to_vec();
3059 let cfg = deterministic::Config::new()
3060 .with_seed(seed)
3061 .with_timeout(Some(Duration::from_secs(5_000)));
3062 let executor = deterministic::Runner::new(cfg);
3063 executor.start(|mut context| async move {
3064 let Fixture {
3066 participants,
3067 schemes,
3068 ..
3069 } = fixture(&mut context, &namespace, n);
3070 let mut oracle =
3071 start_test_network_with_peers(context.child("network"), participants.clone(), true)
3072 .await;
3073 let mut registrations = register_validators(&mut oracle, &participants).await;
3074
3075 let degraded_link = Link {
3077 latency: Duration::from_millis(200),
3078 jitter: Duration::from_millis(150),
3079 success_rate: 0.5,
3080 };
3081 link_validators(
3082 &mut oracle,
3083 &participants,
3084 Action::Link(degraded_link),
3085 None,
3086 )
3087 .await;
3088
3089 let elector = L::default();
3091 let relay = Arc::new(mocks::relay::Relay::new());
3092 let mut reporters = Vec::new();
3093 let mut engine_handlers = Vec::new();
3094 for (idx, validator) in participants.iter().enumerate() {
3095 let context = context
3097 .child("validator")
3098 .with_attribute("public_key", validator);
3099
3100 let reporter_config = mocks::reporter::Config {
3102 participants: participants.clone().try_into().unwrap(),
3103 scheme: schemes[idx].clone(),
3104 elector: elector.clone(),
3105 };
3106 let reporter =
3107 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3108 reporters.push(reporter.clone());
3109 let application_cfg = mocks::application::Config {
3110 hasher: Sha256::default(),
3111 relay: relay.clone(),
3112 me: validator.clone(),
3113 propose_latency: (10.0, 5.0),
3114 verify_latency: (10.0, 5.0),
3115 certify_latency: (10.0, 5.0),
3116 should_certify: mocks::application::Certifier::Always,
3117 };
3118 let (actor, application) = mocks::application::Application::new(
3119 context.child("application"),
3120 application_cfg,
3121 );
3122 actor.start();
3123 let blocker = oracle.control(validator.clone());
3124 let cfg = config::Config {
3125 scheme: schemes[idx].clone(),
3126 elector: elector.clone(),
3127 blocker,
3128 automaton: application.clone(),
3129 relay: application.clone(),
3130 reporter: reporter.clone(),
3131 strategy: Sequential,
3132 partition: validator.to_string(),
3133 mailbox_size: NZUsize!(1024),
3134 epoch: Epoch::new(333),
3135 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
3136 Epoch::new(333),
3137 )),
3138 leader_timeout: Duration::from_secs(1),
3139 certification_timeout: Duration::from_secs(2),
3140 timeout_retry: Duration::from_secs(10),
3141 fetch_timeout: Duration::from_secs(1),
3142 activity_timeout,
3143 skip_timeout,
3144 fetch_concurrent: NZUsize!(4),
3145 replay_buffer: NZUsize!(1024 * 1024),
3146 write_buffer: NZUsize!(1024 * 1024),
3147 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3148 forwarding: ForwardingPolicy::Disabled,
3149 };
3150 let engine = Engine::new(context.child("engine"), cfg);
3151
3152 let (pending, recovered, resolver) = registrations
3154 .remove(validator)
3155 .expect("validator should be registered");
3156 engine_handlers.push(engine.start(pending, recovered, resolver));
3157 }
3158
3159 let mut finalizers = Vec::new();
3161 for reporter in reporters.iter_mut() {
3162 let (mut latest, mut monitor) = reporter.subscribe().await;
3163 finalizers.push(context.child("finalizer").spawn(move |_| async move {
3164 while latest < required_containers {
3165 latest = monitor.recv().await.expect("event missing");
3166 }
3167 }));
3168 }
3169 join_all(finalizers).await;
3170
3171 for reporter in reporters.iter() {
3173 reporter.assert_no_faults();
3175
3176 reporter.assert_no_invalid();
3178 }
3179
3180 let blocked = oracle.blocked().await.unwrap();
3182 assert!(blocked.is_empty());
3183
3184 context.auditor().state()
3185 })
3186 }
3187
3188 fn slow_and_lossy_links<S, F, L>(fixture: F) -> String
3189 where
3190 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3191 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3192 L: Elector<S>,
3193 {
3194 slow_and_lossy_links_seeded::<_, _, L>(6, fixture)
3195 }
3196
3197 test_for_all_fixtures!(slow_and_lossy_links);
3198
3199 fn determinism<S, F, L>(seed: u64, fixture: F)
3200 where
3201 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3202 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S> + Copy,
3203 L: Elector<S>,
3204 {
3205 assert_eq!(
3208 slow_and_lossy_links_seeded::<_, _, L>(seed, fixture),
3209 slow_and_lossy_links_seeded::<_, _, L>(seed, fixture),
3210 );
3211 }
3212
3213 test_for_all_fixtures!(determinism, seeds = 5);
3214
3215 #[test_group("slow")]
3216 #[test_traced]
3217 fn test_distinct_states() {
3218 macro_rules! collect {
3220 ($vec:ident, $suffix:ident, $elector:ty, $fixture:expr) => {
3221 $vec.push((
3222 stringify!($suffix),
3223 slow_and_lossy_links_seeded::<_, _, $elector>(7, $fixture),
3224 ));
3225 };
3226 }
3227 let mut states = Vec::new();
3228 for_each_fixture!(collect!(states));
3229 for pair in states.windows(2) {
3230 assert_ne!(
3231 pair[0].1, pair[1].1,
3232 "state {} equals state {}",
3233 pair[0].0, pair[1].0
3234 );
3235 }
3236 }
3237
3238 fn conflicter<S, F, L>(seed: u64, mut fixture: F)
3239 where
3240 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3241 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3242 L: Elector<S>,
3243 {
3244 let n = 4;
3246 let required_containers = View::new(50);
3247 let activity_timeout = ViewDelta::new(10);
3248 let skip_timeout = ViewDelta::new(5);
3249 let namespace = b"consensus".to_vec();
3250 let cfg = deterministic::Config::new()
3251 .with_seed(seed)
3252 .with_timeout(Some(Duration::from_secs(30)));
3253 let executor = deterministic::Runner::new(cfg);
3254 executor.start(|mut context| async move {
3255 let Fixture {
3257 participants,
3258 schemes,
3259 ..
3260 } = fixture(&mut context, &namespace, n);
3261 let mut oracle =
3262 start_test_network_with_peers(context.child("network"), participants.clone(), true)
3263 .await;
3264 let mut registrations = register_validators(&mut oracle, &participants).await;
3265
3266 let link = Link {
3268 latency: Duration::from_millis(10),
3269 jitter: Duration::from_millis(1),
3270 success_rate: 1.0,
3271 };
3272 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
3273
3274 let elector = L::default();
3276 let relay = Arc::new(mocks::relay::Relay::new());
3277 let mut reporters = Vec::new();
3278 for (idx_scheme, validator) in participants.iter().enumerate() {
3279 let context = context
3281 .child("validator")
3282 .with_attribute("public_key", validator);
3283
3284 let reporter_config = mocks::reporter::Config {
3286 participants: participants.clone().try_into().unwrap(),
3287 scheme: schemes[idx_scheme].clone(),
3288 elector: elector.clone(),
3289 };
3290 let reporter =
3291 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3292 let (pending, recovered, resolver) = registrations
3293 .remove(validator)
3294 .expect("validator should be registered");
3295 if idx_scheme == 0 {
3296 let cfg = mocks::conflicter::Config {
3297 scheme: schemes[idx_scheme].clone(),
3298 };
3299
3300 let engine: mocks::conflicter::Conflicter<_, _, Sha256> =
3301 mocks::conflicter::Conflicter::new(context.child("byzantine_engine"), cfg);
3302 engine.start(pending);
3303 } else {
3304 reporters.push(reporter.clone());
3305 let application_cfg = mocks::application::Config {
3306 hasher: Sha256::default(),
3307 relay: relay.clone(),
3308 me: validator.clone(),
3309 propose_latency: (10.0, 5.0),
3310 verify_latency: (10.0, 5.0),
3311 certify_latency: (10.0, 5.0),
3312 should_certify: mocks::application::Certifier::Always,
3313 };
3314 let (actor, application) = mocks::application::Application::new(
3315 context.child("application"),
3316 application_cfg,
3317 );
3318 actor.start();
3319 let blocker = oracle.control(validator.clone());
3320 let cfg = config::Config {
3321 scheme: schemes[idx_scheme].clone(),
3322 elector: elector.clone(),
3323 blocker,
3324 automaton: application.clone(),
3325 relay: application.clone(),
3326 reporter: reporter.clone(),
3327 strategy: Sequential,
3328 partition: validator.to_string(),
3329 mailbox_size: NZUsize!(1024),
3330 epoch: Epoch::new(333),
3331 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
3332 Epoch::new(333),
3333 )),
3334 leader_timeout: Duration::from_secs(1),
3335 certification_timeout: Duration::from_secs(2),
3336 timeout_retry: Duration::from_secs(10),
3337 fetch_timeout: Duration::from_secs(1),
3338 activity_timeout,
3339 skip_timeout,
3340 fetch_concurrent: NZUsize!(4),
3341 replay_buffer: NZUsize!(1024 * 1024),
3342 write_buffer: NZUsize!(1024 * 1024),
3343 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3344 forwarding: ForwardingPolicy::Disabled,
3345 };
3346 let engine = Engine::new(context.child("engine"), cfg);
3347 engine.start(pending, recovered, resolver);
3348 }
3349 }
3350
3351 let mut finalizers = Vec::new();
3353 for reporter in reporters.iter_mut() {
3354 let (mut latest, mut monitor) = reporter.subscribe().await;
3355 finalizers.push(context.child("finalizer").spawn(move |_| async move {
3356 while latest < required_containers {
3357 latest = monitor.recv().await.expect("event missing");
3358 }
3359 }));
3360 }
3361 join_all(finalizers).await;
3362
3363 let byz = &participants[0];
3365 let mut count_conflicting = 0;
3366 for reporter in reporters.iter() {
3367 {
3369 let faults = reporter.faults.lock();
3370 assert_eq!(faults.len(), 1);
3371 let faulter = faults.get(byz).expect("byzantine party is not faulter");
3372 for faults in faulter.values() {
3373 for fault in faults.iter() {
3374 match fault {
3375 Activity::ConflictingNotarize(_) => {
3376 count_conflicting += 1;
3377 }
3378 Activity::ConflictingFinalize(_) => {
3379 count_conflicting += 1;
3380 }
3381 _ => panic!("unexpected fault: {fault:?}"),
3382 }
3383 }
3384 }
3385 }
3386
3387 reporter.assert_no_invalid();
3389 }
3390 assert!(count_conflicting > 0);
3391
3392 let blocked = oracle.blocked().await.unwrap();
3394 assert!(!blocked.is_empty());
3395 for (a, b) in blocked {
3396 assert_ne!(&a, byz);
3397 assert_eq!(&b, byz);
3398 }
3399 });
3400 }
3401
3402 test_for_all_fixtures!(conflicter, seeds = 5);
3403
3404 fn invalid<S, F, L>(seed: u64, mut fixture: F)
3405 where
3406 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3407 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3408 L: Elector<S>,
3409 {
3410 let n = 4;
3412 let required_containers = View::new(50);
3413 let activity_timeout = ViewDelta::new(10);
3414 let skip_timeout = ViewDelta::new(5);
3415 let namespace = b"consensus".to_vec();
3416 let cfg = deterministic::Config::new()
3417 .with_seed(seed)
3418 .with_timeout(Some(Duration::from_secs(30)));
3419 let executor = deterministic::Runner::new(cfg);
3420 executor.start(|mut context| async move {
3421 let Fixture {
3423 participants,
3424 schemes,
3425 ..
3426 } = fixture(&mut context, &namespace, n);
3427
3428 let schemes: Vec<_> = schemes
3429 .into_iter()
3430 .enumerate()
3431 .map(|(idx, scheme)| {
3432 let is_byzantine = idx == 0;
3433 let behavior = if is_byzantine {
3434 wrapped::Behavior::CorruptSignature
3435 } else {
3436 wrapped::Behavior::Honest
3437 };
3438 wrapped::Scheme::new(scheme, behavior)
3439 })
3440 .collect();
3441
3442 let mut oracle =
3443 start_test_network_with_peers(context.child("network"), participants.clone(), true)
3444 .await;
3445 let mut registrations = register_validators(&mut oracle, &participants).await;
3446
3447 let link = Link {
3449 latency: Duration::from_millis(10),
3450 jitter: Duration::from_millis(1),
3451 success_rate: 1.0,
3452 };
3453 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
3454
3455 let elector = wrapped::Config(L::default());
3457 let relay = Arc::new(mocks::relay::Relay::new());
3458 let mut reporters = Vec::new();
3459 for (idx_scheme, validator) in participants.iter().enumerate() {
3460 let context = context
3462 .child("validator")
3463 .with_attribute("public_key", validator);
3464
3465 let reporter_config = mocks::reporter::Config {
3466 participants: participants.clone().try_into().unwrap(),
3467 scheme: schemes[idx_scheme].clone(),
3468 elector: elector.clone(),
3469 };
3470 let reporter =
3471 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3472 reporters.push(reporter.clone());
3473
3474 let application_cfg = mocks::application::Config {
3475 hasher: Sha256::default(),
3476 relay: relay.clone(),
3477 me: validator.clone(),
3478 propose_latency: (10.0, 5.0),
3479 verify_latency: (10.0, 5.0),
3480 certify_latency: (10.0, 5.0),
3481 should_certify: mocks::application::Certifier::Always,
3482 };
3483 let (actor, application) = mocks::application::Application::new(
3484 context.child("application"),
3485 application_cfg,
3486 );
3487 actor.start();
3488 let blocker = oracle.control(validator.clone());
3489 let cfg = config::Config {
3490 scheme: schemes[idx_scheme].clone(),
3491 elector: elector.clone(),
3492 blocker,
3493 automaton: application.clone(),
3494 relay: application.clone(),
3495 reporter: reporter.clone(),
3496 strategy: Sequential,
3497 partition: validator.clone().to_string(),
3498 mailbox_size: NZUsize!(1024),
3499 epoch: Epoch::new(333),
3500 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
3501 Epoch::new(333),
3502 )),
3503 leader_timeout: Duration::from_secs(1),
3504 certification_timeout: Duration::from_secs(2),
3505 timeout_retry: Duration::from_secs(10),
3506 fetch_timeout: Duration::from_secs(1),
3507 activity_timeout,
3508 skip_timeout,
3509 fetch_concurrent: NZUsize!(4),
3510 replay_buffer: NZUsize!(1024 * 1024),
3511 write_buffer: NZUsize!(1024 * 1024),
3512 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3513 forwarding: ForwardingPolicy::Disabled,
3514 };
3515 let engine = Engine::new(context.child("engine"), cfg);
3516 let (pending, recovered, resolver) = registrations
3517 .remove(validator)
3518 .expect("validator should be registered");
3519 engine.start(pending, recovered, resolver);
3520 }
3521
3522 let mut finalizers = Vec::new();
3526 for reporter in reporters.iter_mut().skip(1) {
3527 let (mut latest, mut monitor) = reporter.subscribe().await;
3528 finalizers.push(context.child("finalizer").spawn(move |_| async move {
3529 while latest < required_containers {
3530 latest = monitor.recv().await.expect("event missing");
3531 }
3532 }));
3533 }
3534 join_all(finalizers).await;
3535
3536 for (i, reporter) in reporters.iter().enumerate() {
3538 reporter.assert_no_faults();
3540
3541 assert!(*reporter.invalid_votes.lock() > 0);
3544
3545 let is_byzantine = i == 0;
3548 if is_byzantine {
3549 assert!(*reporter.invalid_certificates.lock() > 0);
3550 } else {
3551 assert_eq!(*reporter.invalid_certificates.lock(), 0);
3552 }
3553 }
3554
3555 let blocked = oracle.blocked().await.unwrap();
3558 assert!(blocked.len() >= participants.len() - 1);
3559 let byz = &participants[0];
3560 for (_, b) in blocked {
3561 assert_eq!(&b, byz);
3563 }
3564 });
3565 }
3566
3567 test_for_all_fixtures!(invalid, seeds = 5);
3568
3569 fn received_certificates_are_reported<S, F, L>(mut fixture: F)
3571 where
3572 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3573 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3574 L: Elector<S>,
3575 {
3576 let n = 4;
3577 let required_containers = View::new(10);
3578 let activity_timeout = ViewDelta::new(10);
3579 let skip_timeout = ViewDelta::new(5);
3580 let namespace = b"consensus".to_vec();
3581 let cfg = deterministic::Config::new()
3582 .with_seed(0)
3583 .with_timeout(Some(Duration::from_secs(30)));
3584 let executor = deterministic::Runner::new(cfg);
3585 executor.start(|mut context| async move {
3586 let Fixture {
3587 participants,
3588 schemes,
3589 ..
3590 } = fixture(&mut context, &namespace, n);
3591
3592 let mut oracle = start_test_network_with_peers(
3593 context.child("network"),
3594 participants.clone(),
3595 false,
3596 )
3597 .await;
3598 let mut registrations = register_validators(&mut oracle, &participants).await;
3599
3600 let link = Link {
3605 latency: Duration::from_millis(100),
3606 jitter: Duration::from_millis(1),
3607 success_rate: 1.0,
3608 };
3609 fn link_graph(_: usize, i: usize, j: usize) -> bool {
3610 if i == 0 || j == 0 {
3611 return i == 1 || j == 1;
3612 }
3613 true
3614 }
3615 link_validators(
3616 &mut oracle,
3617 &participants,
3618 Action::Link(link),
3619 Some(link_graph),
3620 )
3621 .await;
3622
3623 let elector = L::default();
3624 let relay = Arc::new(mocks::relay::Relay::new());
3625 let mut reporters = Vec::new();
3626 for (idx_scheme, validator) in participants.iter().enumerate() {
3627 let context = context
3628 .child("validator")
3629 .with_attribute("public_key", validator);
3630 let reporter_config = mocks::reporter::Config {
3631 participants: participants.clone().try_into().unwrap(),
3632 scheme: schemes[idx_scheme].clone(),
3633 elector: elector.clone(),
3634 };
3635 let reporter =
3636 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3637 reporters.push(reporter.clone());
3638
3639 let application_cfg = mocks::application::Config {
3640 hasher: Sha256::default(),
3641 relay: relay.clone(),
3642 me: validator.clone(),
3643 propose_latency: (10.0, 5.0),
3644 verify_latency: (10.0, 5.0),
3645 certify_latency: (10.0, 5.0),
3646 should_certify: mocks::application::Certifier::Always,
3647 };
3648 let (actor, application) = mocks::application::Application::new(
3649 context.child("application"),
3650 application_cfg,
3651 );
3652 actor.start();
3653 let blocker = oracle.control(validator.clone());
3654 let cfg = config::Config {
3655 scheme: schemes[idx_scheme].clone(),
3656 elector: elector.clone(),
3657 blocker,
3658 automaton: application.clone(),
3659 relay: application.clone(),
3660 reporter: reporter.clone(),
3661 strategy: Sequential,
3662 partition: validator.clone().to_string(),
3663 mailbox_size: NZUsize!(1024),
3664 epoch: Epoch::new(333),
3665 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
3666 Epoch::new(333),
3667 )),
3668 leader_timeout: Duration::from_secs(1),
3669 certification_timeout: Duration::from_secs(2),
3670 timeout_retry: Duration::from_secs(10),
3671 fetch_timeout: Duration::from_secs(1),
3672 activity_timeout,
3673 skip_timeout,
3674 fetch_concurrent: NZUsize!(4),
3675 replay_buffer: NZUsize!(1024 * 1024),
3676 write_buffer: NZUsize!(1024 * 1024),
3677 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3678 forwarding: ForwardingPolicy::Disabled,
3679 };
3680 let engine = Engine::new(context.child("engine"), cfg);
3681 let (pending, recovered, resolver) = registrations
3682 .remove(validator)
3683 .expect("validator should be registered");
3684 engine.start(pending, recovered, resolver);
3685 }
3686 let excluded_reporter = reporters[0].clone();
3688 let mut honest_reporter = reporters[1].clone();
3689 let (mut honest_latest, mut honest_monitor) = honest_reporter.subscribe().await;
3690 while honest_latest < required_containers {
3691 honest_latest = honest_monitor.recv().await.expect("event missing");
3692 }
3693
3694 context.sleep(Duration::from_secs(1)).await;
3696
3697 let honest_notarized = {
3700 let notarizations = honest_reporter.notarizations.lock();
3701 View::range(View::new(1), required_containers.next())
3702 .filter(|view| notarizations.contains_key(view))
3703 .count()
3704 };
3705 let excluded_notarized = {
3706 let notarizations = excluded_reporter.notarizations.lock();
3707 View::range(View::new(1), required_containers.next())
3708 .filter(|view| notarizations.contains_key(view))
3709 .count()
3710 };
3711 assert!(
3712 excluded_notarized >= honest_notarized.saturating_sub(2),
3713 "honest_notarized: {honest_notarized}, excluded_notarized: {excluded_notarized}"
3714 );
3715
3716 let honest_finalized = {
3717 let finalizations = honest_reporter.finalizations.lock();
3718 View::range(View::new(1), required_containers.next())
3719 .filter(|view| finalizations.contains_key(view))
3720 .count()
3721 };
3722 let excluded_finalized = {
3723 let finalizations = excluded_reporter.finalizations.lock();
3724 View::range(View::new(1), required_containers.next())
3725 .filter(|view| finalizations.contains_key(view))
3726 .count()
3727 };
3728 assert!(
3729 excluded_finalized >= honest_finalized.saturating_sub(2),
3730 "honest_finalized: {honest_finalized}, excluded_finalized: {excluded_finalized}"
3731 );
3732 });
3733 }
3734
3735 test_for_all_fixtures!(received_certificates_are_reported);
3736
3737 fn survives_burst<S, F, L>(mut fixture: F)
3738 where
3739 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3740 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3741 L: Elector<S>,
3742 {
3743 let n = 4;
3744 let epoch = Epoch::new(333);
3745 let namespace = b"mailbox_size_one_certificate_burst".to_vec();
3746 let executor = deterministic::Runner::default();
3747 executor.start(|mut context| async move {
3748 let Fixture {
3749 participants,
3750 schemes,
3751 ..
3752 } = fixture(&mut context, &namespace, n);
3753 let me = participants[0].clone();
3754 let mut oracle =
3755 start_test_network_with_peers(context.child("network"), participants.clone(), true)
3756 .await;
3757 let (pending, recovered, resolver) = register_validator(&mut oracle, me.clone()).await;
3758
3759 let injector_pk = PrivateKey::from_seed(9_000_000).public_key();
3760 let (mut injector_sender, _injector_receiver) = oracle
3761 .control(injector_pk.clone())
3762 .register(1, TEST_QUOTA)
3763 .await
3764 .unwrap();
3765 let link = Link {
3766 latency: Duration::from_millis(0),
3767 jitter: Duration::from_millis(0),
3768 success_rate: 1.0,
3769 };
3770 oracle
3771 .add_link(injector_pk.clone(), me.clone(), link)
3772 .await
3773 .unwrap();
3774 oracle.manager().track(
3775 1,
3776 TrackedPeers::new(
3777 Set::from_iter_dedup(std::iter::once(me.clone())),
3778 Set::from_iter_dedup(std::iter::once(injector_pk.clone())),
3779 ),
3780 );
3781 context.sleep(Duration::from_millis(1)).await;
3782
3783 let quorum = quorum(n) as usize;
3784 let notarization = |view: View, parent: View, payload: &[u8]| {
3785 let proposal =
3786 Proposal::new(Round::new(epoch, view), parent, Sha256::hash(payload));
3787 let votes: Vec<_> = schemes
3788 .iter()
3789 .take(quorum)
3790 .map(|scheme| TNotarize::sign(scheme, proposal.clone()).unwrap())
3791 .collect();
3792 TNotarization::from_notarizes(&schemes[0], &votes, &Sequential)
3793 .expect("notarization requires quorum")
3794 };
3795 let finalization = |view: View, parent: View, payload: &[u8]| {
3796 let proposal =
3797 Proposal::new(Round::new(epoch, view), parent, Sha256::hash(payload));
3798 let votes: Vec<_> = schemes
3799 .iter()
3800 .take(quorum)
3801 .map(|scheme| TFinalize::sign(scheme, proposal.clone()).unwrap())
3802 .collect();
3803 TFinalization::from_finalizes(&schemes[0], &votes, &Sequential)
3804 .expect("finalization requires quorum")
3805 };
3806
3807 for certificate in [
3809 Certificate::Notarization(notarization(View::new(1), View::zero(), b"payload-1")),
3810 Certificate::Notarization(notarization(View::new(2), View::new(1), b"payload-2")),
3811 Certificate::Notarization(notarization(View::new(3), View::new(2), b"payload-3")),
3812 Certificate::Finalization(finalization(View::new(3), View::new(2), b"payload-3")),
3813 ] {
3814 injector_sender.send(Recipients::One(me.clone()), certificate.encode(), true);
3815 }
3816
3817 let elector = L::default();
3818 let reporter_config = mocks::reporter::Config {
3819 participants: participants.clone().try_into().unwrap(),
3820 scheme: schemes[0].clone(),
3821 elector: elector.clone(),
3822 };
3823 let reporter =
3824 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3825 let mut monitor_reporter = reporter.clone();
3826 let (mut latest, mut monitor) = monitor_reporter.subscribe().await;
3827
3828 let relay = Arc::new(mocks::relay::Relay::new());
3829 let application_cfg = mocks::application::Config {
3830 hasher: Sha256::default(),
3831 relay: relay.clone(),
3832 me: me.clone(),
3833 propose_latency: (0.0, 0.0),
3834 verify_latency: (0.0, 0.0),
3835 certify_latency: (0.0, 0.0),
3836 should_certify: mocks::application::Certifier::Always,
3837 };
3838 let (mut application_actor, application) =
3839 mocks::application::Application::new(context.child("application"), application_cfg);
3840 application_actor.set_stall_proposals(true);
3841 application_actor.start();
3842
3843 let cfg = config::Config {
3844 scheme: schemes[0].clone(),
3845 elector,
3846 blocker: oracle.control(me.clone()),
3847 automaton: application.clone(),
3848 relay: application,
3849 reporter,
3850 strategy: Sequential,
3851 partition: me.to_string(),
3852 mailbox_size: NZUsize!(1),
3853 epoch,
3854 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(epoch)),
3855 leader_timeout: Duration::from_secs(1),
3856 certification_timeout: Duration::from_secs(2),
3857 timeout_retry: Duration::from_secs(10),
3858 fetch_timeout: Duration::from_secs(1),
3859 activity_timeout: ViewDelta::new(10),
3860 skip_timeout: ViewDelta::new(5),
3861 fetch_concurrent: NZUsize!(4),
3862 replay_buffer: NZUsize!(1024 * 1024),
3863 write_buffer: NZUsize!(1024 * 1024),
3864 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3865 forwarding: ForwardingPolicy::Disabled,
3866 };
3867 let engine = Engine::new(context.child("engine"), cfg);
3868 engine.start(pending, recovered, resolver);
3869
3870 while latest < View::new(3) {
3871 latest = monitor.recv().await.expect("finalization event missing");
3872 }
3873 });
3874 }
3875
3876 test_for_all_fixtures!(survives_burst);
3877
3878 fn impersonator<S, F, L>(seed: u64, mut fixture: F)
3879 where
3880 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
3881 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
3882 L: Elector<S>,
3883 {
3884 let n = 4;
3886 let required_containers = View::new(50);
3887 let activity_timeout = ViewDelta::new(10);
3888 let skip_timeout = ViewDelta::new(5);
3889 let namespace = b"consensus".to_vec();
3890 let cfg = deterministic::Config::new()
3891 .with_seed(seed)
3892 .with_timeout(Some(Duration::from_secs(30)));
3893 let executor = deterministic::Runner::new(cfg);
3894 executor.start(|mut context| async move {
3895 let Fixture {
3897 participants,
3898 schemes,
3899 ..
3900 } = fixture(&mut context, &namespace, n);
3901 let mut oracle =
3902 start_test_network_with_peers(context.child("network"), participants.clone(), true)
3903 .await;
3904 let mut registrations = register_validators(&mut oracle, &participants).await;
3905
3906 let link = Link {
3908 latency: Duration::from_millis(10),
3909 jitter: Duration::from_millis(1),
3910 success_rate: 1.0,
3911 };
3912 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
3913
3914 let elector = L::default();
3916 let relay = Arc::new(mocks::relay::Relay::new());
3917 let mut reporters = Vec::new();
3918 for (idx_scheme, validator) in participants.iter().enumerate() {
3919 let context = context
3921 .child("validator")
3922 .with_attribute("public_key", validator);
3923
3924 let reporter_config = mocks::reporter::Config {
3926 participants: participants.clone().try_into().unwrap(),
3927 scheme: schemes[idx_scheme].clone(),
3928 elector: elector.clone(),
3929 };
3930 let reporter =
3931 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
3932 let (pending, recovered, resolver) = registrations
3933 .remove(validator)
3934 .expect("validator should be registered");
3935 if idx_scheme == 0 {
3936 let cfg = mocks::impersonator::Config {
3937 scheme: schemes[idx_scheme].clone(),
3938 };
3939
3940 let engine: mocks::impersonator::Impersonator<_, _, Sha256> =
3941 mocks::impersonator::Impersonator::new(
3942 context.child("byzantine_engine"),
3943 cfg,
3944 );
3945 engine.start(pending);
3946 } else {
3947 reporters.push(reporter.clone());
3948 let application_cfg = mocks::application::Config {
3949 hasher: Sha256::default(),
3950 relay: relay.clone(),
3951 me: validator.clone(),
3952 propose_latency: (10.0, 5.0),
3953 verify_latency: (10.0, 5.0),
3954 certify_latency: (10.0, 5.0),
3955 should_certify: mocks::application::Certifier::Always,
3956 };
3957 let (actor, application) = mocks::application::Application::new(
3958 context.child("application"),
3959 application_cfg,
3960 );
3961 actor.start();
3962 let blocker = oracle.control(validator.clone());
3963 let cfg = config::Config {
3964 scheme: schemes[idx_scheme].clone(),
3965 elector: elector.clone(),
3966 blocker,
3967 automaton: application.clone(),
3968 relay: application.clone(),
3969 reporter: reporter.clone(),
3970 strategy: Sequential,
3971 partition: validator.clone().to_string(),
3972 mailbox_size: NZUsize!(1024),
3973 epoch: Epoch::new(333),
3974 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
3975 Epoch::new(333),
3976 )),
3977 leader_timeout: Duration::from_secs(1),
3978 certification_timeout: Duration::from_secs(2),
3979 timeout_retry: Duration::from_secs(10),
3980 fetch_timeout: Duration::from_secs(1),
3981 activity_timeout,
3982 skip_timeout,
3983 fetch_concurrent: NZUsize!(4),
3984 replay_buffer: NZUsize!(1024 * 1024),
3985 write_buffer: NZUsize!(1024 * 1024),
3986 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
3987 forwarding: ForwardingPolicy::Disabled,
3988 };
3989 let engine = Engine::new(context.child("engine"), cfg);
3990 engine.start(pending, recovered, resolver);
3991 }
3992 }
3993
3994 let mut finalizers = Vec::new();
3996 for reporter in reporters.iter_mut() {
3997 let (mut latest, mut monitor) = reporter.subscribe().await;
3998 finalizers.push(context.child("finalizer").spawn(move |_| async move {
3999 while latest < required_containers {
4000 latest = monitor.recv().await.expect("event missing");
4001 }
4002 }));
4003 }
4004 join_all(finalizers).await;
4005
4006 let byz = &participants[0];
4008 for reporter in reporters.iter() {
4009 reporter.assert_no_faults();
4011
4012 reporter.assert_no_invalid();
4014 }
4015
4016 let blocked = oracle.blocked().await.unwrap();
4018 assert!(!blocked.is_empty());
4019 for (a, b) in blocked {
4020 assert_ne!(&a, byz);
4021 assert_eq!(&b, byz);
4022 }
4023 });
4024 }
4025
4026 test_for_all_fixtures!(impersonator, seeds = 5);
4027
4028 fn equivocator_seeded<S, F, L>(seed: u64, mut fixture: F) -> bool
4029 where
4030 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4031 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4032 L: Elector<S>,
4033 {
4034 let n = 7;
4036 let required_containers = View::new(50);
4037 let activity_timeout = ViewDelta::new(10);
4038 let skip_timeout = ViewDelta::new(5);
4039 let namespace = b"consensus".to_vec();
4040 let cfg = deterministic::Config::new()
4041 .with_seed(seed)
4042 .with_timeout(Some(Duration::from_secs(60)));
4043 let executor = deterministic::Runner::new(cfg);
4044 executor.start(|mut context| async move {
4045 let Fixture {
4047 participants,
4048 schemes,
4049 ..
4050 } = fixture(&mut context, &namespace, n);
4051 let mut oracle =
4052 start_test_network_with_peers(context.child("network"), participants.clone(), true)
4053 .await;
4054 let mut registrations = register_validators(&mut oracle, &participants).await;
4055
4056 let link = Link {
4058 latency: Duration::from_millis(10),
4059 jitter: Duration::from_millis(1),
4060 success_rate: 1.0,
4061 };
4062 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4063
4064 let elector = L::default();
4066 let mut engines = Vec::new();
4067 let relay = Arc::new(mocks::relay::Relay::new());
4068 let mut reporters = Vec::new();
4069 for (idx_scheme, validator) in participants.iter().enumerate() {
4070 let context = context
4072 .child("validator")
4073 .with_attribute("public_key", validator);
4074
4075 let reporter_config = mocks::reporter::Config {
4077 participants: participants.clone().try_into().unwrap(),
4078 scheme: schemes[idx_scheme].clone(),
4079 elector: elector.clone(),
4080 };
4081 let reporter =
4082 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4083 reporters.push(reporter.clone());
4084 let (pending, recovered, resolver) = registrations
4085 .remove(validator)
4086 .expect("validator should be registered");
4087 if idx_scheme == 0 {
4088 let cfg = mocks::equivocator::Config {
4089 scheme: schemes[idx_scheme].clone(),
4090 epoch: Epoch::new(333),
4091 relay: relay.clone(),
4092 hasher: Sha256::default(),
4093 elector: elector.clone(),
4094 };
4095
4096 let engine = mocks::equivocator::Equivocator::new(
4097 context.child("byzantine_engine"),
4098 cfg,
4099 );
4100 engines.push(engine.start(pending, recovered));
4101 } else {
4102 let application_cfg = mocks::application::Config {
4103 hasher: Sha256::default(),
4104 relay: relay.clone(),
4105 me: validator.clone(),
4106 propose_latency: (10.0, 5.0),
4107 verify_latency: (10.0, 5.0),
4108 certify_latency: (10.0, 5.0),
4109 should_certify: mocks::application::Certifier::Always,
4110 };
4111 let (actor, application) = mocks::application::Application::new(
4112 context.child("application"),
4113 application_cfg,
4114 );
4115 actor.start();
4116 let blocker = oracle.control(validator.clone());
4117 let cfg = config::Config {
4118 scheme: schemes[idx_scheme].clone(),
4119 elector: elector.clone(),
4120 blocker,
4121 automaton: application.clone(),
4122 relay: application.clone(),
4123 reporter: reporter.clone(),
4124 strategy: Sequential,
4125 partition: validator.to_string(),
4126 mailbox_size: NZUsize!(1024),
4127 epoch: Epoch::new(333),
4128 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
4129 Epoch::new(333),
4130 )),
4131 leader_timeout: Duration::from_secs(1),
4132 certification_timeout: Duration::from_secs(2),
4133 timeout_retry: Duration::from_secs(10),
4134 fetch_timeout: Duration::from_secs(1),
4135 activity_timeout,
4136 skip_timeout,
4137 fetch_concurrent: NZUsize!(4),
4138 replay_buffer: NZUsize!(1024 * 1024),
4139 write_buffer: NZUsize!(1024 * 1024),
4140 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4141 forwarding: ForwardingPolicy::Disabled,
4142 };
4143 let engine = Engine::new(context.child("engine"), cfg);
4144 engines.push(engine.start(pending, recovered, resolver));
4145 }
4146 }
4147
4148 let mut finalizers = Vec::new();
4150 for reporter in reporters.iter_mut().skip(1) {
4151 let (mut latest, mut monitor) = reporter.subscribe().await;
4152 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4153 while latest < required_containers {
4154 latest = monitor.recv().await.expect("event missing");
4155 }
4156 }));
4157 }
4158 join_all(finalizers).await;
4159
4160 let idx = context.random_range(1..engines.len()); let validator = &participants[idx];
4163 let handle = engines.remove(idx);
4164 handle.abort();
4165 let _ = handle.await;
4166 reporters.remove(idx);
4167 info!(idx, ?validator, "aborted validator");
4168
4169 let mut finalizers = Vec::new();
4171 for reporter in reporters.iter_mut().skip(1) {
4172 let (mut latest, mut monitor) = reporter.subscribe().await;
4173 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4174 while latest < View::new(required_containers.get() * 2) {
4175 latest = monitor.recv().await.expect("event missing");
4176 }
4177 }));
4178 }
4179 join_all(finalizers).await;
4180
4181 info!(idx, ?validator, "restarting validator");
4183 let context = context
4184 .child("validator_restarted")
4185 .with_attribute("public_key", validator);
4186
4187 let reporter_config = mocks::reporter::Config {
4189 participants: participants.clone().try_into().unwrap(),
4190 scheme: schemes[idx].clone(),
4191 elector: elector.clone(),
4192 };
4193 let reporter =
4194 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4195 let (pending, recovered, resolver) =
4196 register_validator(&mut oracle, validator.clone()).await;
4197 reporters.push(reporter.clone());
4198 let application_cfg = mocks::application::Config {
4199 hasher: Sha256::default(),
4200 relay: relay.clone(),
4201 me: validator.clone(),
4202 propose_latency: (10.0, 5.0),
4203 verify_latency: (10.0, 5.0),
4204 certify_latency: (10.0, 5.0),
4205 should_certify: mocks::application::Certifier::Always,
4206 };
4207 let (actor, application) =
4208 mocks::application::Application::new(context.child("application"), application_cfg);
4209 actor.start();
4210 let blocker = oracle.control(validator.clone());
4211 let cfg = config::Config {
4212 scheme: schemes[idx].clone(),
4213 elector: elector.clone(),
4214 blocker,
4215 automaton: application.clone(),
4216 relay: application.clone(),
4217 reporter: reporter.clone(),
4218 strategy: Sequential,
4219 partition: validator.to_string(),
4220 mailbox_size: NZUsize!(1024),
4221 epoch: Epoch::new(333),
4222 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(Epoch::new(
4223 333,
4224 ))),
4225 leader_timeout: Duration::from_secs(1),
4226 certification_timeout: Duration::from_secs(2),
4227 timeout_retry: Duration::from_secs(10),
4228 fetch_timeout: Duration::from_secs(1),
4229 activity_timeout,
4230 skip_timeout,
4231 fetch_concurrent: NZUsize!(4),
4232 replay_buffer: NZUsize!(1024 * 1024),
4233 write_buffer: NZUsize!(1024 * 1024),
4234 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4235 forwarding: ForwardingPolicy::Disabled,
4236 };
4237 let engine = Engine::new(context.child("engine"), cfg);
4238 engine.start(pending, recovered, resolver);
4239
4240 let mut finalizers = Vec::new();
4242 for reporter in reporters.iter_mut().skip(1) {
4243 let (mut latest, mut monitor) = reporter.subscribe().await;
4244 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4245 while latest < View::new(required_containers.get() * 3) {
4246 latest = monitor.recv().await.expect("event missing");
4247 }
4248 }));
4249 }
4250 join_all(finalizers).await;
4251
4252 let byz = &participants[0];
4256 let blocked = oracle.blocked().await.unwrap();
4257 for (a, b) in &blocked {
4258 assert_ne!(a, byz);
4259 assert_eq!(b, byz);
4260 }
4261 !blocked.is_empty()
4262 })
4263 }
4264
4265 fn equivocator<S, F, L>(fixture: F)
4266 where
4267 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4268 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S> + Copy,
4269 L: Elector<S>,
4270 {
4271 let detected = (0..5).any(|seed| equivocator_seeded::<_, _, L>(seed, fixture));
4272 assert!(
4273 detected,
4274 "expected at least one seed to detect equivocation"
4275 );
4276 }
4277
4278 test_for_all_fixtures!(equivocator);
4279
4280 fn reconfigurer<S, F, L>(seed: u64, mut fixture: F)
4281 where
4282 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4283 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4284 L: Elector<S>,
4285 {
4286 let n = 4;
4288 let required_containers = View::new(50);
4289 let activity_timeout = ViewDelta::new(10);
4290 let skip_timeout = ViewDelta::new(5);
4291 let namespace = b"consensus".to_vec();
4292 let cfg = deterministic::Config::new()
4293 .with_seed(seed)
4294 .with_timeout(Some(Duration::from_secs(30)));
4295 let executor = deterministic::Runner::new(cfg);
4296 executor.start(|mut context| async move {
4297 let Fixture {
4299 participants,
4300 schemes,
4301 ..
4302 } = fixture(&mut context, &namespace, n);
4303 let mut oracle =
4304 start_test_network_with_peers(context.child("network"), participants.clone(), true)
4305 .await;
4306 let mut registrations = register_validators(&mut oracle, &participants).await;
4307
4308 let link = Link {
4310 latency: Duration::from_millis(10),
4311 jitter: Duration::from_millis(1),
4312 success_rate: 1.0,
4313 };
4314 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4315
4316 let elector = L::default();
4318 let relay = Arc::new(mocks::relay::Relay::new());
4319 let mut reporters = Vec::new();
4320 for (idx_scheme, validator) in participants.iter().enumerate() {
4321 let context = context
4323 .child("validator")
4324 .with_attribute("public_key", validator);
4325
4326 let reporter_config = mocks::reporter::Config {
4328 participants: participants.clone().try_into().unwrap(),
4329 scheme: schemes[idx_scheme].clone(),
4330 elector: elector.clone(),
4331 };
4332 let reporter =
4333 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4334 let (pending, recovered, resolver) = registrations
4335 .remove(validator)
4336 .expect("validator should be registered");
4337 if idx_scheme == 0 {
4338 let cfg = mocks::reconfigurer::Config {
4339 scheme: schemes[idx_scheme].clone(),
4340 };
4341 let engine: mocks::reconfigurer::Reconfigurer<_, _, Sha256> =
4342 mocks::reconfigurer::Reconfigurer::new(
4343 context.child("byzantine_engine"),
4344 cfg,
4345 );
4346 engine.start(pending);
4347 } else {
4348 reporters.push(reporter.clone());
4349 let application_cfg = mocks::application::Config {
4350 hasher: Sha256::default(),
4351 relay: relay.clone(),
4352 me: validator.clone(),
4353 propose_latency: (10.0, 5.0),
4354 verify_latency: (10.0, 5.0),
4355 certify_latency: (10.0, 5.0),
4356 should_certify: mocks::application::Certifier::Always,
4357 };
4358 let (actor, application) = mocks::application::Application::new(
4359 context.child("application"),
4360 application_cfg,
4361 );
4362 actor.start();
4363 let blocker = oracle.control(validator.clone());
4364 let cfg = config::Config {
4365 scheme: schemes[idx_scheme].clone(),
4366 elector: elector.clone(),
4367 blocker,
4368 automaton: application.clone(),
4369 relay: application.clone(),
4370 reporter: reporter.clone(),
4371 strategy: Sequential,
4372 partition: validator.to_string(),
4373 mailbox_size: NZUsize!(1024),
4374 epoch: Epoch::new(333),
4375 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
4376 Epoch::new(333),
4377 )),
4378 leader_timeout: Duration::from_secs(1),
4379 certification_timeout: Duration::from_secs(2),
4380 timeout_retry: Duration::from_secs(10),
4381 fetch_timeout: Duration::from_secs(1),
4382 activity_timeout,
4383 skip_timeout,
4384 fetch_concurrent: NZUsize!(4),
4385 replay_buffer: NZUsize!(1024 * 1024),
4386 write_buffer: NZUsize!(1024 * 1024),
4387 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4388 forwarding: ForwardingPolicy::Disabled,
4389 };
4390 let engine = Engine::new(context.child("engine"), cfg);
4391 engine.start(pending, recovered, resolver);
4392 }
4393 }
4394
4395 let mut finalizers = Vec::new();
4397 for reporter in reporters.iter_mut() {
4398 let (mut latest, mut monitor) = reporter.subscribe().await;
4399 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4400 while latest < required_containers {
4401 latest = monitor.recv().await.expect("event missing");
4402 }
4403 }));
4404 }
4405 join_all(finalizers).await;
4406
4407 let byz = &participants[0];
4409 for reporter in reporters.iter() {
4410 reporter.assert_no_faults();
4412
4413 reporter.assert_no_invalid();
4415 }
4416
4417 let blocked = oracle.blocked().await.unwrap();
4419 assert!(!blocked.is_empty());
4420 for (a, b) in blocked {
4421 assert_ne!(&a, byz);
4422 assert_eq!(&b, byz);
4423 }
4424 });
4425 }
4426
4427 test_for_all_fixtures!(reconfigurer, seeds = 5);
4428
4429 fn nuller<S, F, L>(seed: u64, mut fixture: F)
4430 where
4431 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4432 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4433 L: Elector<S>,
4434 {
4435 let n = 4;
4437 let required_containers = View::new(50);
4438 let activity_timeout = ViewDelta::new(10);
4439 let skip_timeout = ViewDelta::new(5);
4440 let namespace = b"consensus".to_vec();
4441 let cfg = deterministic::Config::new()
4442 .with_seed(seed)
4443 .with_timeout(Some(Duration::from_secs(30)));
4444 let executor = deterministic::Runner::new(cfg);
4445 executor.start(|mut context| async move {
4446 let Fixture {
4448 participants,
4449 schemes,
4450 ..
4451 } = fixture(&mut context, &namespace, n);
4452 let mut oracle =
4453 start_test_network_with_peers(context.child("network"), participants.clone(), true)
4454 .await;
4455 let mut registrations = register_validators(&mut oracle, &participants).await;
4456
4457 let link = Link {
4459 latency: Duration::from_millis(10),
4460 jitter: Duration::from_millis(1),
4461 success_rate: 1.0,
4462 };
4463 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4464
4465 let elector = L::default();
4467 let relay = Arc::new(mocks::relay::Relay::new());
4468 let mut reporters = Vec::new();
4469 for (idx_scheme, validator) in participants.iter().enumerate() {
4470 let context = context
4472 .child("validator")
4473 .with_attribute("public_key", validator);
4474
4475 let reporter_config = mocks::reporter::Config {
4477 participants: participants.clone().try_into().unwrap(),
4478 scheme: schemes[idx_scheme].clone(),
4479 elector: elector.clone(),
4480 };
4481 let reporter =
4482 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4483 let (pending, recovered, resolver) = registrations
4484 .remove(validator)
4485 .expect("validator should be registered");
4486 if idx_scheme == 0 {
4487 let cfg = mocks::nuller::Config {
4488 scheme: schemes[idx_scheme].clone(),
4489 };
4490 let engine: mocks::nuller::Nuller<_, _, Sha256> =
4491 mocks::nuller::Nuller::new(context.child("byzantine_engine"), cfg);
4492 engine.start(pending);
4493 } else {
4494 reporters.push(reporter.clone());
4495 let application_cfg = mocks::application::Config {
4496 hasher: Sha256::default(),
4497 relay: relay.clone(),
4498 me: validator.clone(),
4499 propose_latency: (10.0, 5.0),
4500 verify_latency: (10.0, 5.0),
4501 certify_latency: (10.0, 5.0),
4502 should_certify: mocks::application::Certifier::Always,
4503 };
4504 let (actor, application) = mocks::application::Application::new(
4505 context.child("application"),
4506 application_cfg,
4507 );
4508 actor.start();
4509 let blocker = oracle.control(validator.clone());
4510 let cfg = config::Config {
4511 scheme: schemes[idx_scheme].clone(),
4512 elector: elector.clone(),
4513 blocker,
4514 automaton: application.clone(),
4515 relay: application.clone(),
4516 reporter: reporter.clone(),
4517 strategy: Sequential,
4518 partition: validator.clone().to_string(),
4519 mailbox_size: NZUsize!(1024),
4520 epoch: Epoch::new(333),
4521 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
4522 Epoch::new(333),
4523 )),
4524 leader_timeout: Duration::from_secs(1),
4525 certification_timeout: Duration::from_secs(2),
4526 timeout_retry: Duration::from_secs(10),
4527 fetch_timeout: Duration::from_secs(1),
4528 activity_timeout,
4529 skip_timeout,
4530 fetch_concurrent: NZUsize!(4),
4531 replay_buffer: NZUsize!(1024 * 1024),
4532 write_buffer: NZUsize!(1024 * 1024),
4533 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4534 forwarding: ForwardingPolicy::Disabled,
4535 };
4536 let engine = Engine::new(context.child("engine"), cfg);
4537 engine.start(pending, recovered, resolver);
4538 }
4539 }
4540
4541 let mut finalizers = Vec::new();
4543 for reporter in reporters.iter_mut() {
4544 let (mut latest, mut monitor) = reporter.subscribe().await;
4545 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4546 while latest < required_containers {
4547 latest = monitor.recv().await.expect("event missing");
4548 }
4549 }));
4550 }
4551 join_all(finalizers).await;
4552
4553 let byz = &participants[0];
4555 let mut count_nullify_and_finalize = 0;
4556 for reporter in reporters.iter() {
4557 {
4559 let faults = reporter.faults.lock();
4560 assert_eq!(faults.len(), 1);
4561 let faulter = faults.get(byz).expect("byzantine party is not faulter");
4562 for faults in faulter.values() {
4563 for fault in faults.iter() {
4564 match fault {
4565 Activity::NullifyFinalize(_) => {
4566 count_nullify_and_finalize += 1;
4567 }
4568 _ => panic!("unexpected fault: {fault:?}"),
4569 }
4570 }
4571 }
4572 }
4573
4574 reporter.assert_no_invalid();
4576 }
4577 assert!(count_nullify_and_finalize > 0);
4578
4579 let blocked = oracle.blocked().await.unwrap();
4581 assert!(!blocked.is_empty());
4582 for (a, b) in blocked {
4583 assert_ne!(&a, byz);
4584 assert_eq!(&b, byz);
4585 }
4586 });
4587 }
4588
4589 test_for_all_fixtures!(nuller, seeds = 5);
4590
4591 fn outdated<S, F, L>(seed: u64, mut fixture: F)
4592 where
4593 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4594 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4595 L: Elector<S>,
4596 {
4597 let n = 4;
4599 let required_containers = View::new(100);
4600 let activity_timeout = ViewDelta::new(10);
4601 let skip_timeout = ViewDelta::new(5);
4602 let namespace = b"consensus".to_vec();
4603 let cfg = deterministic::Config::new()
4604 .with_seed(seed)
4605 .with_timeout(Some(Duration::from_secs(30)));
4606 let executor = deterministic::Runner::new(cfg);
4607 executor.start(|mut context| async move {
4608 let Fixture {
4610 participants,
4611 schemes,
4612 ..
4613 } = fixture(&mut context, &namespace, n);
4614 let mut oracle =
4615 start_test_network_with_peers(context.child("network"), participants.clone(), true)
4616 .await;
4617 let mut registrations = register_validators(&mut oracle, &participants).await;
4618
4619 let link = Link {
4621 latency: Duration::from_millis(10),
4622 jitter: Duration::from_millis(1),
4623 success_rate: 1.0,
4624 };
4625 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4626
4627 let elector = L::default();
4629 let relay = Arc::new(mocks::relay::Relay::new());
4630 let mut reporters = Vec::new();
4631 for (idx_scheme, validator) in participants.iter().enumerate() {
4632 let context = context
4634 .child("validator")
4635 .with_attribute("public_key", validator);
4636
4637 let reporter_config = mocks::reporter::Config {
4639 participants: participants.clone().try_into().unwrap(),
4640 scheme: schemes[idx_scheme].clone(),
4641 elector: elector.clone(),
4642 };
4643 let reporter =
4644 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4645 let (pending, recovered, resolver) = registrations
4646 .remove(validator)
4647 .expect("validator should be registered");
4648 if idx_scheme == 0 {
4649 let cfg = mocks::outdated::Config {
4650 scheme: schemes[idx_scheme].clone(),
4651 view_delta: ViewDelta::new(activity_timeout.get().saturating_mul(4)),
4652 };
4653 let engine: mocks::outdated::Outdated<_, _, Sha256> =
4654 mocks::outdated::Outdated::new(context.child("byzantine_engine"), cfg);
4655 engine.start(pending);
4656 } else {
4657 reporters.push(reporter.clone());
4658 let application_cfg = mocks::application::Config {
4659 hasher: Sha256::default(),
4660 relay: relay.clone(),
4661 me: validator.clone(),
4662 propose_latency: (10.0, 5.0),
4663 verify_latency: (10.0, 5.0),
4664 certify_latency: (10.0, 5.0),
4665 should_certify: mocks::application::Certifier::Always,
4666 };
4667 let (actor, application) = mocks::application::Application::new(
4668 context.child("application"),
4669 application_cfg,
4670 );
4671 actor.start();
4672 let blocker = oracle.control(validator.clone());
4673 let cfg = config::Config {
4674 scheme: schemes[idx_scheme].clone(),
4675 elector: elector.clone(),
4676 blocker,
4677 automaton: application.clone(),
4678 relay: application.clone(),
4679 reporter: reporter.clone(),
4680 strategy: Sequential,
4681 partition: validator.clone().to_string(),
4682 mailbox_size: NZUsize!(1024),
4683 epoch: Epoch::new(333),
4684 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
4685 Epoch::new(333),
4686 )),
4687 leader_timeout: Duration::from_secs(1),
4688 certification_timeout: Duration::from_secs(2),
4689 timeout_retry: Duration::from_secs(10),
4690 fetch_timeout: Duration::from_secs(1),
4691 activity_timeout,
4692 skip_timeout,
4693 fetch_concurrent: NZUsize!(4),
4694 replay_buffer: NZUsize!(1024 * 1024),
4695 write_buffer: NZUsize!(1024 * 1024),
4696 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4697 forwarding: ForwardingPolicy::Disabled,
4698 };
4699 let engine = Engine::new(context.child("engine"), cfg);
4700 engine.start(pending, recovered, resolver);
4701 }
4702 }
4703
4704 let mut finalizers = Vec::new();
4706 for reporter in reporters.iter_mut() {
4707 let (mut latest, mut monitor) = reporter.subscribe().await;
4708 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4709 while latest < required_containers {
4710 latest = monitor.recv().await.expect("event missing");
4711 }
4712 }));
4713 }
4714 join_all(finalizers).await;
4715
4716 for reporter in reporters.iter() {
4718 reporter.assert_no_faults();
4720
4721 reporter.assert_no_invalid();
4723 }
4724
4725 let blocked = oracle.blocked().await.unwrap();
4727 assert!(blocked.is_empty());
4728 });
4729 }
4730
4731 test_for_all_fixtures!(outdated, seeds = 5);
4732
4733 fn run_1k<S, F, L>(mut fixture: F)
4734 where
4735 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4736 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4737 L: Elector<S>,
4738 {
4739 let n = 10;
4741 let required_containers = View::new(1_000);
4742 let activity_timeout = ViewDelta::new(10);
4743 let skip_timeout = ViewDelta::new(5);
4744 let namespace = b"consensus".to_vec();
4745 let cfg = deterministic::Config::new();
4746 let executor = deterministic::Runner::new(cfg);
4747 executor.start(|mut context| async move {
4748 let Fixture {
4750 participants,
4751 schemes,
4752 ..
4753 } = fixture(&mut context, &namespace, n);
4754 let mut oracle =
4755 start_test_network_with_peers(context.child("network"), participants.clone(), true)
4756 .await;
4757 let mut registrations = register_validators(&mut oracle, &participants).await;
4758
4759 let link = Link {
4761 latency: Duration::from_millis(80),
4762 jitter: Duration::from_millis(10),
4763 success_rate: 0.98,
4764 };
4765 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4766
4767 let elector = L::default();
4769 let relay = Arc::new(mocks::relay::Relay::new());
4770 let mut reporters = Vec::new();
4771 let mut engine_handlers = Vec::new();
4772 for (idx, validator) in participants.iter().enumerate() {
4773 let context = context
4775 .child("validator")
4776 .with_attribute("public_key", validator);
4777
4778 let reporter_config = mocks::reporter::Config {
4780 participants: participants.clone().try_into().unwrap(),
4781 scheme: schemes[idx].clone(),
4782 elector: elector.clone(),
4783 };
4784 let reporter =
4785 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4786 reporters.push(reporter.clone());
4787 let application_cfg = mocks::application::Config {
4788 hasher: Sha256::default(),
4789 relay: relay.clone(),
4790 me: validator.clone(),
4791 propose_latency: (100.0, 50.0),
4792 verify_latency: (50.0, 40.0),
4793 certify_latency: (50.0, 40.0),
4794 should_certify: mocks::application::Certifier::Always,
4795 };
4796 let (actor, application) = mocks::application::Application::new(
4797 context.child("application"),
4798 application_cfg,
4799 );
4800 actor.start();
4801 let blocker = oracle.control(validator.clone());
4802 let cfg = config::Config {
4803 scheme: schemes[idx].clone(),
4804 elector: elector.clone(),
4805 blocker,
4806 automaton: application.clone(),
4807 relay: application.clone(),
4808 reporter: reporter.clone(),
4809 strategy: Sequential,
4810 partition: validator.to_string(),
4811 mailbox_size: NZUsize!(1024),
4812 epoch: Epoch::new(333),
4813 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
4814 Epoch::new(333),
4815 )),
4816 leader_timeout: Duration::from_secs(1),
4817 certification_timeout: Duration::from_secs(2),
4818 timeout_retry: Duration::from_secs(10),
4819 fetch_timeout: Duration::from_secs(1),
4820 activity_timeout,
4821 skip_timeout,
4822 fetch_concurrent: NZUsize!(4),
4823 replay_buffer: NZUsize!(1024 * 1024),
4824 write_buffer: NZUsize!(1024 * 1024),
4825 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4826 forwarding: ForwardingPolicy::Disabled,
4827 };
4828 let engine = Engine::new(context.child("engine"), cfg);
4829
4830 let (pending, recovered, resolver) = registrations
4832 .remove(validator)
4833 .expect("validator should be registered");
4834 engine_handlers.push(engine.start(pending, recovered, resolver));
4835 }
4836
4837 let mut finalizers = Vec::new();
4839 for reporter in reporters.iter_mut() {
4840 let (mut latest, mut monitor) = reporter.subscribe().await;
4841 finalizers.push(context.child("finalizer").spawn(move |_| async move {
4842 while latest < required_containers {
4843 latest = monitor.recv().await.expect("event missing");
4844 }
4845 }));
4846 }
4847 join_all(finalizers).await;
4848
4849 for reporter in reporters.iter() {
4851 reporter.assert_no_faults();
4853
4854 reporter.assert_no_invalid();
4856 }
4857
4858 let blocked = oracle.blocked().await.unwrap();
4860 assert!(blocked.is_empty());
4861 })
4862 }
4863
4864 #[test_group("slow")]
4865 #[test_traced]
4866 fn test_1k() {
4867 run_1k::<_, _, RoundRobin>(scheme_mocks::fixture);
4868 }
4869
4870 fn engine_shutdown<S, F, L>(seed: u64, mut fixture: F, graceful: bool)
4871 where
4872 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
4873 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
4874 L: Elector<S>,
4875 {
4876 let n = 1;
4877 let namespace = b"consensus".to_vec();
4878 let cfg = deterministic::Config::default()
4879 .with_seed(seed)
4880 .with_timeout(Some(Duration::from_secs(10)));
4881 let executor = deterministic::Runner::new(cfg);
4882 executor.start(|mut context| async move {
4883 let Fixture {
4885 participants,
4886 schemes,
4887 ..
4888 } = fixture(&mut context, &namespace, n);
4889 let mut oracle =
4890 start_test_network_with_peers(context.child("network"), participants.clone(), true)
4891 .await;
4892 let mut registrations = register_validators(&mut oracle, &participants).await;
4893
4894 let link = Link {
4896 latency: Duration::from_millis(1),
4897 jitter: Duration::from_millis(0),
4898 success_rate: 1.0,
4899 };
4900 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
4901
4902 let elector = L::default();
4904 let reporter_config = mocks::reporter::Config {
4905 participants: participants.clone().try_into().unwrap(),
4906 scheme: schemes[0].clone(),
4907 elector: elector.clone(),
4908 };
4909 let reporter =
4910 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
4911 let relay = Arc::new(mocks::relay::Relay::new());
4912 let application_cfg = mocks::application::Config {
4913 hasher: Sha256::default(),
4914 relay: relay.clone(),
4915 me: participants[0].clone(),
4916 propose_latency: (1.0, 0.0),
4917 verify_latency: (1.0, 0.0),
4918 certify_latency: (1.0, 0.0),
4919 should_certify: mocks::application::Certifier::Always,
4920 };
4921 let (actor, application) =
4922 mocks::application::Application::new(context.child("application"), application_cfg);
4923 actor.start();
4924 let blocker = oracle.control(participants[0].clone());
4925 let cfg = config::Config {
4926 scheme: schemes[0].clone(),
4927 elector: elector.clone(),
4928 blocker,
4929 automaton: application.clone(),
4930 relay: application.clone(),
4931 reporter: reporter.clone(),
4932 strategy: Sequential,
4933 partition: participants[0].clone().to_string(),
4934 mailbox_size: NZUsize!(64),
4935 epoch: Epoch::new(333),
4936 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(Epoch::new(
4937 333,
4938 ))),
4939 leader_timeout: Duration::from_millis(50),
4940 certification_timeout: Duration::from_millis(100),
4941 timeout_retry: Duration::from_millis(250),
4942 fetch_timeout: Duration::from_millis(50),
4943 activity_timeout: ViewDelta::new(4),
4944 skip_timeout: ViewDelta::new(2),
4945 fetch_concurrent: NZUsize!(4),
4946 replay_buffer: NZUsize!(1024 * 16),
4947 write_buffer: NZUsize!(1024 * 16),
4948 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
4949 forwarding: ForwardingPolicy::Disabled,
4950 };
4951 let engine = Engine::new(context.child("engine"), cfg);
4952
4953 let (pending, recovered, resolver) = registrations
4955 .remove(&participants[0])
4956 .expect("validator should be registered");
4957 let handle = engine.start(pending, recovered, resolver);
4958
4959 context.sleep(Duration::from_millis(1000)).await;
4961
4962 let running_before = count_running_tasks(&context, "engine");
4964 assert!(
4965 running_before > 0,
4966 "at least one engine task should be running"
4967 );
4968
4969 context.sleep(Duration::from_millis(1500)).await;
4971 assert!(
4972 count_running_tasks(&context, "engine") > 0,
4973 "engine tasks should still be running"
4974 );
4975
4976 let running_after = if graceful {
4978 let result = context
4979 .child("stop")
4980 .stop(0, Some(Duration::from_secs(5)))
4981 .await;
4982 assert!(
4983 result.is_ok(),
4984 "graceful shutdown should complete: {result:?}"
4985 );
4986 count_running_tasks(&context, "engine")
4987 } else {
4988 handle.abort();
4989 let _ = handle.await; context.sleep(Duration::from_millis(1000)).await;
4993 count_running_tasks(&context, "engine")
4994 };
4995 assert_eq!(
4996 running_after, 0,
4997 "all engine tasks should be stopped, but {running_after} still running"
4998 );
4999 });
5000 }
5001
5002 fn children_shutdown_on_engine_abort<S, F, L>(seed: u64, fixture: F)
5003 where
5004 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
5005 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
5006 L: Elector<S>,
5007 {
5008 engine_shutdown::<S, F, L>(seed, fixture, false);
5009 }
5010
5011 test_for_all_fixtures!(children_shutdown_on_engine_abort, seeds = 10);
5012
5013 fn graceful_shutdown<S, F, L>(seed: u64, fixture: F)
5014 where
5015 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
5016 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
5017 L: Elector<S>,
5018 {
5019 engine_shutdown::<S, F, L>(seed, fixture, true);
5020 }
5021
5022 test_for_all_fixtures!(graceful_shutdown, seeds = 10);
5023
5024 fn attributable_reporter_filtering<S, F, L>(mut fixture: F)
5025 where
5026 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
5027 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
5028 L: Elector<S>,
5029 {
5030 let n = 3;
5031 let required_containers = View::new(10);
5032 let activity_timeout = ViewDelta::new(10);
5033 let skip_timeout = ViewDelta::new(5);
5034 let namespace = b"consensus".to_vec();
5035 let executor = deterministic::Runner::timed(Duration::from_secs(30));
5036 executor.start(|mut context| async move {
5037 let Fixture {
5039 participants,
5040 schemes,
5041 ..
5042 } = fixture(&mut context, &namespace, n);
5043 let mut oracle = start_test_network_with_peers(
5044 context.child("network"),
5045 participants.clone(),
5046 false,
5047 )
5048 .await;
5049 let mut registrations = register_validators(&mut oracle, &participants).await;
5050
5051 let link = Link {
5053 latency: Duration::from_millis(10),
5054 jitter: Duration::from_millis(1),
5055 success_rate: 1.0,
5056 };
5057 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
5058
5059 let elector = L::default();
5061 let relay = Arc::new(mocks::relay::Relay::new());
5062 let mut reporters = Vec::new();
5063 for (idx, validator) in participants.iter().enumerate() {
5064 let context = context
5065 .child("validator")
5066 .with_attribute("public_key", validator);
5067
5068 let reporter_config = mocks::reporter::Config {
5069 participants: participants.clone().try_into().unwrap(),
5070 scheme: schemes[idx].clone(),
5071 elector: elector.clone(),
5072 };
5073 let mock_reporter =
5074 mocks::reporter::Reporter::new(context.child("mock_reporter"), reporter_config);
5075
5076 let attributable_reporter = scheme::reporter::AttributableReporter::new(
5078 context.child("rng"),
5079 schemes[idx].clone(),
5080 mock_reporter.clone(),
5081 Sequential,
5082 true, );
5084 reporters.push(mock_reporter.clone());
5085
5086 let application_cfg = mocks::application::Config {
5087 hasher: Sha256::default(),
5088 relay: relay.clone(),
5089 me: validator.clone(),
5090 propose_latency: (10.0, 5.0),
5091 verify_latency: (10.0, 5.0),
5092 certify_latency: (10.0, 5.0),
5093 should_certify: mocks::application::Certifier::Always,
5094 };
5095 let (actor, application) = mocks::application::Application::new(
5096 context.child("application"),
5097 application_cfg,
5098 );
5099 actor.start();
5100 let blocker = oracle.control(validator.clone());
5101 let cfg = config::Config {
5102 scheme: schemes[idx].clone(),
5103 elector: elector.clone(),
5104 blocker,
5105 automaton: application.clone(),
5106 relay: application.clone(),
5107 reporter: attributable_reporter,
5108 strategy: Sequential,
5109 partition: validator.to_string(),
5110 mailbox_size: NZUsize!(1024),
5111 epoch: Epoch::new(333),
5112 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
5113 Epoch::new(333),
5114 )),
5115 leader_timeout: Duration::from_secs(1),
5116 certification_timeout: Duration::from_secs(2),
5117 timeout_retry: Duration::from_secs(10),
5118 fetch_timeout: Duration::from_secs(1),
5119 activity_timeout,
5120 skip_timeout,
5121 fetch_concurrent: NZUsize!(4),
5122 replay_buffer: NZUsize!(1024 * 1024),
5123 write_buffer: NZUsize!(1024 * 1024),
5124 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
5125 forwarding: ForwardingPolicy::Disabled,
5126 };
5127 let engine = Engine::new(context.child("engine"), cfg);
5128
5129 let (pending, recovered, resolver) = registrations
5131 .remove(validator)
5132 .expect("validator should be registered");
5133 engine.start(pending, recovered, resolver);
5134 }
5135
5136 let mut finalizers = Vec::new();
5138 for reporter in reporters.iter_mut() {
5139 let (mut latest, mut monitor) = reporter.subscribe().await;
5140 finalizers.push(context.child("finalizer").spawn(move |_| async move {
5141 while latest < required_containers {
5142 latest = monitor.recv().await.expect("event missing");
5143 }
5144 }));
5145 }
5146 join_all(finalizers).await;
5147
5148 for reporter in reporters.iter() {
5150 reporter.assert_no_faults();
5152
5153 reporter.assert_no_invalid();
5155
5156 {
5158 let notarizations = reporter.notarizations.lock();
5159 let finalizations = reporter.finalizations.lock();
5160 assert!(
5161 !notarizations.is_empty() || !finalizations.is_empty(),
5162 "Certificates should be reported"
5163 );
5164 }
5165
5166 let notarizes = reporter.notarizes.lock();
5168 let last_view = notarizes.keys().max().cloned().unwrap_or_default();
5169 for (view, payloads) in notarizes.iter() {
5170 if *view == last_view {
5171 continue; }
5173
5174 let signers: usize = payloads.values().map(|signers| signers.len()).sum();
5175
5176 if S::is_attributable() {
5178 assert!(signers > 1, "view {view}: {signers}");
5179 } else {
5180 assert_eq!(signers, 0);
5182 }
5183 }
5184
5185 let finalizes = reporter.finalizes.lock();
5187 for payloads in finalizes.values() {
5188 let signers: usize = payloads.values().map(|signers| signers.len()).sum();
5189
5190 if S::is_attributable() {
5192 assert!(signers > 1);
5193 } else {
5194 assert_eq!(signers, 0);
5196 }
5197 }
5198 }
5199
5200 let blocked = oracle.blocked().await.unwrap();
5202 assert!(blocked.is_empty());
5203 });
5204 }
5205
5206 test_for_all_fixtures!(attributable_reporter_filtering);
5207
5208 fn split_views_no_lockup<S, F, L>(mut fixture: F)
5209 where
5210 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
5211 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
5212 L: Elector<S>,
5213 {
5214 enum ParticipantType {
5227 Group1, Group2, Ignorant, Byzantine, }
5232 let get_type = |idx: usize| -> ParticipantType {
5233 match idx {
5234 0..3 => ParticipantType::Group1,
5235 3..6 => ParticipantType::Group2,
5236 6 => ParticipantType::Ignorant,
5237 7..10 => ParticipantType::Byzantine,
5238 _ => unreachable!(),
5239 }
5240 };
5241
5242 let n = 10;
5244 let quorum = quorum(n) as usize;
5245 assert_eq!(quorum, 7);
5246 let activity_timeout = ViewDelta::new(10);
5247 let skip_timeout = ViewDelta::new(5);
5248 let namespace = b"consensus".to_vec();
5249 let executor = deterministic::Runner::timed(Duration::from_secs(300));
5250 executor.start(|mut context| async move {
5251 let Fixture {
5253 participants,
5254 schemes,
5255 ..
5256 } = fixture(&mut context, &namespace, n);
5257 let mut oracle = start_test_network_with_peers(
5258 context.child("network"),
5259 participants.clone(),
5260 false,
5261 )
5262 .await;
5263 let mut registrations = register_validators(&mut oracle, &participants).await;
5264
5265 let build_finalization = |proposal: &Proposal<D>| -> TFinalization<_, D> {
5269 let votes: Vec<_> = (0..=quorum)
5270 .map(|i| TFinalize::sign(&schemes[i], proposal.clone()).unwrap())
5271 .collect();
5272 TFinalization::from_finalizes(&schemes[0], &votes, &Sequential)
5273 .expect("finalization quorum")
5274 };
5275 let build_notarization = |proposal: &Proposal<D>| -> TNotarization<_, D> {
5277 let votes: Vec<_> = (0..=quorum)
5278 .map(|i| TNotarize::sign(&schemes[i], proposal.clone()).unwrap())
5279 .collect();
5280 TNotarization::from_notarizes(&schemes[0], &votes, &Sequential)
5281 .expect("notarization quorum")
5282 };
5283 let build_nullification = |round: Round| -> TNullification<_> {
5284 let votes: Vec<_> = (0..=quorum)
5285 .map(|i| TNullify::sign::<D>(&schemes[i], round).unwrap())
5286 .collect();
5287 TNullification::from_nullifies(&schemes[0], &votes, &Sequential)
5288 .expect("nullification quorum")
5289 };
5290 let f_view = 1;
5292 let round_f = Round::new(Epoch::new(333), View::new(f_view));
5293 let payload_b0 = Sha256::hash(b"B_F");
5294 let proposal_b0 = Proposal::new(round_f, View::new(f_view - 1), payload_b0);
5295 let payload_b1a = Sha256::hash(b"B_G1");
5296 let proposal_b1a = Proposal::new(
5297 Round::new(Epoch::new(333), View::new(f_view + 1)),
5298 View::new(f_view),
5299 payload_b1a,
5300 );
5301 let payload_b1b = Sha256::hash(b"B_G2");
5302 let proposal_b1b = Proposal::new(
5303 Round::new(Epoch::new(333), View::new(f_view + 2)),
5304 View::new(f_view),
5305 payload_b1b,
5306 );
5307
5308 let b0_notarization = build_notarization(&proposal_b0);
5310 let b0_finalization = build_finalization(&proposal_b0);
5311 let b1a_notarization = build_notarization(&proposal_b1a);
5313 let b1b_notarization = build_notarization(&proposal_b1b);
5314 let null_a = build_nullification(Round::new(Epoch::new(333), View::new(f_view + 1)));
5316 let null_b = build_nullification(Round::new(Epoch::new(333), View::new(f_view + 2)));
5317
5318 let injector_pk = PrivateKey::from_seed(1_000_000).public_key();
5320 let (mut injector_sender, _inj_certificate_receiver) = oracle
5321 .control(injector_pk.clone())
5322 .register(1, TEST_QUOTA)
5323 .await
5324 .unwrap();
5325
5326 let link = Link {
5328 latency: Duration::from_millis(10),
5329 jitter: Duration::from_millis(0),
5330 success_rate: 1.0,
5331 };
5332 for p in participants.iter() {
5333 oracle
5334 .add_link(injector_pk.clone(), p.clone(), link.clone())
5335 .await
5336 .unwrap();
5337 }
5338 oracle.manager().track(
5339 1,
5340 TrackedPeers::new(
5341 Set::from_iter_dedup(participants.iter().cloned()),
5342 Set::from_iter_dedup(std::slice::from_ref(&injector_pk).iter().cloned()),
5343 ),
5344 );
5345 context.sleep(Duration::from_millis(10)).await;
5346
5347 let msg = Certificate::<_, D>::Notarization(b0_notarization).encode();
5351 injector_sender.send(Recipients::All, msg, true);
5352 let msg = Certificate::<_, D>::Finalization(b0_finalization).encode();
5353 injector_sender.send(Recipients::All, msg, true);
5354 let notarization_msg = Certificate::<_, D>::Notarization(b1a_notarization);
5356 let nullification_msg = Certificate::<_, D>::Nullification(null_a.clone());
5357 for (i, participant) in participants.iter().enumerate() {
5358 let recipient = Recipients::One(participant.clone());
5359 let msg = match get_type(i) {
5360 ParticipantType::Group1 => notarization_msg.encode(),
5361 _ => nullification_msg.encode(),
5362 };
5363 injector_sender.send(recipient, msg, true);
5364 }
5365 let notarization_msg = Certificate::<_, D>::Notarization(b1b_notarization);
5367 let nullification_msg = Certificate::<_, D>::Nullification(null_b.clone());
5368 for (i, participant) in participants.iter().enumerate() {
5369 let recipient = Recipients::One(participant.clone());
5370 let msg = match get_type(i) {
5371 ParticipantType::Group2 => notarization_msg.encode(),
5372 _ => nullification_msg.encode(),
5373 };
5374 injector_sender.send(recipient, msg, true);
5375 }
5376
5377 let elector = L::default();
5383 let relay = Arc::new(mocks::relay::Relay::new());
5384 let mut honest_reporters = Vec::new();
5385 for (idx, validator) in participants.iter().enumerate() {
5386 let (pending, recovered, resolver) = registrations
5387 .remove(validator)
5388 .expect("validator should be registered");
5389 let participant_type = get_type(idx);
5390 if matches!(participant_type, ParticipantType::Byzantine) {
5391 let cfg = mocks::nullify_only::Config {
5393 scheme: schemes[idx].clone(),
5394 };
5395 let engine: mocks::nullify_only::NullifyOnly<_, _, Sha256> =
5396 mocks::nullify_only::NullifyOnly::new(
5397 context
5398 .child("byzantine")
5399 .with_attribute("public_key", validator),
5400 cfg,
5401 );
5402 engine.start(pending);
5403 drop(recovered);
5405 drop(resolver);
5406 } else {
5407 let reporter_config = mocks::reporter::Config {
5409 participants: participants.clone().try_into().unwrap(),
5410 scheme: schemes[idx].clone(),
5411 elector: elector.clone(),
5412 };
5413 let reporter = mocks::reporter::Reporter::new(
5414 context
5415 .child("reporter")
5416 .with_attribute("public_key", validator),
5417 reporter_config,
5418 );
5419 honest_reporters.push(reporter.clone());
5420
5421 let application_cfg = mocks::application::Config {
5422 hasher: Sha256::default(),
5423 relay: relay.clone(),
5424 me: validator.clone(),
5425 propose_latency: (250.0, 50.0), verify_latency: (10.0, 5.0),
5427 certify_latency: (10.0, 5.0),
5428 should_certify: mocks::application::Certifier::Always,
5429 };
5430 let (actor, application) = mocks::application::Application::new(
5431 context
5432 .child("application")
5433 .with_attribute("public_key", validator),
5434 application_cfg,
5435 );
5436 actor.start();
5437 let blocker = oracle.control(validator.clone());
5438 let cfg = config::Config {
5439 scheme: schemes[idx].clone(),
5440 elector: elector.clone(),
5441 blocker,
5442 automaton: application.clone(),
5443 relay: application.clone(),
5444 reporter: reporter.clone(),
5445 strategy: Sequential,
5446 partition: validator.to_string(),
5447 mailbox_size: NZUsize!(1024),
5448 epoch: Epoch::new(333),
5449 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
5450 Epoch::new(333),
5451 )),
5452 leader_timeout: Duration::from_secs(10),
5453 certification_timeout: Duration::from_secs(10),
5454 timeout_retry: Duration::from_secs(10),
5455 fetch_timeout: Duration::from_secs(1),
5456 activity_timeout,
5457 skip_timeout,
5458 fetch_concurrent: NZUsize!(4),
5459 replay_buffer: NZUsize!(1024 * 1024),
5460 write_buffer: NZUsize!(1024 * 1024),
5461 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
5462 forwarding: ForwardingPolicy::Disabled,
5463 };
5464 let engine = Engine::new(
5465 context
5466 .child("engine")
5467 .with_attribute("public_key", validator),
5468 cfg,
5469 );
5470 engine.start(pending, recovered, resolver);
5471 }
5472 }
5473
5474 context.sleep(Duration::from_secs(2)).await;
5476
5477 let view = View::new(f_view);
5482 for reporter in honest_reporters.iter() {
5483 let finalizations = reporter.finalizations.lock();
5484 assert!(finalizations.contains_key(&view));
5485 }
5486
5487 let view = View::new(f_view + 1);
5491 for (i, reporter) in honest_reporters.iter().enumerate() {
5492 let finalizations = reporter.finalizations.lock();
5493 assert!(!finalizations.contains_key(&view));
5494 let nullifications = reporter.nullifications.lock();
5495 let notarizations = reporter.notarizations.lock();
5496 match get_type(i) {
5497 ParticipantType::Group1 => {
5498 assert!(notarizations.contains_key(&view));
5499 assert!(!nullifications.contains_key(&view));
5500 }
5501 _ => {
5502 assert!(nullifications.contains_key(&view));
5503 assert!(!notarizations.contains_key(&view));
5504 }
5505 }
5506 }
5507
5508 let view = View::new(f_view + 2);
5512 for (i, reporter) in honest_reporters.iter().enumerate() {
5513 let finalizations = reporter.finalizations.lock();
5514 assert!(!finalizations.contains_key(&view));
5515 let nullifications = reporter.nullifications.lock();
5516 let notarizations = reporter.notarizations.lock();
5517 match get_type(i) {
5518 ParticipantType::Group2 => {
5519 assert!(notarizations.contains_key(&view));
5520 assert!(!nullifications.contains_key(&view));
5521 }
5522 _ => {
5523 assert!(nullifications.contains_key(&view));
5524 assert!(!notarizations.contains_key(&view));
5525 }
5526 }
5527 }
5528
5529 let next_view = View::new(f_view + 3);
5531 for (i, reporter) in honest_reporters.iter().enumerate() {
5532 let nullifies = reporter.nullifies.lock();
5533 assert!(!nullifies.contains_key(&next_view), "reporter {i}");
5534 }
5535
5536 link_validators(&mut oracle, &participants, Action::Link(link.clone()), None).await;
5540
5541 {
5543 let target = View::new(f_view + 3);
5544 let mut finalizers = Vec::new();
5545 for reporter in honest_reporters.iter_mut() {
5546 let (mut latest, mut monitor) = reporter.subscribe().await;
5547 finalizers.push(
5548 context
5549 .child("resume_finalizer")
5550 .spawn(move |_| async move {
5551 while latest < target {
5552 latest = monitor.recv().await.expect("event missing");
5553 }
5554 }),
5555 );
5556 }
5557 join_all(finalizers).await;
5558 }
5559
5560 for reporter in honest_reporters.iter() {
5562 reporter.assert_no_faults();
5563 reporter.assert_no_invalid();
5564 }
5565 let blocked = oracle.blocked().await.unwrap();
5566 assert!(blocked.is_empty(), "blocked peers: {blocked:?}");
5567 });
5568 }
5569
5570 test_for_all_fixtures!(split_views_no_lockup);
5571
5572 fn tle<V, L>()
5573 where
5574 V: Variant,
5575 L: Elector<bls12381_threshold_vrf::Scheme<PublicKey, V>>,
5576 {
5577 let n = 4;
5579 let namespace = b"consensus".to_vec();
5580 let activity_timeout = ViewDelta::new(100);
5581 let skip_timeout = ViewDelta::new(50);
5582 let executor = deterministic::Runner::timed(Duration::from_secs(30));
5583 executor.start(|mut context| async move {
5584 let Fixture {
5586 participants,
5587 schemes,
5588 ..
5589 } = bls12381_threshold_vrf::fixture::<V, _>(&mut context, &namespace, n);
5590 let mut oracle =
5591 start_test_network_with_peers(context.child("network"), participants.clone(), true)
5592 .await;
5593 let mut registrations = register_validators(&mut oracle, &participants).await;
5594
5595 let link = Link {
5597 latency: Duration::from_millis(10),
5598 jitter: Duration::from_millis(5),
5599 success_rate: 1.0,
5600 };
5601 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
5602
5603 let elector = L::default();
5605 let relay = Arc::new(mocks::relay::Relay::new());
5606 let mut reporters = Vec::new();
5607 let mut engine_handlers = Vec::new();
5608 let monitor_reporter = Arc::new(Mutex::new(None));
5609 for (idx, validator) in participants.iter().enumerate() {
5610 let context = context
5612 .child("validator")
5613 .with_attribute("public_key", validator);
5614
5615 let reporter_config = mocks::reporter::Config {
5617 participants: participants.clone().try_into().unwrap(),
5618 scheme: schemes[idx].clone(),
5619 elector: elector.clone(),
5620 };
5621 let reporter =
5622 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
5623 reporters.push(reporter.clone());
5624 if idx == 0 {
5625 *monitor_reporter.lock() = Some(reporter.clone());
5626 }
5627
5628 let application_cfg = mocks::application::Config {
5630 hasher: Sha256::default(),
5631 relay: relay.clone(),
5632 me: validator.clone(),
5633 propose_latency: (10.0, 5.0),
5634 verify_latency: (10.0, 5.0),
5635 certify_latency: (10.0, 5.0),
5636 should_certify: mocks::application::Certifier::Always,
5637 };
5638 let (actor, application) = mocks::application::Application::new(
5639 context.child("application"),
5640 application_cfg,
5641 );
5642 actor.start();
5643 let blocker = oracle.control(validator.clone());
5644 let cfg = config::Config {
5645 scheme: schemes[idx].clone(),
5646 elector: elector.clone(),
5647 blocker,
5648 automaton: application.clone(),
5649 relay: application.clone(),
5650 reporter: reporter.clone(),
5651 strategy: Sequential,
5652 partition: validator.to_string(),
5653 mailbox_size: NZUsize!(1024),
5654 epoch: Epoch::new(333),
5655 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
5656 Epoch::new(333),
5657 )),
5658 leader_timeout: Duration::from_millis(100),
5659 certification_timeout: Duration::from_millis(200),
5660 timeout_retry: Duration::from_millis(500),
5661 fetch_timeout: Duration::from_millis(100),
5662 activity_timeout,
5663 skip_timeout,
5664 fetch_concurrent: NZUsize!(4),
5665 replay_buffer: NZUsize!(1024 * 1024),
5666 write_buffer: NZUsize!(1024 * 1024),
5667 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
5668 forwarding: ForwardingPolicy::Disabled,
5669 };
5670 let engine = Engine::new(context.child("engine"), cfg);
5671
5672 let (pending, recovered, resolver) = registrations
5674 .remove(validator)
5675 .expect("validator should be registered");
5676 engine_handlers.push(engine.start(pending, recovered, resolver));
5677 }
5678
5679 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);
5685
5686 let reporter = monitor_reporter.lock().clone().unwrap();
5688 loop {
5689 context.sleep(Duration::from_millis(100)).await;
5691 let notarizations = reporter.notarizations.lock();
5692 let Some(notarization) = notarizations.get(&target.view()) else {
5693 continue;
5694 };
5695
5696 let seed = notarization.seed();
5698 let decrypted = seed
5699 .decrypt(&ciphertext)
5700 .expect("Decryption should succeed with valid seed signature");
5701 assert_eq!(
5702 message,
5703 decrypted.as_ref(),
5704 "Decrypted message should match original message"
5705 );
5706 break;
5707 }
5708 });
5709 }
5710
5711 #[test_traced]
5712 fn test_tle() {
5713 tle::<MinPk, Random>();
5714 tle::<MinSig, Random>();
5715 }
5716
5717 fn run_hailstorm<S, F, L>(
5718 seed: u64,
5719 shutdowns: usize,
5720 interval: ViewDelta,
5721 mut fixture: F,
5722 ) -> String
5723 where
5724 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
5725 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
5726 L: Elector<S>,
5727 {
5728 let n = 5;
5730 let activity_timeout = ViewDelta::new(10);
5731 let skip_timeout = ViewDelta::new(5);
5732 let namespace = b"consensus".to_vec();
5733 let cfg = deterministic::Config::new().with_seed(seed);
5734 let executor = deterministic::Runner::new(cfg);
5735 executor.start(|mut context| async move {
5736 let Fixture {
5738 participants,
5739 schemes,
5740 ..
5741 } = fixture(&mut context, &namespace, n);
5742 let mut oracle =
5743 start_test_network_with_peers(context.child("network"), participants.clone(), true)
5744 .await;
5745 let mut registrations = register_validators(&mut oracle, &participants).await;
5746
5747 let link = Link {
5749 latency: Duration::from_millis(10),
5750 jitter: Duration::from_millis(1),
5751 success_rate: 1.0,
5752 };
5753 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
5754
5755 let elector = L::default();
5757 let relay = Arc::new(mocks::relay::Relay::new());
5758 let mut reporters = BTreeMap::new();
5759 let mut engine_handlers = BTreeMap::new();
5760 for (idx, validator) in participants.iter().enumerate() {
5761 let context = context
5763 .child("validator")
5764 .with_attribute("public_key", validator);
5765
5766 let reporter_config = mocks::reporter::Config {
5768 participants: participants.clone().try_into().unwrap(),
5769 scheme: schemes[idx].clone(),
5770 elector: elector.clone(),
5771 };
5772 let reporter =
5773 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
5774 reporters.insert(idx, reporter.clone());
5775 let application_cfg = mocks::application::Config {
5776 hasher: Sha256::default(),
5777 relay: relay.clone(),
5778 me: validator.clone(),
5779 propose_latency: (10.0, 5.0),
5780 verify_latency: (10.0, 5.0),
5781 certify_latency: (10.0, 5.0),
5782 should_certify: mocks::application::Certifier::Always,
5783 };
5784 let (actor, application) = mocks::application::Application::new(
5785 context.child("application"),
5786 application_cfg,
5787 );
5788 actor.start();
5789 let blocker = oracle.control(validator.clone());
5790 let cfg = config::Config {
5791 scheme: schemes[idx].clone(),
5792 elector: elector.clone(),
5793 blocker,
5794 automaton: application.clone(),
5795 relay: application.clone(),
5796 reporter: reporter.clone(),
5797 strategy: Sequential,
5798 partition: validator.to_string(),
5799 mailbox_size: NZUsize!(1024),
5800 epoch: Epoch::new(333),
5801 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
5802 Epoch::new(333),
5803 )),
5804 leader_timeout: Duration::from_secs(1),
5805 certification_timeout: Duration::from_secs(2),
5806 timeout_retry: Duration::from_secs(10),
5807 fetch_timeout: Duration::from_secs(1),
5808 activity_timeout,
5809 skip_timeout,
5810 fetch_concurrent: NZUsize!(4),
5811 replay_buffer: NZUsize!(1024 * 1024),
5812 write_buffer: NZUsize!(1024 * 1024),
5813 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
5814 forwarding: ForwardingPolicy::Disabled,
5815 };
5816 let engine = Engine::new(context.child("engine"), cfg);
5817
5818 let (pending, recovered, resolver) = registrations
5820 .remove(validator)
5821 .expect("validator should be registered");
5822 engine_handlers.insert(idx, engine.start(pending, recovered, resolver));
5823 }
5824
5825 let mut target = View::zero();
5827 for i in 0..shutdowns {
5828 target = target.saturating_add(interval);
5830
5831 let mut finalizers = Vec::new();
5833 for reporter in reporters.values_mut() {
5834 let (mut latest, mut monitor) = reporter.subscribe().await;
5835 finalizers.push(context.child("finalizer").spawn(move |_| async move {
5836 while latest < target {
5837 latest = monitor.recv().await.expect("event missing");
5838 }
5839 }));
5840 }
5841 join_all(finalizers).await;
5842 target = target.saturating_add(interval);
5843
5844 let idx = context.random_range(0..engine_handlers.len());
5846 let validator = &participants[idx];
5847 let handle = engine_handlers.remove(&idx).unwrap();
5848 handle.abort();
5849 let _ = handle.await;
5850 let selected_reporter = reporters.remove(&idx).unwrap();
5851 info!(idx, ?validator, "shutdown validator");
5852
5853 let mut finalizers = Vec::new();
5855 for reporter in reporters.values_mut() {
5856 let (mut latest, mut monitor) = reporter.subscribe().await;
5857 finalizers.push(context.child("finalizer").spawn(move |_| async move {
5858 while latest < target {
5859 latest = monitor.recv().await.expect("event missing");
5860 }
5861 }));
5862 }
5863 join_all(finalizers).await;
5864 target = target.saturating_add(interval);
5865
5866 info!(idx, ?validator, "restarting validator");
5868 let context = context
5869 .child("validator_restarted")
5870 .with_attribute("public_key", validator)
5871 .with_attribute("restart", i);
5872
5873 let (pending, recovered, resolver) =
5875 register_validator(&mut oracle, validator.clone()).await;
5876 let application_cfg = mocks::application::Config {
5877 hasher: Sha256::default(),
5878 relay: relay.clone(),
5879 me: validator.clone(),
5880 propose_latency: (10.0, 5.0),
5881 verify_latency: (10.0, 5.0),
5882 certify_latency: (10.0, 5.0),
5883 should_certify: mocks::application::Certifier::Always,
5884 };
5885 let (actor, application) = mocks::application::Application::new(
5886 context.child("application"),
5887 application_cfg,
5888 );
5889 actor.start();
5890 reporters.insert(idx, selected_reporter.clone());
5891 let blocker = oracle.control(validator.clone());
5892 let cfg = config::Config {
5893 scheme: schemes[idx].clone(),
5894 elector: elector.clone(),
5895 blocker,
5896 automaton: application.clone(),
5897 relay: application.clone(),
5898 reporter: selected_reporter,
5899 strategy: Sequential,
5900 partition: validator.to_string(),
5901 mailbox_size: NZUsize!(1024),
5902 epoch: Epoch::new(333),
5903 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
5904 Epoch::new(333),
5905 )),
5906 leader_timeout: Duration::from_secs(1),
5907 certification_timeout: Duration::from_secs(2),
5908 timeout_retry: Duration::from_secs(10),
5909 fetch_timeout: Duration::from_secs(1),
5910 activity_timeout,
5911 skip_timeout,
5912 fetch_concurrent: NZUsize!(4),
5913 replay_buffer: NZUsize!(1024 * 1024),
5914 write_buffer: NZUsize!(1024 * 1024),
5915 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
5916 forwarding: ForwardingPolicy::Disabled,
5917 };
5918 let engine = Engine::new(context.child("engine"), cfg);
5919 engine_handlers.insert(idx, engine.start(pending, recovered, resolver));
5920
5921 let mut finalizers = Vec::new();
5923 for reporter in reporters.values_mut() {
5924 let (mut latest, mut monitor) = reporter.subscribe().await;
5925 finalizers.push(context.child("finalizer").spawn(move |_| async move {
5926 while latest < target {
5927 latest = monitor.recv().await.expect("event missing");
5928 }
5929 }));
5930 }
5931 join_all(finalizers).await;
5932 info!(idx, ?validator, "validator recovered");
5933 }
5934
5935 let latest_complete = target.saturating_sub(activity_timeout);
5937 for reporter in reporters.values() {
5938 reporter.assert_no_faults();
5940
5941 reporter.assert_no_invalid();
5943
5944 let mut notarized = HashMap::new();
5946 let mut finalized = HashMap::new();
5947 {
5948 let notarizes = reporter.notarizes.lock();
5949 for view in View::range(View::new(1), latest_complete) {
5950 let Some(payloads) = notarizes.get(&view) else {
5952 continue;
5953 };
5954 if payloads.len() > 1 {
5955 panic!("view: {view}");
5956 }
5957 let (digest, _) = payloads.iter().next().unwrap();
5958 notarized.insert(view, *digest);
5959 }
5960 }
5961 {
5962 let notarizations = reporter.notarizations.lock();
5963 for view in View::range(View::new(1), latest_complete) {
5964 let Some(notarization) = notarizations.get(&view) else {
5966 continue;
5967 };
5968 let Some(digest) = notarized.get(&view) else {
5969 continue;
5970 };
5971 assert_eq!(¬arization.proposal.payload, digest);
5972 }
5973 }
5974 {
5975 let finalizes = reporter.finalizes.lock();
5976 for view in View::range(View::new(1), latest_complete) {
5977 let Some(payloads) = finalizes.get(&view) else {
5979 continue;
5980 };
5981 if payloads.len() > 1 {
5982 panic!("view: {view}");
5983 }
5984 let (digest, _) = payloads.iter().next().unwrap();
5985 finalized.insert(view, *digest);
5986
5987 if view > latest_complete {
5989 continue;
5990 }
5991
5992 let nullifies = reporter.nullifies.lock();
5994 let Some(nullifies) = nullifies.get(&view) else {
5995 continue;
5996 };
5997 for finalizers in payloads.values() {
5998 for finalizer in finalizers.iter() {
5999 if nullifies.contains(finalizer) {
6000 panic!("should not nullify and finalize at same view");
6001 }
6002 }
6003 }
6004 }
6005 }
6006 {
6007 let finalizations = reporter.finalizations.lock();
6008 for view in View::range(View::new(1), latest_complete) {
6009 let Some(finalization) = finalizations.get(&view) else {
6011 continue;
6012 };
6013 let Some(digest) = finalized.get(&view) else {
6014 continue;
6015 };
6016 assert_eq!(&finalization.proposal.payload, digest);
6017 }
6018 }
6019 }
6020
6021 let blocked = oracle.blocked().await.unwrap();
6023 assert!(blocked.is_empty());
6024
6025 context.auditor().state()
6027 })
6028 }
6029
6030 fn hailstorm<S, F, L>(fixture: F)
6033 where
6034 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
6035 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S> + Copy,
6036 L: Elector<S>,
6037 {
6038 assert_eq!(
6039 run_hailstorm::<_, _, L>(0, 10, ViewDelta::new(15), fixture),
6040 run_hailstorm::<_, _, L>(0, 10, ViewDelta::new(15), fixture),
6041 );
6042 }
6043
6044 test_for_all_fixtures!(hailstorm);
6045
6046 #[derive(Clone, Copy, Debug)]
6083 struct TwinsCampaign {
6084 n: u32,
6085 rounds: usize,
6086 mode: twins::Mode,
6087 max_cases: usize,
6088 trailing_finalizations: usize,
6089 }
6090
6091 fn twins_campaign<S, F, L>(
6092 rng: &mut impl CryptoRng,
6093 campaign: TwinsCampaign,
6094 link: Link,
6095 mut fixture: F,
6096 ) where
6097 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
6098 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
6099 L: Elector<S>,
6100 {
6101 let n = campaign.n;
6102 let faults = N3f1::max_faults(n) as usize;
6103 let cases = twins::cases(
6104 rng,
6105 twins::Framework {
6106 participants: n as usize,
6107 faults,
6108 rounds: campaign.rounds,
6109 mode: campaign.mode,
6110 max_cases: campaign.max_cases,
6111 },
6112 );
6113 assert!(
6114 !cases.is_empty(),
6115 "twins campaign should generate at least one case"
6116 );
6117 for case in cases {
6118 let scenario = case.scenario.clone();
6119 let twin_indices = case.compromised.clone();
6120 assert_eq!(
6121 twin_indices.len(),
6122 faults,
6123 "unexpected twins count for n={n} (expected f={faults})",
6124 );
6125
6126 let activity_timeout = ViewDelta::new(10);
6127 let skip_timeout = ViewDelta::new(5);
6128 let namespace = b"consensus".to_vec();
6129 let link = link.clone();
6130 let trailing_finalizations = campaign.trailing_finalizations;
6131 let mut case_fixture =
6132 |ctx: &mut deterministic::Context, ns: &[u8], n: u32| fixture(ctx, ns, n);
6133 let cfg = deterministic::Config::new().with_rng(Box::new(StdRng::from_rng(&mut *rng)));
6134 let executor = deterministic::Runner::new(cfg);
6135 executor.start(|mut context| async move {
6136 let Fixture {
6137 participants,
6138 schemes,
6139 ..
6140 } = case_fixture(&mut context, &namespace, n);
6141 let participants: Arc<[_]> = participants.into();
6142 let mut oracle = start_test_network_with_peers(
6143 context.child("network"),
6144 participants.iter().cloned(),
6145 false,
6146 )
6147 .await;
6148 let mut registrations = register_validators(&mut oracle, &participants).await;
6149 link_validators(&mut oracle, &participants, Action::Link(link), None).await;
6150
6151 let elector = TwinsElector::new(L::default(), &scenario, n as usize);
6152 let relay = Arc::new(mocks::relay::Relay::new());
6153 let mut reporters = Vec::new();
6154 let mut engine_handlers = Vec::new();
6155 let twin_index_set: HashSet<usize> = twin_indices.iter().copied().collect();
6156
6157 for idx in twin_indices.iter().copied() {
6159 let validator = &participants[idx];
6160 let (
6161 (vote_sender, vote_receiver),
6162 (certificate_sender, certificate_receiver),
6163 (_resolver_sender, _resolver_receiver),
6164 ) = registrations
6165 .remove(validator)
6166 .expect("validator should be registered");
6167
6168 let make_vote_forwarder = || {
6169 let participants = participants.clone();
6170 let scenario = scenario.clone();
6171 move |origin: SplitOrigin, _: &Recipients<_>, message: &IoBuf| {
6172 let msg: Vote<S, D> = Vote::decode(message.clone()).unwrap();
6173 let (primary, secondary) =
6174 scenario.partitions(msg.view(), participants.as_ref());
6175 match origin {
6176 SplitOrigin::Primary => Some(Recipients::Some(primary)),
6177 SplitOrigin::Secondary => Some(Recipients::Some(secondary)),
6178 }
6179 }
6180 };
6181 let make_certificate_forwarder = || {
6182 let codec = schemes[idx].certificate_codec_config();
6183 let participants = participants.clone();
6184 let scenario = scenario.clone();
6185 move |origin: SplitOrigin, _: &Recipients<_>, message: &IoBuf| {
6186 let msg: Certificate<S, D> =
6187 Certificate::decode_cfg(&mut message.as_ref(), &codec).unwrap();
6188 let (primary, secondary) =
6189 scenario.partitions(msg.view(), participants.as_ref());
6190 match origin {
6191 SplitOrigin::Primary => Some(Recipients::Some(primary)),
6192 SplitOrigin::Secondary => Some(Recipients::Some(secondary)),
6193 }
6194 }
6195 };
6196 let make_vote_router = || {
6197 let participants = participants.clone();
6198 let scenario = scenario.clone();
6199 move |(sender, message): &(_, IoBuf)| {
6200 let msg: Vote<S, D> = Vote::decode(message.clone()).unwrap();
6201 scenario.route(msg.view(), sender, participants.as_ref())
6202 }
6203 };
6204 let make_certificate_router = || {
6205 let codec = schemes[idx].certificate_codec_config();
6206 let participants = participants.clone();
6207 let scenario = scenario.clone();
6208 move |(sender, message): &(_, IoBuf)| {
6209 let msg: Certificate<S, D> =
6210 Certificate::decode_cfg(&mut message.as_ref(), &codec).unwrap();
6211 scenario.route(msg.view(), sender, participants.as_ref())
6212 }
6213 };
6214 let (vote_sender_primary, vote_sender_secondary) =
6215 vote_sender.split_with(make_vote_forwarder());
6216 let (vote_receiver_primary, vote_receiver_secondary) = vote_receiver
6217 .split_with(
6218 context.child("pending_split").with_attribute("index", idx),
6219 make_vote_router(),
6220 );
6221 let (certificate_sender_primary, certificate_sender_secondary) =
6222 certificate_sender.split_with(make_certificate_forwarder());
6223 let (certificate_receiver_primary, certificate_receiver_secondary) =
6224 certificate_receiver.split_with(
6225 context
6226 .child("recovered_split")
6227 .with_attribute("index", idx),
6228 make_certificate_router(),
6229 );
6230
6231 for (twin_label, pending, recovered) in [
6232 (
6233 "primary",
6234 (vote_sender_primary, vote_receiver_primary),
6235 (certificate_sender_primary, certificate_receiver_primary),
6236 ),
6237 (
6238 "secondary",
6239 (vote_sender_secondary, vote_receiver_secondary),
6240 (certificate_sender_secondary, certificate_receiver_secondary),
6241 ),
6242 ] {
6243 let partition = format!("twin_{idx}_{twin_label}");
6244 let context = context
6245 .child("twin")
6246 .with_attribute("index", idx)
6247 .with_attribute("side", twin_label);
6248
6249 let reporter_config = mocks::reporter::Config {
6250 participants: participants.as_ref().try_into().unwrap(),
6251 scheme: schemes[idx].clone(),
6252 elector: elector.clone(),
6253 };
6254 let reporter = mocks::reporter::Reporter::new(
6255 context.child("reporter"),
6256 reporter_config,
6257 );
6258 reporters.push(reporter.clone());
6259
6260 let application_cfg = mocks::application::Config {
6261 hasher: Sha256::default(),
6262 relay: relay.clone(),
6263 me: validator.clone(),
6264 propose_latency: (10.0, 5.0),
6265 verify_latency: (10.0, 5.0),
6266 certify_latency: (10.0, 5.0),
6267 should_certify: mocks::application::Certifier::Always,
6268 };
6269 let (actor, application) = mocks::application::Application::new(
6270 context.child("application"),
6271 application_cfg,
6272 );
6273 actor.start();
6274
6275 let blocker = oracle.control(validator.clone());
6276 let cfg = config::Config {
6277 scheme: schemes[idx].clone(),
6278 elector: elector.clone(),
6279 blocker,
6280 automaton: application.clone(),
6281 relay: application.clone(),
6282 reporter: reporter.clone(),
6283 strategy: Sequential,
6284 partition,
6285 mailbox_size: NZUsize!(1024),
6286 epoch: Epoch::new(333),
6287 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
6288 Epoch::new(333),
6289 )),
6290 leader_timeout: Duration::from_secs(1),
6291 certification_timeout: Duration::from_millis(1_500),
6292 timeout_retry: Duration::from_secs(10),
6293 fetch_timeout: Duration::from_secs(1),
6294 activity_timeout,
6295 skip_timeout,
6296 fetch_concurrent: NZUsize!(4),
6297 replay_buffer: NZUsize!(1024 * 1024),
6298 write_buffer: NZUsize!(1024 * 1024),
6299 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
6300 forwarding: ForwardingPolicy::Disabled,
6301 };
6302 let engine = Engine::new(context.child("engine"), cfg);
6303 engine_handlers.push(engine.start(
6304 pending,
6305 recovered,
6306 inert_channel(participants.as_ref()),
6307 ));
6308 }
6309 }
6310
6311 let honest_start = reporters.len();
6313 for (idx, validator) in participants.iter().enumerate() {
6314 if twin_index_set.contains(&idx) {
6315 continue;
6316 }
6317
6318 let partition = format!("honest_{idx}");
6319 let context = context.child("honest").with_attribute("index", idx);
6320
6321 let reporter_config = mocks::reporter::Config {
6322 participants: participants.as_ref().try_into().unwrap(),
6323 scheme: schemes[idx].clone(),
6324 elector: elector.clone(),
6325 };
6326 let reporter =
6327 mocks::reporter::Reporter::new(context.child("reporter"), reporter_config);
6328 reporters.push(reporter.clone());
6329
6330 let application_cfg = mocks::application::Config {
6331 hasher: Sha256::default(),
6332 relay: relay.clone(),
6333 me: validator.clone(),
6334 propose_latency: (10.0, 5.0),
6335 verify_latency: (10.0, 5.0),
6336 certify_latency: (10.0, 5.0),
6337 should_certify: mocks::application::Certifier::Always,
6338 };
6339 let (actor, application) = mocks::application::Application::new(
6340 context.child("application"),
6341 application_cfg,
6342 );
6343 actor.start();
6344
6345 let blocker = oracle.control(validator.clone());
6346 let cfg = config::Config {
6347 scheme: schemes[idx].clone(),
6348 elector: elector.clone(),
6349 blocker,
6350 automaton: application.clone(),
6351 relay: application.clone(),
6352 reporter: reporter.clone(),
6353 strategy: Sequential,
6354 partition,
6355 mailbox_size: NZUsize!(1024),
6356 epoch: Epoch::new(333),
6357 floor: config::Floor::Genesis(mocks::application::genesis::<Sha256>(
6358 Epoch::new(333),
6359 )),
6360 leader_timeout: Duration::from_secs(1),
6361 certification_timeout: Duration::from_millis(1_500),
6362 timeout_retry: Duration::from_secs(10),
6363 fetch_timeout: Duration::from_secs(1),
6364 activity_timeout,
6365 skip_timeout,
6366 fetch_concurrent: NZUsize!(4),
6367 replay_buffer: NZUsize!(1024 * 1024),
6368 write_buffer: NZUsize!(1024 * 1024),
6369 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
6370 forwarding: ForwardingPolicy::Disabled,
6371 };
6372 let engine = Engine::new(context.child("engine"), cfg);
6373
6374 let (
6375 (pending_sender, pending_receiver),
6376 (recovered_sender, recovered_receiver),
6377 _,
6378 ) = registrations
6379 .remove(validator)
6380 .expect("validator should be registered");
6381 engine_handlers.push(engine.start(
6382 (pending_sender, pending_receiver),
6383 (recovered_sender, recovered_receiver),
6384 inert_channel(participants.as_ref()),
6385 ));
6386 }
6387
6388 let prefix_end = View::new(scenario.rounds().len() as u64);
6398 let mut finalizers = Vec::new();
6399 for (i, reporter) in reporters.iter_mut().skip(honest_start).enumerate() {
6400 let (_latest, mut monitor) = reporter.subscribe().await;
6401 let required = trailing_finalizations;
6402 finalizers.push(context.child("finalizer").with_attribute("index", i).spawn(
6403 move |_| async move {
6404 let mut count = 0usize;
6405 while count < required {
6406 let view = monitor.recv().await.expect("event missing");
6407 if view > prefix_end {
6408 count += 1;
6409 }
6410 }
6411 },
6412 ));
6413 }
6414 join_all(finalizers).await;
6415
6416 let mut finalized_at_view: BTreeMap<View, D> = BTreeMap::new();
6418 for reporter in reporters.iter().skip(honest_start) {
6419 let finalizations = reporter.finalizations.lock();
6420 for (view, finalization) in finalizations.iter() {
6421 let digest = finalization.proposal.payload;
6422 if let Some(existing) = finalized_at_view.get(view) {
6423 assert_eq!(
6424 existing, &digest,
6425 "safety violation: conflicting finalizations at view {view}"
6426 );
6427 } else {
6428 finalized_at_view.insert(*view, digest);
6429 }
6430 }
6431 }
6432
6433 for reporter in reporters.iter().skip(honest_start) {
6435 reporter.assert_no_invalid();
6436 }
6437
6438 let twin_identities: HashSet<_> = twin_indices
6440 .iter()
6441 .map(|idx| participants[*idx].clone())
6442 .collect();
6443 let mut notarized_by_honest_signer: BTreeMap<View, HashMap<PublicKey, D>> =
6444 BTreeMap::new();
6445 let mut finalized_by_honest_signer: BTreeMap<View, HashMap<PublicKey, D>> =
6446 BTreeMap::new();
6447 for reporter in reporters.iter().skip(honest_start) {
6448 let notarizes = reporter.notarizes.lock();
6449 for (view, payloads) in notarizes.iter() {
6450 let signers = notarized_by_honest_signer.entry(*view).or_default();
6451 for (digest, payload_signers) in payloads.iter() {
6452 for signer in payload_signers.iter() {
6453 if twin_identities.contains(signer) {
6454 continue;
6455 }
6456 if let Some(existing) = signers.insert(signer.clone(), *digest) {
6457 assert_eq!(
6458 existing, *digest,
6459 "honest signer produced conflicting notarizes at view {view}"
6460 );
6461 }
6462 }
6463 }
6464 }
6465
6466 let finalizes = reporter.finalizes.lock();
6467 for (view, payloads) in finalizes.iter() {
6468 let signers = finalized_by_honest_signer.entry(*view).or_default();
6469 for (digest, payload_signers) in payloads.iter() {
6470 for signer in payload_signers.iter() {
6471 if twin_identities.contains(signer) {
6472 continue;
6473 }
6474 if let Some(existing) = signers.insert(signer.clone(), *digest) {
6475 assert_eq!(
6476 existing, *digest,
6477 "honest signer produced conflicting finalizes at view {view}"
6478 );
6479 }
6480 }
6481 }
6482 }
6483 }
6484
6485 for reporter in reporters.iter().skip(honest_start) {
6487 let faults = reporter.faults.lock();
6488 for faulter in faults.keys() {
6489 assert!(
6490 twin_identities.contains(faulter),
6491 "fault from non-twin participant"
6492 );
6493 }
6494 }
6495
6496 let blocked = oracle.blocked().await.unwrap();
6497 for (_, faulter) in blocked {
6498 assert!(
6499 twin_identities.contains(&faulter),
6500 "blocked peer attributed to non-twin participant"
6501 );
6502 }
6503 });
6504 }
6505 }
6506
6507 const TWINS_CAMPAIGN: TwinsCampaign = TwinsCampaign {
6508 n: 5,
6509 rounds: 3,
6510 mode: twins::Mode::Sampled,
6511 max_cases: 20,
6512 trailing_finalizations: 10,
6513 };
6514
6515 const TWINS_LINK: Link = Link {
6516 latency: Duration::from_millis(500),
6517 jitter: Duration::from_millis(500),
6518 success_rate: 1.0,
6519 };
6520
6521 #[test_group("slow")]
6522 #[test_traced("INFO")]
6523 fn test_twins_sampled() {
6524 for link in [
6525 Link {
6526 latency: Duration::from_millis(10),
6527 jitter: Duration::from_millis(10),
6528 success_rate: 1.0,
6529 },
6530 TWINS_LINK,
6531 ] {
6532 twins_campaign::<_, _, RoundRobin>(
6533 &mut test_rng(),
6534 TWINS_CAMPAIGN,
6535 link,
6536 scheme_mocks::fixture,
6537 );
6538 }
6539 }
6540
6541 #[test_group("slow")]
6542 #[test_traced("INFO")]
6543 fn test_twins_sustained() {
6544 let campaign = TwinsCampaign {
6545 mode: twins::Mode::Sustained,
6546 ..TWINS_CAMPAIGN
6547 };
6548 for link in [
6549 Link {
6550 latency: Duration::from_millis(10),
6551 jitter: Duration::from_millis(10),
6552 success_rate: 1.0,
6553 },
6554 TWINS_LINK,
6555 ] {
6556 twins_campaign::<_, _, RoundRobin>(
6557 &mut test_rng(),
6558 campaign,
6559 link,
6560 scheme_mocks::fixture,
6561 );
6562 }
6563 }
6564
6565 #[test_group("slow")]
6566 #[test_traced("INFO")]
6567 fn test_twins_large_sampled() {
6568 let campaign = TwinsCampaign {
6569 n: 10,
6570 rounds: 5,
6571 ..TWINS_CAMPAIGN
6572 };
6573 twins_campaign::<_, _, RoundRobin>(
6574 &mut test_rng(),
6575 campaign,
6576 TWINS_LINK,
6577 scheme_mocks::fixture,
6578 );
6579 }
6580
6581 #[test_group("slow")]
6582 #[test_traced("INFO")]
6583 fn test_twins_large_sustained() {
6584 let campaign = TwinsCampaign {
6585 n: 10,
6586 rounds: 5,
6587 mode: twins::Mode::Sustained,
6588 ..TWINS_CAMPAIGN
6589 };
6590 twins_campaign::<_, _, RoundRobin>(
6591 &mut test_rng(),
6592 campaign,
6593 TWINS_LINK,
6594 scheme_mocks::fixture,
6595 );
6596 }
6597
6598 fn twins<S, F, L>(fixture: F)
6599 where
6600 S: Scheme<Sha256Digest, PublicKey = PublicKey>,
6601 F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
6602 L: Elector<S>,
6603 {
6604 twins_campaign::<_, _, L>(&mut test_rng(), TWINS_CAMPAIGN, TWINS_LINK, fixture);
6605 }
6606
6607 test_for_all_fixtures!(twins, level = "INFO");
6608}