use bytecheck::CheckBytes;
use rkyv::{Archive, Deserialize, Serialize};
pub type BlockHeight = u64;
pub const MATURITY: u64 = 2 * EPOCH;
pub const EPOCH: u64 = 2160;
#[derive(
Debug, Default, Clone, PartialEq, Eq, Archive, Deserialize, Serialize,
)]
#[archive_attr(derive(CheckBytes))]
#[allow(clippy::module_name_repetitions)]
pub struct StakeData {
pub amount: Option<(u64, BlockHeight)>,
pub reward: u64,
pub counter: u64,
}
impl StakeData {
#[must_use]
pub const fn new(
value: u64,
reward: u64,
block_height: BlockHeight,
) -> Self {
let eligibility = Self::eligibility_from_height(block_height);
Self::with_eligibility(value, reward, eligibility)
}
#[must_use]
pub const fn with_eligibility(
value: u64,
reward: u64,
eligibility: BlockHeight,
) -> Self {
let amount = match value {
0 => None,
_ => Some((value, eligibility)),
};
Self {
amount,
reward,
counter: 0,
}
}
#[must_use]
pub const fn amount(&self) -> Option<&(u64, BlockHeight)> {
self.amount.as_ref()
}
#[must_use]
pub const fn reward(&self) -> u64 {
self.reward
}
#[must_use]
pub const fn counter(&self) -> u64 {
self.counter
}
pub fn insert_amount(&mut self, value: u64, block_height: BlockHeight) {
assert_ne!(value, 0, "A stake can't have zero value");
assert!(self.amount.is_none(), "Can't stake twice for the same key!");
let eligibility = Self::eligibility_from_height(block_height);
self.amount = Some((value, eligibility));
}
pub fn increase_reward(&mut self, value: u64) {
self.reward += value;
}
pub fn remove_amount(&mut self) -> (u64, BlockHeight) {
self.amount
.take()
.expect("Can't withdraw non-existing amount!")
}
pub fn deplete_reward(&mut self) {
self.reward = 0;
}
pub fn increment_counter(&mut self) {
self.counter += 1;
}
#[must_use]
pub fn is_valid(&self, block_height: BlockHeight) -> bool {
self.amount
.map(|(_, eligibility)| block_height >= eligibility)
.unwrap_or_default()
}
#[must_use]
pub const fn eligibility_from_height(block_height: BlockHeight) -> u64 {
let epoch = EPOCH - block_height % EPOCH;
block_height + MATURITY + epoch
}
}