1use crate::types::{Height, Round};
2use bytes::{Buf, BufMut, Bytes};
3use commonware_actor::mailbox::{self, Overflow, Policy, Sender};
4use commonware_codec::{EncodeSize, Error as CodecError, Read, ReadExt, Write};
5use commonware_cryptography::Digest;
6use commonware_resolver::{p2p::Producer, Consumer, Delivery, Fetch as ResolverFetch};
7use commonware_runtime::Metrics;
8use commonware_utils::{channel::oneshot, Span};
9use std::{
10 collections::VecDeque,
11 fmt::{Debug, Display},
12 hash::{Hash, Hasher},
13 num::NonZeroUsize,
14 sync::mpsc::TryRecvError,
15};
16
17const BLOCK_REQUEST: u8 = 0;
19const FINALIZED_REQUEST: u8 = 1;
20const NOTARIZED_REQUEST: u8 = 2;
21
22pub(crate) enum Message<D: Digest> {
25 Deliver {
27 delivery: Delivery<Key<D>, Annotation>,
29 value: Bytes,
31 response: oneshot::Sender<bool>,
33 },
34 Produce {
36 key: Key<D>,
38 response: oneshot::Sender<Bytes>,
40 },
41}
42
43impl<D: Digest> Message<D> {
44 pub(crate) fn response_closed(&self) -> bool {
46 match self {
47 Self::Deliver { response, .. } => response.is_closed(),
48 Self::Produce { response, .. } => response.is_closed(),
49 }
50 }
51}
52
53pub(crate) struct Pending<D: Digest>(VecDeque<Message<D>>);
55
56impl<D: Digest> Default for Pending<D> {
57 fn default() -> Self {
58 Self(VecDeque::new())
59 }
60}
61
62impl<D: Digest> Overflow<Message<D>> for Pending<D> {
63 fn is_empty(&self) -> bool {
64 self.0.is_empty()
65 }
66
67 fn drain<F>(&mut self, mut push: F)
68 where
69 F: FnMut(Message<D>) -> Option<Message<D>>,
70 {
71 while let Some(message) = self.0.pop_front() {
72 if message.response_closed() {
73 continue;
74 }
75
76 if let Some(message) = push(message) {
77 self.0.push_front(message);
78 break;
79 }
80 }
81 }
82}
83
84impl<D: Digest> Policy for Message<D> {
85 type Overflow = Pending<D>;
86
87 fn handle(overflow: &mut Self::Overflow, message: Self) {
88 if message.response_closed() {
89 return;
90 }
91 overflow.0.push_back(message);
92 }
93}
94
95#[derive(Clone)]
100pub struct Handler<D: Digest> {
101 sender: Sender<Message<D>>,
102}
103
104impl<D: Digest> Handler<D> {
105 pub(crate) const fn new(sender: Sender<Message<D>>) -> Self {
107 Self { sender }
108 }
109}
110
111pub fn init<D: Digest>(metrics: impl Metrics, capacity: NonZeroUsize) -> (Receiver<D>, Handler<D>) {
113 let (sender, receiver) = mailbox::new(metrics, capacity);
114 (Receiver::new(receiver), Handler::new(sender))
115}
116
117pub struct Receiver<D: Digest> {
119 inner: mailbox::Receiver<Message<D>>,
120}
121
122impl<D: Digest> Receiver<D> {
123 pub(crate) const fn new(inner: mailbox::Receiver<Message<D>>) -> Self {
124 Self { inner }
125 }
126
127 pub(crate) async fn recv(&mut self) -> Option<Message<D>> {
128 self.inner.recv().await
129 }
130
131 pub(crate) fn try_recv(&mut self) -> Result<Message<D>, TryRecvError> {
132 self.inner.try_recv()
133 }
134}
135
136impl<D: Digest> Consumer for Handler<D> {
137 type Key = Key<D>;
138 type Value = Bytes;
139 type Subscriber = Annotation;
140
141 fn deliver(
142 &mut self,
143 delivery: Delivery<Self::Key, Self::Subscriber>,
144 value: Self::Value,
145 ) -> oneshot::Receiver<bool> {
146 let (response, receiver) = oneshot::channel();
147 let _ = self.sender.enqueue(Message::Deliver {
148 delivery,
149 value,
150 response,
151 });
152 receiver
153 }
154}
155
156impl<D: Digest> Producer for Handler<D> {
157 type Key = Key<D>;
158
159 fn produce(&mut self, key: Self::Key) -> oneshot::Receiver<Bytes> {
160 let (response, receiver) = oneshot::channel();
161 let _ = self.sender.enqueue(Message::Produce { key, response });
162 receiver
163 }
164}
165
166#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
184pub enum Annotation {
185 Notarization { round: Round },
187 Certified { height: Height },
194 Finalized(Finalized),
196}
197
198#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
200pub enum Finalized {
201 ByHeight { height: Height },
203 ByRound { round: Round },
208}
209
210#[derive(Clone, Copy)]
212pub enum Key<D: Digest> {
213 Block(D),
215 Finalized {
216 height: Height,
217 },
218 Notarized {
219 round: Round,
220 },
221}
222
223impl<D: Digest> Key<D> {
224 const fn subject(&self) -> u8 {
226 match self {
227 Self::Block(_) => BLOCK_REQUEST,
228 Self::Finalized { .. } => FINALIZED_REQUEST,
229 Self::Notarized { .. } => NOTARIZED_REQUEST,
230 }
231 }
232}
233
234#[derive(Clone, Copy, Debug, Eq, PartialEq)]
236pub(crate) enum RequestKind<D: Digest> {
237 Notarized { round: Round },
239 Finalized { height: Height },
241 CertifiedBlock { commitment: D, height: Height },
243 FinalizedBlockByHeight { commitment: D, height: Height },
245 FinalizedBlockByRound { commitment: D, round: Round },
247}
248
249#[derive(Clone, Copy, Debug, Eq, PartialEq)]
251pub struct Request<D: Digest> {
252 kind: RequestKind<D>,
253}
254
255impl<D: Digest> Request<D> {
256 pub const fn notarized(round: Round) -> Self {
258 Self {
259 kind: RequestKind::Notarized { round },
260 }
261 }
262
263 pub const fn finalized(height: Height) -> Self {
265 Self {
266 kind: RequestKind::Finalized { height },
267 }
268 }
269
270 pub const fn certified_block(commitment: D, height: Height) -> Self {
272 Self {
273 kind: RequestKind::CertifiedBlock { commitment, height },
274 }
275 }
276
277 pub const fn finalized_block_by_height(commitment: D, height: Height) -> Self {
279 Self {
280 kind: RequestKind::FinalizedBlockByHeight { commitment, height },
281 }
282 }
283
284 pub const fn finalized_block_by_round(commitment: D, round: Round) -> Self {
286 Self {
287 kind: RequestKind::FinalizedBlockByRound { commitment, round },
288 }
289 }
290
291 pub(crate) fn above_height_floor(&self, floor: Height) -> bool {
292 match self.kind {
293 RequestKind::Finalized { height }
294 | RequestKind::CertifiedBlock { height, .. }
295 | RequestKind::FinalizedBlockByHeight { height, .. } => height > floor,
296 RequestKind::Notarized { .. } | RequestKind::FinalizedBlockByRound { .. } => true,
297 }
298 }
299
300 pub(crate) fn above_round_floor(&self, floor: Round) -> bool {
301 match self.kind {
302 RequestKind::Notarized { round } | RequestKind::FinalizedBlockByRound { round, .. } => {
303 round > floor
304 }
305 RequestKind::Finalized { .. }
306 | RequestKind::CertifiedBlock { .. }
307 | RequestKind::FinalizedBlockByHeight { .. } => true,
308 }
309 }
310
311 pub(crate) const fn into_inner(self) -> ResolverFetch<Key<D>, Annotation> {
312 match self.kind {
313 RequestKind::Notarized { round } => ResolverFetch {
314 key: Key::Notarized { round },
315 subscriber: Annotation::Notarization { round },
316 },
317 RequestKind::Finalized { height } => ResolverFetch {
318 key: Key::Finalized { height },
319 subscriber: Annotation::Finalized(Finalized::ByHeight { height }),
320 },
321 RequestKind::CertifiedBlock { commitment, height } => ResolverFetch {
322 key: Key::Block(commitment),
323 subscriber: Annotation::Certified { height },
324 },
325 RequestKind::FinalizedBlockByHeight { commitment, height } => ResolverFetch {
326 key: Key::Block(commitment),
327 subscriber: Annotation::Finalized(Finalized::ByHeight { height }),
328 },
329 RequestKind::FinalizedBlockByRound { commitment, round } => ResolverFetch {
330 key: Key::Block(commitment),
331 subscriber: Annotation::Finalized(Finalized::ByRound { round }),
332 },
333 }
334 }
335}
336
337impl<D: Digest> From<Request<D>> for ResolverFetch<Key<D>, Annotation> {
338 fn from(fetch: Request<D>) -> Self {
339 fetch.into_inner()
340 }
341}
342
343pub(crate) fn above_height_floor<D: Digest>(
348 height: Height,
349) -> impl Fn(&Key<D>, &Annotation) -> bool + Send + 'static {
350 move |request, annotation| match (request, annotation) {
351 (Key::Finalized { height: requested }, _) => *requested > height,
352 (
353 Key::Block(_),
354 Annotation::Certified { height: requested }
355 | Annotation::Finalized(Finalized::ByHeight { height: requested }),
356 ) => *requested > height,
357 _ => true,
358 }
359}
360
361pub(crate) fn above_round_floor<D: Digest>(
366 round: Round,
367) -> impl Fn(&Key<D>, &Annotation) -> bool + Send + 'static {
368 move |request, annotation| match (request, annotation) {
369 (Key::Notarized { round: requested }, _) => *requested > round,
370 (Key::Block(_), Annotation::Finalized(Finalized::ByRound { round: requested })) => {
371 *requested > round
372 }
373 _ => true,
374 }
375}
376
377impl<D: Digest> Write for Key<D> {
378 fn write(&self, buf: &mut impl BufMut) {
379 self.subject().write(buf);
380 match self {
381 Self::Block(commitment) => commitment.write(buf),
382 Self::Finalized { height } => height.write(buf),
383 Self::Notarized { round } => round.write(buf),
384 }
385 }
386}
387
388impl<D: Digest> Read for Key<D> {
389 type Cfg = ();
390
391 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
392 let request = match u8::read(buf)? {
393 BLOCK_REQUEST => Self::Block(D::read(buf)?),
394 FINALIZED_REQUEST => Self::Finalized {
395 height: Height::read(buf)?,
396 },
397 NOTARIZED_REQUEST => Self::Notarized {
398 round: Round::read(buf)?,
399 },
400 i => return Err(CodecError::InvalidEnum(i)),
401 };
402 Ok(request)
403 }
404}
405
406impl<D: Digest> EncodeSize for Key<D> {
407 fn encode_size(&self) -> usize {
408 1 + match self {
409 Self::Block(commitment) => commitment.encode_size(),
410 Self::Finalized { height } => height.encode_size(),
411 Self::Notarized { round } => round.encode_size(),
412 }
413 }
414}
415
416impl<D: Digest> Span for Key<D> {}
417
418impl<D: Digest> PartialEq for Key<D> {
419 fn eq(&self, other: &Self) -> bool {
420 match (&self, &other) {
421 (Self::Block(a), Self::Block(b)) => a == b,
422 (Self::Finalized { height: a }, Self::Finalized { height: b }) => a == b,
423 (Self::Notarized { round: a }, Self::Notarized { round: b }) => a == b,
424 _ => false,
425 }
426 }
427}
428
429impl<D: Digest> Eq for Key<D> {}
430
431impl<D: Digest> Ord for Key<D> {
432 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
433 match (&self, &other) {
434 (Self::Block(a), Self::Block(b)) => a.cmp(b),
435 (Self::Finalized { height: a }, Self::Finalized { height: b }) => a.cmp(b),
436 (Self::Notarized { round: a }, Self::Notarized { round: b }) => a.cmp(b),
437 (a, b) => a.subject().cmp(&b.subject()),
438 }
439 }
440}
441
442impl<D: Digest> PartialOrd for Key<D> {
443 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
444 Some(self.cmp(other))
445 }
446}
447
448impl<D: Digest> Hash for Key<D> {
449 fn hash<H: Hasher>(&self, state: &mut H) {
450 self.subject().hash(state);
451 match self {
452 Self::Block(commitment) => commitment.hash(state),
453 Self::Finalized { height } => height.hash(state),
454 Self::Notarized { round } => round.hash(state),
455 }
456 }
457}
458
459impl<D: Digest> Display for Key<D> {
460 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
461 match self {
462 Self::Block(commitment) => write!(f, "Block({commitment:?})"),
463 Self::Finalized { height } => write!(f, "Finalized({height:?})"),
464 Self::Notarized { round } => write!(f, "Notarized({round:?})"),
465 }
466 }
467}
468
469impl<D: Digest> Debug for Key<D> {
470 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471 match self {
472 Self::Block(commitment) => write!(f, "Block({commitment:?})"),
473 Self::Finalized { height } => write!(f, "Finalized({height:?})"),
474 Self::Notarized { round } => write!(f, "Notarized({round:?})"),
475 }
476 }
477}
478
479#[cfg(feature = "arbitrary")]
480impl<D: Digest> arbitrary::Arbitrary<'_> for Key<D>
481where
482 D: for<'a> arbitrary::Arbitrary<'a>,
483{
484 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
485 let choice = u.int_in_range(0..=2)?;
486 match choice {
487 0 => Ok(Self::Block(u.arbitrary()?)),
488 1 => Ok(Self::Finalized {
489 height: u.arbitrary()?,
490 }),
491 2 => Ok(Self::Notarized {
492 round: u.arbitrary()?,
493 }),
494 _ => unreachable!(),
495 }
496 }
497}
498
499#[cfg(test)]
500mod tests {
501 use super::*;
502 use crate::types::{Epoch, View};
503 use commonware_codec::{Encode, ReadExt};
504 use commonware_cryptography::{
505 sha256::{Digest as Sha256Digest, Sha256},
506 Hasher as _,
507 };
508 use std::collections::BTreeSet;
509
510 type D = Sha256Digest;
511
512 #[test]
513 fn handler_drain_skips_closed_responses() {
514 let mut overflow = Pending::<D>::default();
515
516 let (closed_response, closed_receiver) = oneshot::channel();
517 Message::handle(
518 &mut overflow,
519 Message::Produce {
520 key: Key::Finalized {
521 height: Height::new(1),
522 },
523 response: closed_response,
524 },
525 );
526 drop(closed_receiver);
527
528 let (open_response, _open_receiver) = oneshot::channel();
529 Message::handle(
530 &mut overflow,
531 Message::Produce {
532 key: Key::Finalized {
533 height: Height::new(2),
534 },
535 response: open_response,
536 },
537 );
538
539 let mut messages = Vec::new();
540 Overflow::drain(&mut overflow, |message| {
541 messages.push(message);
542 None
543 });
544
545 assert_eq!(messages.len(), 1);
546 assert!(matches!(
547 messages.pop(),
548 Some(Message::Produce {
549 key: Key::Finalized { height },
550 ..
551 }) if height == Height::new(2)
552 ));
553 }
554
555 #[test]
556 fn test_cross_variant_hash_differs() {
557 use std::{
558 collections::hash_map::DefaultHasher,
559 hash::{Hash, Hasher},
560 };
561
562 fn hash_of<T: Hash>(t: &T) -> u64 {
563 let mut h = DefaultHasher::new();
564 t.hash(&mut h);
565 h.finish()
566 }
567
568 let finalized = Key::<D>::Finalized {
569 height: Height::new(1),
570 };
571 let notarized = Key::<D>::Notarized {
572 round: Round::new(Epoch::new(0), View::new(1)),
573 };
574 assert_ne!(hash_of(&finalized), hash_of(¬arized));
575 }
576
577 #[test]
578 fn test_subject_block_encoding() {
579 let commitment = Sha256::hash(b"test");
580 let request = Key::<D>::Block(commitment);
581
582 let encoded = request.encode();
584 assert_eq!(encoded.len(), 33); assert_eq!(encoded[0], 0); let mut buf = encoded.as_ref();
589 let decoded = Key::<D>::read(&mut buf).unwrap();
590 assert_eq!(request, decoded);
591 assert_eq!(decoded, Key::Block(commitment));
592 }
593
594 #[test]
595 fn test_subject_finalized_encoding() {
596 let height = Height::new(12345u64);
597 let request = Key::<D>::Finalized { height };
598
599 let encoded = request.encode();
601 assert_eq!(encoded[0], 1); let mut buf = encoded.as_ref();
605 let decoded = Key::<D>::read(&mut buf).unwrap();
606 assert_eq!(request, decoded);
607 assert_eq!(decoded, Key::Finalized { height });
608 }
609
610 #[test]
611 fn test_subject_notarized_encoding() {
612 let round = Round::new(Epoch::new(67890), View::new(12345));
613 let request = Key::<D>::Notarized { round };
614
615 let encoded = request.encode();
617 assert_eq!(encoded[0], 2); let mut buf = encoded.as_ref();
621 let decoded = Key::<D>::read(&mut buf).unwrap();
622 assert_eq!(request, decoded);
623 assert_eq!(decoded, Key::Notarized { round });
624 }
625
626 #[test]
627 fn test_subject_decode_rejects_invalid_enum_tag() {
628 let bad = [3u8];
629 let mut buf = bad.as_ref();
630 assert!(matches!(
631 Key::<D>::read(&mut buf),
632 Err(CodecError::InvalidEnum(3))
633 ));
634 }
635
636 #[test]
637 fn test_subject_hash() {
638 use std::collections::HashSet;
639
640 let r1 = Key::<D>::Finalized {
641 height: Height::new(100),
642 };
643 let r2 = Key::<D>::Finalized {
644 height: Height::new(100),
645 };
646 let r3 = Key::<D>::Finalized {
647 height: Height::new(200),
648 };
649
650 let mut set = HashSet::new();
651 set.insert(r1);
652 assert!(!set.insert(r2)); assert!(set.insert(r3)); }
655
656 #[test]
657 fn test_height_floor_predicate() {
658 let floor = Height::new(100);
659 let higher_finalized = Key::<D>::Finalized {
660 height: Height::new(200),
661 };
662 let notarized = Key::<D>::Notarized {
663 round: Round::new(Epoch::new(333), View::new(150)),
664 };
665 let block = Key::<D>::Block(Sha256::hash(b"block"));
666 let stale_finalized = Annotation::Finalized(Finalized::ByHeight {
667 height: Height::new(100),
668 });
669 let fresh_certified = Annotation::Certified {
670 height: Height::new(101),
671 };
672 let stale_certified = Annotation::Certified {
673 height: Height::new(100),
674 };
675
676 let predicate = above_height_floor(floor);
677 assert!(predicate(
678 &higher_finalized,
679 &Annotation::Finalized(Finalized::ByHeight {
680 height: Height::new(200),
681 })
682 ));
683 assert!(predicate(
684 ¬arized,
685 &Annotation::Notarization {
686 round: Round::new(Epoch::new(333), View::new(150)),
687 }
688 ));
689 assert!(predicate(&block, &fresh_certified));
690
691 let same_height = Key::<D>::Finalized {
692 height: Height::new(100),
693 };
694 assert!(!predicate(
695 &same_height,
696 &Annotation::Finalized(Finalized::ByHeight {
697 height: Height::new(100),
698 })
699 ));
700 assert!(!predicate(&block, &stale_finalized));
701 assert!(!predicate(&block, &stale_certified));
702 }
703
704 #[test]
705 fn test_round_floor_predicate() {
706 let floor = Round::new(Epoch::new(1), View::new(10));
707 let block = Key::<D>::Block(Sha256::hash(b"block"));
708 let higher_notarized = Key::<D>::Notarized {
709 round: Round::new(Epoch::new(1), View::new(11)),
710 };
711 let same_notarized = Key::<D>::Notarized {
712 round: Round::new(Epoch::new(1), View::new(10)),
713 };
714 let finalized = Key::<D>::Finalized {
715 height: Height::new(100),
716 };
717
718 let predicate = above_round_floor(floor);
719 assert!(predicate(
720 &higher_notarized,
721 &Annotation::Notarization {
722 round: Round::new(Epoch::new(1), View::new(11)),
723 }
724 ));
725 assert!(predicate(
726 &finalized,
727 &Annotation::Finalized(Finalized::ByHeight {
728 height: Height::new(100),
729 })
730 ));
731 assert!(predicate(
732 &block,
733 &Annotation::Finalized(Finalized::ByRound {
734 round: Round::new(Epoch::new(1), View::new(11)),
735 })
736 ));
737 assert!(!predicate(
738 &same_notarized,
739 &Annotation::Notarization {
740 round: Round::new(Epoch::new(1), View::new(10)),
741 }
742 ));
743 assert!(!predicate(
744 &block,
745 &Annotation::Finalized(Finalized::ByRound {
746 round: Round::new(Epoch::new(1), View::new(10)),
747 })
748 ));
749 }
750
751 #[test]
752 fn test_encode_size() {
753 let commitment = Sha256::hash(&[0u8; 32]);
754 let r1 = Key::<D>::Block(commitment);
755 let r2 = Key::<D>::Finalized {
756 height: Height::new(u64::MAX),
757 };
758 let r3 = Key::<D>::Notarized {
759 round: Round::new(Epoch::new(333), View::new(0)),
760 };
761
762 assert_eq!(r1.encode_size(), r1.encode().len());
764 assert_eq!(r2.encode_size(), r2.encode().len());
765 assert_eq!(r3.encode_size(), r3.encode().len());
766 }
767
768 #[test]
769 fn test_request_ord_same_variant() {
770 let commitment1 = Sha256::hash(b"test1");
772 let commitment2 = Sha256::hash(b"test2");
773 let block1 = Key::<D>::Block(commitment1);
774 let block2 = Key::<D>::Block(commitment2);
775
776 if commitment1 < commitment2 {
778 assert!(block1 < block2);
779 assert!(block2 > block1);
780 } else {
781 assert!(block1 > block2);
782 assert!(block2 < block1);
783 }
784
785 let fin1 = Key::<D>::Finalized {
787 height: Height::new(100),
788 };
789 let fin2 = Key::<D>::Finalized {
790 height: Height::new(200),
791 };
792 let fin3 = Key::<D>::Finalized {
793 height: Height::new(200),
794 };
795
796 assert!(fin1 < fin2);
797 assert!(fin2 > fin1);
798 assert_eq!(fin2.cmp(&fin3), std::cmp::Ordering::Equal);
799
800 let not1 = Key::<D>::Notarized {
802 round: Round::new(Epoch::new(333), View::new(50)),
803 };
804 let not2 = Key::<D>::Notarized {
805 round: Round::new(Epoch::new(333), View::new(150)),
806 };
807 let not3 = Key::<D>::Notarized {
808 round: Round::new(Epoch::new(333), View::new(150)),
809 };
810
811 assert!(not1 < not2);
812 assert!(not2 > not1);
813 assert_eq!(not2.cmp(¬3), std::cmp::Ordering::Equal);
814 }
815
816 #[test]
817 fn test_request_ord_cross_variant() {
818 let commitment = Sha256::hash(b"test");
819 let block = Key::<D>::Block(commitment);
820 let finalized = Key::<D>::Finalized {
821 height: Height::new(100),
822 };
823 let notarized = Key::<D>::Notarized {
824 round: Round::new(Epoch::new(333), View::new(200)),
825 };
826
827 assert!(block < finalized);
829 assert!(block < notarized);
830 assert!(finalized < notarized);
831
832 assert!(finalized > block);
833 assert!(notarized > block);
834 assert!(notarized > finalized);
835
836 assert_eq!(block.cmp(&finalized), std::cmp::Ordering::Less);
838 assert_eq!(block.cmp(¬arized), std::cmp::Ordering::Less);
839 assert_eq!(finalized.cmp(¬arized), std::cmp::Ordering::Less);
840 assert_eq!(finalized.cmp(&block), std::cmp::Ordering::Greater);
841 assert_eq!(notarized.cmp(&block), std::cmp::Ordering::Greater);
842 assert_eq!(notarized.cmp(&finalized), std::cmp::Ordering::Greater);
843 }
844
845 #[test]
846 fn test_request_partial_ord() {
847 let commitment1 = Sha256::hash(b"test1");
848 let commitment2 = Sha256::hash(b"test2");
849 let block1 = Key::<D>::Block(commitment1);
850 let block2 = Key::<D>::Block(commitment2);
851 let finalized = Key::<D>::Finalized {
852 height: Height::new(100),
853 };
854 let notarized = Key::<D>::Notarized {
855 round: Round::new(Epoch::new(333), View::new(200)),
856 };
857
858 assert!(block1.partial_cmp(&block2).is_some());
860 assert!(block1.partial_cmp(&finalized).is_some());
861 assert!(finalized.partial_cmp(¬arized).is_some());
862
863 assert_eq!(
865 block1.partial_cmp(&finalized),
866 Some(std::cmp::Ordering::Less)
867 );
868 assert_eq!(
869 finalized.partial_cmp(¬arized),
870 Some(std::cmp::Ordering::Less)
871 );
872 assert_eq!(
873 notarized.partial_cmp(&block1),
874 Some(std::cmp::Ordering::Greater)
875 );
876 }
877
878 #[test]
879 fn test_request_ord_sorting() {
880 let commitment1 = Sha256::hash(b"a");
881 let commitment2 = Sha256::hash(b"b");
882 let commitment3 = Sha256::hash(b"c");
883
884 let requests = vec![
885 Key::<D>::Notarized {
886 round: Round::new(Epoch::new(333), View::new(300)),
887 },
888 Key::<D>::Block(commitment2),
889 Key::<D>::Finalized {
890 height: Height::new(200),
891 },
892 Key::<D>::Block(commitment1),
893 Key::<D>::Notarized {
894 round: Round::new(Epoch::new(333), View::new(250)),
895 },
896 Key::<D>::Finalized {
897 height: Height::new(100),
898 },
899 Key::<D>::Block(commitment3),
900 ];
901
902 let sorted: Vec<_> = requests
904 .into_iter()
905 .collect::<BTreeSet<_>>()
906 .into_iter()
907 .collect();
908
909 assert_eq!(sorted.len(), 7);
911
912 assert!(matches!(sorted[0], Key::<D>::Block(_)));
914 assert!(matches!(sorted[1], Key::<D>::Block(_)));
915 assert!(matches!(sorted[2], Key::<D>::Block(_)));
916
917 assert_eq!(
919 sorted[3],
920 Key::<D>::Finalized {
921 height: Height::new(100)
922 }
923 );
924 assert_eq!(
925 sorted[4],
926 Key::<D>::Finalized {
927 height: Height::new(200)
928 }
929 );
930
931 assert_eq!(
933 sorted[5],
934 Key::<D>::Notarized {
935 round: Round::new(Epoch::new(333), View::new(250))
936 }
937 );
938 assert_eq!(
939 sorted[6],
940 Key::<D>::Notarized {
941 round: Round::new(Epoch::new(333), View::new(300))
942 }
943 );
944 }
945
946 #[test]
947 fn test_request_ord_edge_cases() {
948 let min_finalized = Key::<D>::Finalized {
950 height: Height::new(0),
951 };
952 let max_finalized = Key::<D>::Finalized {
953 height: Height::new(u64::MAX),
954 };
955 let min_notarized = Key::<D>::Notarized {
956 round: Round::new(Epoch::new(333), View::new(0)),
957 };
958 let max_notarized = Key::<D>::Notarized {
959 round: Round::new(Epoch::new(333), View::new(u64::MAX)),
960 };
961
962 assert!(min_finalized < max_finalized);
963 assert!(min_notarized < max_notarized);
964 assert!(max_finalized < min_notarized);
965
966 let commitment = Sha256::hash(b"self");
968 let block = Key::<D>::Block(commitment);
969 assert_eq!(block.cmp(&block), std::cmp::Ordering::Equal);
970 assert_eq!(min_finalized.cmp(&min_finalized), std::cmp::Ordering::Equal);
971 assert_eq!(max_notarized.cmp(&max_notarized), std::cmp::Ordering::Equal);
972 }
973
974 #[cfg(feature = "arbitrary")]
975 mod conformance {
976 use super::*;
977 use commonware_codec::conformance::CodecConformance;
978
979 commonware_conformance::conformance_tests! {
980 CodecConformance<Key<D>>
981 }
982 }
983}