tendermint/
trust_threshold.rs1use core::fmt::{self, Debug, Display};
4
5use serde::{de::DeserializeOwned, Deserialize, Serialize};
6
7use crate::{error::Error, prelude::*, serializers};
8
9pub trait TrustThreshold: Copy + Clone + Debug + Serialize + DeserializeOwned {
13 fn is_enough_power(&self, signed_voting_power: u64, total_voting_power: u64) -> bool;
16}
17
18#[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 pub const ONE_THIRD: Self = Self {
38 numerator: 1,
39 denominator: 3,
40 };
41
42 pub const TWO_THIRDS: Self = Self {
44 numerator: 2,
45 denominator: 3,
46 };
47
48 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 pub fn numerator(&self) -> u64 {
71 self.numerator
72 }
73
74 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#[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 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 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 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 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}