1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
use crate::blockcfg::{
BlockDate, ChainLength, EpochRewardsInfo, Header, HeaderHash, Leadership, Ledger,
};
use chain_impl_mockchain::{multiverse, vote::VotePlanStatus};
use chain_time::{
era::{EpochPosition, EpochSlotOffset},
Epoch, Slot, TimeFrame,
};
use std::{
sync::Arc,
time::{Duration, SystemTime},
};
/// a reference to a block in the blockchain
#[derive(Clone)]
pub struct Ref {
/// Reference holder for the object in the `Multiverse<Ledger>`.
ledger: multiverse::Ref<Ledger>,
/// the time frame applicable in the current branch of the blockchain
time_frame: Arc<TimeFrame>,
/// the leadership used to validate the current header's leader
///
/// this object will be shared between different Ref of the same epoch
epoch_leadership_schedule: Arc<Leadership>,
/// If present, this is the rewards info distributed at the beginning of
/// the epoch. Useful to follow up on the reward distribution history
epoch_rewards_info: Option<Arc<EpochRewardsInfo>>,
/// keep the Block header in memory, this will avoid retrieving
/// the data from the storage if needs be
header: Header,
/// holder to the previous epoch state or more precisely the previous epoch's
/// last `Ref`. Every time there is a transition this value will be filled with
/// the parent `Ref`. Otherwise it will be copied from `Ref` to `Ref`.
///
previous_epoch_state: Option<Arc<Ref>>,
}
impl Ref {
/// create a new `Ref`
pub fn new(
ledger: multiverse::Ref<Ledger>,
time_frame: Arc<TimeFrame>,
epoch_leadership_schedule: Arc<Leadership>,
epoch_rewards_info: Option<Arc<EpochRewardsInfo>>,
header: Header,
previous_epoch_state: Option<Arc<Ref>>,
) -> Self {
debug_assert_eq!(
*ledger.id(),
header.hash(),
"expect the ledger reference to be for the same `Header`"
);
Ref {
ledger,
time_frame,
epoch_leadership_schedule,
epoch_rewards_info,
header,
previous_epoch_state,
}
}
/// retrieve the header hash of the `Ref`
pub fn hash(&self) -> HeaderHash {
*self.ledger.id()
}
/// access the reference's parent hash
pub fn block_parent_hash(&self) -> HeaderHash {
self.header().block_parent_hash()
}
/// retrieve the block date of the `Ref`
pub fn block_date(&self) -> BlockDate {
self.header().block_date()
}
/// retrieve the chain length, the number of blocks created
/// between the block0 and this block. This is useful to compare
/// the density of 2 branches.
pub fn chain_length(&self) -> ChainLength {
self.header().chain_length()
}
/// access the `Header` of the block pointed by this `Ref`
pub fn header(&self) -> &Header {
&self.header
}
pub fn ledger(&self) -> Arc<Ledger> {
self.ledger.state_arc()
}
/// get the time frame in application in the current branch of the blockchain
pub fn time_frame(&self) -> &Arc<TimeFrame> {
&self.time_frame
}
pub fn epoch_leadership_schedule(&self) -> &Arc<Leadership> {
&self.epoch_leadership_schedule
}
/// access the rewards info that were distributed at the end of the previous epoch
/// (and that are accessible/visible from this epoch only).
pub fn epoch_rewards_info(&self) -> Option<&Arc<EpochRewardsInfo>> {
self.epoch_rewards_info.as_ref()
}
pub fn last_ref_previous_epoch(&self) -> Option<&Arc<Ref>> {
self.previous_epoch_state.as_ref()
}
/// get the chain_time's `Slot`. This allows to compute an accurate
/// block time via a given time_frame or a precise block time
pub fn slot(&self) -> Slot {
let era = self.epoch_leadership_schedule().era();
let epoch = Epoch(self.header.block_date().epoch);
let slot = EpochSlotOffset(self.header.block_date().slot_id);
era.from_era_to_slot(EpochPosition { epoch, slot })
}
/// retrieve the time of the associated block.
pub fn time(&self) -> SystemTime {
let slot = self.slot();
let time_frame = self.time_frame();
if let Some(time) = time_frame.slot_to_systemtime(slot) {
time
} else {
// this case cannot happen because we cannot have a time_frame
// change during the lifetime of the object.
unsafe { std::hint::unreachable_unchecked() }
}
}
/// retrieve the time of the slot of the block. If the block is set
/// in the future, this function will return an error.
pub fn elapsed(&self) -> Result<Duration, std::time::SystemTimeError> {
SystemTime::now().duration_since(self.time())
}
/// clone all active vote plans at this given state
///
/// this includes, votes to be voted on, on going votes, votes to be resolved and votes
/// to result into a change on the ledger
pub fn active_vote_plans(&self) -> Vec<VotePlanStatus> {
self.ledger.state().active_vote_plans()
}
}