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