1use crate::{Epochable, Viewable};
41use bytes::BufMut;
42use commonware_codec::{Buf, EncodeSize, Error, MaxEncodeSize, Read, ReadExt, Write, varint::UInt};
43#[cfg(not(target_arch = "wasm32"))]
44use commonware_runtime::telemetry::traces::TracedExt;
45use commonware_utils::sequence::U64;
46use core::{
47 fmt::{self, Display, Formatter},
48 marker::PhantomData,
49 num::{NonZeroU32, NonZeroU64},
50 ops::RangeInclusive,
51};
52
53#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
58#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
59pub struct Epoch(u64);
60
61impl Epoch {
62 pub const fn zero() -> Self {
64 Self(0)
65 }
66
67 pub const fn new(value: u64) -> Self {
69 Self(value)
70 }
71
72 pub const fn get(self) -> u64 {
74 self.0
75 }
76
77 pub const fn is_zero(self) -> bool {
79 self.0 == 0
80 }
81
82 pub const fn next(self) -> Self {
89 Self(self.0.checked_add(1).expect("epoch overflow"))
90 }
91
92 pub fn previous(self) -> Option<Self> {
98 self.0.checked_sub(1).map(Self)
99 }
100
101 pub const fn saturating_add(self, delta: EpochDelta) -> Self {
103 Self(self.0.saturating_add(delta.0))
104 }
105
106 pub fn checked_sub(self, delta: EpochDelta) -> Option<Self> {
108 self.0.checked_sub(delta.0).map(Self)
109 }
110
111 pub const fn saturating_sub(self, delta: EpochDelta) -> Self {
113 Self(self.0.saturating_sub(delta.0))
114 }
115}
116
117impl Display for Epoch {
118 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
119 write!(f, "{}", self.0)
120 }
121}
122
123impl Read for Epoch {
124 type Cfg = ();
125
126 fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, Error> {
127 let value: u64 = UInt::read(buf)?.into();
128 Ok(Self(value))
129 }
130}
131
132impl Write for Epoch {
133 fn write(&self, buf: &mut impl BufMut) {
134 UInt(self.0).write(buf);
135 }
136}
137
138impl EncodeSize for Epoch {
139 fn encode_size(&self) -> usize {
140 UInt(self.0).encode_size()
141 }
142}
143
144impl MaxEncodeSize for Epoch {
145 const MAX_ENCODE_SIZE: usize = UInt::<u64>::MAX_ENCODE_SIZE;
146}
147
148impl From<Epoch> for U64 {
149 fn from(epoch: Epoch) -> Self {
150 Self::from(epoch.get())
151 }
152}
153
154#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
158#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
159pub struct Height(u64);
160
161impl Height {
162 pub const fn zero() -> Self {
164 Self(0)
165 }
166
167 pub const fn new(value: u64) -> Self {
169 Self(value)
170 }
171
172 pub const fn get(self) -> u64 {
174 self.0
175 }
176
177 pub const fn is_zero(self) -> bool {
179 self.0 == 0
180 }
181
182 pub const fn next(self) -> Self {
189 Self(self.0.checked_add(1).expect("height overflow"))
190 }
191
192 pub fn previous(self) -> Option<Self> {
198 self.0.checked_sub(1).map(Self)
199 }
200
201 pub const fn saturating_add(self, delta: HeightDelta) -> Self {
203 Self(self.0.saturating_add(delta.0))
204 }
205
206 pub const fn saturating_sub(self, delta: HeightDelta) -> Self {
208 Self(self.0.saturating_sub(delta.0))
209 }
210
211 pub fn delta_from(self, other: Self) -> Option<HeightDelta> {
213 self.0.checked_sub(other.0).map(HeightDelta::new)
214 }
215
216 pub const fn range(start: Self, end: Self) -> HeightRange {
220 HeightRange {
221 inner: start.get()..end.get(),
222 }
223 }
224}
225
226impl Display for Height {
227 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
228 write!(f, "{}", self.0)
229 }
230}
231
232impl Read for Height {
233 type Cfg = ();
234
235 fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, Error> {
236 let value: u64 = UInt::read(buf)?.into();
237 Ok(Self(value))
238 }
239}
240
241impl Write for Height {
242 fn write(&self, buf: &mut impl BufMut) {
243 UInt(self.0).write(buf);
244 }
245}
246
247impl EncodeSize for Height {
248 fn encode_size(&self) -> usize {
249 UInt(self.0).encode_size()
250 }
251}
252
253impl MaxEncodeSize for Height {
254 const MAX_ENCODE_SIZE: usize = UInt::<u64>::MAX_ENCODE_SIZE;
255}
256
257impl From<Height> for U64 {
258 fn from(height: Height) -> Self {
259 Self::from(height.get())
260 }
261}
262
263#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
268#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
269pub struct View(u64);
270
271impl View {
272 pub const fn zero() -> Self {
274 Self(0)
275 }
276
277 pub const fn new(value: u64) -> Self {
279 Self(value)
280 }
281
282 pub const fn get(self) -> u64 {
284 self.0
285 }
286
287 pub const fn is_zero(self) -> bool {
289 self.0 == 0
290 }
291
292 pub const fn next(self) -> Self {
299 Self(self.0.checked_add(1).expect("view overflow"))
300 }
301
302 pub fn previous(self) -> Option<Self> {
308 self.0.checked_sub(1).map(Self)
309 }
310
311 pub const fn saturating_add(self, delta: ViewDelta) -> Self {
313 Self(self.0.saturating_add(delta.0))
314 }
315
316 pub const fn saturating_sub(self, delta: ViewDelta) -> Self {
318 Self(self.0.saturating_sub(delta.0))
319 }
320
321 pub const fn range(start: Self, end: Self) -> ViewRange {
325 ViewRange {
326 inner: start.get()..end.get(),
327 }
328 }
329
330 pub const fn term_start(self, term_length: TermLength) -> Self {
338 let term_length = term_length.get();
339 let Self(view) = self;
340 if view == 0 {
341 return self;
342 }
343 let base = (view - 1) / term_length * term_length;
345 Self(base).next()
346 }
347
348 pub const fn is_term_start(self, term_length: TermLength) -> bool {
350 let start = self.term_start(term_length);
351 self.get() == start.get()
352 }
353
354 pub const fn same_term(self, other: Self, term_length: TermLength) -> bool {
356 let start = self.term_start(term_length);
357 let other_start = other.term_start(term_length);
358 start.get() == other_start.get()
359 }
360
361 pub const fn term_end(self, term_length: TermLength) -> Self {
367 if self.0 == 0 {
368 return self;
369 }
370 let end = self
371 .term_start(term_length)
372 .get()
373 .checked_add(term_length.get() - 1)
374 .expect("view term_end overflow");
375 Self(end)
376 }
377
378 pub const fn next_term_start(self, term_length: TermLength) -> Self {
382 self.term_end(term_length).next()
383 }
384
385 pub const fn term_index(self, term_length: TermLength) -> u64 {
391 self.get().div_ceil(term_length.get())
392 }
393
394 pub const fn covers(self, view: Self, term_length: TermLength) -> bool {
399 self.get() <= view.get() && self.same_term(view, term_length)
400 }
401
402 pub const fn covering_range(self, term_length: TermLength) -> RangeInclusive<Self> {
408 self.term_start(term_length)..=self
409 }
410
411 pub const fn admits(self, pending: Self, term_length: TermLength) -> bool {
425 if pending.get() <= self.get() || pending.get() == self.next().get() {
426 return true;
427 }
428 pending.is_term_start(term_length) && self.same_term(Self(pending.get() - 1), term_length)
434 }
435}
436
437impl Display for View {
438 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
439 write!(f, "{}", self.0)
440 }
441}
442
443#[cfg(not(target_arch = "wasm32"))]
444impl TracedExt for Epoch {
445 fn traced(self) -> i64 {
446 self.0.traced()
447 }
448}
449
450#[cfg(not(target_arch = "wasm32"))]
451impl TracedExt for Height {
452 fn traced(self) -> i64 {
453 self.0.traced()
454 }
455}
456
457#[cfg(not(target_arch = "wasm32"))]
458impl TracedExt for View {
459 fn traced(self) -> i64 {
460 self.0.traced()
461 }
462}
463
464impl Read for View {
465 type Cfg = ();
466
467 fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, Error> {
468 let value: u64 = UInt::read(buf)?.into();
469 Ok(Self(value))
470 }
471}
472
473impl Write for View {
474 fn write(&self, buf: &mut impl BufMut) {
475 UInt(self.0).write(buf);
476 }
477}
478
479impl EncodeSize for View {
480 fn encode_size(&self) -> usize {
481 UInt(self.0).encode_size()
482 }
483}
484
485impl MaxEncodeSize for View {
486 const MAX_ENCODE_SIZE: usize = UInt::<u64>::MAX_ENCODE_SIZE;
487}
488
489impl From<View> for U64 {
490 fn from(view: View) -> Self {
491 Self::from(view.get())
492 }
493}
494
495#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
503pub struct Delta<T>(u64, PhantomData<T>);
504
505impl<T> Delta<T> {
506 pub const fn zero() -> Self {
508 Self(0, PhantomData)
509 }
510
511 pub const fn new(value: u64) -> Self {
513 Self(value, PhantomData)
514 }
515
516 pub const fn get(self) -> u64 {
518 self.0
519 }
520
521 pub const fn is_zero(self) -> bool {
523 self.0 == 0
524 }
525}
526
527impl<T> Display for Delta<T> {
528 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
529 write!(f, "{}", self.0)
530 }
531}
532
533pub type EpochDelta = Delta<Epoch>;
538
539pub type HeightDelta = Delta<Height>;
544
545pub type ViewDelta = Delta<View>;
550
551#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
578pub struct TermLength(u32);
579
580impl TermLength {
581 pub const MAX: Self = Self(u32::MAX);
585
586 pub const ONE: Self = Self(1);
588
589 pub const fn new(length: NonZeroU32) -> Self {
591 Self(length.get())
592 }
593
594 pub const fn get(self) -> u64 {
596 self.0 as u64
597 }
598}
599
600impl Default for TermLength {
601 fn default() -> Self {
602 Self::ONE
603 }
604}
605
606#[cfg(feature = "arbitrary")]
607impl arbitrary::Arbitrary<'_> for TermLength {
608 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
609 Ok(Self(u.int_in_range(1..=u32::MAX)?))
610 }
611}
612
613impl Display for TermLength {
614 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
615 write!(f, "{}", self.0)
616 }
617}
618
619#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
624#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
625pub struct Round {
626 epoch: Epoch,
627 view: View,
628}
629
630impl Round {
631 pub const fn new(epoch: Epoch, view: View) -> Self {
633 Self { epoch, view }
634 }
635
636 pub const fn zero() -> Self {
638 Self::new(Epoch::zero(), View::zero())
639 }
640
641 pub const fn epoch(self) -> Epoch {
643 self.epoch
644 }
645
646 pub const fn view(self) -> View {
648 self.view
649 }
650}
651
652impl Epochable for Round {
653 fn epoch(&self) -> Epoch {
654 self.epoch
655 }
656}
657
658impl Viewable for Round {
659 fn view(&self) -> View {
660 self.view
661 }
662}
663
664impl From<(Epoch, View)> for Round {
665 fn from((epoch, view): (Epoch, View)) -> Self {
666 Self { epoch, view }
667 }
668}
669
670impl From<Round> for (Epoch, View) {
671 fn from(round: Round) -> Self {
672 (round.epoch, round.view)
673 }
674}
675
676#[derive(Clone, Copy, Debug, PartialEq, Eq)]
680pub enum EpochPhase {
681 Early,
683 Midpoint,
685 Late,
687}
688
689#[derive(Clone, Copy, Debug, PartialEq, Eq)]
691pub struct EpochInfo {
692 epoch: Epoch,
693 height: Height,
694 first: Height,
695 last: Height,
696}
697
698impl EpochInfo {
699 pub const fn new(epoch: Epoch, height: Height, first: Height, last: Height) -> Self {
701 Self {
702 epoch,
703 height,
704 first,
705 last,
706 }
707 }
708
709 pub const fn epoch(&self) -> Epoch {
711 self.epoch
712 }
713
714 pub const fn height(&self) -> Height {
716 self.height
717 }
718
719 pub const fn first(&self) -> Height {
721 self.first
722 }
723
724 pub const fn last(&self) -> Height {
726 self.last
727 }
728
729 pub const fn length(&self) -> HeightDelta {
731 HeightDelta::new(self.last.get() - self.first.get() + 1)
732 }
733
734 pub const fn relative(&self) -> Height {
736 Height::new(self.height.get() - self.first.get())
737 }
738
739 pub const fn phase(&self) -> EpochPhase {
741 let relative = self.relative().get();
742 let midpoint = self.length().get() / 2;
743
744 if relative < midpoint {
745 EpochPhase::Early
746 } else if relative == midpoint {
747 EpochPhase::Midpoint
748 } else {
749 EpochPhase::Late
750 }
751 }
752}
753
754pub trait Epocher: Clone + Send + Sync + 'static {
764 fn containing(&self, height: Height) -> Option<EpochInfo>;
768
769 fn first(&self, epoch: Epoch) -> Option<Height>;
773
774 fn last(&self, epoch: Epoch) -> Option<Height>;
778}
779
780#[derive(Clone, Debug, PartialEq, Eq)]
785pub struct FixedEpocher(u64);
786
787impl FixedEpocher {
788 pub const fn new(length: NonZeroU64) -> Self {
801 assert!(length.get() > 1, "epoch length must exceed one");
802 Self(length.get())
803 }
804
805 fn bounds(&self, epoch: Epoch) -> Option<(Height, Height)> {
808 let first = epoch.get().checked_mul(self.0)?;
809 let last = first.checked_add(self.0 - 1)?;
810 Some((Height::new(first), Height::new(last)))
811 }
812
813 pub fn midpoint(&self, epoch: Epoch) -> Option<Height> {
817 let (first, _) = self.bounds(epoch)?;
818 first.get().checked_add(self.0 / 2).map(Height::new)
819 }
820}
821
822impl Epocher for FixedEpocher {
823 fn containing(&self, height: Height) -> Option<EpochInfo> {
824 let epoch = Epoch::new(height.get() / self.0);
825 let (first, last) = self.bounds(epoch)?;
826 Some(EpochInfo::new(epoch, height, first, last))
827 }
828
829 fn first(&self, epoch: Epoch) -> Option<Height> {
830 self.bounds(epoch).map(|(first, _)| first)
831 }
832
833 fn last(&self, epoch: Epoch) -> Option<Height> {
834 self.bounds(epoch).map(|(_, last)| last)
835 }
836}
837
838impl Read for Round {
839 type Cfg = ();
840
841 fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, Error> {
842 Ok(Self {
843 epoch: Epoch::read(buf)?,
844 view: View::read(buf)?,
845 })
846 }
847}
848
849impl Write for Round {
850 fn write(&self, buf: &mut impl BufMut) {
851 self.epoch.write(buf);
852 self.view.write(buf);
853 }
854}
855
856impl EncodeSize for Round {
857 fn encode_size(&self) -> usize {
858 self.epoch.encode_size() + self.view.encode_size()
859 }
860}
861
862impl MaxEncodeSize for Round {
863 const MAX_ENCODE_SIZE: usize = Epoch::MAX_ENCODE_SIZE + View::MAX_ENCODE_SIZE;
864}
865
866impl Display for Round {
867 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
868 write!(f, "({}, {})", self.epoch, self.view)
869 }
870}
871
872pub struct ViewRange {
876 inner: std::ops::Range<u64>,
877}
878
879impl Iterator for ViewRange {
880 type Item = View;
881
882 fn next(&mut self) -> Option<Self::Item> {
883 self.inner.next().map(View::new)
884 }
885
886 fn size_hint(&self) -> (usize, Option<usize>) {
887 self.inner.size_hint()
888 }
889}
890
891impl DoubleEndedIterator for ViewRange {
892 fn next_back(&mut self) -> Option<Self::Item> {
893 self.inner.next_back().map(View::new)
894 }
895}
896
897impl ExactSizeIterator for ViewRange {
898 fn len(&self) -> usize {
899 self.size_hint().0
900 }
901}
902
903pub struct HeightRange {
907 inner: std::ops::Range<u64>,
908}
909
910impl Iterator for HeightRange {
911 type Item = Height;
912
913 fn next(&mut self) -> Option<Self::Item> {
914 self.inner.next().map(Height::new)
915 }
916
917 fn size_hint(&self) -> (usize, Option<usize>) {
918 self.inner.size_hint()
919 }
920}
921
922impl DoubleEndedIterator for HeightRange {
923 fn next_back(&mut self) -> Option<Self::Item> {
924 self.inner.next_back().map(Height::new)
925 }
926}
927
928impl ExactSizeIterator for HeightRange {
929 fn len(&self) -> usize {
930 self.size_hint().0
931 }
932}
933
934pub use commonware_utils::Participant;
936
937commonware_macros::stability_scope!(ALPHA {
938 pub mod coding {
939 use commonware_codec::{Encode, FixedArray, FixedSize, Read, ReadExt, Write};
942 use commonware_coding::{Config as CodingConfig, Scheme};
943 use commonware_cryptography::{Digest, Digestible, Hasher};
944 use commonware_math::algebra::Random;
945 use commonware_utils::{Array, NZU16, Span};
946 use core::{
947 cmp::Ordering,
948 hash::{Hash, Hasher as StdHasher},
949 marker::PhantomData,
950 num::NonZeroU16,
951 ops::Deref,
952 };
953 use rand_core::CryptoRng;
954
955 pub const COMMITMENT_DIGEST_SIZE: usize = 32;
960
961 pub const COMMITMENT_SIZE: usize = 3 * COMMITMENT_DIGEST_SIZE + CodingConfig::SIZE;
963
964 #[derive(FixedArray)]
979 #[fixed_array(bytes([u8; COMMITMENT_SIZE]))]
980 pub struct Commitment<B, C, H>([u8; COMMITMENT_SIZE], PhantomData<(B, C, H)>);
981
982 impl<B, C, H> Clone for Commitment<B, C, H> {
983 fn clone(&self) -> Self {
984 *self
985 }
986 }
987
988 impl<B, C, H> Copy for Commitment<B, C, H> {}
989
990 impl<B, C, H> PartialEq for Commitment<B, C, H> {
991 fn eq(&self, other: &Self) -> bool {
992 self.0 == other.0
993 }
994 }
995
996 impl<B, C, H> Eq for Commitment<B, C, H> {}
997
998 impl<B, C, H> PartialOrd for Commitment<B, C, H> {
999 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1000 Some(self.cmp(other))
1001 }
1002 }
1003
1004 impl<B, C, H> Ord for Commitment<B, C, H> {
1005 fn cmp(&self, other: &Self) -> Ordering {
1006 self.0.cmp(&other.0)
1007 }
1008 }
1009
1010 impl<B, C, H> Hash for Commitment<B, C, H> {
1011 fn hash<S: StdHasher>(&self, state: &mut S) {
1012 self.0.hash(state);
1013 }
1014 }
1015
1016 impl<B: Digestible, C: Scheme, H: Hasher> Commitment<B, C, H> {
1017 const BLOCK_OFFSET: usize = 0;
1018 const ROOT_OFFSET: usize = Self::BLOCK_OFFSET + COMMITMENT_DIGEST_SIZE;
1019 const CONTEXT_OFFSET: usize = Self::ROOT_OFFSET + COMMITMENT_DIGEST_SIZE;
1020 const CONFIG_OFFSET: usize = Self::CONTEXT_OFFSET + COMMITMENT_DIGEST_SIZE;
1021
1022 pub fn block(&self) -> B::Digest {
1024 self.field(Self::BLOCK_OFFSET)
1025 }
1026
1027 pub fn root(&self) -> C::Commitment {
1029 self.field(Self::ROOT_OFFSET)
1030 }
1031
1032 pub fn context(&self) -> H::Digest {
1034 self.field(Self::CONTEXT_OFFSET)
1035 }
1036
1037 pub fn config(&self) -> CodingConfig {
1039 self.field(Self::CONFIG_OFFSET)
1040 }
1041
1042 fn field<T: ReadExt + FixedSize>(&self, offset: usize) -> T {
1043 T::read(&mut commonware_codec::Copying(&self.0[offset..offset + T::SIZE]))
1044 .expect("fields are validated on decode and typed construction")
1045 }
1046
1047 fn validate_field<T: ReadExt + FixedSize>(
1049 bytes: &[u8],
1050 offset: usize,
1051 reason: &'static str,
1052 ) -> Result<(), commonware_codec::Error> {
1053 let field_end = offset + T::SIZE;
1054 let padding_end = offset + COMMITMENT_DIGEST_SIZE;
1055 T::read(&mut commonware_codec::Copying(&bytes[offset..field_end]))
1056 .map_err(|_| commonware_codec::Error::Invalid("Commitment", reason))?;
1057 if bytes[field_end..padding_end].iter().any(|byte| *byte != 0) {
1058 return Err(commonware_codec::Error::Invalid(
1059 "Commitment",
1060 "non-zero digest padding",
1061 ));
1062 }
1063 Ok(())
1064 }
1065
1066 const fn assert_layout() {
1068 assert!(
1069 B::Digest::SIZE <= COMMITMENT_DIGEST_SIZE,
1070 "block digest exceeds commitment field size"
1071 );
1072 assert!(
1073 C::Commitment::SIZE <= COMMITMENT_DIGEST_SIZE,
1074 "coding root exceeds commitment field size"
1075 );
1076 assert!(
1077 H::Digest::SIZE <= COMMITMENT_DIGEST_SIZE,
1078 "context digest exceeds commitment field size"
1079 );
1080 }
1081 }
1082
1083 impl<B: Digestible, C: Scheme, H: Hasher> Random for Commitment<B, C, H> {
1084 fn random(mut rng: impl CryptoRng) -> Self {
1085 let one = NZU16!(1);
1086 let shards = rng.next_u32();
1087 let config = CodingConfig {
1088 minimum_shards: NonZeroU16::new(shards as u16).unwrap_or(one),
1089 extra_shards: NonZeroU16::new((shards >> 16) as u16).unwrap_or(one),
1090 };
1091 Self::from((
1092 B::Digest::random(&mut rng),
1093 C::Commitment::random(&mut rng),
1094 H::Digest::random(&mut rng),
1095 config,
1096 ))
1097 }
1098 }
1099
1100 impl<B: Digestible, C: Scheme, H: Hasher> Digest for Commitment<B, C, H> {
1101 const EMPTY: Self = {
1104 Self::assert_layout();
1105 Self([0u8; COMMITMENT_SIZE], PhantomData)
1106 };
1107 }
1108
1109 impl<B: Digestible, C: Scheme, H: Hasher> Write for Commitment<B, C, H> {
1110 fn write(&self, buf: &mut impl bytes::BufMut) {
1111 buf.put_slice(self.as_ref());
1112 }
1113 }
1114
1115 impl<B: Digestible, C: Scheme, H: Hasher> FixedSize for Commitment<B, C, H> {
1116 const SIZE: usize = COMMITMENT_SIZE;
1117 }
1118
1119 impl<B: Digestible, C: Scheme, H: Hasher> Read for Commitment<B, C, H> {
1120 type Cfg = ();
1121
1122 fn read_cfg(
1123 buf: &mut impl commonware_codec::Buf,
1124 _cfg: &Self::Cfg,
1125 ) -> Result<Self, commonware_codec::Error> {
1126 const { Self::assert_layout() };
1127 let arr = <[u8; COMMITMENT_SIZE]>::read(buf)?;
1128
1129 Self::validate_field::<B::Digest>(
1130 &arr,
1131 Self::BLOCK_OFFSET,
1132 "invalid block digest",
1133 )?;
1134 Self::validate_field::<C::Commitment>(
1135 &arr,
1136 Self::ROOT_OFFSET,
1137 "invalid coding root",
1138 )?;
1139 Self::validate_field::<H::Digest>(
1140 &arr,
1141 Self::CONTEXT_OFFSET,
1142 "invalid context digest",
1143 )?;
1144 let mut cursor = commonware_codec::Copying(&arr[Self::CONFIG_OFFSET..]);
1145 CodingConfig::read(&mut cursor).map_err(|_| {
1146 commonware_codec::Error::Invalid("Commitment", "invalid embedded CodingConfig")
1147 })?;
1148
1149 Ok(Self(arr, PhantomData))
1150 }
1151 }
1152
1153 impl<B: Digestible, C: Scheme, H: Hasher> AsRef<[u8]> for Commitment<B, C, H> {
1154 fn as_ref(&self) -> &[u8] {
1155 &self.0
1156 }
1157 }
1158
1159 impl<B: Digestible, C: Scheme, H: Hasher> Deref for Commitment<B, C, H> {
1160 type Target = [u8];
1161
1162 fn deref(&self) -> &Self::Target {
1163 self.as_ref()
1164 }
1165 }
1166
1167 impl<B: Digestible, C: Scheme, H: Hasher> core::fmt::Display for Commitment<B, C, H> {
1168 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1169 write!(f, "{}", commonware_formatting::Hex(self.as_ref()))
1170 }
1171 }
1172
1173 impl<B: Digestible, C: Scheme, H: Hasher> core::fmt::Debug for Commitment<B, C, H> {
1174 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1175 write!(f, "{}", commonware_formatting::Hex(self.as_ref()))
1176 }
1177 }
1178
1179 impl<B: Digestible, C: Scheme, H: Hasher> Default for Commitment<B, C, H> {
1180 fn default() -> Self {
1181 Self::EMPTY
1182 }
1183 }
1184
1185 impl<B: Digestible, C: Scheme, H: Hasher>
1186 From<(B::Digest, C::Commitment, H::Digest, CodingConfig)> for Commitment<B, C, H>
1187 {
1188 fn from(
1189 (block, root, context, config): (B::Digest, C::Commitment, H::Digest, CodingConfig),
1190 ) -> Self {
1191 const { Self::assert_layout() };
1192
1193 let mut buf = [0u8; COMMITMENT_SIZE];
1194 buf[Self::BLOCK_OFFSET..Self::BLOCK_OFFSET + B::Digest::SIZE]
1195 .copy_from_slice(&block);
1196 buf[Self::ROOT_OFFSET..Self::ROOT_OFFSET + C::Commitment::SIZE]
1197 .copy_from_slice(&root);
1198 buf[Self::CONTEXT_OFFSET..Self::CONTEXT_OFFSET + H::Digest::SIZE]
1199 .copy_from_slice(&context);
1200 buf[Self::CONFIG_OFFSET..].copy_from_slice(&config.encode());
1201 Self(buf, PhantomData)
1202 }
1203 }
1204
1205 impl<B: Digestible, C: Scheme, H: Hasher> Span for Commitment<B, C, H> {}
1206
1207 impl<B: Digestible, C: Scheme, H: Hasher> Array for Commitment<B, C, H> {}
1208
1209 #[cfg(feature = "arbitrary")]
1210 impl<B, C, H> arbitrary::Arbitrary<'_> for Commitment<B, C, H>
1211 where
1212 B: Digestible,
1213 B::Digest: for<'a> arbitrary::Arbitrary<'a>,
1214 C: Scheme,
1215 C::Commitment: for<'a> arbitrary::Arbitrary<'a>,
1216 H: Hasher,
1217 H::Digest: for<'a> arbitrary::Arbitrary<'a>,
1218 {
1219 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1220 Ok(Self::from((
1221 B::Digest::arbitrary(u)?,
1222 C::Commitment::arbitrary(u)?,
1223 H::Digest::arbitrary(u)?,
1224 CodingConfig::arbitrary(u)?,
1225 )))
1226 }
1227 }
1228 }
1229});
1230
1231#[cfg(test)]
1232mod tests {
1233 use super::*;
1234 use crate::types::coding::{COMMITMENT_SIZE, Commitment};
1235 use commonware_codec::{DecodeExt, Encode, EncodeSize, FixedSize};
1236 use commonware_coding::{Config as CodingConfig, ReedSolomon};
1237 use commonware_cryptography::{Digest as DigestTrait, Digestible, Hasher};
1238 use commonware_math::algebra::Random;
1239 use commonware_parallel::Strategy;
1240 use commonware_utils::{Array, NZU16, NZU64, Span, test_rng};
1241 use std::{marker::PhantomData, ops::Deref};
1242
1243 #[derive(Clone)]
1244 struct TestBlock<D>(PhantomData<D>);
1245
1246 impl<D: DigestTrait> Digestible for TestBlock<D> {
1247 type Digest = D;
1248
1249 fn digest(&self) -> Self::Digest {
1250 unreachable!("test block is only used to bind commitment digest types")
1251 }
1252 }
1253
1254 #[derive(Clone)]
1255 struct TestHasher<D>(PhantomData<D>);
1256
1257 impl<D> Default for TestHasher<D> {
1258 fn default() -> Self {
1259 Self(PhantomData)
1260 }
1261 }
1262
1263 impl<D: DigestTrait> Hasher for TestHasher<D> {
1264 type Digest = D;
1265
1266 fn hash(_parts: &[&[u8]]) -> Self::Digest {
1267 D::EMPTY
1268 }
1269
1270 fn hash_pair(
1271 _left: &[&[u8]],
1272 _right: &[&[u8]],
1273 _strategy: &impl Strategy,
1274 ) -> (Self::Digest, Self::Digest) {
1275 (D::EMPTY, D::EMPTY)
1276 }
1277
1278 fn update(&mut self, _message: &[u8]) -> &mut Self {
1279 self
1280 }
1281
1282 fn finalize(self) -> (Self, Self::Digest) {
1283 (self, D::EMPTY)
1284 }
1285 }
1286
1287 #[test]
1288 fn test_epoch_constructors() {
1289 assert_eq!(Epoch::zero().get(), 0);
1290 assert_eq!(Epoch::new(42).get(), 42);
1291 assert_eq!(Epoch::default().get(), 0);
1292 }
1293
1294 #[test]
1295 fn test_epoch_is_zero() {
1296 assert!(Epoch::zero().is_zero());
1297 assert!(Epoch::new(0).is_zero());
1298 assert!(!Epoch::new(1).is_zero());
1299 assert!(!Epoch::new(100).is_zero());
1300 }
1301
1302 #[test]
1303 fn test_epoch_next() {
1304 assert_eq!(Epoch::zero().next().get(), 1);
1305 assert_eq!(Epoch::new(5).next().get(), 6);
1306 assert_eq!(Epoch::new(999).next().get(), 1000);
1307 }
1308
1309 #[test]
1310 #[should_panic(expected = "epoch overflow")]
1311 fn test_epoch_next_overflow() {
1312 Epoch::new(u64::MAX).next();
1313 }
1314
1315 #[test]
1316 fn test_epoch_previous() {
1317 assert_eq!(Epoch::zero().previous(), None);
1318 assert_eq!(Epoch::new(1).previous(), Some(Epoch::zero()));
1319 assert_eq!(Epoch::new(5).previous(), Some(Epoch::new(4)));
1320 assert_eq!(Epoch::new(1000).previous(), Some(Epoch::new(999)));
1321 }
1322
1323 #[test]
1324 fn test_epoch_saturating_add() {
1325 assert_eq!(Epoch::zero().saturating_add(EpochDelta::new(5)).get(), 5);
1326 assert_eq!(Epoch::new(10).saturating_add(EpochDelta::new(20)).get(), 30);
1327 assert_eq!(
1328 Epoch::new(u64::MAX)
1329 .saturating_add(EpochDelta::new(1))
1330 .get(),
1331 u64::MAX
1332 );
1333 assert_eq!(
1334 Epoch::new(u64::MAX - 5)
1335 .saturating_add(EpochDelta::new(10))
1336 .get(),
1337 u64::MAX
1338 );
1339 }
1340
1341 #[test]
1342 fn test_epoch_checked_sub() {
1343 assert_eq!(
1344 Epoch::new(10).checked_sub(EpochDelta::new(5)),
1345 Some(Epoch::new(5))
1346 );
1347 assert_eq!(
1348 Epoch::new(5).checked_sub(EpochDelta::new(5)),
1349 Some(Epoch::zero())
1350 );
1351 assert_eq!(Epoch::new(5).checked_sub(EpochDelta::new(10)), None);
1352 assert_eq!(Epoch::zero().checked_sub(EpochDelta::new(1)), None);
1353 }
1354
1355 #[test]
1356 fn test_epoch_saturating_sub() {
1357 assert_eq!(Epoch::new(10).saturating_sub(EpochDelta::new(5)).get(), 5);
1358 assert_eq!(Epoch::new(5).saturating_sub(EpochDelta::new(5)).get(), 0);
1359 assert_eq!(Epoch::new(5).saturating_sub(EpochDelta::new(10)).get(), 0);
1360 assert_eq!(Epoch::zero().saturating_sub(EpochDelta::new(100)).get(), 0);
1361 }
1362
1363 #[test]
1364 fn test_epoch_display() {
1365 assert_eq!(format!("{}", Epoch::zero()), "0");
1366 assert_eq!(format!("{}", Epoch::new(42)), "42");
1367 assert_eq!(format!("{}", Epoch::new(1000)), "1000");
1368 }
1369
1370 #[test]
1371 fn test_epoch_ordering() {
1372 assert!(Epoch::zero() < Epoch::new(1));
1373 assert!(Epoch::new(5) < Epoch::new(10));
1374 assert!(Epoch::new(10) > Epoch::new(5));
1375 assert_eq!(Epoch::new(42), Epoch::new(42));
1376 }
1377
1378 #[test]
1379 fn test_epoch_encode_decode() {
1380 let cases = vec![0u64, 1, 127, 128, 255, 256, u64::MAX];
1381 for value in cases {
1382 let epoch = Epoch::new(value);
1383 let encoded = epoch.encode();
1384 assert_eq!(encoded.len(), epoch.encode_size());
1385 let decoded = Epoch::decode(encoded).unwrap();
1386 assert_eq!(epoch, decoded);
1387 }
1388 }
1389
1390 #[test]
1391 fn test_height_constructors() {
1392 assert_eq!(Height::zero().get(), 0);
1393 assert_eq!(Height::new(42).get(), 42);
1394 assert_eq!(Height::new(100).get(), 100);
1395 assert_eq!(Height::default().get(), 0);
1396 }
1397
1398 #[test]
1399 fn test_height_is_zero() {
1400 assert!(Height::zero().is_zero());
1401 assert!(Height::new(0).is_zero());
1402 assert!(!Height::new(1).is_zero());
1403 assert!(!Height::new(100).is_zero());
1404 }
1405
1406 #[test]
1407 fn test_height_next() {
1408 assert_eq!(Height::zero().next().get(), 1);
1409 assert_eq!(Height::new(5).next().get(), 6);
1410 assert_eq!(Height::new(999).next().get(), 1000);
1411 }
1412
1413 #[test]
1414 #[should_panic(expected = "height overflow")]
1415 fn test_height_next_overflow() {
1416 Height::new(u64::MAX).next();
1417 }
1418
1419 #[test]
1420 fn test_height_previous() {
1421 assert_eq!(Height::zero().previous(), None);
1422 assert_eq!(Height::new(1).previous(), Some(Height::zero()));
1423 assert_eq!(Height::new(5).previous(), Some(Height::new(4)));
1424 assert_eq!(Height::new(1000).previous(), Some(Height::new(999)));
1425 }
1426
1427 #[test]
1428 fn test_height_saturating_add() {
1429 let delta5 = HeightDelta::new(5);
1430 let delta100 = HeightDelta::new(100);
1431 assert_eq!(Height::zero().saturating_add(delta5).get(), 5);
1432 assert_eq!(Height::new(10).saturating_add(delta100).get(), 110);
1433 assert_eq!(
1434 Height::new(u64::MAX)
1435 .saturating_add(HeightDelta::new(1))
1436 .get(),
1437 u64::MAX
1438 );
1439 }
1440
1441 #[test]
1442 fn test_height_saturating_sub() {
1443 let delta5 = HeightDelta::new(5);
1444 let delta100 = HeightDelta::new(100);
1445 assert_eq!(Height::new(10).saturating_sub(delta5).get(), 5);
1446 assert_eq!(Height::new(5).saturating_sub(delta5).get(), 0);
1447 assert_eq!(Height::new(5).saturating_sub(delta100).get(), 0);
1448 assert_eq!(Height::zero().saturating_sub(delta100).get(), 0);
1449 }
1450
1451 #[test]
1452 fn test_height_display() {
1453 assert_eq!(format!("{}", Height::zero()), "0");
1454 assert_eq!(format!("{}", Height::new(42)), "42");
1455 assert_eq!(format!("{}", Height::new(1000)), "1000");
1456 }
1457
1458 #[test]
1459 fn test_height_ordering() {
1460 assert!(Height::zero() < Height::new(1));
1461 assert!(Height::new(5) < Height::new(10));
1462 assert!(Height::new(10) > Height::new(5));
1463 assert_eq!(Height::new(42), Height::new(42));
1464 }
1465
1466 #[test]
1467 fn test_height_encode_decode() {
1468 let cases = vec![0u64, 1, 127, 128, 255, 256, u64::MAX];
1469 for value in cases {
1470 let height = Height::new(value);
1471 let encoded = height.encode();
1472 assert_eq!(encoded.len(), height.encode_size());
1473 let decoded = Height::decode(encoded).unwrap();
1474 assert_eq!(height, decoded);
1475 }
1476 }
1477
1478 #[test]
1479 fn test_height_delta_from() {
1480 assert_eq!(
1481 Height::new(10).delta_from(Height::new(3)),
1482 Some(HeightDelta::new(7))
1483 );
1484 assert_eq!(
1485 Height::new(5).delta_from(Height::new(5)),
1486 Some(HeightDelta::zero())
1487 );
1488 assert_eq!(Height::new(3).delta_from(Height::new(10)), None);
1489 assert_eq!(Height::zero().delta_from(Height::new(1)), None);
1490 }
1491
1492 #[test]
1493 fn height_range_iterates() {
1494 let collected: Vec<_> = Height::range(Height::new(3), Height::new(6))
1495 .map(Height::get)
1496 .collect();
1497 assert_eq!(collected, vec![3, 4, 5]);
1498 }
1499
1500 #[test]
1501 fn height_range_empty() {
1502 let collected: Vec<_> = Height::range(Height::new(5), Height::new(5)).collect();
1503 assert_eq!(collected, vec![]);
1504
1505 let collected: Vec<_> = Height::range(Height::new(10), Height::new(5)).collect();
1506 assert_eq!(collected, vec![]);
1507 }
1508
1509 #[test]
1510 fn height_range_single() {
1511 let collected: Vec<_> = Height::range(Height::new(5), Height::new(6))
1512 .map(Height::get)
1513 .collect();
1514 assert_eq!(collected, vec![5]);
1515 }
1516
1517 #[test]
1518 fn height_range_size_hint() {
1519 let range = Height::range(Height::new(3), Height::new(10));
1520 assert_eq!(range.size_hint(), (7, Some(7)));
1521 assert_eq!(range.len(), 7);
1522
1523 let empty = Height::range(Height::new(5), Height::new(5));
1524 assert_eq!(empty.size_hint(), (0, Some(0)));
1525 assert_eq!(empty.len(), 0);
1526 }
1527
1528 #[test]
1529 fn height_range_rev() {
1530 let collected: Vec<_> = Height::range(Height::new(3), Height::new(7))
1531 .rev()
1532 .map(Height::get)
1533 .collect();
1534 assert_eq!(collected, vec![6, 5, 4, 3]);
1535 }
1536
1537 #[test]
1538 fn height_range_double_ended() {
1539 let mut range = Height::range(Height::new(5), Height::new(10));
1540 assert_eq!(range.next(), Some(Height::new(5)));
1541 assert_eq!(range.next_back(), Some(Height::new(9)));
1542 assert_eq!(range.next(), Some(Height::new(6)));
1543 assert_eq!(range.next_back(), Some(Height::new(8)));
1544 assert_eq!(range.len(), 1);
1545 assert_eq!(range.next(), Some(Height::new(7)));
1546 assert_eq!(range.next(), None);
1547 assert_eq!(range.next_back(), None);
1548 }
1549
1550 #[test]
1551 fn test_view_constructors() {
1552 assert_eq!(View::zero().get(), 0);
1553 assert_eq!(View::new(42).get(), 42);
1554 assert_eq!(View::new(100).get(), 100);
1555 assert_eq!(View::default().get(), 0);
1556 }
1557
1558 #[test]
1559 fn test_view_is_zero() {
1560 assert!(View::zero().is_zero());
1561 assert!(View::new(0).is_zero());
1562 assert!(!View::new(1).is_zero());
1563 assert!(!View::new(100).is_zero());
1564 }
1565
1566 #[test]
1567 fn test_view_next() {
1568 assert_eq!(View::zero().next().get(), 1);
1569 assert_eq!(View::new(5).next().get(), 6);
1570 assert_eq!(View::new(999).next().get(), 1000);
1571 }
1572
1573 #[test]
1574 #[should_panic(expected = "view overflow")]
1575 fn test_view_next_overflow() {
1576 View::new(u64::MAX).next();
1577 }
1578
1579 #[test]
1580 fn test_view_previous() {
1581 assert_eq!(View::zero().previous(), None);
1582 assert_eq!(View::new(1).previous(), Some(View::zero()));
1583 assert_eq!(View::new(5).previous(), Some(View::new(4)));
1584 assert_eq!(View::new(1000).previous(), Some(View::new(999)));
1585 }
1586
1587 #[test]
1588 fn test_view_saturating_add() {
1589 let delta5 = ViewDelta::new(5);
1590 let delta100 = ViewDelta::new(100);
1591 assert_eq!(View::zero().saturating_add(delta5).get(), 5);
1592 assert_eq!(View::new(10).saturating_add(delta100).get(), 110);
1593 assert_eq!(
1594 View::new(u64::MAX).saturating_add(ViewDelta::new(1)).get(),
1595 u64::MAX
1596 );
1597 }
1598
1599 #[test]
1600 fn test_view_saturating_sub() {
1601 let delta5 = ViewDelta::new(5);
1602 let delta100 = ViewDelta::new(100);
1603 assert_eq!(View::new(10).saturating_sub(delta5).get(), 5);
1604 assert_eq!(View::new(5).saturating_sub(delta5).get(), 0);
1605 assert_eq!(View::new(5).saturating_sub(delta100).get(), 0);
1606 assert_eq!(View::zero().saturating_sub(delta100).get(), 0);
1607 }
1608
1609 #[test]
1610 fn test_view_display() {
1611 assert_eq!(format!("{}", View::zero()), "0");
1612 assert_eq!(format!("{}", View::new(42)), "42");
1613 assert_eq!(format!("{}", View::new(1000)), "1000");
1614 }
1615
1616 #[test]
1617 fn test_view_ordering() {
1618 assert!(View::zero() < View::new(1));
1619 assert!(View::new(5) < View::new(10));
1620 assert!(View::new(10) > View::new(5));
1621 assert_eq!(View::new(42), View::new(42));
1622 }
1623
1624 #[test]
1625 fn test_view_encode_decode() {
1626 let cases = vec![0u64, 1, 127, 128, 255, 256, u64::MAX];
1627 for value in cases {
1628 let view = View::new(value);
1629 let encoded = view.encode();
1630 assert_eq!(encoded.len(), view.encode_size());
1631 let decoded = View::decode(encoded).unwrap();
1632 assert_eq!(view, decoded);
1633 }
1634 }
1635
1636 #[test]
1637 fn test_view_term_start() {
1638 let cases = [
1639 (0, 5, 0),
1640 (1, 1, 1),
1641 (5, 1, 5),
1642 (6, 1, 6),
1643 (7, 1, 7),
1644 (1, 5, 1),
1645 (5, 5, 1),
1646 (6, 5, 6),
1647 (10, 5, 6),
1648 (11, 5, 11),
1649 (12, 3, 10),
1650 ];
1651 for (view, term_length, expected) in cases {
1652 assert_eq!(
1653 View::new(view).term_start(TermLength::new(commonware_utils::NZU32!(term_length))),
1654 View::new(expected),
1655 "view={view}, term_length={term_length}"
1656 );
1657 }
1658 }
1659
1660 #[test]
1661 fn test_view_term_end() {
1662 let cases = [
1663 (0, 5, 0),
1664 (1, 1, 1),
1665 (5, 1, 5),
1666 (1, 5, 5),
1667 (5, 5, 5),
1668 (6, 5, 10),
1669 (10, 5, 10),
1670 (11, 5, 15),
1671 (12, 3, 12),
1672 ];
1673 for (view, term_length, expected) in cases {
1674 assert_eq!(
1675 View::new(view).term_end(TermLength::new(commonware_utils::NZU32!(term_length))),
1676 View::new(expected),
1677 "view={view}, term_length={term_length}"
1678 );
1679 }
1680 }
1681
1682 #[test]
1683 fn test_view_is_term_start() {
1684 let cases = [
1685 (0, 1, true),
1686 (1, 1, true),
1687 (5, 1, true),
1688 (1, 5, true),
1689 (5, 5, false),
1690 (6, 5, true),
1691 (10, 5, false),
1692 (11, 5, true),
1693 ];
1694 for (view, term_length, expected) in cases {
1695 assert_eq!(
1696 View::new(view)
1697 .is_term_start(TermLength::new(commonware_utils::NZU32!(term_length))),
1698 expected,
1699 "view={view}, term_length={term_length}"
1700 );
1701 }
1702 }
1703
1704 #[test]
1705 fn test_view_same_term() {
1706 let cases = [
1707 (0, 0, 1, true),
1708 (0, 0, 5, true),
1709 (0, 1, 5, false),
1710 (0, 5, 5, false),
1711 (1, 1, 1, true),
1712 (1, 2, 5, true),
1713 (1, 5, 5, true),
1714 (5, 6, 5, false),
1715 (6, 10, 5, true),
1716 (10, 11, 5, false),
1717 (11, 15, 5, true),
1718 ];
1719 for (a, b, term_length, expected) in cases {
1720 assert_eq!(
1721 View::new(a).same_term(
1722 View::new(b),
1723 TermLength::new(commonware_utils::NZU32!(term_length))
1724 ),
1725 expected,
1726 "a={a}, b={b}, term_length={term_length}"
1727 );
1728 }
1729 }
1730
1731 #[test]
1732 fn test_view_next_term_start() {
1733 let cases = [
1734 (0, 1, 1),
1735 (5, 1, 6),
1736 (1, 5, 6),
1737 (5, 5, 6),
1738 (6, 5, 11),
1739 (10, 5, 11),
1740 (11, 5, 16),
1741 (12, 3, 13),
1742 ];
1743 for (view, term_length, expected) in cases {
1744 assert_eq!(
1745 View::new(view)
1746 .next_term_start(TermLength::new(commonware_utils::NZU32!(term_length))),
1747 View::new(expected),
1748 "view={view}, term_length={term_length}"
1749 );
1750 }
1751 }
1752
1753 #[test]
1754 fn test_view_term_index() {
1755 let cases = [
1756 (0, 1, 0),
1757 (1, 1, 1),
1758 (5, 1, 5),
1759 (0, 5, 0),
1760 (1, 5, 1),
1761 (5, 5, 1),
1762 (6, 5, 2),
1763 (10, 5, 2),
1764 (11, 5, 3),
1765 ];
1766 for (view, term_length, expected) in cases {
1767 assert_eq!(
1768 View::new(view).term_index(TermLength::new(commonware_utils::NZU32!(term_length))),
1769 expected,
1770 "view={view}, term_length={term_length}"
1771 );
1772 }
1773 }
1774
1775 #[test]
1776 fn test_view_covers() {
1777 let cases = [
1778 (0, 0, 5, true),
1779 (0, 3, 5, false),
1780 (1, 0, 5, false),
1781 (1, 1, 1, true),
1782 (1, 2, 1, false),
1783 (2, 1, 1, false),
1784 (6, 6, 5, true),
1785 (6, 8, 5, true),
1786 (6, 10, 5, true),
1787 (6, 11, 5, false),
1788 (8, 6, 5, false),
1789 (6, 5, 5, false),
1790 ];
1791 for (nullified, view, term_length, expected) in cases {
1792 assert_eq!(
1793 View::new(nullified).covers(
1794 View::new(view),
1795 TermLength::new(commonware_utils::NZU32!(term_length))
1796 ),
1797 expected,
1798 "nullified={nullified}, view={view}, term_length={term_length}"
1799 );
1800 }
1801 }
1802
1803 #[test]
1804 fn test_view_admits() {
1805 let cases = [
1806 (0, 0, 5, true),
1807 (0, 1, 5, true),
1808 (0, 2, 5, false),
1809 (0, 5, 5, false),
1810 (5, 4, 1, true),
1811 (5, 5, 1, true),
1812 (5, 6, 1, true),
1813 (5, 7, 1, false),
1814 (6, 7, 5, true),
1815 (6, 11, 5, true),
1816 (6, 8, 5, false),
1817 (6, 12, 5, false),
1818 (10, 11, 5, true),
1819 (10, 12, 5, false),
1820 ];
1821 for (current, pending, term_length, expected) in cases {
1822 assert_eq!(
1823 View::new(current).admits(
1824 View::new(pending),
1825 TermLength::new(commonware_utils::NZU32!(term_length))
1826 ),
1827 expected,
1828 "current={current}, pending={pending}, term_length={term_length}"
1829 );
1830 }
1831 }
1832
1833 #[test]
1834 #[should_panic(expected = "view term_end overflow")]
1835 fn test_view_term_end_overflow_panics() {
1836 let _ = View::new(u64::MAX).term_end(TermLength::new(commonware_utils::NZU32!(2)));
1837 }
1838
1839 #[test]
1840 #[should_panic(expected = "view overflow")]
1841 fn test_view_next_term_start_overflow_panics() {
1842 let _ = View::new(u64::MAX).next_term_start(TermLength::ONE);
1843 }
1844
1845 #[test]
1846 fn test_view_admits_near_max_does_not_panic() {
1847 let term_length = TermLength::new(commonware_utils::NZU32!(5));
1848 let current = View::new(u64::MAX - 2);
1851 assert!(current.admits(View::new(0), term_length));
1852 assert!(current.admits(View::new(u64::MAX - 1), term_length));
1853 assert!(!current.admits(View::new(u64::MAX), term_length));
1854 }
1855
1856 #[test]
1857 fn test_view_delta_constructors() {
1858 assert_eq!(ViewDelta::zero().get(), 0);
1859 assert_eq!(ViewDelta::new(42).get(), 42);
1860 assert_eq!(ViewDelta::new(100).get(), 100);
1861 assert_eq!(ViewDelta::default().get(), 0);
1862 }
1863
1864 #[test]
1865 fn test_view_delta_is_zero() {
1866 assert!(ViewDelta::zero().is_zero());
1867 assert!(ViewDelta::new(0).is_zero());
1868 assert!(!ViewDelta::new(1).is_zero());
1869 assert!(!ViewDelta::new(100).is_zero());
1870 }
1871
1872 #[test]
1873 fn test_view_delta_display() {
1874 assert_eq!(format!("{}", ViewDelta::zero()), "0");
1875 assert_eq!(format!("{}", ViewDelta::new(42)), "42");
1876 assert_eq!(format!("{}", ViewDelta::new(1000)), "1000");
1877 }
1878
1879 #[test]
1880 fn test_view_delta_ordering() {
1881 assert!(ViewDelta::zero() < ViewDelta::new(1));
1882 assert!(ViewDelta::new(5) < ViewDelta::new(10));
1883 assert!(ViewDelta::new(10) > ViewDelta::new(5));
1884 assert_eq!(ViewDelta::new(42), ViewDelta::new(42));
1885 }
1886
1887 #[test]
1888 fn test_round_cmp() {
1889 assert!(Round::new(Epoch::new(1), View::new(2)) < Round::new(Epoch::new(1), View::new(3)));
1890 assert!(Round::new(Epoch::new(1), View::new(2)) < Round::new(Epoch::new(2), View::new(1)));
1891 }
1892
1893 #[test]
1894 fn test_round_encode_decode_roundtrip() {
1895 let r: Round = (Epoch::new(42), View::new(1_000_000)).into();
1896 let encoded = r.encode();
1897 assert_eq!(encoded.len(), r.encode_size());
1898 let decoded = Round::decode(encoded).unwrap();
1899 assert_eq!(r, decoded);
1900 }
1901
1902 #[test]
1903 fn test_round_conversions() {
1904 let r: Round = (Epoch::new(5), View::new(6)).into();
1905 assert_eq!(r.epoch(), Epoch::new(5));
1906 assert_eq!(r.view(), View::new(6));
1907 let tuple: (Epoch, View) = r.into();
1908 assert_eq!(tuple, (Epoch::new(5), View::new(6)));
1909 }
1910
1911 #[test]
1912 fn test_round_new() {
1913 let r = Round::new(Epoch::new(10), View::new(20));
1914 assert_eq!(r.epoch(), Epoch::new(10));
1915 assert_eq!(r.view(), View::new(20));
1916
1917 let r2 = Round::new(Epoch::new(5), View::new(15));
1918 assert_eq!(r2.epoch(), Epoch::new(5));
1919 assert_eq!(r2.view(), View::new(15));
1920 }
1921
1922 #[test]
1923 fn test_round_display() {
1924 let r = Round::new(Epoch::new(5), View::new(100));
1925 assert_eq!(format!("{r}"), "(5, 100)");
1926 }
1927
1928 #[test]
1929 fn view_range_iterates() {
1930 let collected: Vec<_> = View::range(View::new(3), View::new(6))
1931 .map(View::get)
1932 .collect();
1933 assert_eq!(collected, vec![3, 4, 5]);
1934 }
1935
1936 #[test]
1937 fn view_range_empty() {
1938 let collected: Vec<_> = View::range(View::new(5), View::new(5)).collect();
1939 assert_eq!(collected, vec![]);
1940
1941 let collected: Vec<_> = View::range(View::new(10), View::new(5)).collect();
1942 assert_eq!(collected, vec![]);
1943 }
1944
1945 #[test]
1946 fn view_range_single() {
1947 let collected: Vec<_> = View::range(View::new(5), View::new(6))
1948 .map(View::get)
1949 .collect();
1950 assert_eq!(collected, vec![5]);
1951 }
1952
1953 #[test]
1954 fn view_range_size_hint() {
1955 let range = View::range(View::new(3), View::new(10));
1956 assert_eq!(range.size_hint(), (7, Some(7)));
1957 assert_eq!(range.len(), 7);
1958
1959 let empty = View::range(View::new(5), View::new(5));
1960 assert_eq!(empty.size_hint(), (0, Some(0)));
1961 assert_eq!(empty.len(), 0);
1962 }
1963
1964 #[test]
1965 fn view_range_collect() {
1966 let views: Vec<View> = View::range(View::new(0), View::new(3)).collect();
1967 assert_eq!(views, vec![View::zero(), View::new(1), View::new(2)]);
1968 }
1969
1970 #[test]
1971 fn view_range_iterator_next() {
1972 let mut range = View::range(View::new(5), View::new(8));
1973 assert_eq!(range.next(), Some(View::new(5)));
1974 assert_eq!(range.next(), Some(View::new(6)));
1975 assert_eq!(range.next(), Some(View::new(7)));
1976 assert_eq!(range.next(), None);
1977 assert_eq!(range.next(), None); }
1979
1980 #[test]
1981 fn view_range_exact_size_iterator() {
1982 let range = View::range(View::new(10), View::new(15));
1983 assert_eq!(range.len(), 5);
1984 assert_eq!(range.size_hint(), (5, Some(5)));
1985
1986 let mut range = View::range(View::new(10), View::new(15));
1987 assert_eq!(range.len(), 5);
1988 range.next();
1989 assert_eq!(range.len(), 4);
1990 range.next();
1991 assert_eq!(range.len(), 3);
1992 }
1993
1994 #[test]
1995 fn view_range_rev() {
1996 let collected: Vec<_> = View::range(View::new(3), View::new(7))
1998 .rev()
1999 .map(View::get)
2000 .collect();
2001 assert_eq!(collected, vec![6, 5, 4, 3]);
2002 }
2003
2004 #[test]
2005 fn view_range_double_ended() {
2006 let mut range = View::range(View::new(5), View::new(10));
2008 assert_eq!(range.next(), Some(View::new(5)));
2009 assert_eq!(range.next_back(), Some(View::new(9)));
2010 assert_eq!(range.next(), Some(View::new(6)));
2011 assert_eq!(range.next_back(), Some(View::new(8)));
2012 assert_eq!(range.len(), 1);
2013 assert_eq!(range.next(), Some(View::new(7)));
2014 assert_eq!(range.next(), None);
2015 assert_eq!(range.next_back(), None);
2016 }
2017
2018 #[test]
2019 fn test_fixed_epoch_strategy() {
2020 let epocher = FixedEpocher::new(NZU64!(100));
2021
2022 let bounds = epocher.containing(Height::zero()).unwrap();
2024 assert_eq!(bounds.epoch(), Epoch::new(0));
2025 assert_eq!(bounds.first(), Height::zero());
2026 assert_eq!(bounds.last(), Height::new(99));
2027 assert_eq!(bounds.length(), HeightDelta::new(100));
2028
2029 let bounds = epocher.containing(Height::new(99)).unwrap();
2030 assert_eq!(bounds.epoch(), Epoch::new(0));
2031
2032 let bounds = epocher.containing(Height::new(100)).unwrap();
2033 assert_eq!(bounds.epoch(), Epoch::new(1));
2034 assert_eq!(bounds.first(), Height::new(100));
2035 assert_eq!(bounds.last(), Height::new(199));
2036
2037 assert_eq!(epocher.first(Epoch::new(0)), Some(Height::zero()));
2039 assert_eq!(epocher.last(Epoch::new(0)), Some(Height::new(99)));
2040 assert_eq!(epocher.first(Epoch::new(1)), Some(Height::new(100)));
2041 assert_eq!(epocher.last(Epoch::new(1)), Some(Height::new(199)));
2042 assert_eq!(epocher.first(Epoch::new(5)), Some(Height::new(500)));
2043 assert_eq!(epocher.last(Epoch::new(5)), Some(Height::new(599)));
2044 }
2045
2046 #[test]
2047 fn test_epoch_bounds_relative() {
2048 let epocher = FixedEpocher::new(NZU64!(100));
2049
2050 assert_eq!(
2052 epocher.containing(Height::zero()).unwrap().relative(),
2053 Height::zero()
2054 );
2055 assert_eq!(
2056 epocher.containing(Height::new(50)).unwrap().relative(),
2057 Height::new(50)
2058 );
2059 assert_eq!(
2060 epocher.containing(Height::new(99)).unwrap().relative(),
2061 Height::new(99)
2062 );
2063
2064 assert_eq!(
2066 epocher.containing(Height::new(100)).unwrap().relative(),
2067 Height::zero()
2068 );
2069 assert_eq!(
2070 epocher.containing(Height::new(150)).unwrap().relative(),
2071 Height::new(50)
2072 );
2073 assert_eq!(
2074 epocher.containing(Height::new(199)).unwrap().relative(),
2075 Height::new(99)
2076 );
2077
2078 assert_eq!(
2080 epocher.containing(Height::new(500)).unwrap().relative(),
2081 Height::zero()
2082 );
2083 assert_eq!(
2084 epocher.containing(Height::new(567)).unwrap().relative(),
2085 Height::new(67)
2086 );
2087 assert_eq!(
2088 epocher.containing(Height::new(599)).unwrap().relative(),
2089 Height::new(99)
2090 );
2091 }
2092
2093 #[test]
2094 fn test_epoch_bounds_phase() {
2095 let epocher = FixedEpocher::new(NZU64!(30));
2097
2098 assert_eq!(
2100 epocher.containing(Height::zero()).unwrap().phase(),
2101 EpochPhase::Early
2102 );
2103 assert_eq!(
2104 epocher.containing(Height::new(14)).unwrap().phase(),
2105 EpochPhase::Early
2106 );
2107
2108 assert_eq!(
2110 epocher.containing(Height::new(15)).unwrap().phase(),
2111 EpochPhase::Midpoint
2112 );
2113
2114 assert_eq!(
2116 epocher.containing(Height::new(16)).unwrap().phase(),
2117 EpochPhase::Late
2118 );
2119 assert_eq!(
2120 epocher.containing(Height::new(29)).unwrap().phase(),
2121 EpochPhase::Late
2122 );
2123
2124 assert_eq!(
2126 epocher.containing(Height::new(30)).unwrap().phase(),
2127 EpochPhase::Early
2128 );
2129 assert_eq!(
2130 epocher.containing(Height::new(44)).unwrap().phase(),
2131 EpochPhase::Early
2132 );
2133 assert_eq!(
2134 epocher.containing(Height::new(45)).unwrap().phase(),
2135 EpochPhase::Midpoint
2136 );
2137 assert_eq!(
2138 epocher.containing(Height::new(46)).unwrap().phase(),
2139 EpochPhase::Late
2140 );
2141
2142 let epocher = FixedEpocher::new(NZU64!(10));
2144 assert_eq!(
2145 epocher.containing(Height::zero()).unwrap().phase(),
2146 EpochPhase::Early
2147 );
2148 assert_eq!(
2149 epocher.containing(Height::new(4)).unwrap().phase(),
2150 EpochPhase::Early
2151 );
2152 assert_eq!(
2153 epocher.containing(Height::new(5)).unwrap().phase(),
2154 EpochPhase::Midpoint
2155 );
2156 assert_eq!(
2157 epocher.containing(Height::new(6)).unwrap().phase(),
2158 EpochPhase::Late
2159 );
2160 assert_eq!(
2161 epocher.containing(Height::new(9)).unwrap().phase(),
2162 EpochPhase::Late
2163 );
2164
2165 let epocher = FixedEpocher::new(NZU64!(11));
2167 assert_eq!(
2168 epocher.containing(Height::zero()).unwrap().phase(),
2169 EpochPhase::Early
2170 );
2171 assert_eq!(
2172 epocher.containing(Height::new(4)).unwrap().phase(),
2173 EpochPhase::Early
2174 );
2175 assert_eq!(
2176 epocher.containing(Height::new(5)).unwrap().phase(),
2177 EpochPhase::Midpoint
2178 );
2179 assert_eq!(
2180 epocher.containing(Height::new(6)).unwrap().phase(),
2181 EpochPhase::Late
2182 );
2183 assert_eq!(
2184 epocher.containing(Height::new(10)).unwrap().phase(),
2185 EpochPhase::Late
2186 );
2187 }
2188
2189 #[test]
2190 #[should_panic(expected = "epoch length must exceed one")]
2191 fn test_fixed_epocher_rejects_length_one() {
2192 let _ = FixedEpocher::new(NZU64!(1));
2193 }
2194
2195 #[test]
2196 fn test_fixed_epocher_overflow() {
2197 let epocher = FixedEpocher::new(NZU64!(100));
2199
2200 let last_valid_first = Height::new(18446744073709551500u64);
2209 let last_valid_last = Height::new(18446744073709551599u64);
2210
2211 let result = epocher.containing(last_valid_first);
2212 assert!(result.is_some());
2213 let bounds = result.unwrap();
2214 assert_eq!(bounds.first(), last_valid_first);
2215 assert_eq!(bounds.last(), last_valid_last);
2216
2217 let result = epocher.containing(last_valid_last);
2218 assert!(result.is_some());
2219 assert_eq!(result.unwrap().last(), last_valid_last);
2220
2221 let overflow_height = last_valid_last.next();
2223 assert!(epocher.containing(overflow_height).is_none());
2224
2225 assert!(epocher.containing(Height::new(u64::MAX)).is_none());
2227
2228 let epocher = FixedEpocher::new(NZU64!(2));
2230
2231 let result = epocher.containing(Height::new(u64::MAX - 1));
2233 assert!(result.is_some());
2234 assert_eq!(result.unwrap().last(), Height::new(u64::MAX));
2235
2236 let result = epocher.containing(Height::new(u64::MAX));
2239 assert!(result.is_some());
2240 assert_eq!(result.unwrap().last(), Height::new(u64::MAX));
2241
2242 let epocher = FixedEpocher::new(NZU64!(2));
2244 let result = epocher.containing(Height::new(u64::MAX));
2245 assert!(result.is_some());
2246 assert_eq!(result.unwrap().last(), Height::new(u64::MAX));
2247
2248 let epocher = FixedEpocher::new(NZU64!(u64::MAX));
2250 assert!(epocher.containing(Height::new(u64::MAX)).is_none());
2251
2252 let epocher = FixedEpocher::new(NZU64!(100));
2254 let last_valid_epoch = Epoch::new(184467440737095515);
2255 let first_invalid_epoch = Epoch::new(184467440737095516);
2256
2257 assert!(epocher.first(last_valid_epoch).is_some());
2259 assert!(epocher.last(last_valid_epoch).is_some());
2260 let first = epocher.first(last_valid_epoch).unwrap();
2261 assert!(epocher.containing(first).is_some());
2262 assert_eq!(
2263 epocher.containing(first).unwrap().last(),
2264 epocher.last(last_valid_epoch).unwrap()
2265 );
2266
2267 assert!(epocher.first(first_invalid_epoch).is_none());
2269 assert!(epocher.last(first_invalid_epoch).is_none());
2270 assert!(epocher.containing(last_valid_last.next()).is_none());
2271 }
2272
2273 #[test]
2274 fn test_coding_commitment_fallible_digest() {
2275 #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
2276 struct Digest([u8; Self::SIZE]);
2277
2278 impl Random for Digest {
2279 fn random(mut rng: impl rand_core::CryptoRng) -> Self {
2280 let mut buf = [0u8; Self::SIZE];
2281 rng.fill_bytes(&mut buf);
2282 Self(buf)
2283 }
2284 }
2285
2286 impl commonware_cryptography::Digest for Digest {
2287 const EMPTY: Self = Self([0u8; Self::SIZE]);
2288 }
2289
2290 impl Write for Digest {
2291 fn write(&self, buf: &mut impl BufMut) {
2292 buf.put_slice(&self.0);
2293 }
2294 }
2295
2296 impl FixedSize for Digest {
2297 const SIZE: usize = 32;
2298 }
2299
2300 impl Read for Digest {
2301 type Cfg = ();
2302
2303 fn read_cfg(
2304 _: &mut impl commonware_codec::Buf,
2305 _: &Self::Cfg,
2306 ) -> Result<Self, commonware_codec::Error> {
2307 Err(commonware_codec::Error::Invalid(
2308 "Digest",
2309 "read not implemented",
2310 ))
2311 }
2312 }
2313
2314 impl AsRef<[u8]> for Digest {
2315 fn as_ref(&self) -> &[u8] {
2316 &self.0
2317 }
2318 }
2319
2320 impl Deref for Digest {
2321 type Target = [u8];
2322
2323 fn deref(&self) -> &Self::Target {
2324 &self.0
2325 }
2326 }
2327
2328 impl core::fmt::Display for Digest {
2329 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2330 write!(f, "{}", commonware_formatting::Hex(self.as_ref()))
2331 }
2332 }
2333
2334 impl core::fmt::Debug for Digest {
2335 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2336 write!(f, "Digest({})", commonware_formatting::Hex(self.as_ref()))
2337 }
2338 }
2339
2340 impl Span for Digest {}
2341 impl Array for Digest {}
2342
2343 let digest = Digest::random(test_rng());
2344 let config = CodingConfig {
2345 minimum_shards: NZU16!(1),
2346 extra_shards: NZU16!(1),
2347 };
2348 type Sha256Digest = commonware_cryptography::sha256::Digest;
2349 type InvalidBlockCommitment =
2350 Commitment<TestBlock<Digest>, ReedSolomon<TestHasher<Digest>>, TestHasher<Digest>>;
2351 let commitment = InvalidBlockCommitment::from((digest, digest, digest, config));
2352 assert!(InvalidBlockCommitment::decode(commitment.encode()).is_err());
2353
2354 type InvalidRootCommitment = Commitment<
2355 TestBlock<Sha256Digest>,
2356 ReedSolomon<TestHasher<Digest>>,
2357 TestHasher<Sha256Digest>,
2358 >;
2359 let commitment =
2360 InvalidRootCommitment::from((Sha256Digest::EMPTY, digest, Sha256Digest::EMPTY, config));
2361 assert!(InvalidRootCommitment::decode(commitment.encode()).is_err());
2362
2363 type InvalidContextCommitment = Commitment<
2364 TestBlock<Sha256Digest>,
2365 ReedSolomon<TestHasher<Sha256Digest>>,
2366 TestHasher<Digest>,
2367 >;
2368 let commitment = InvalidContextCommitment::from((
2369 Sha256Digest::EMPTY,
2370 Sha256Digest::EMPTY,
2371 digest,
2372 config,
2373 ));
2374 assert!(InvalidContextCommitment::decode(commitment.encode()).is_err());
2375 }
2376
2377 #[test]
2378 fn test_coding_commitment_supports_short_digest_types() {
2379 type CrcCommitment = Commitment<
2380 TestBlock<commonware_cryptography::crc32::Digest>,
2381 ReedSolomon<commonware_cryptography::Crc32>,
2382 commonware_cryptography::Crc32,
2383 >;
2384
2385 let block = commonware_cryptography::crc32::Digest::from(1);
2386 let root = commonware_cryptography::crc32::Digest::from(2);
2387 let context = commonware_cryptography::crc32::Digest::from(3);
2388 let config = CodingConfig {
2389 minimum_shards: NZU16!(1),
2390 extra_shards: NZU16!(1),
2391 };
2392 let commitment = CrcCommitment::from((block, root, context, config));
2393
2394 assert_eq!(CrcCommitment::SIZE, COMMITMENT_SIZE);
2395 assert_eq!(commitment.encode().len(), COMMITMENT_SIZE);
2396
2397 let decoded = CrcCommitment::decode(commitment.encode()).unwrap();
2398 assert_eq!(decoded.block(), block);
2399 assert_eq!(decoded.root(), root);
2400 assert_eq!(decoded.context(), context);
2401 assert_eq!(decoded.config(), config);
2402 }
2403
2404 #[test]
2405 fn test_coding_commitment_rejects_non_zero_digest_padding() {
2406 type CrcCommitment = Commitment<
2407 TestBlock<commonware_cryptography::crc32::Digest>,
2408 ReedSolomon<commonware_cryptography::Crc32>,
2409 commonware_cryptography::Crc32,
2410 >;
2411
2412 let config = CodingConfig {
2413 minimum_shards: NZU16!(1),
2414 extra_shards: NZU16!(1),
2415 };
2416 let commitment = CrcCommitment::from((
2417 commonware_cryptography::crc32::Digest::from(1),
2418 commonware_cryptography::crc32::Digest::from(2),
2419 commonware_cryptography::crc32::Digest::from(3),
2420 config,
2421 ));
2422 let encoded = commitment.encode();
2423 for offset in [
2424 commonware_cryptography::crc32::Digest::SIZE,
2425 32 + commonware_cryptography::crc32::Digest::SIZE,
2426 64 + commonware_cryptography::crc32::Digest::SIZE,
2427 ] {
2428 let mut malformed = encoded.to_vec();
2429 malformed[offset] = 1;
2430 assert!(CrcCommitment::decode(malformed).is_err());
2431 }
2432 }
2433
2434 #[cfg(feature = "arbitrary")]
2435 mod conformance {
2436 use super::{coding::Commitment, *};
2437 use commonware_codec::conformance::CodecConformance;
2438 use commonware_cryptography::sha256::{Digest as Sha256Digest, Sha256};
2439
2440 type TestCommitment = Commitment<TestBlock<Sha256Digest>, ReedSolomon<Sha256>, Sha256>;
2441
2442 commonware_conformance::conformance_tests! {
2443 CodecConformance<Epoch>,
2444 CodecConformance<Height>,
2445 CodecConformance<View>,
2446 CodecConformance<Round>,
2447 CodecConformance<TestCommitment>,
2448 }
2449 }
2450}