tendermint/
trust_threshold.rsuse core::{
convert::TryFrom,
fmt::{self, Debug, Display},
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::{error::Error, prelude::*, serializers};
pub trait TrustThreshold: Copy + Clone + Debug + Serialize + DeserializeOwned {
fn is_enough_power(&self, signed_voting_power: u64, total_voting_power: u64) -> bool;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(
try_from = "RawTrustThresholdFraction",
into = "RawTrustThresholdFraction"
)]
pub struct TrustThresholdFraction {
numerator: u64,
denominator: u64,
}
impl TrustThresholdFraction {
pub const ONE_THIRD: Self = Self {
numerator: 1,
denominator: 3,
};
pub const TWO_THIRDS: Self = Self {
numerator: 2,
denominator: 3,
};
pub fn new(numerator: u64, denominator: u64) -> Result<Self, Error> {
if numerator > denominator {
return Err(Error::trust_threshold_too_large());
}
if denominator == 0 {
return Err(Error::undefined_trust_threshold());
}
if 3 * numerator < denominator {
return Err(Error::trust_threshold_too_small());
}
Ok(Self {
numerator,
denominator,
})
}
pub fn numerator(&self) -> u64 {
self.numerator
}
pub fn denominator(&self) -> u64 {
self.denominator
}
}
impl TryFrom<RawTrustThresholdFraction> for TrustThresholdFraction {
type Error = Error;
fn try_from(value: RawTrustThresholdFraction) -> Result<Self, Self::Error> {
Self::new(value.numerator, value.denominator)
}
}
impl From<TrustThresholdFraction> for RawTrustThresholdFraction {
fn from(f: TrustThresholdFraction) -> Self {
Self {
numerator: f.numerator,
denominator: f.denominator,
}
}
}
impl TrustThreshold for TrustThresholdFraction {
fn is_enough_power(&self, signed_voting_power: u64, total_voting_power: u64) -> bool {
signed_voting_power * self.denominator > total_voting_power * self.numerator
}
}
impl Default for TrustThresholdFraction {
fn default() -> Self {
Self::ONE_THIRD
}
}
impl Display for TrustThresholdFraction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.numerator, self.denominator)
}
}
#[derive(Serialize, Deserialize)]
pub struct RawTrustThresholdFraction {
#[serde(with = "serializers::from_str")]
numerator: u64,
#[serde(with = "serializers::from_str")]
denominator: u64,
}
#[cfg(test)]
mod test {
use proptest::prelude::*;
use super::*;
fn to_json(num: u64, denom: u64) -> String {
format!("{{\"numerator\": \"{num}\", \"denominator\": \"{denom}\"}}")
}
fn from_json(num: u64, denom: u64) -> Result<TrustThresholdFraction, serde_json::Error> {
let json = to_json(num, denom);
serde_json::from_str::<TrustThresholdFraction>(&json)
}
prop_compose! {
fn arb_correct_frac(num: u64)(denom in (num+1)..=(3*num)) -> (u64, u64) {
(num, denom)
}
}
proptest! {
#[test]
fn too_large(num in 2..1000u64) {
let denom = num - 1;
assert!(TrustThresholdFraction::new(num, denom).is_err());
assert!(from_json(num, denom).is_err());
}
#[test]
fn undefined(num in 1..1000u64) {
let denom = 0u64;
assert!(TrustThresholdFraction::new(num, denom).is_err());
assert!(from_json(num, denom).is_err());
}
#[test]
fn too_small(num in 1..1000u64) {
let denom = (num * 3) + 1;
assert!(TrustThresholdFraction::new(num, denom).is_err());
assert!(from_json(num, denom).is_err());
}
#[test]
fn just_right((num, denom) in arb_correct_frac(1000)) {
let frac = TrustThresholdFraction::new(num, denom).unwrap();
assert_eq!(frac.numerator(), num);
assert_eq!(frac.denominator(), denom);
let frac = from_json(num, denom).unwrap();
assert_eq!(frac.numerator(), num);
assert_eq!(frac.denominator(), denom);
}
#[test]
fn can_be_one(num in 1..1000u64) {
assert!(TrustThresholdFraction::new(num, num).is_ok());
assert!(from_json(num, num).is_ok());
}
}
}