use chia_protocol::Coin;
use crate::error::{DidError, DidResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SingletonAmount(u64);
impl SingletonAmount {
pub const MINIMAL: Self = Self(1);
pub const fn new(amount: u64) -> DidResult<Self> {
if amount % 2 == 0 {
return Err(DidError::EvenSingletonAmount(amount));
}
Ok(Self(amount))
}
pub const fn from_funding_coin(coin: &Coin) -> DidResult<Self> {
Self::new(coin.amount)
}
#[must_use]
pub const fn get(self) -> u64 {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use chia_protocol::Bytes32;
fn coin_of(amount: u64) -> Coin {
Coin::new(Bytes32::default(), Bytes32::default(), amount)
}
#[test]
fn odd_amounts_are_accepted_and_read_back_unchanged() {
for amount in [1_u64, 3, 1_000_001, u64::MAX] {
let proven = SingletonAmount::new(amount)
.expect("an odd amount is a valid singleton amount")
.get();
assert_eq!(proven, amount);
}
}
#[test]
fn even_amounts_are_refused_by_value() {
for amount in [0_u64, 2, 1_000_000, u64::MAX - 1] {
assert!(
matches!(
SingletonAmount::new(amount),
Err(DidError::EvenSingletonAmount(reported)) if reported == amount
),
"{amount} is even and must be refused, naming itself"
);
}
}
#[test]
fn from_funding_coin_reads_the_coins_amount() {
assert_eq!(
SingletonAmount::from_funding_coin(&coin_of(7))
.expect("7 is odd")
.get(),
7
);
assert!(matches!(
SingletonAmount::from_funding_coin(&coin_of(8)),
Err(DidError::EvenSingletonAmount(8))
));
}
#[test]
fn consts_are_odd_and_would_pass_the_constructor() {
assert_const_would_pass_the_constructor("MINIMAL", SingletonAmount::MINIMAL);
}
fn assert_const_would_pass_the_constructor(name: &str, amount: SingletonAmount) {
assert!(
SingletonAmount::new(amount.get()).is_ok(),
"{name} = {} bypasses new(), so it must itself be odd",
amount.get()
);
}
}