1pub mod scheme;
54pub mod types;
55
56cfg_if::cfg_if! {
57 if #[cfg(not(target_arch = "wasm32"))] {
58 mod ack_manager;
59 use ack_manager::AckManager;
60 mod config;
61 pub use config::Config;
62 mod engine;
63 pub use engine::Engine;
64 mod metrics;
65 mod tip_manager;
66 use tip_manager::TipManager;
67 }
68}
69
70#[cfg(test)]
71pub mod mocks;
72
73#[cfg(test)]
74mod tests {
75 use super::{
76 mocks,
77 types::{ChunkSigner, ChunkVerifier},
78 Config, Engine,
79 };
80 use crate::{
81 ordered_broadcast::scheme::{
82 bls12381_multisig, bls12381_threshold, ed25519, secp256r1, Scheme,
83 },
84 types::{Epoch, EpochDelta, Height, HeightDelta},
85 };
86 use commonware_cryptography::{
87 bls12381::primitives::variant::{MinPk, MinSig},
88 certificate::{self, mocks::Fixture},
89 ed25519::{PrivateKey, PublicKey},
90 sha256::Digest as Sha256Digest,
91 Signer as _,
92 };
93 use commonware_macros::{select, test_group, test_traced};
94 use commonware_p2p::simulated::{Link, Network, Oracle, Receiver, Sender};
95 use commonware_parallel::Sequential;
96 use commonware_runtime::{
97 buffer::paged::CacheRef,
98 deterministic::{self, Context},
99 Clock, Quota, Runner, Spawner, Supervisor as _,
100 };
101 use commonware_utils::{
102 channel::{fallible::OneshotExt, oneshot},
103 NZUsize, NZU16, NZU64,
104 };
105 use futures::future::join_all;
106 use std::{
107 collections::{BTreeMap, HashMap},
108 num::{NonZeroU16, NonZeroU32, NonZeroUsize},
109 time::Duration,
110 };
111 use tracing::debug;
112
113 macro_rules! for_each_fixture {
115 ($cb:ident!($($args:tt)*)) => {
116 $cb!($($args)*, bls12381_threshold_min_pk, bls12381_threshold::fixture::<MinPk, _>);
117 $cb!($($args)*, bls12381_threshold_min_sig, bls12381_threshold::fixture::<MinSig, _>);
118 $cb!($($args)*, bls12381_multisig_min_pk, bls12381_multisig::fixture::<MinPk, _>);
119 $cb!($($args)*, bls12381_multisig_min_sig, bls12381_multisig::fixture::<MinSig, _>);
120 $cb!($($args)*, ed25519, ed25519::fixture);
121 $cb!($($args)*, secp256r1, secp256r1::fixture);
122 };
123 }
124
125 macro_rules! test_for_all_fixtures {
128 ($callee:ident) => {
129 for_each_fixture!(test_for_all_fixtures!(@emit $callee));
130 };
131 (@emit $callee:ident, $suffix:ident, $fixture:expr) => {
132 paste::paste! {
133 #[test_group("slow")]
134 #[test_traced]
135 fn [<test_ $callee _ $suffix>]() {
136 $callee($fixture);
137 }
138 }
139 };
140 }
141
142 const PAGE_SIZE: NonZeroU16 = NZU16!(1024);
143 const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(10);
144 const TEST_QUOTA: Quota = Quota::per_second(NonZeroU32::MAX);
145 const TEST_NAMESPACE: &[u8] = b"ordered_broadcast_test";
146
147 type Registrations<P> = BTreeMap<
148 P,
149 (
150 (Sender<P, deterministic::Context>, Receiver<P>),
151 (Sender<P, deterministic::Context>, Receiver<P>),
152 ),
153 >;
154
155 async fn register_participants(
156 oracle: &mut Oracle<PublicKey, deterministic::Context>,
157 participants: &[PublicKey],
158 ) -> Registrations<PublicKey> {
159 let mut registrations = BTreeMap::new();
160 for participant in participants.iter() {
161 let control = oracle.control(participant.clone());
162 let (a1, a2) = control.register(0, TEST_QUOTA).await.unwrap();
163 let (b1, b2) = control.register(1, TEST_QUOTA).await.unwrap();
164 registrations.insert(participant.clone(), ((a1, a2), (b1, b2)));
165 }
166 registrations
167 }
168
169 enum Action {
170 Link(Link),
171 Update(Link),
172 Unlink,
173 }
174
175 async fn link_participants(
176 oracle: &mut Oracle<PublicKey, deterministic::Context>,
177 participants: &[PublicKey],
178 action: Action,
179 restrict_to: Option<fn(usize, usize, usize) -> bool>,
180 ) {
181 for (i1, v1) in participants.iter().enumerate() {
182 for (i2, v2) in participants.iter().enumerate() {
183 if v2 == v1 {
184 continue;
185 }
186 if let Some(f) = restrict_to {
187 if !f(participants.len(), i1, i2) {
188 continue;
189 }
190 }
191 if matches!(action, Action::Update(_) | Action::Unlink) {
192 oracle.remove_link(v1.clone(), v2.clone()).await.unwrap();
193 }
194 if let Action::Link(ref link) | Action::Update(ref link) = action {
195 oracle
196 .add_link(v1.clone(), v2.clone(), link.clone())
197 .await
198 .unwrap();
199 }
200 }
201 }
202 }
203
204 const RELIABLE_LINK: Link = Link {
205 latency: Duration::from_millis(10),
206 jitter: Duration::from_millis(1),
207 success_rate: 1.0,
208 };
209
210 async fn initialize_simulation<S: certificate::Scheme>(
211 context: Context,
212 fixture: &Fixture<S>,
213 link: Link,
214 ) -> (
215 Oracle<PublicKey, deterministic::Context>,
216 Registrations<PublicKey>,
217 ) {
218 let (network, mut oracle) = Network::new_with_peers(
219 context.child("network"),
220 commonware_p2p::simulated::Config {
221 max_size: 1024 * 1024,
222 disconnect_on_block: true,
223 tracked_peer_sets: NZUsize!(1),
224 },
225 fixture.participants.clone(),
226 )
227 .await;
228 network.start();
229
230 let registrations = register_participants(&mut oracle, &fixture.participants).await;
231 link_participants(&mut oracle, &fixture.participants, Action::Link(link), None).await;
232 (oracle, registrations)
233 }
234
235 #[allow(clippy::too_many_arguments)]
236 fn spawn_validator_engines<S>(
237 context: Context,
238 fixture: &Fixture<S>,
239 sequencer_pks: &[PublicKey],
240 registrations: &mut Registrations<PublicKey>,
241 rebroadcast_timeout: Duration,
242 invalid_when: fn(Height) -> bool,
243 misses_allowed: Option<usize>,
244 epoch: Epoch,
245 ) -> BTreeMap<PublicKey, mocks::ReporterMailbox<PublicKey, S, Sha256Digest>>
246 where
247 S: Scheme<PublicKey, Sha256Digest>,
248 {
249 let mut reporters = BTreeMap::new();
250 let namespace = b"my testing namespace";
251
252 for (idx, validator) in fixture.participants.iter().enumerate() {
253 let context = context
254 .child("validator")
255 .with_attribute("public_key", validator);
256 let monitor = mocks::Monitor::new(epoch);
257 let sequencers = mocks::Sequencers::<PublicKey>::new(sequencer_pks.to_vec());
258
259 let validators_provider = mocks::Provider::new();
261 assert!(validators_provider.register(epoch, fixture.schemes[idx].clone()));
262
263 let automaton = mocks::Automaton::<PublicKey>::new(invalid_when);
264 let chunk_verifier = ChunkVerifier::new(namespace);
265 let (reporter, reporter_mailbox) = mocks::Reporter::new(
266 context.child("reporter"),
267 chunk_verifier.clone(),
268 fixture.verifier.clone(),
269 misses_allowed,
270 );
271 reporter.start();
272 reporters.insert(validator.clone(), reporter_mailbox);
273
274 let engine = Engine::new(
275 context.child("engine"),
276 Config {
277 sequencer_signer: Some(ChunkSigner::new(
278 namespace,
279 fixture.private_keys[idx].clone(),
280 )),
281 chunk_verifier,
282 sequencers_provider: sequencers,
283 validators_provider,
284 automaton: automaton.clone(),
285 relay: automaton.clone(),
286 reporter: reporters.get(validator).unwrap().clone(),
287 monitor,
288 priority_proposals: false,
289 priority_acks: false,
290 rebroadcast_timeout,
291 epoch_bounds: (EpochDelta::new(1), EpochDelta::new(1)),
292 height_bound: HeightDelta::new(2),
293 journal_heights_per_section: NZU64!(10),
294 journal_replay_buffer: NZUsize!(4096),
295 journal_write_buffer: NZUsize!(4096),
296 journal_name_prefix: format!("ordered-broadcast-seq-{validator}-"),
297 journal_compression: Some(3),
298 journal_page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
299 strategy: Sequential,
300 },
301 );
302
303 let ((a1, a2), (b1, b2)) = registrations.remove(validator).unwrap();
304 engine.start((a1, a2), (b1, b2));
305 }
306 reporters
307 }
308
309 async fn await_reporters<S>(
310 context: Context,
311 sequencers: Vec<PublicKey>,
312 reporters: &BTreeMap<PublicKey, mocks::ReporterMailbox<PublicKey, S, Sha256Digest>>,
313 threshold: (Height, Epoch, bool),
314 ) where
315 S: certificate::Scheme,
316 {
317 let (threshold_height, threshold_epoch, require_contiguous) =
318 (threshold.0, threshold.1, threshold.2);
319 let mut receivers = Vec::new();
320 for (reporter, mailbox) in reporters.iter() {
321 for sequencer in sequencers.iter() {
323 let (tx, rx) = oneshot::channel();
325 receivers.push(rx);
326
327 context.child("reporter_watcher").spawn({
328 let reporter = reporter.clone();
329 let sequencer = sequencer.clone();
330 let mut mailbox = mailbox.clone();
331 move |context| async move {
332 loop {
333 let (height, epoch) = mailbox
334 .get_tip(sequencer.clone())
335 .await
336 .unwrap_or((Height::zero(), Epoch::zero()));
337 debug!(height = %height, epoch = %epoch, ?sequencer, ?reporter, "reporter");
338 let contiguous_height = mailbox
339 .get_contiguous_tip(sequencer.clone())
340 .await
341 .unwrap_or(Height::zero());
342 if height >= threshold_height
343 && epoch >= threshold_epoch
344 && (!require_contiguous || contiguous_height >= threshold_height)
345 {
346 tx.send_lossy(sequencer.clone());
347 break;
348 }
349 context.sleep(Duration::from_millis(100)).await;
350 }
351 }
352 });
353 }
354 }
355
356 let results = join_all(receivers).await;
358 assert_eq!(results.len(), sequencers.len() * reporters.len());
359
360 for result in results {
362 assert!(result.is_ok(), "reporter was cancelled");
363 }
364 }
365
366 async fn get_max_height<S: certificate::Scheme>(
367 reporters: &mut BTreeMap<PublicKey, mocks::ReporterMailbox<PublicKey, S, Sha256Digest>>,
368 ) -> Height {
369 let mut max_height = Height::zero();
370 for (sequencer, mailbox) in reporters.iter_mut() {
371 let (height, _) = mailbox
372 .get_tip(sequencer.clone())
373 .await
374 .unwrap_or((Height::zero(), Epoch::zero()));
375 if height > max_height {
376 max_height = height;
377 }
378 }
379 max_height
380 }
381
382 fn all_online<S, F>(fixture: F)
383 where
384 S: Scheme<PublicKey, Sha256Digest>,
385 F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
386 {
387 let runner = deterministic::Runner::timed(Duration::from_secs(120));
388
389 runner.start(|mut context| async move {
390 let epoch = Epoch::new(111);
391 let num_validators = 4;
392 let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators);
393
394 let (_oracle, mut registrations) =
395 initialize_simulation(context.child("simulation"), &fixture, RELIABLE_LINK).await;
396
397 let reporters = spawn_validator_engines(
398 context.child("validators"),
399 &fixture,
400 &fixture.participants,
401 &mut registrations,
402 Duration::from_secs(5),
403 |_| false,
404 Some(5),
405 epoch,
406 );
407
408 await_reporters(
409 context.child("reporter"),
410 reporters.keys().cloned().collect::<Vec<_>>(),
411 &reporters,
412 (Height::new(100), epoch, true),
413 )
414 .await;
415 });
416 }
417
418 test_for_all_fixtures!(all_online);
419
420 fn unclean_shutdown<S, F>(fixture: F)
421 where
422 S: Scheme<PublicKey, Sha256Digest>,
423 F: Fn(&mut deterministic::Context, &[u8], u32) -> Fixture<S> + Clone,
424 {
425 let mut prev_checkpoint = None;
426 let epoch = Epoch::new(111);
427 let num_validators = 4;
428 let crash_after = Duration::from_secs(5);
429 let target_height = Height::new(30);
430
431 loop {
432 let fixture = fixture.clone();
433 let f = |mut context: deterministic::Context| async move {
434 let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators);
435
436 let (network, mut oracle) = Network::new_with_peers(
437 context.child("network"),
438 commonware_p2p::simulated::Config {
439 max_size: 1024 * 1024,
440 disconnect_on_block: true,
441 tracked_peer_sets: NZUsize!(1),
442 },
443 fixture.participants.clone(),
444 )
445 .await;
446 network.start();
447
448 let mut registrations =
449 register_participants(&mut oracle, &fixture.participants).await;
450 link_participants(
451 &mut oracle,
452 &fixture.participants,
453 Action::Link(RELIABLE_LINK),
454 None,
455 )
456 .await;
457
458 let reporters = spawn_validator_engines(
459 context.child("validator"),
460 &fixture,
461 &fixture.participants,
462 &mut registrations,
463 Duration::from_secs(5),
464 |_| false,
465 None,
466 epoch,
467 );
468
469 let crash = context.sleep(crash_after);
471 let run = await_reporters(
472 context.child("reporter"),
473 reporters.keys().cloned().collect::<Vec<_>>(),
474 &reporters,
475 (target_height, epoch, true),
476 );
477
478 select! {
479 _ = crash => false,
480 _ = run => true,
481 }
482 };
483
484 let (complete, checkpoint) = prev_checkpoint
485 .map_or_else(
486 || deterministic::Runner::timed(Duration::from_secs(180)),
487 deterministic::Runner::from,
488 )
489 .start_and_recover(f);
490
491 if complete {
492 break;
493 }
494
495 prev_checkpoint = Some(checkpoint);
496 }
497 }
498
499 test_for_all_fixtures!(unclean_shutdown);
500
501 fn network_partition<S, F>(fixture: F)
502 where
503 S: Scheme<PublicKey, Sha256Digest>,
504 F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
505 {
506 let runner = deterministic::Runner::timed(Duration::from_secs(60));
507
508 runner.start(|mut context| async move {
509 let epoch = Epoch::new(111);
510 let num_validators = 4;
511 let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators);
512
513 let (mut oracle, mut registrations) =
515 initialize_simulation(context.child("simulation"), &fixture, RELIABLE_LINK).await;
516 let mut reporters = spawn_validator_engines(
517 context.child("validators"),
518 &fixture,
519 &fixture.participants,
520 &mut registrations,
521 Duration::from_secs(1),
522 |_| false,
523 None,
524 epoch,
525 );
526
527 link_participants(&mut oracle, &fixture.participants, Action::Unlink, None).await;
529 context.sleep(Duration::from_secs(30)).await;
530
531 let max_height = get_max_height(&mut reporters).await;
533
534 link_participants(
536 &mut oracle,
537 &fixture.participants,
538 Action::Link(RELIABLE_LINK),
539 None,
540 )
541 .await;
542 await_reporters(
543 context.child("reporter"),
544 reporters.keys().cloned().collect::<Vec<_>>(),
545 &reporters,
546 (
547 max_height.saturating_add(HeightDelta::new(100)),
548 epoch,
549 false,
550 ),
551 )
552 .await;
553 });
554 }
555
556 test_for_all_fixtures!(network_partition);
557
558 fn slow_and_lossy_links_seeded<S, F>(fixture: F, seed: u64) -> String
559 where
560 S: Scheme<PublicKey, Sha256Digest>,
561 F: Fn(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
562 {
563 let cfg = deterministic::Config::new()
564 .with_seed(seed)
565 .with_timeout(Some(Duration::from_secs(40)));
566 let runner = deterministic::Runner::new(cfg);
567
568 runner.start(|mut context| async move {
569 let epoch = Epoch::new(111);
570 let num_validators = 4;
571 let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators);
572
573 let (mut oracle, mut registrations) =
574 initialize_simulation(context.child("simulation"), &fixture, RELIABLE_LINK).await;
575 let delayed_link = Link {
576 latency: Duration::from_millis(50),
577 jitter: Duration::from_millis(40),
578 success_rate: 0.5,
579 };
580 link_participants(
581 &mut oracle,
582 &fixture.participants,
583 Action::Update(delayed_link),
584 None,
585 )
586 .await;
587
588 let reporters = spawn_validator_engines(
589 context.child("validators"),
590 &fixture,
591 &fixture.participants,
592 &mut registrations,
593 Duration::from_millis(150),
594 |_| false,
595 None,
596 epoch,
597 );
598
599 await_reporters(
600 context.child("reporter"),
601 reporters.keys().cloned().collect::<Vec<_>>(),
602 &reporters,
603 (Height::new(40), epoch, false),
604 )
605 .await;
606
607 context.auditor().state()
608 })
609 }
610
611 fn slow_and_lossy_links<S, F>(fixture: F)
612 where
613 S: Scheme<PublicKey, Sha256Digest>,
614 F: Fn(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
615 {
616 slow_and_lossy_links_seeded(fixture, 0);
617 }
618
619 test_for_all_fixtures!(slow_and_lossy_links);
620
621 fn determinism<S, F>(fixture: F)
622 where
623 S: Scheme<PublicKey, Sha256Digest>,
624 F: Fn(&mut deterministic::Context, &[u8], u32) -> Fixture<S> + Copy,
625 {
626 for seed in 1..6 {
629 assert_eq!(
630 slow_and_lossy_links_seeded(fixture, seed),
631 slow_and_lossy_links_seeded(fixture, seed),
632 );
633 }
634 }
635
636 test_for_all_fixtures!(determinism);
637
638 #[test_group("slow")]
639 #[test_traced]
640 fn test_distinct_states() {
641 macro_rules! collect {
643 ($vec:ident, $suffix:ident, $fixture:expr) => {
644 $vec.push((
645 stringify!($suffix),
646 slow_and_lossy_links_seeded($fixture, 7),
647 ));
648 };
649 }
650 let mut states = Vec::new();
651 for_each_fixture!(collect!(states));
652 for pair in states.windows(2) {
653 assert_ne!(
654 pair[0].1, pair[1].1,
655 "state {} equals state {}",
656 pair[0].0, pair[1].0
657 );
658 }
659 }
660
661 fn invalid_signature_injection<S, F>(fixture: F)
662 where
663 S: Scheme<PublicKey, Sha256Digest>,
664 F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
665 {
666 let runner = deterministic::Runner::timed(Duration::from_secs(30));
667
668 runner.start(|mut context| async move {
669 let epoch = Epoch::new(111);
670 let num_validators = 4;
671 let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators);
672
673 let (_oracle, mut registrations) =
674 initialize_simulation(context.child("simulation"), &fixture, RELIABLE_LINK).await;
675
676 let reporters = spawn_validator_engines(
677 context.child("validators"),
678 &fixture,
679 &fixture.participants,
680 &mut registrations,
681 Duration::from_secs(5),
682 |i| i.get() % 10 == 0,
683 None,
684 epoch,
685 );
686
687 await_reporters(
688 context.child("reporter"),
689 reporters.keys().cloned().collect::<Vec<_>>(),
690 &reporters,
691 (Height::new(100), epoch, true),
692 )
693 .await;
694 });
695 }
696
697 test_for_all_fixtures!(invalid_signature_injection);
698
699 fn updated_epoch<S, F>(fixture: F)
700 where
701 S: Scheme<PublicKey, Sha256Digest>,
702 F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
703 {
704 let runner = deterministic::Runner::timed(Duration::from_secs(60));
705
706 runner.start(|mut context| async move {
707 let epoch = Epoch::new(111);
708 let num_validators = 4;
709 let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators);
710
711 let (mut oracle, mut registrations) =
713 initialize_simulation(context.child("simulation"), &fixture, RELIABLE_LINK).await;
714
715 let mut reporters = BTreeMap::new();
716
717 let mut validators_providers = HashMap::new();
719 let mut monitors = HashMap::new();
720 let namespace = b"my testing namespace";
721
722 for (idx, validator) in fixture.participants.iter().enumerate() {
723 let context = context
724 .child("validator")
725 .with_attribute("public_key", validator);
726 let monitor = mocks::Monitor::new(epoch);
727 monitors.insert(validator.clone(), monitor.clone());
728 let sequencers = mocks::Sequencers::<PublicKey>::new(fixture.participants.clone());
729
730 let validators_provider = mocks::Provider::new();
732 assert!(validators_provider.register(epoch, fixture.schemes[idx].clone()));
733 validators_providers.insert(validator.clone(), validators_provider.clone());
734
735 let automaton = mocks::Automaton::<PublicKey>::new(|_| false);
736 let chunk_verifier = ChunkVerifier::new(namespace);
737 let (reporter, reporter_mailbox) = mocks::Reporter::new(
738 context.child("reporter"),
739 chunk_verifier.clone(),
740 fixture.verifier.clone(),
741 Some(5),
742 );
743 reporter.start();
744 reporters.insert(validator.clone(), reporter_mailbox);
745
746 let engine = Engine::new(
747 context.child("engine"),
748 Config {
749 sequencer_signer: Some(ChunkSigner::new(
750 namespace,
751 fixture.private_keys[idx].clone(),
752 )),
753 chunk_verifier,
754 sequencers_provider: sequencers,
755 validators_provider,
756 relay: automaton.clone(),
757 automaton: automaton.clone(),
758 reporter: reporters.get(validator).unwrap().clone(),
759 monitor,
760 epoch_bounds: (EpochDelta::new(1), EpochDelta::new(1)),
761 height_bound: HeightDelta::new(2),
762 rebroadcast_timeout: Duration::from_secs(1),
763 priority_acks: false,
764 priority_proposals: false,
765 journal_heights_per_section: NZU64!(10),
766 journal_replay_buffer: NZUsize!(4096),
767 journal_write_buffer: NZUsize!(4096),
768 journal_name_prefix: format!("ordered-broadcast-seq-{validator}-"),
769 journal_compression: Some(3),
770 journal_page_cache: CacheRef::from_pooler(
771 &context,
772 PAGE_SIZE,
773 PAGE_CACHE_SIZE,
774 ),
775 strategy: Sequential,
776 },
777 );
778
779 let ((a1, a2), (b1, b2)) = registrations.remove(validator).unwrap();
780 engine.start((a1, a2), (b1, b2));
781 }
782
783 await_reporters(
785 context.child("reporter"),
786 reporters.keys().cloned().collect::<Vec<_>>(),
787 &reporters,
788 (Height::new(100), epoch, true),
789 )
790 .await;
791
792 link_participants(&mut oracle, &fixture.participants, Action::Unlink, None).await;
794 context.sleep(Duration::from_secs(30)).await;
795
796 let max_height = get_max_height(&mut reporters).await;
798
799 let next_epoch = epoch.next();
801 for (validator, monitor) in monitors.iter() {
802 monitor.update(next_epoch);
803 let idx = fixture
805 .participants
806 .iter()
807 .position(|v| v == validator)
808 .unwrap();
809 let validators_provider = validators_providers.get(validator).unwrap();
810 assert!(validators_provider.register(next_epoch, fixture.schemes[idx].clone()));
811 }
812
813 link_participants(
815 &mut oracle,
816 &fixture.participants,
817 Action::Link(RELIABLE_LINK),
818 None,
819 )
820 .await;
821 await_reporters(
822 context.child("reporter"),
823 reporters.keys().cloned().collect::<Vec<_>>(),
824 &reporters,
825 (
826 max_height.saturating_add(HeightDelta::new(100)),
827 next_epoch,
828 true,
829 ),
830 )
831 .await;
832 });
833 }
834
835 test_for_all_fixtures!(updated_epoch);
836
837 fn external_sequencer<S, F>(fixture: F)
838 where
839 S: Scheme<PublicKey, Sha256Digest>,
840 F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
841 {
842 let runner = deterministic::Runner::timed(Duration::from_secs(60));
843 runner.start(|mut context| async move {
844 let epoch = Epoch::new(111);
845 let num_validators = 4;
846 let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators);
847
848 let sequencer = PrivateKey::from_seed(u64::MAX);
850
851 let mut participants = fixture.participants.clone();
853 participants.push(sequencer.public_key());
854
855 let (network, mut oracle) = Network::new_with_peers(
857 context.child("network"),
858 commonware_p2p::simulated::Config {
859 max_size: 1024 * 1024,
860 disconnect_on_block: true,
861 tracked_peer_sets: NZUsize!(1),
862 },
863 participants.clone(),
864 )
865 .await;
866 network.start();
867
868 let mut registrations = register_participants(&mut oracle, &participants).await;
870 link_participants(
871 &mut oracle,
872 &participants,
873 Action::Link(RELIABLE_LINK),
874 None,
875 )
876 .await;
877
878 let mut reporters = BTreeMap::new();
880 let namespace = b"my testing namespace";
881
882 for (idx, validator) in fixture.participants.iter().enumerate() {
884 let context = context
885 .child("validator")
886 .with_attribute("public_key", validator);
887 let monitor = mocks::Monitor::new(epoch);
888 let sequencers = mocks::Sequencers::<PublicKey>::new(vec![sequencer.public_key()]);
889
890 let validators_provider = mocks::Provider::new();
892 assert!(validators_provider.register(epoch, fixture.schemes[idx].clone()));
893
894 let automaton = mocks::Automaton::<PublicKey>::new(|_| false);
895
896 let chunk_verifier = ChunkVerifier::new(namespace);
897 let (reporter, reporter_mailbox) = mocks::Reporter::new(
898 context.child("reporter"),
899 chunk_verifier.clone(),
900 fixture.verifier.clone(),
901 Some(5),
902 );
903 reporter.start();
904 reporters.insert(validator.clone(), reporter_mailbox);
905
906 let engine = Engine::new(
907 context.child("engine"),
908 Config {
909 sequencer_signer: None::<ChunkSigner<PrivateKey>>, chunk_verifier,
911 sequencers_provider: sequencers,
912 validators_provider,
913 relay: automaton.clone(),
914 automaton: automaton.clone(),
915 reporter: reporters.get(validator).unwrap().clone(),
916 monitor,
917 epoch_bounds: (EpochDelta::new(1), EpochDelta::new(1)),
918 height_bound: HeightDelta::new(2),
919 rebroadcast_timeout: Duration::from_secs(5),
920 priority_acks: false,
921 priority_proposals: false,
922 journal_heights_per_section: NZU64!(10),
923 journal_replay_buffer: NZUsize!(4096),
924 journal_write_buffer: NZUsize!(4096),
925 journal_name_prefix: format!("ordered-broadcast-seq-{validator}-"),
926 journal_compression: Some(3),
927 journal_page_cache: CacheRef::from_pooler(
928 &context,
929 PAGE_SIZE,
930 PAGE_CACHE_SIZE,
931 ),
932 strategy: Sequential,
933 },
934 );
935
936 let ((a1, a2), (b1, b2)) = registrations.remove(validator).unwrap();
937 engine.start((a1, a2), (b1, b2));
938 }
939
940 {
942 let context = context.child("sequencer");
943 let automaton = mocks::Automaton::<PublicKey>::new(|_| false);
944 let chunk_verifier = ChunkVerifier::new(namespace);
945 let (reporter, reporter_mailbox) = mocks::Reporter::new(
946 context.child("reporter"),
947 chunk_verifier.clone(),
948 fixture.verifier.clone(),
949 Some(5),
950 );
951 reporter.start();
952 reporters.insert(sequencer.public_key(), reporter_mailbox);
953
954 let validators_provider = mocks::Provider::new();
957 assert!(validators_provider.register(epoch, fixture.verifier.clone()));
958
959 let engine = Engine::new(
960 context.child("engine"),
961 Config {
962 sequencer_signer: Some(ChunkSigner::new(namespace, sequencer.clone())),
963 chunk_verifier,
964 sequencers_provider: mocks::Sequencers::<PublicKey>::new(vec![
965 sequencer.public_key()
966 ]),
967 validators_provider,
968 relay: automaton.clone(),
969 automaton,
970 reporter: reporters.get(&sequencer.public_key()).unwrap().clone(),
971 monitor: mocks::Monitor::new(epoch),
972 epoch_bounds: (EpochDelta::new(1), EpochDelta::new(1)),
973 height_bound: HeightDelta::new(2),
974 rebroadcast_timeout: Duration::from_secs(5),
975 priority_acks: false,
976 priority_proposals: false,
977 journal_heights_per_section: NZU64!(10),
978 journal_replay_buffer: NZUsize!(4096),
979 journal_write_buffer: NZUsize!(4096),
980 journal_name_prefix: format!(
981 "ordered-broadcast-seq-{}-",
982 sequencer.public_key()
983 ),
984 journal_compression: Some(3),
985 journal_page_cache: CacheRef::from_pooler(
986 &context,
987 PAGE_SIZE,
988 PAGE_CACHE_SIZE,
989 ),
990 strategy: Sequential,
991 },
992 );
993
994 let ((a1, a2), (b1, b2)) = registrations.remove(&sequencer.public_key()).unwrap();
995 engine.start((a1, a2), (b1, b2));
996 }
997
998 await_reporters(
1000 context.child("reporter"),
1001 vec![sequencer.public_key()],
1002 &reporters,
1003 (Height::new(100), epoch, true),
1004 )
1005 .await;
1006 });
1007 }
1008
1009 test_for_all_fixtures!(external_sequencer);
1010
1011 fn run_1k<S, F>(fixture: F)
1012 where
1013 S: Scheme<PublicKey, Sha256Digest>,
1014 F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture<S>,
1015 {
1016 let cfg = deterministic::Config::new();
1017 let runner = deterministic::Runner::new(cfg);
1018
1019 runner.start(|mut context| async move {
1020 let epoch = Epoch::new(111);
1021 let num_validators = 10;
1022 let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators);
1023
1024 let delayed_link = Link {
1025 latency: Duration::from_millis(80),
1026 jitter: Duration::from_millis(10),
1027 success_rate: 0.98,
1028 };
1029
1030 let (mut oracle, mut registrations) =
1031 initialize_simulation(context.child("simulation"), &fixture, RELIABLE_LINK).await;
1032
1033 link_participants(
1035 &mut oracle,
1036 &fixture.participants,
1037 Action::Update(delayed_link),
1038 None,
1039 )
1040 .await;
1041
1042 let sequencers: Vec<PublicKey> =
1044 fixture.participants[0..num_validators as usize / 2].to_vec();
1045
1046 let reporters = spawn_validator_engines(
1047 context.child("validators"),
1048 &fixture,
1049 &sequencers,
1050 &mut registrations,
1051 Duration::from_millis(150),
1052 |_| false,
1053 None,
1054 epoch,
1055 );
1056
1057 await_reporters(
1058 context.child("reporter"),
1059 sequencers,
1060 &reporters,
1061 (Height::new(1_000), epoch, false),
1062 )
1063 .await;
1064 })
1065 }
1066
1067 #[test_group("slow")]
1068 #[test_traced]
1069 fn test_1k() {
1070 run_1k(mocks::scheme::fixture);
1071 }
1072}