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