use crate::types::{epoch_state::EpochState, on_chain_config::ValidatorSet, transaction::Version};
use core::fmt::{Display, Formatter};
use diem_crypto::hash::{HashValue, ACCUMULATOR_PLACEHOLDER_HASH};
#[cfg(any(test))]
use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};
pub type Round = u64;
pub const GENESIS_EPOCH: u64 = 0;
pub const GENESIS_ROUND: Round = 0;
pub const GENESIS_VERSION: Version = 0;
pub const GENESIS_TIMESTAMP_USECS: u64 = 0;
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[cfg_attr(any(test), derive(Arbitrary))]
pub struct BlockInfo {
epoch: u64,
round: Round,
id: HashValue,
executed_state_id: HashValue,
version: Version,
timestamp_usecs: u64,
next_epoch_state: Option<EpochState>,
}
impl BlockInfo {
pub fn new(
epoch: u64,
round: Round,
id: HashValue,
executed_state_id: HashValue,
version: Version,
timestamp_usecs: u64,
next_epoch_state: Option<EpochState>,
) -> Self {
Self {
epoch,
round,
id,
executed_state_id,
version,
timestamp_usecs,
next_epoch_state,
}
}
pub fn empty() -> Self {
Self {
epoch: 0,
round: 0,
id: HashValue::zero(),
executed_state_id: HashValue::zero(),
version: 0,
timestamp_usecs: 0,
next_epoch_state: None,
}
}
#[cfg(any(test))]
pub fn random(round: Round) -> Self {
Self {
epoch: 1,
round,
id: HashValue::zero(),
executed_state_id: HashValue::zero(),
version: 0,
timestamp_usecs: 0,
next_epoch_state: None,
}
}
pub fn genesis(genesis_state_root_hash: HashValue, validator_set: ValidatorSet) -> Self {
Self {
epoch: GENESIS_EPOCH,
round: GENESIS_ROUND,
id: HashValue::zero(),
executed_state_id: genesis_state_root_hash,
version: GENESIS_VERSION,
timestamp_usecs: GENESIS_TIMESTAMP_USECS,
next_epoch_state: Some(EpochState {
epoch: 1,
verifier: (&validator_set).into(),
}),
}
}
#[cfg(any(test))]
pub fn mock_genesis(validator_set: Option<ValidatorSet>) -> Self {
let validator_set = validator_set.unwrap_or_else(ValidatorSet::empty);
Self::genesis(*ACCUMULATOR_PLACEHOLDER_HASH, validator_set)
}
pub fn next_block_epoch(&self) -> u64 {
self.next_epoch_state().map_or(self.epoch(), |e| e.epoch)
}
pub fn change_timestamp(&mut self, timestamp: u64) {
assert!(self.allow_timestamp_change(timestamp));
self.timestamp_usecs = timestamp;
}
fn allow_timestamp_change(&self, timestamp: u64) -> bool {
self.has_reconfiguration() && self.timestamp_usecs >= timestamp
}
pub fn epoch(&self) -> u64 {
self.epoch
}
pub fn executed_state_id(&self) -> HashValue {
self.executed_state_id
}
pub fn has_reconfiguration(&self) -> bool {
self.next_epoch_state.is_some()
}
pub fn id(&self) -> HashValue {
self.id
}
pub fn next_epoch_state(&self) -> Option<&EpochState> {
self.next_epoch_state.as_ref()
}
pub fn round(&self) -> Round {
self.round
}
pub fn timestamp_usecs(&self) -> u64 {
self.timestamp_usecs
}
pub fn version(&self) -> Version {
self.version
}
pub fn match_ordered_only(&self, executed_block_info: &BlockInfo) -> bool {
self.epoch == executed_block_info.epoch
&& self.round == executed_block_info.round
&& self.id == executed_block_info.id
&& (self.timestamp_usecs == executed_block_info.timestamp_usecs
|| (self.timestamp_usecs > executed_block_info.timestamp_usecs
&& executed_block_info.has_reconfiguration()))
}
pub fn is_ordered_only(&self) -> bool {
*self != BlockInfo::empty()
&& self.next_epoch_state == None
&& self.executed_state_id == *ACCUMULATOR_PLACEHOLDER_HASH
&& self.version == 0
}
}
impl Display for BlockInfo {
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
write!(
f,
"BlockInfo: [epoch: {}, round: {}, id: {}, executed_state_id: {}, version: {}, timestamp (us): {}, next_epoch_state: {}]",
self.epoch(),
self.round(),
self.id(),
self.executed_state_id(),
self.version(),
self.timestamp_usecs(),
self.next_epoch_state.as_ref().map_or("None".to_string(), |epoch_state| format!("{}", epoch_state)),
)
}
}