commonware_consensus/
types.rs

1//! Consensus types shared across the crate.
2
3use bytes::{Buf, BufMut};
4use commonware_codec::{EncodeSize, Error, Read, ReadExt, Write};
5use std::fmt::Display;
6
7/// Epoch is the type used to represent a distinct set of validators.
8///
9/// Represents a contiguous sequence of views in which the set of validators is constant.
10/// When the set of participants changes, the epoch increments.
11pub type Epoch = u64;
12
13/// View is a monotonically increasing counter that represents the current slot of a single
14/// consensus engine (i.e. within a single epoch).
15pub type View = u64;
16
17/// Round is a tuple of ([Epoch], [View]).
18#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub struct Round(Epoch, View);
20
21impl Round {
22    pub fn new(epoch: Epoch, view: View) -> Self {
23        Self(epoch, view)
24    }
25
26    pub fn epoch(&self) -> Epoch {
27        self.0
28    }
29
30    pub fn view(&self) -> View {
31        self.1
32    }
33}
34
35impl From<(Epoch, View)> for Round {
36    fn from((epoch, view): (Epoch, View)) -> Self {
37        Self(epoch, view)
38    }
39}
40
41impl From<Round> for (Epoch, View) {
42    fn from(round: Round) -> Self {
43        (round.epoch(), round.view())
44    }
45}
46
47impl Read for Round {
48    type Cfg = ();
49
50    fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, Error> {
51        Ok(Self(Epoch::read(buf)?, View::read(buf)?))
52    }
53}
54
55impl Write for Round {
56    fn write(&self, buf: &mut impl BufMut) {
57        self.epoch().write(buf);
58        self.view().write(buf);
59    }
60}
61
62impl EncodeSize for Round {
63    fn encode_size(&self) -> usize {
64        self.epoch().encode_size() + self.view().encode_size()
65    }
66}
67
68impl Display for Round {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        write!(f, "Round({}, {})", self.0, self.1)
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use commonware_codec::{DecodeExt, Encode, EncodeSize};
78
79    #[test]
80    fn test_round_cmp() {
81        assert!(Round::from((1, 2)) < Round::from((1, 3)));
82        assert!(Round::from((1, 2)) < Round::from((2, 1)));
83    }
84
85    #[test]
86    fn test_round_encode_decode_roundtrip() {
87        let r = Round::new(42, 1_000_000);
88        let encoded = r.encode();
89        assert_eq!(encoded.len(), r.encode_size());
90        let decoded = Round::decode(encoded).unwrap();
91        assert_eq!(r, decoded);
92    }
93
94    #[test]
95    fn test_round_conversions() {
96        let r: Round = (5u64, 6u64).into();
97        assert_eq!(r.epoch(), 5);
98        assert_eq!(r.view(), 6);
99        let tuple: (Epoch, View) = r.into();
100        assert_eq!(tuple, (5, 6));
101    }
102}