celestia_tendermint/
trust_threshold.rs1use 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
12pub trait TrustThreshold: Copy + Clone + Debug + Serialize + DeserializeOwned {
16 fn is_enough_power(&self, signed_voting_power: u64, total_voting_power: u64) -> bool;
19}
20
21#[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 pub const ONE_THIRD: Self = Self {
41 numerator: 1,
42 denominator: 3,
43 };
44
45 pub const TWO_THIRDS: Self = Self {
47 numerator: 2,
48 denominator: 3,
49 };
50
51 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 pub fn numerator(&self) -> u64 {
74 self.numerator
75 }
76
77 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#[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 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 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 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 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}