tendermint/
trust_threshold.rs

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