celestia_tendermint/
trust_threshold.rs

1//! Define traits and instances for dealing with trust thresholds.
2
3use core::{
4    convert::TryFrom,
5    fmt::{self, Debug, Display},
6};
7
8use serde::{de::DeserializeOwned, Deserialize, Serialize};
9
10use crate::{error::Error, prelude::*, serializers};
11
12/// TrustThreshold defines how much of the total voting power of a known
13/// and trusted validator set is sufficient for a commit to be
14/// accepted going forward.
15pub trait TrustThreshold: Copy + Clone + Debug + Serialize + DeserializeOwned {
16    /// Check whether the given signed voting power is sufficient according to
17    /// this trust threshold against the given total voting power.
18    fn is_enough_power(&self, signed_voting_power: u64, total_voting_power: u64) -> bool;
19}
20
21/// TrustThresholdFraction defines what fraction of the total voting power of a known
22/// and trusted validator set is sufficient for a commit to be
23/// accepted going forward.
24/// The [`Default::default()`] returns true, iff at least a third of the trusted
25/// voting power signed (in other words at least one honest validator signed).
26/// Some clients might require more than +1/3 and can implement their own
27/// [`TrustThreshold`] which can be passed into all relevant methods.
28#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(
30    try_from = "RawTrustThresholdFraction",
31    into = "RawTrustThresholdFraction"
32)]
33pub struct TrustThresholdFraction {
34    numerator: u64,
35    denominator: u64,
36}
37
38impl TrustThresholdFraction {
39    /// Constant for a trust threshold of 1/3.
40    pub const ONE_THIRD: Self = Self {
41        numerator: 1,
42        denominator: 3,
43    };
44
45    /// Constant for a trust threshold of 2/3.
46    pub const TWO_THIRDS: Self = Self {
47        numerator: 2,
48        denominator: 3,
49    };
50
51    /// Instantiate a TrustThresholdFraction if the given denominator and
52    /// numerator are valid.
53    ///
54    /// The parameters are valid iff `1/3 <= numerator/denominator <= 1`.
55    /// In any other case we return an error.
56    pub fn new(numerator: u64, denominator: u64) -> Result<Self, Error> {
57        if numerator > denominator {
58            return Err(Error::trust_threshold_too_large());
59        }
60        if denominator == 0 {
61            return Err(Error::undefined_trust_threshold());
62        }
63        if 3 * numerator < denominator {
64            return Err(Error::trust_threshold_too_small());
65        }
66        Ok(Self {
67            numerator,
68            denominator,
69        })
70    }
71
72    /// The numerator of this fraction.
73    pub fn numerator(&self) -> u64 {
74        self.numerator
75    }
76
77    /// The denominator of this fraction.
78    pub fn denominator(&self) -> u64 {
79        self.denominator
80    }
81}
82
83impl TryFrom<RawTrustThresholdFraction> for TrustThresholdFraction {
84    type Error = Error;
85
86    fn try_from(value: RawTrustThresholdFraction) -> Result<Self, Self::Error> {
87        Self::new(value.numerator, value.denominator)
88    }
89}
90
91impl From<TrustThresholdFraction> for RawTrustThresholdFraction {
92    fn from(f: TrustThresholdFraction) -> Self {
93        Self {
94            numerator: f.numerator,
95            denominator: f.denominator,
96        }
97    }
98}
99
100impl TrustThreshold for TrustThresholdFraction {
101    fn is_enough_power(&self, signed_voting_power: u64, total_voting_power: u64) -> bool {
102        signed_voting_power * self.denominator > total_voting_power * self.numerator
103    }
104}
105
106impl Default for TrustThresholdFraction {
107    fn default() -> Self {
108        Self::ONE_THIRD
109    }
110}
111
112impl Display for TrustThresholdFraction {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        write!(f, "{}/{}", self.numerator, self.denominator)
115    }
116}
117
118/// Facilitates validation of [`TrustThresholdFraction`] instances when
119/// deserializing them.
120#[derive(Serialize, Deserialize)]
121pub struct RawTrustThresholdFraction {
122    #[serde(with = "serializers::from_str")]
123    numerator: u64,
124    #[serde(with = "serializers::from_str")]
125    denominator: u64,
126}
127
128#[cfg(test)]
129mod test {
130    use proptest::prelude::*;
131
132    use super::*;
133
134    fn to_json(num: u64, denom: u64) -> String {
135        format!("{{\"numerator\": \"{num}\", \"denominator\": \"{denom}\"}}")
136    }
137
138    fn from_json(num: u64, denom: u64) -> Result<TrustThresholdFraction, serde_json::Error> {
139        let json = to_json(num, denom);
140        serde_json::from_str::<TrustThresholdFraction>(&json)
141    }
142
143    prop_compose! {
144        // num < denom <= 3*num
145        fn arb_correct_frac(num: u64)(denom in (num+1)..=(3*num)) -> (u64, u64) {
146            (num, denom)
147        }
148    }
149
150    proptest! {
151        #[test]
152        fn too_large(num in 2..1000u64) {
153            // Just smaller than the numerator
154            let denom = num - 1;
155            assert!(TrustThresholdFraction::new(num, denom).is_err());
156            assert!(from_json(num, denom).is_err());
157        }
158
159        #[test]
160        fn undefined(num in 1..1000u64) {
161            // Numerator should be irrelevant
162            let denom = 0u64;
163            assert!(TrustThresholdFraction::new(num, denom).is_err());
164            assert!(from_json(num, denom).is_err());
165        }
166
167        #[test]
168        fn too_small(num in 1..1000u64) {
169            // Just larger than 3 times the numerator
170            let denom = (num * 3) + 1;
171            assert!(TrustThresholdFraction::new(num, denom).is_err());
172            assert!(from_json(num, denom).is_err());
173        }
174
175        #[test]
176        fn just_right((num, denom) in arb_correct_frac(1000)) {
177            let frac = TrustThresholdFraction::new(num, denom).unwrap();
178            assert_eq!(frac.numerator(), num);
179            assert_eq!(frac.denominator(), denom);
180
181            let frac = from_json(num, denom).unwrap();
182            assert_eq!(frac.numerator(), num);
183            assert_eq!(frac.denominator(), denom);
184        }
185
186        #[test]
187        fn can_be_one(num in 1..1000u64) {
188            assert!(TrustThresholdFraction::new(num, num).is_ok());
189            assert!(from_json(num, num).is_ok());
190        }
191    }
192}