use crate::block0;
use chain_impl_mockchain::{
block::Block,
header::{BlockDate, Header},
leadership::{self, Leadership, Verification},
ledger::{EpochRewardsInfo, Ledger},
};
use chain_time::{
era::{EpochPosition, EpochSlotOffset},
Epoch, Slot, TimeFrame,
};
use std::time::SystemTime;
use thiserror::Error;
pub struct EpochInfo {
time_frame: TimeFrame,
epoch_leadership_schedule: Leadership,
epoch_rewards_info: Option<EpochRewardsInfo>,
}
#[derive(Debug, Error)]
pub enum EpochInfoError {
#[error("Cannot needed information from the block0")]
InvalidBlock0(
#[source]
#[from]
block0::Block0Error,
),
#[error("Block Header's verification failed")]
HeaderVerification {
#[source]
#[from]
source: leadership::Error,
},
}
impl EpochInfo {
pub(crate) fn new(block0: &Block, ledger: &Ledger) -> Result<Self, EpochInfoError> {
let epoch = block0.header().block_date().epoch;
let time_frame = {
let start_time = block0::start_time(block0)?;
let slot_duration = block0::slot_duration(block0)?;
TimeFrame::new(
chain_time::Timeline::new(start_time),
chain_time::SlotDuration::from_secs(slot_duration.as_secs() as u32),
)
};
let epoch_leadership_schedule = Leadership::new(epoch, ledger);
let epoch_rewards_info = None;
Ok(Self {
time_frame,
epoch_leadership_schedule,
epoch_rewards_info,
})
}
pub(crate) fn chain(
&self,
leadership: Leadership,
epoch_rewards_info: Option<EpochRewardsInfo>,
) -> Self {
Self {
time_frame: self.time_frame.clone(),
epoch_leadership_schedule: leadership,
epoch_rewards_info,
}
}
pub fn check_header(&self, header: &Header) -> Result<(), EpochInfoError> {
match self.epoch_leadership_schedule.verify(header) {
Verification::Failure(error) => {
Err(EpochInfoError::HeaderVerification { source: error })
}
Verification::Success => Ok(()),
}
}
pub fn epoch(&self) -> u32 {
self.epoch_leadership_schedule.epoch()
}
pub fn slot_of(&self, date: BlockDate) -> Slot {
let epoch = Epoch(date.epoch);
let slot = EpochSlotOffset(date.slot_id);
let pos = EpochPosition { epoch, slot };
self.epoch_leadership_schedule.era().from_era_to_slot(pos)
}
pub fn time_of(&self, date: BlockDate) -> Option<SystemTime> {
let slot = self.slot_of(date);
self.time_frame.slot_to_systemtime(slot)
}
pub fn epoch_leadership_schedule(&self) -> &Leadership {
&self.epoch_leadership_schedule
}
pub fn epoch_rewards_info(&self) -> Option<&EpochRewardsInfo> {
self.epoch_rewards_info.as_ref()
}
}