1mod config;
26pub use config::Config;
27mod engine;
28pub use engine::Engine;
29mod ingress;
30pub use ingress::Mailbox;
31pub(crate) use ingress::Message;
32mod metrics;
33
34#[cfg(test)]
35pub mod mocks;
36
37#[cfg(test)]
38mod tests {
39 use super::{mocks::TestMessage, *};
40 use crate::Broadcaster;
41 use commonware_actor::{
42 mailbox::{Overflow, Policy},
43 Feedback,
44 };
45 use commonware_codec::RangeCfg;
46 use commonware_cryptography::{
47 ed25519::{PrivateKey, PublicKey},
48 Digestible, Hasher, Sha256, Signer as _,
49 };
50 use commonware_macros::test_traced;
51 use commonware_p2p::{
52 simulated::{Link, Network, Oracle, Receiver, Sender},
53 Manager as _, Recipients, Sender as _, TrackedPeers,
54 };
55 use commonware_runtime::{
56 deterministic, telemetry::metrics::count_running_tasks, Clock, Error, IoBuf, Metrics as _,
57 Quota, Runner, Supervisor as _,
58 };
59 use commonware_utils::NZUsize;
60 use std::{
61 collections::{BTreeMap, VecDeque},
62 num::NonZeroU32,
63 time::Duration,
64 };
65
66 const CACHE_SIZE: usize = 10;
68
69 const A_JIFFY: Duration = Duration::from_millis(10);
72
73 const NETWORK_SPEED: Duration = Duration::from_millis(100);
75
76 const NETWORK_SPEED_WITH_BUFFER: Duration = Duration::from_millis(200);
78
79 const TEST_QUOTA: Quota = Quota::per_second(NonZeroU32::MAX);
81
82 type Registrations = BTreeMap<
83 PublicKey,
84 (
85 Sender<PublicKey, deterministic::Context>,
86 Receiver<PublicKey>,
87 ),
88 >;
89
90 async fn initialize_simulation(
91 context: deterministic::Context,
92 num_peers: u32,
93 success_rate: f64,
94 ) -> (
95 Vec<PublicKey>,
96 Registrations,
97 Oracle<PublicKey, deterministic::Context>,
98 ) {
99 let (network, oracle) = Network::<deterministic::Context, PublicKey>::new(
100 context,
101 commonware_p2p::simulated::Config {
102 max_size: 1024 * 1024,
103 disconnect_on_block: true,
104 tracked_peer_sets: NZUsize!(1),
105 },
106 );
107 network.start();
108
109 let mut schemes = (0..num_peers)
110 .map(|i| PrivateKey::from_seed(i as u64))
111 .collect::<Vec<_>>();
112 schemes.sort_by_key(|s| s.public_key());
113 let peers: Vec<PublicKey> = schemes.iter().map(|c| c.public_key()).collect();
114
115 let mut registrations: Registrations = BTreeMap::new();
116 for peer in peers.iter() {
117 let (sender, receiver) = oracle
118 .control(peer.clone())
119 .register(0, TEST_QUOTA)
120 .await
121 .unwrap();
122 registrations.insert(peer.clone(), (sender, receiver));
123 }
124
125 let link = Link {
127 latency: NETWORK_SPEED,
128 jitter: Duration::ZERO,
129 success_rate,
130 };
131 for p1 in peers.iter() {
132 for p2 in peers.iter() {
133 if p2 == p1 {
134 continue;
135 }
136 oracle
137 .add_link(p1.clone(), p2.clone(), link.clone())
138 .await
139 .unwrap();
140 }
141 }
142
143 let all_peers = commonware_utils::ordered::Set::from_iter_dedup(peers.clone());
145 oracle.manager().track(0, all_peers);
146
147 (peers, registrations, oracle)
148 }
149
150 #[test]
151 fn policy_handles_closed_responders() {
152 let mut overflow = <Message<PublicKey, TestMessage> as Policy>::Overflow::default();
153 let pending_subscribe = TestMessage::shared(b"pending_subscribe");
154 let pending_get = TestMessage::shared(b"pending_get");
155 let open_subscribe = TestMessage::shared(b"open_subscribe");
156 let open_get = TestMessage::shared(b"open_get");
157 let current_get = TestMessage::shared(b"current_get");
158
159 let (closed_responder, closed_receiver) = commonware_utils::channel::oneshot::channel();
160 <Message<PublicKey, TestMessage> as Policy>::handle(
161 &mut overflow,
162 Message::Subscribe {
163 digest: pending_subscribe.digest(),
164 responder: closed_responder,
165 },
166 );
167 drop(closed_receiver);
168
169 let (open_responder, _open_receiver) = commonware_utils::channel::oneshot::channel();
170 <Message<PublicKey, TestMessage> as Policy>::handle(
171 &mut overflow,
172 Message::Subscribe {
173 digest: open_subscribe.digest(),
174 responder: open_responder,
175 },
176 );
177
178 let (closed_responder, closed_receiver) = commonware_utils::channel::oneshot::channel();
179 <Message<PublicKey, TestMessage> as Policy>::handle(
180 &mut overflow,
181 Message::Get {
182 digest: pending_get.digest(),
183 responder: closed_responder,
184 },
185 );
186 drop(closed_receiver);
187
188 let (open_responder, _open_receiver) = commonware_utils::channel::oneshot::channel();
189 <Message<PublicKey, TestMessage> as Policy>::handle(
190 &mut overflow,
191 Message::Get {
192 digest: open_get.digest(),
193 responder: open_responder,
194 },
195 );
196
197 let (current_responder, current_receiver) = commonware_utils::channel::oneshot::channel();
198 drop(current_receiver);
199 <Message<PublicKey, TestMessage> as Policy>::handle(
200 &mut overflow,
201 Message::Get {
202 digest: current_get.digest(),
203 responder: current_responder,
204 },
205 );
206
207 let mut drained = VecDeque::new();
208 overflow.drain(|message| {
209 drained.push_back(message);
210 None
211 });
212
213 assert_eq!(drained.len(), 2);
214 assert!(drained.iter().any(|message| matches!(
215 message,
216 Message::Subscribe { digest, responder }
217 if *digest == open_subscribe.digest() && !responder.is_closed()
218 )));
219 assert!(drained.iter().any(|message| matches!(
220 message,
221 Message::Get { digest, responder }
222 if *digest == open_get.digest() && !responder.is_closed()
223 )));
224 }
225
226 #[test]
227 fn policy_drain_continues_until_rejected_message() {
228 let mut overflow = <Message<PublicKey, TestMessage> as Policy>::Overflow::default();
229 let first = TestMessage::shared(b"first");
230 let second = TestMessage::shared(b"second");
231 let third = TestMessage::shared(b"third");
232
233 let (closed_responder, closed_receiver) = commonware_utils::channel::oneshot::channel();
234 <Message<PublicKey, TestMessage> as Policy>::handle(
235 &mut overflow,
236 Message::Subscribe {
237 digest: TestMessage::shared(b"closed").digest(),
238 responder: closed_responder,
239 },
240 );
241 drop(closed_receiver);
242
243 let (first_responder, _first_receiver) = commonware_utils::channel::oneshot::channel();
244 <Message<PublicKey, TestMessage> as Policy>::handle(
245 &mut overflow,
246 Message::Get {
247 digest: first.digest(),
248 responder: first_responder,
249 },
250 );
251 let (second_responder, _second_receiver) = commonware_utils::channel::oneshot::channel();
252 <Message<PublicKey, TestMessage> as Policy>::handle(
253 &mut overflow,
254 Message::Get {
255 digest: second.digest(),
256 responder: second_responder,
257 },
258 );
259 let (third_responder, _third_receiver) = commonware_utils::channel::oneshot::channel();
260 <Message<PublicKey, TestMessage> as Policy>::handle(
261 &mut overflow,
262 Message::Get {
263 digest: third.digest(),
264 responder: third_responder,
265 },
266 );
267
268 let mut drained = VecDeque::new();
269 overflow.drain(|message| {
270 drained.push_back(message);
271 if drained.len() == 3 {
272 drained.pop_back()
273 } else {
274 None
275 }
276 });
277
278 assert_eq!(drained.len(), 2);
279 assert!(matches!(
280 &drained[0],
281 Message::Get { digest, responder }
282 if *digest == first.digest() && !responder.is_closed()
283 ));
284 assert!(matches!(
285 &drained[1],
286 Message::Get { digest, responder }
287 if *digest == second.digest() && !responder.is_closed()
288 ));
289
290 overflow.drain(|message| {
291 drained.push_back(message);
292 None
293 });
294 assert_eq!(drained.len(), 3);
295 assert!(matches!(
296 &drained[2],
297 Message::Get { digest, responder }
298 if *digest == third.digest() && !responder.is_closed()
299 ));
300 }
301
302 #[test]
303 fn policy_drain_stops_after_returned_message_closes() {
304 let mut overflow = <Message<PublicKey, TestMessage> as Policy>::Overflow::default();
305 let first = TestMessage::shared(b"first");
306 let second = TestMessage::shared(b"second");
307
308 let (first_responder, first_receiver) = commonware_utils::channel::oneshot::channel();
309 <Message<PublicKey, TestMessage> as Policy>::handle(
310 &mut overflow,
311 Message::Get {
312 digest: first.digest(),
313 responder: first_responder,
314 },
315 );
316 let (second_responder, _second_receiver) = commonware_utils::channel::oneshot::channel();
317 <Message<PublicKey, TestMessage> as Policy>::handle(
318 &mut overflow,
319 Message::Get {
320 digest: second.digest(),
321 responder: second_responder,
322 },
323 );
324
325 let mut first_receiver = Some(first_receiver);
326 let mut attempts = 0;
327 overflow.drain(|message| {
328 attempts += 1;
329 drop(first_receiver.take());
330 Some(message)
331 });
332 assert_eq!(attempts, 1);
333
334 let mut drained = VecDeque::new();
335 overflow.drain(|message| {
336 drained.push_back(message);
337 None
338 });
339 assert_eq!(drained.len(), 1);
340 assert!(matches!(
341 &drained[0],
342 Message::Get { digest, responder }
343 if *digest == second.digest() && !responder.is_closed()
344 ));
345 }
346
347 async fn spawn_peer_engines(
348 context: deterministic::Context,
349 oracle: &Oracle<PublicKey, deterministic::Context>,
350 registrations: &mut Registrations,
351 ) -> BTreeMap<PublicKey, Mailbox<PublicKey, TestMessage>> {
352 let mut mailboxes = BTreeMap::new();
353 while let Some((peer, network)) = registrations.pop_first() {
354 let context = context.child("peer").with_attribute("public_key", &peer);
355 let config = Config {
356 public_key: peer.clone(),
357 mailbox_size: NZUsize!(1024),
358 deque_size: CACHE_SIZE,
359 priority: false,
360 codec_config: RangeCfg::from(..),
361 peer_provider: oracle.manager(),
362 };
363 let (engine, engine_mailbox) =
364 Engine::<_, PublicKey, TestMessage, _>::new(context, config);
365 mailboxes.insert(peer.clone(), engine_mailbox);
366 engine.start(network);
367 }
368
369 context.sleep(A_JIFFY).await;
372 mailboxes
373 }
374
375 #[test_traced]
376 fn test_broadcast() {
377 let runner = deterministic::Runner::timed(Duration::from_secs(5));
378 runner.start(|context| async move {
379 let (peers, mut registrations, oracle) =
380 initialize_simulation(context.child("network"), 4, 1.0).await;
381 let mailboxes =
382 spawn_peer_engines(context.child("peers"), &oracle, &mut registrations).await;
383
384 let message = TestMessage::shared(b"hello world test message");
386 let first_mailbox = mailboxes.get(peers.first().unwrap()).unwrap().clone();
387 assert!(first_mailbox
388 .broadcast(Recipients::All, message.clone())
389 .accepted());
390
391 context.sleep(Duration::from_secs(1)).await;
393
394 for peer in peers.iter() {
396 let mailbox = mailboxes.get(peer).unwrap().clone();
397 let digest = message.digest();
398 let receiver = mailbox.subscribe(digest);
399 let received_message = receiver.await.ok();
400 assert_eq!(received_message.unwrap(), message.clone());
401 }
402
403 let message = TestMessage::shared(b"hello world again");
405 assert!(first_mailbox
406 .broadcast(Recipients::All, message.clone())
407 .accepted());
408
409 context.sleep(Duration::from_secs(1)).await;
411
412 let mut found = 0;
414 for peer in peers.iter() {
415 let mailbox = mailboxes.get(peer).unwrap().clone();
416 let digest = message.digest();
417 let receiver = mailbox.get(digest).await;
418 if let Some(receiver) = receiver {
419 assert_eq!(receiver, message.clone());
420 found += 1;
421 }
422 }
423 assert!(found > 0, "No peers received the message");
424 });
425 }
426
427 #[test_traced]
428 fn test_self_retrieval() {
429 let runner = deterministic::Runner::timed(Duration::from_secs(5));
430 runner.start(|context| async move {
431 let (peers, mut registrations, oracle) =
433 initialize_simulation(context.child("network"), 1, 1.0).await;
434 let mailboxes =
435 spawn_peer_engines(context.child("peers"), &oracle, &mut registrations).await;
436
437 let mailbox_a = mailboxes.get(&peers[0]).unwrap().clone();
439
440 let m1 = TestMessage::shared(b"hello world");
442 let digest_m1 = m1.digest();
443
444 let receiver_before = mailbox_a.get(digest_m1).await;
446 assert!(receiver_before.is_none());
447
448 let receiver_before = mailbox_a.subscribe(digest_m1);
450
451 assert!(mailbox_a.broadcast(Recipients::All, m1.clone()).accepted());
453
454 let msg_before = receiver_before
456 .await
457 .expect("Pre-broadcast retrieval failed");
458 assert_eq!(msg_before, m1);
459
460 let receiver_after = mailbox_a.get(digest_m1).await;
462 assert_eq!(receiver_after, Some(m1.clone()));
463
464 let receiver_after = mailbox_a.subscribe(digest_m1);
466
467 let start = context.current();
469 let msg_after = receiver_after
470 .await
471 .expect("Post-broadcast retrieval failed");
472 let duration = context.current().duration_since(start).unwrap();
473
474 assert_eq!(msg_after, m1);
476
477 assert!(duration < A_JIFFY, "get not instant");
479 });
480 }
481
482 #[test_traced]
483 fn test_packet_loss() {
484 let runner = deterministic::Runner::timed(Duration::from_secs(30));
485 runner.start(|context| async move {
486 let (peers, mut registrations, oracle) =
487 initialize_simulation(context.child("network"), 10, 0.1).await;
488 let mailboxes =
489 spawn_peer_engines(context.child("peers"), &oracle, &mut registrations).await;
490
491 let message = TestMessage::shared(b"hello world test message");
493 let first_mailbox = mailboxes.get(peers.first().unwrap()).unwrap().clone();
494
495 let digest = message.digest();
497 for i in 0..100 {
498 assert!(first_mailbox
500 .broadcast(Recipients::All, message.clone())
501 .accepted());
502 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
503
504 let mut all_received = true;
506 for peer in peers.iter() {
507 let mailbox = mailboxes.get(peer).unwrap().clone();
508 let receiver = mailbox.subscribe(digest);
509 let has = match context.timeout(A_JIFFY, receiver).await {
510 Ok(r) => r.is_ok(),
511 Err(Error::Timeout) => false,
512 Err(e) => panic!("unexpected error: {e:?}"),
513 };
514 all_received &= has;
515 }
516 if all_received {
518 assert!(i > 0, "Message received on first try");
519 return;
520 }
521 }
522 panic!("Not all peers received the message after retries");
523 });
524 }
525
526 #[test_traced]
527 fn test_get_cached() {
528 let runner = deterministic::Runner::timed(Duration::from_secs(5));
529 runner.start(|context| async move {
530 let (peers, mut registrations, oracle) =
531 initialize_simulation(context.child("network"), 2, 1.0).await;
532 let mailboxes =
533 spawn_peer_engines(context.child("peers"), &oracle, &mut registrations).await;
534
535 let message = TestMessage::shared(b"cached message");
537 let first_mailbox = mailboxes.get(peers.first().unwrap()).unwrap().clone();
538 assert!(first_mailbox
539 .broadcast(Recipients::All, message.clone())
540 .accepted());
541
542 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
544
545 let digest = message.digest();
547 let mailbox = mailboxes.get(peers.last().unwrap()).unwrap().clone();
548 let receiver = mailbox.subscribe(digest);
549 let start = context.current();
550 let received = receiver.await.expect("failed to get cached message");
551 let duration = context.current().duration_since(start).unwrap();
552 assert_eq!(received, message);
553 assert!(duration < A_JIFFY, "get not instant",);
554 });
555 }
556
557 #[test_traced]
558 fn test_get_nonexistent() {
559 let runner = deterministic::Runner::timed(Duration::from_secs(5));
560 runner.start(|context| async move {
561 let (peers, mut registrations, oracle) =
562 initialize_simulation(context.child("network"), 2, 1.0).await;
563 let mailboxes =
564 spawn_peer_engines(context.child("peers"), &oracle, &mut registrations).await;
565
566 let message = TestMessage::shared(b"future message");
568 let digest = message.digest();
569 let mailbox1 = mailboxes.get(&peers[0]).unwrap().clone();
570 let mailbox2 = mailboxes.get(&peers[1]).unwrap().clone();
571 let receiver = mailbox1.subscribe(digest);
572
573 let dummy1 = mailbox1.subscribe(digest);
575 let dummy2 = mailbox2.subscribe(digest);
576 drop(dummy1);
577 drop(dummy2);
578
579 assert!(mailbox1
581 .broadcast(Recipients::All, message.clone())
582 .accepted());
583
584 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
586
587 let received = receiver.await.expect("receiver1 should get message");
589 assert_eq!(received, message);
590 });
591 }
592
593 #[test_traced]
594 fn test_cache_eviction_single_peer() {
595 let runner = deterministic::Runner::timed(Duration::from_secs(5));
596 runner.start(|context| async move {
597 let (peers, mut registrations, oracle) =
598 initialize_simulation(context.child("network"), 2, 1.0).await;
599 let mailboxes =
600 spawn_peer_engines(context.child("peers"), &oracle, &mut registrations).await;
601
602 let mailbox = mailboxes.get(&peers[0]).unwrap().clone();
604 let mut messages = vec![];
605 for i in 0..CACHE_SIZE + 1 {
606 messages.push(TestMessage::shared(format!("message {i}").as_bytes()));
607 }
608 for message in messages.iter() {
609 assert!(mailbox
610 .broadcast(Recipients::All, message.clone())
611 .accepted());
612 }
613
614 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
616
617 let peer_mailbox = mailboxes.get(&peers[1]).unwrap().clone();
619 for msg in messages.iter().skip(1) {
620 let result = peer_mailbox.subscribe(msg.digest()).await.unwrap();
621 assert_eq!(result, msg.clone());
622 }
623
624 let receiver = peer_mailbox.subscribe(messages[0].digest());
626 match context.timeout(A_JIFFY, receiver).await {
627 Ok(_) => panic!("receiver should have failed"),
628 Err(Error::Timeout) => {} Err(e) => panic!("unexpected error: {e:?}"),
630 }
631 });
632 }
633
634 #[test_traced]
635 fn test_cache_eviction_multi_peer() {
636 let runner = deterministic::Runner::timed(Duration::from_secs(10));
637 runner.start(|context| async move {
638 let (peers, mut registrations, oracle) =
640 initialize_simulation(context.child("network"), 3, 1.0).await;
641 let mailboxes =
642 spawn_peer_engines(context.child("peers"), &oracle, &mut registrations).await;
643
644 let mailbox_a = mailboxes.get(&peers[0]).unwrap().clone();
646 let mailbox_b = mailboxes.get(&peers[1]).unwrap().clone();
647 let mailbox_c = mailboxes.get(&peers[2]).unwrap().clone();
648
649 let m1 = TestMessage::shared(b"message M1");
651 let digest_m1 = m1.digest();
652 assert!(mailbox_a.broadcast(Recipients::All, m1.clone()).accepted());
653 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
654
655 assert!(mailbox_c.broadcast(Recipients::All, m1.clone()).accepted());
657 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
658
659 let mut new_messages_a = Vec::with_capacity(CACHE_SIZE);
663 for i in 0..CACHE_SIZE {
664 new_messages_a.push(TestMessage::shared(format!("A{i}").as_bytes()));
665 }
666 for msg in &new_messages_a {
667 assert!(mailbox_a.broadcast(Recipients::All, msg.clone()).accepted());
668 }
669 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
670
671 let receiver = mailbox_b.subscribe(digest_m1);
673 let received = receiver.await.expect("M1 should be retrievable");
674 assert_eq!(received, m1);
675
676 let mut new_messages_c = Vec::with_capacity(CACHE_SIZE);
678 for i in 0..CACHE_SIZE {
679 new_messages_c.push(TestMessage::shared(format!("C{i}").as_bytes()));
680 }
681 for msg in &new_messages_c {
682 assert!(mailbox_c.broadcast(Recipients::All, msg.clone()).accepted());
683 }
684 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
685
686 let receiver = mailbox_b.subscribe(digest_m1);
688 match context.timeout(A_JIFFY, receiver).await {
689 Ok(_) => panic!("M1 should not be retrievable"),
690 Err(Error::Timeout) => {} Err(e) => panic!("unexpected error: {e:?}"),
692 }
693 });
694 }
695
696 #[test_traced]
697 fn test_selective_recipients() {
698 let runner = deterministic::Runner::timed(Duration::from_secs(5));
699 runner.start(|context| async move {
700 let (peers, mut registrations, oracle) =
701 initialize_simulation(context.child("network"), 4, 1.0).await;
702
703 let sender_pk = peers[0].clone();
704 let target_peer = peers[1].clone();
705 let non_target_peer = peers[2].clone();
706
707 let mailboxes =
708 spawn_peer_engines(context.child("peers"), &oracle, &mut registrations).await;
709 let sender_mb = mailboxes.get(&sender_pk).unwrap().clone();
710
711 let msg = TestMessage::shared(b"selective-broadcast");
712 assert!(sender_mb
713 .broadcast(Recipients::One(target_peer.clone()), msg.clone())
714 .accepted());
715
716 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
717
718 let got_target = mailboxes
720 .get(&target_peer)
721 .unwrap()
722 .clone()
723 .get(msg.digest())
724 .await;
725 assert_eq!(got_target, Some(msg.clone()));
726
727 let got_other = mailboxes
729 .get(&non_target_peer)
730 .unwrap()
731 .clone()
732 .get(msg.digest())
733 .await;
734 assert!(got_other.is_none());
735 });
736 }
737
738 #[test_traced]
739 fn test_ref_count_across_peers() {
740 let runner = deterministic::Runner::timed(Duration::from_secs(10));
741 runner.start(|context| async move {
742 let (peers, mut registrations, oracle) =
744 initialize_simulation(context.child("network"), 3, 1.0).await;
745 let mailboxes =
746 spawn_peer_engines(context.child("peers"), &oracle, &mut registrations).await;
747
748 let p0 = peers[0].clone();
749 let p1 = peers[1].clone();
750 let observer = peers[2].clone();
751
752 let mb0 = mailboxes.get(&p0).unwrap().clone();
753 let mb1 = mailboxes.get(&p1).unwrap().clone();
754 let obs = mailboxes.get(&observer).unwrap().clone();
755
756 let dup = TestMessage::shared(b"dup");
758 let digest = dup.digest();
759
760 assert!(mb0.broadcast(Recipients::All, dup.clone()).accepted());
762 assert!(mb1.broadcast(Recipients::All, dup.clone()).accepted());
763 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
764
765 assert_eq!(obs.get(digest).await, Some(dup.clone()));
767
768 for i in 0..CACHE_SIZE {
770 let spam = TestMessage::shared(format!("p0-{i}").into_bytes());
771 assert!(mb0.broadcast(Recipients::All, spam).accepted());
772 }
773 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
774 assert_eq!(obs.get(digest).await, Some(dup.clone()));
775
776 for i in 0..CACHE_SIZE {
778 let spam = TestMessage::shared(format!("p1-{i}").into_bytes());
779 assert!(mb1.broadcast(Recipients::All, spam).accepted());
780 }
781 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
782 assert!(obs.get(digest).await.is_none());
783 });
784 }
785
786 #[test_traced]
787 fn test_deterministic_retrieval() {
788 let run = |seed: u64| {
789 let config = deterministic::Config::new()
790 .with_seed(seed)
791 .with_timeout(Some(Duration::from_secs(5)));
792 let runner = deterministic::Runner::new(config);
793 runner.start(|context| async move {
794 let (peers, mut registrations, oracle) =
795 initialize_simulation(context.child("network"), 1, 1.0).await;
796 let mailboxes =
797 spawn_peer_engines(context.child("peers"), &oracle, &mut registrations).await;
798
799 let sender1 = peers[0].clone();
800 let mb1 = mailboxes.get(&sender1).unwrap().clone();
801
802 let m1 = TestMessage::shared(b"content-1");
804 let m2 = TestMessage::shared(b"content-2");
805 let m3 = TestMessage::shared(b"content-3");
806 assert!(mb1.broadcast(Recipients::All, m1.clone()).accepted());
807 assert!(mb1.broadcast(Recipients::All, m2.clone()).accepted());
808 assert!(mb1.broadcast(Recipients::All, m3.clone()).accepted());
809
810 let mut hasher = Sha256::default();
811 for msg in [&m1, &m2, &m3] {
812 if let Some(value) = mb1.get(msg.digest()).await {
813 hasher.update(&value.content);
814 }
815 }
816 hasher.finalize()
817 })
818 };
819
820 for seed in 0..10 {
821 let h1 = run(seed);
822 let h2 = run(seed);
823
824 assert_eq!(h1, h2, "Messages returned in different order for {seed}");
825 }
826 }
827
828 #[test_traced]
829 fn test_malformed_network_payload_does_not_break_valid_traffic() {
830 let runner = deterministic::Runner::timed(Duration::from_secs(10));
831 runner.start(|context| async move {
832 let (peers, mut registrations, oracle) =
833 initialize_simulation(context.child("network"), 3, 1.0).await;
834
835 let attacker = peers[0].clone();
836 let honest = peers[1].clone();
837 let victim = peers[2].clone();
838
839 let (mut attacker_sender, _) = registrations.remove(&attacker).unwrap();
840 let mailboxes =
841 spawn_peer_engines(context.child("peers"), &oracle, &mut registrations).await;
842 let honest_mailbox = mailboxes.get(&honest).unwrap().clone();
843 let victim_mailbox = mailboxes.get(&victim).unwrap().clone();
844
845 let sent = attacker_sender.send(
847 Recipients::One(victim.clone()),
848 IoBuf::from(vec![0xFF]),
849 false,
850 );
851 assert_eq!(sent, vec![victim.clone()]);
852 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
853
854 let message = TestMessage::shared(b"valid-after-malformed");
856 assert!(honest_mailbox
857 .broadcast(Recipients::One(victim.clone()), message.clone())
858 .accepted());
859 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
860
861 let received = victim_mailbox
862 .subscribe(message.digest())
863 .await
864 .expect("victim should receive valid message after malformed payload");
865 assert_eq!(received, message);
866 });
867 }
868
869 #[test_traced]
870 fn test_dropped_waiters_for_missing_digest_are_cleaned_up() {
871 let runner = deterministic::Runner::timed(Duration::from_secs(10));
872 runner.start(|context| async move {
873 let (peers, mut registrations, oracle) =
874 initialize_simulation(context.child("network"), 1, 1.0).await;
875 let peer = peers[0].clone();
876 let (sender, receiver) = registrations.remove(&peer).unwrap();
877
878 let engine_context = context.child("waiter_cleanup");
879 let config = Config {
880 public_key: peer,
881 mailbox_size: NZUsize!(1024),
882 deque_size: CACHE_SIZE,
883 priority: false,
884 codec_config: RangeCfg::from(..),
885 peer_provider: oracle.manager(),
886 };
887 let (engine, mailbox) =
888 Engine::<_, PublicKey, TestMessage, _>::new(engine_context, config);
889 engine.start((sender, receiver));
890
891 let missing = TestMessage::shared(b"never-arrives");
892 let missing_digest = missing.digest();
893 let rx1 = mailbox.subscribe(missing_digest);
894 let rx2 = mailbox.subscribe(missing_digest);
895
896 let _ = mailbox
898 .get(TestMessage::shared(b"before-cleanup").digest())
899 .await;
900 context.sleep(A_JIFFY).await;
901 let metrics_before = context.encode();
902 let waiter_values_before: Vec<f64> = metrics_before
903 .lines()
904 .filter(|line| {
905 line.starts_with("waiters")
906 || (line.contains("_waiters")
907 && !line.starts_with("# HELP")
908 && !line.starts_with("# TYPE"))
909 })
910 .filter_map(|line| line.split_whitespace().last())
911 .filter_map(|value| value.parse::<f64>().ok())
912 .collect();
913 assert!(
914 !waiter_values_before.is_empty(),
915 "waiters metric not found in output:\n{metrics_before}"
916 );
917 assert!(
918 waiter_values_before.iter().any(|value| *value > 0.0),
919 "expected positive waiters before cleanup, got:\n{metrics_before}"
920 );
921
922 drop(rx1);
923 drop(rx2);
924
925 let _ = mailbox
927 .get(TestMessage::shared(b"after-cleanup").digest())
928 .await;
929 context.sleep(A_JIFFY).await;
930
931 let metrics_after = context.encode();
932 let waiter_values_after: Vec<f64> = metrics_after
933 .lines()
934 .filter(|line| {
935 line.starts_with("waiters")
936 || (line.contains("_waiters")
937 && !line.starts_with("# HELP")
938 && !line.starts_with("# TYPE"))
939 })
940 .filter_map(|line| line.split_whitespace().last())
941 .filter_map(|value| value.parse::<f64>().ok())
942 .collect();
943 assert!(
944 !waiter_values_after.is_empty(),
945 "waiters metric not found in output:\n{metrics_after}"
946 );
947 assert!(
948 waiter_values_after.iter().all(|value| *value == 0.0),
949 "expected zero retained waiters, got:\n{metrics_after}"
950 );
951 });
952 }
953
954 #[allow(clippy::type_complexity)]
955 async fn spawn_peer_engines_with_handles(
956 context: deterministic::Context,
957 oracle: &Oracle<PublicKey, deterministic::Context>,
958 registrations: &mut Registrations,
959 ) -> (
960 BTreeMap<PublicKey, Mailbox<PublicKey, TestMessage>>,
961 Vec<commonware_runtime::Handle<()>>,
962 ) {
963 let mut mailboxes = BTreeMap::new();
964 let mut handles = Vec::new();
965 while let Some((peer, network)) = registrations.pop_first() {
966 let ctx = context.child("peer").with_attribute("public_key", &peer);
967 let config = Config {
968 public_key: peer.clone(),
969 mailbox_size: NZUsize!(1024),
970 deque_size: CACHE_SIZE,
971 priority: false,
972 codec_config: RangeCfg::from(..),
973 peer_provider: oracle.manager(),
974 };
975 let (engine, engine_mailbox) = Engine::<_, PublicKey, TestMessage, _>::new(ctx, config);
976 mailboxes.insert(peer.clone(), engine_mailbox);
977 handles.push(engine.start(network));
978 }
979
980 context.sleep(A_JIFFY).await;
981 (mailboxes, handles)
982 }
983
984 #[test_traced]
985 fn test_operations_after_shutdown_do_not_panic() {
986 let runner = deterministic::Runner::timed(Duration::from_secs(5));
987 runner.start(|context| async move {
988 let (peers, mut registrations, oracle) =
989 initialize_simulation(context.child("network"), 2, 1.0).await;
990 let (mut mailboxes, handles) = spawn_peer_engines_with_handles(
991 context.child("peers"),
992 &oracle,
993 &mut registrations,
994 )
995 .await;
996
997 let message = TestMessage::shared(b"test message");
999 let mailbox = mailboxes.remove(&peers[0]).unwrap();
1000 assert!(
1001 mailbox
1002 .broadcast(Recipients::All, message.clone())
1003 .accepted(),
1004 "broadcast should succeed before shutdown"
1005 );
1006
1007 for handle in handles {
1009 handle.abort();
1010 }
1011 context.sleep(Duration::from_millis(100)).await;
1012
1013 assert_eq!(
1017 mailbox.broadcast(Recipients::All, message.clone()),
1018 Feedback::Closed,
1019 "broadcast after shutdown should return Closed"
1020 );
1021
1022 let digest = message.digest();
1024 let receiver = mailbox.subscribe(digest);
1025 let result = receiver.await;
1026 assert!(
1027 result.is_err(),
1028 "subscribe after shutdown should return Canceled"
1029 );
1030
1031 let result = mailbox.get(digest).await;
1033 assert!(result.is_none(), "get after shutdown should return None");
1034 });
1035 }
1036
1037 fn clean_shutdown(seed: u64) {
1038 let cfg = deterministic::Config::new()
1039 .with_seed(seed)
1040 .with_timeout(Some(Duration::from_secs(30)));
1041 let runner = deterministic::Runner::new(cfg);
1042 runner.start(|context| async move {
1043 let (peers, mut registrations, oracle) =
1044 initialize_simulation(context.child("network"), 2, 1.0).await;
1045
1046 let (mailboxes, handles) = spawn_peer_engines_with_handles(
1047 context.child("peers"),
1048 &oracle,
1049 &mut registrations,
1050 )
1051 .await;
1052
1053 context.sleep(Duration::from_millis(100)).await;
1055
1056 let running_before = count_running_tasks(&context, "peers");
1058 assert!(
1059 running_before > 0,
1060 "at least one peer engine task should be running"
1061 );
1062
1063 let message = TestMessage::shared(b"test message");
1065 let mailbox = mailboxes.get(&peers[0]).unwrap().clone();
1066 assert!(
1067 mailbox
1068 .broadcast(Recipients::All, message.clone())
1069 .accepted(),
1070 "broadcast should succeed"
1071 );
1072
1073 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
1075
1076 let peer_mailbox = mailboxes.get(&peers[1]).unwrap().clone();
1078 let received = peer_mailbox.get(message.digest()).await;
1079 assert_eq!(received, Some(message));
1080
1081 for handle in handles {
1083 handle.abort();
1084 }
1085 context.sleep(Duration::from_millis(100)).await;
1086
1087 let running_after = count_running_tasks(&context, "peers");
1089 assert_eq!(
1090 running_after, 0,
1091 "all peer engine tasks should be stopped, but {running_after} still running"
1092 );
1093 });
1094 }
1095
1096 #[test]
1097 fn test_clean_shutdown() {
1098 for seed in 0..25 {
1099 clean_shutdown(seed);
1100 }
1101 }
1102
1103 #[test_traced]
1104 fn test_peer_set_update_evicts_disconnected_peer_buffers() {
1105 let runner = deterministic::Runner::timed(Duration::from_secs(5));
1106 runner.start(|context| async move {
1107 let (peers, mut registrations, oracle) =
1108 initialize_simulation(context.child("network"), 3, 1.0).await;
1109
1110 let peer_a = peers[0].clone();
1111 let peer_b = peers[1].clone();
1112 let peer_c = peers[2].clone();
1113
1114 let network_b = registrations.remove(&peer_b).unwrap();
1116 let config_b = Config {
1117 public_key: peer_b.clone(),
1118 mailbox_size: NZUsize!(1024),
1119 deque_size: CACHE_SIZE,
1120 priority: false,
1121 codec_config: RangeCfg::from(..),
1122 peer_provider: oracle.manager(),
1123 };
1124 let (engine_b, mailbox_b) =
1125 Engine::<_, PublicKey, TestMessage, _>::new(context.child("peer_b"), config_b);
1126 engine_b.start(network_b);
1127
1128 let mut mailboxes = BTreeMap::new();
1130 mailboxes.insert(peer_b.clone(), mailbox_b);
1131 for (peer, network) in registrations {
1132 let ctx = context.child("peer").with_attribute("public_key", &peer);
1133 let config = Config {
1134 public_key: peer.clone(),
1135 mailbox_size: NZUsize!(1024),
1136 deque_size: CACHE_SIZE,
1137 priority: false,
1138 codec_config: RangeCfg::from(..),
1139 peer_provider: oracle.manager(),
1140 };
1141 let (engine, mailbox) = Engine::<_, PublicKey, TestMessage, _>::new(ctx, config);
1142 mailboxes.insert(peer, mailbox);
1143 engine.start(network);
1144 }
1145 context.sleep(A_JIFFY).await;
1146
1147 let msg = TestMessage::shared(b"eviction-test");
1149 let mailbox_a = mailboxes.get(&peer_a).unwrap().clone();
1150 assert!(mailbox_a.broadcast(Recipients::All, msg.clone()).accepted());
1151 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
1152
1153 let mailbox_b = mailboxes.get(&peer_b).unwrap().clone();
1155 assert_eq!(
1156 mailbox_b.get(msg.digest()).await,
1157 Some(msg.clone()),
1158 "peer B should have the message before eviction"
1159 );
1160
1161 let remaining = commonware_utils::ordered::Set::from_iter_dedup(vec![peer_b, peer_c]);
1163 oracle.manager().track(1, remaining);
1164 context.sleep(A_JIFFY).await;
1165
1166 assert!(
1168 mailbox_b.get(msg.digest()).await.is_none(),
1169 "message should be evicted after peer A left the peer set"
1170 );
1171 });
1172 }
1173
1174 #[test_traced]
1175 fn test_peer_set_update_evicts_peers_not_in_latest_set_even_if_still_in_overlap() {
1176 let runner = deterministic::Runner::timed(Duration::from_secs(5));
1177 runner.start(|context| async move {
1178 let (network, oracle) = Network::<deterministic::Context, PublicKey>::new(
1180 context.child("network"),
1181 commonware_p2p::simulated::Config {
1182 max_size: 1024 * 1024,
1183 disconnect_on_block: true,
1184 tracked_peer_sets: NZUsize!(2),
1185 },
1186 );
1187 network.start();
1188
1189 let mut schemes = (0..3)
1190 .map(|i| PrivateKey::from_seed(i as u64))
1191 .collect::<Vec<_>>();
1192 schemes.sort_by_key(|s| s.public_key());
1193 let peers: Vec<PublicKey> = schemes.iter().map(|c| c.public_key()).collect();
1194 let peer_a = peers[0].clone();
1195 let peer_b = peers[1].clone();
1196 let peer_c = peers[2].clone();
1197
1198 let mut registrations: Registrations = BTreeMap::new();
1199 for peer in peers.iter() {
1200 let (sender, receiver) = oracle
1201 .control(peer.clone())
1202 .register(0, TEST_QUOTA)
1203 .await
1204 .unwrap();
1205 registrations.insert(peer.clone(), (sender, receiver));
1206 }
1207 let link = Link {
1208 latency: NETWORK_SPEED,
1209 jitter: Duration::ZERO,
1210 success_rate: 1.0,
1211 };
1212 for p1 in peers.iter() {
1213 for p2 in peers.iter() {
1214 if p2 != p1 {
1215 oracle
1216 .add_link(p1.clone(), p2.clone(), link.clone())
1217 .await
1218 .unwrap();
1219 }
1220 }
1221 }
1222
1223 let all = commonware_utils::ordered::Set::from_iter_dedup(peers.clone());
1225 oracle.manager().track(0, all);
1226
1227 let network_b = registrations.remove(&peer_b).unwrap();
1229 let config_b = Config {
1230 public_key: peer_b.clone(),
1231 mailbox_size: NZUsize!(1024),
1232 deque_size: CACHE_SIZE,
1233 priority: false,
1234 codec_config: RangeCfg::from(..),
1235 peer_provider: oracle.manager(),
1236 };
1237 let (engine_b, mailbox_b) =
1238 Engine::<_, PublicKey, TestMessage, _>::new(context.child("peer_b"), config_b);
1239 engine_b.start(network_b);
1240
1241 let mut mailboxes = BTreeMap::new();
1242 mailboxes.insert(peer_b.clone(), mailbox_b);
1243 for (peer, network) in registrations {
1244 let ctx = context.child("peer").with_attribute("public_key", &peer);
1245 let config = Config {
1246 public_key: peer.clone(),
1247 mailbox_size: NZUsize!(1024),
1248 deque_size: CACHE_SIZE,
1249 priority: false,
1250 codec_config: RangeCfg::from(..),
1251 peer_provider: oracle.manager(),
1252 };
1253 let (engine, mailbox) = Engine::<_, PublicKey, TestMessage, _>::new(ctx, config);
1254 mailboxes.insert(peer, mailbox);
1255 engine.start(network);
1256 }
1257 context.sleep(A_JIFFY).await;
1258
1259 let msg = TestMessage::shared(b"eviction-latest-test");
1261 let mailbox_a = mailboxes.get(&peer_a).unwrap().clone();
1262 assert!(mailbox_a.broadcast(Recipients::All, msg.clone()).accepted());
1263 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
1264
1265 let mailbox_b = mailboxes.get(&peer_b).unwrap().clone();
1266 assert_eq!(
1267 mailbox_b.get(msg.digest()).await,
1268 Some(msg.clone()),
1269 "peer B should have the message before eviction"
1270 );
1271
1272 let remaining = commonware_utils::ordered::Set::from_iter_dedup(vec![
1276 peer_b.clone(),
1277 peer_c.clone(),
1278 ]);
1279 oracle.manager().track(1, remaining);
1280 context.sleep(A_JIFFY).await;
1281
1282 assert!(
1283 mailbox_b.get(msg.digest()).await.is_none(),
1284 "message should be evicted: peer A is not in the latest peer set"
1285 );
1286
1287 let fresh = TestMessage::shared(b"post-eviction-latest-test");
1289 assert!(mailbox_a
1290 .broadcast(Recipients::All, fresh.clone())
1291 .accepted());
1292 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
1293
1294 assert!(
1295 mailbox_b.get(fresh.digest()).await.is_none(),
1296 "message should not be rebuffered after peer A left latest.primary"
1297 );
1298 });
1299 }
1300
1301 #[test_traced]
1302 fn test_initial_latest_peer_set_blocks_sender_not_in_latest_primary() {
1303 let runner = deterministic::Runner::timed(Duration::from_secs(5));
1304 runner.start(|context| async move {
1305 let (network, oracle) = Network::<deterministic::Context, PublicKey>::new(
1306 context.child("network"),
1307 commonware_p2p::simulated::Config {
1308 max_size: 1024 * 1024,
1309 disconnect_on_block: true,
1310 tracked_peer_sets: NZUsize!(1),
1311 },
1312 );
1313 network.start();
1314
1315 let mut schemes = (0..3)
1316 .map(|i| PrivateKey::from_seed(i as u64))
1317 .collect::<Vec<_>>();
1318 schemes.sort_by_key(|s| s.public_key());
1319 let peers: Vec<PublicKey> = schemes.iter().map(|c| c.public_key()).collect();
1320 let peer_a = peers[0].clone();
1321 let peer_b = peers[1].clone();
1322 let peer_c = peers[2].clone();
1323
1324 let mut registrations: Registrations = BTreeMap::new();
1325 for peer in &peers {
1326 let (sender, receiver) = oracle
1327 .control(peer.clone())
1328 .register(0, TEST_QUOTA)
1329 .await
1330 .unwrap();
1331 registrations.insert(peer.clone(), (sender, receiver));
1332 }
1333 let link = Link {
1334 latency: NETWORK_SPEED,
1335 jitter: Duration::ZERO,
1336 success_rate: 1.0,
1337 };
1338 for p1 in &peers {
1339 for p2 in &peers {
1340 if p1 != p2 {
1341 oracle
1342 .add_link(p1.clone(), p2.clone(), link.clone())
1343 .await
1344 .unwrap();
1345 }
1346 }
1347 }
1348
1349 let latest_primary = commonware_utils::ordered::Set::from_iter_dedup(vec![
1350 peer_b.clone(),
1351 peer_c.clone(),
1352 ]);
1353 let latest_secondary =
1354 commonware_utils::ordered::Set::from_iter_dedup(vec![peer_a.clone()]);
1355 oracle
1356 .manager()
1357 .track(0, TrackedPeers::new(latest_primary, latest_secondary));
1358
1359 let mut mailboxes = BTreeMap::new();
1360 for (peer, network) in registrations {
1361 let ctx = context.child("peer").with_attribute("public_key", &peer);
1362 let config = Config {
1363 public_key: peer.clone(),
1364 mailbox_size: NZUsize!(1024),
1365 deque_size: CACHE_SIZE,
1366 priority: false,
1367 codec_config: RangeCfg::from(..),
1368 peer_provider: oracle.manager(),
1369 };
1370 let (engine, mailbox) = Engine::<_, PublicKey, TestMessage, _>::new(ctx, config);
1371 mailboxes.insert(peer, mailbox);
1372 engine.start(network);
1373 }
1374 context.sleep(A_JIFFY).await;
1375
1376 let mailbox_a = mailboxes.get(&peer_a).unwrap().clone();
1377 let mailbox_b = mailboxes.get(&peer_b).unwrap().clone();
1378 let msg = TestMessage::shared(b"startup-latest-primary-only");
1379 assert!(
1380 mailbox_a
1381 .broadcast(Recipients::All, msg.clone())
1382 .accepted(),
1383 "Recipients::All is accepted locally; cache policy is separate"
1384 );
1385 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
1386
1387 assert_eq!(
1388 mailbox_a.get(msg.digest()).await,
1389 None,
1390 "sender not in latest.primary should not buffer, including own broadcasts"
1391 );
1392 assert!(
1393 mailbox_b.get(msg.digest()).await.is_none(),
1394 "peer B should not cache messages from a sender excluded by the initial latest.primary set"
1395 );
1396 });
1397 }
1398
1399 #[test_traced]
1403 fn test_broadcast_queued_before_start_respects_initial_latest_primary() {
1404 let runner = deterministic::Runner::timed(Duration::from_secs(5));
1405 runner.start(|context| async move {
1406 let (peers, mut registrations, oracle) =
1408 initialize_simulation(context.child("network"), 1, 1.0).await;
1409 let peer = peers[0].clone();
1410 let network = registrations.remove(&peer).unwrap();
1411 let config = Config {
1412 public_key: peer.clone(),
1413 mailbox_size: NZUsize!(1024),
1414 deque_size: CACHE_SIZE,
1415 priority: false,
1416 codec_config: RangeCfg::from(..),
1417 peer_provider: oracle.manager(),
1418 };
1419 let (engine, mailbox) =
1420 Engine::<_, PublicKey, TestMessage, _>::new(context.child("peer"), config);
1421
1422 let msg = TestMessage::shared(b"queued-before-start");
1424 assert!(mailbox
1425 .broadcast(Recipients::All, msg.clone())
1426 .accepted());
1427
1428 engine.start(network);
1430
1431 assert_eq!(
1432 mailbox.get(msg.digest()).await,
1433 Some(msg),
1434 "sender is already in the initial latest.primary set, so its local broadcast should be cached"
1435 );
1436 });
1437 }
1438
1439 #[test_traced]
1440 fn test_engine_starts_before_initial_peer_set_and_delivers_after_tracking() {
1441 let runner = deterministic::Runner::timed(Duration::from_secs(5));
1442 runner.start(|context| async move {
1443 let (network, oracle) = Network::<deterministic::Context, PublicKey>::new(
1444 context.child("network"),
1445 commonware_p2p::simulated::Config {
1446 max_size: 1024 * 1024,
1447 disconnect_on_block: true,
1448 tracked_peer_sets: NZUsize!(1),
1449 },
1450 );
1451 network.start();
1452
1453 let mut schemes = (0..2)
1454 .map(|i| PrivateKey::from_seed(i as u64))
1455 .collect::<Vec<_>>();
1456 schemes.sort_by_key(|s| s.public_key());
1457 let peers: Vec<PublicKey> = schemes.iter().map(|c| c.public_key()).collect();
1458 let peer_a = peers[0].clone();
1459 let peer_b = peers[1].clone();
1460
1461 let mut registrations: Registrations = BTreeMap::new();
1462 for peer in &peers {
1463 let (sender, receiver) = oracle
1464 .control(peer.clone())
1465 .register(0, TEST_QUOTA)
1466 .await
1467 .unwrap();
1468 registrations.insert(peer.clone(), (sender, receiver));
1469 }
1470
1471 let link = Link {
1472 latency: NETWORK_SPEED,
1473 jitter: Duration::ZERO,
1474 success_rate: 1.0,
1475 };
1476 for p1 in &peers {
1477 for p2 in &peers {
1478 if p1 != p2 {
1479 oracle
1480 .add_link(p1.clone(), p2.clone(), link.clone())
1481 .await
1482 .unwrap();
1483 }
1484 }
1485 }
1486
1487 let mut mailboxes = BTreeMap::new();
1488 for (peer, network) in registrations {
1489 let ctx = context.child("peer").with_attribute("public_key", &peer);
1490 let config = Config {
1491 public_key: peer.clone(),
1492 mailbox_size: NZUsize!(1024),
1493 deque_size: CACHE_SIZE,
1494 priority: false,
1495 codec_config: RangeCfg::from(..),
1496 peer_provider: oracle.manager(),
1497 };
1498 let (engine, mailbox) = Engine::<_, PublicKey, TestMessage, _>::new(ctx, config);
1499 mailboxes.insert(peer, mailbox);
1500 engine.start(network);
1501 }
1502
1503 let mailbox_a = mailboxes.get(&peer_a).unwrap().clone();
1504 let mailbox_b = mailboxes.get(&peer_b).unwrap().clone();
1505
1506 let before = TestMessage::shared(b"before-tracking");
1507 assert!(
1508 mailbox_a
1509 .broadcast(Recipients::All, before.clone())
1510 .accepted(),
1511 "broadcast request should be accepted before a peer set is tracked"
1512 );
1513 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
1514
1515 assert_eq!(
1516 mailbox_a.get(before.digest()).await,
1517 None,
1518 "without latest.primary, local broadcasts are not buffered"
1519 );
1520 assert!(
1521 mailbox_b.get(before.digest()).await.is_none(),
1522 "without latest.primary, remote peers do not cache inbound messages"
1523 );
1524
1525 oracle.manager().track(
1526 0,
1527 commonware_utils::ordered::Set::from_iter_dedup(peers.clone()),
1528 );
1529 context.sleep(A_JIFFY).await;
1530
1531 let after = TestMessage::shared(b"after-tracking");
1532 assert!(mailbox_a
1533 .broadcast(Recipients::All, after.clone())
1534 .accepted());
1535 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
1536
1537 assert_eq!(mailbox_b.get(after.digest()).await, Some(after));
1538 });
1539 }
1540
1541 #[test_traced]
1542 fn test_peer_set_update_preserves_shared_messages() {
1543 let runner = deterministic::Runner::timed(Duration::from_secs(5));
1544 runner.start(|context| async move {
1545 let (peers, mut registrations, oracle) =
1546 initialize_simulation(context.child("network"), 3, 1.0).await;
1547
1548 let peer_a = peers[0].clone();
1549 let peer_b = peers[1].clone();
1550 let peer_c = peers[2].clone();
1551
1552 let network_b = registrations.remove(&peer_b).unwrap();
1554 let config_b = Config {
1555 public_key: peer_b.clone(),
1556 mailbox_size: NZUsize!(1024),
1557 deque_size: CACHE_SIZE,
1558 priority: false,
1559 codec_config: RangeCfg::from(..),
1560 peer_provider: oracle.manager(),
1561 };
1562 let (engine_b, mailbox_b) =
1563 Engine::<_, PublicKey, TestMessage, _>::new(context.child("peer_b"), config_b);
1564 engine_b.start(network_b);
1565
1566 let mut mailboxes = BTreeMap::new();
1568 mailboxes.insert(peer_b.clone(), mailbox_b);
1569 for (peer, network) in registrations {
1570 let ctx = context.child("peer").with_attribute("public_key", &peer);
1571 let config = Config {
1572 public_key: peer.clone(),
1573 mailbox_size: NZUsize!(1024),
1574 deque_size: CACHE_SIZE,
1575 priority: false,
1576 codec_config: RangeCfg::from(..),
1577 peer_provider: oracle.manager(),
1578 };
1579 let (engine, mailbox) = Engine::<_, PublicKey, TestMessage, _>::new(ctx, config);
1580 mailboxes.insert(peer, mailbox);
1581 engine.start(network);
1582 }
1583 context.sleep(A_JIFFY).await;
1584
1585 let msg = TestMessage::shared(b"shared-msg");
1587 let mailbox_a = mailboxes.get(&peer_a).unwrap().clone();
1588 let mailbox_c = mailboxes.get(&peer_c).unwrap().clone();
1589 assert!(mailbox_a.broadcast(Recipients::All, msg.clone()).accepted());
1590 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
1591 assert!(mailbox_c.broadcast(Recipients::All, msg.clone()).accepted());
1592 context.sleep(NETWORK_SPEED_WITH_BUFFER).await;
1593
1594 let mailbox_b = mailboxes.get(&peer_b).unwrap().clone();
1596 assert_eq!(mailbox_b.get(msg.digest()).await, Some(msg.clone()));
1597
1598 let remaining = commonware_utils::ordered::Set::from_iter_dedup(vec![peer_b, peer_c]);
1600 oracle.manager().track(1, remaining);
1601 context.sleep(A_JIFFY).await;
1602
1603 assert_eq!(
1605 mailbox_b.get(msg.digest()).await,
1606 Some(msg.clone()),
1607 "message should survive when another peer in the primary set still references it"
1608 );
1609 });
1610 }
1611}