use super::ConsensusGraph;
use crate::errors::{invalid_params, Result as CoreResult};
use cfx_parameters::consensus::*;
use cfx_types::H256;
use primitives::{compute_block_number, EpochNumber};
use std::cmp::min;
impl ConsensusGraph {
pub fn block_count(&self) -> u64 {
self.inner.read_recursive().total_processed_block_count()
}
pub fn get_height_from_epoch_number(
&self, epoch_number: EpochNumber,
) -> Result<u64, String> {
Ok(match epoch_number {
EpochNumber::Earliest => 0,
EpochNumber::LatestCheckpoint => {
self.latest_checkpoint_epoch_number()
}
EpochNumber::LatestConfirmed => {
self.latest_confirmed_epoch_number()
}
EpochNumber::LatestMined => self.best_epoch_number(),
EpochNumber::LatestFinalized => {
self.latest_finalized_epoch_number()
}
EpochNumber::LatestState => self.best_executed_state_epoch_number(),
EpochNumber::Number(num) => {
let epoch_num = num;
if epoch_num > self.inner.read_recursive().best_epoch_number() {
return Err("Invalid params: expected a numbers with less than largest epoch number.".to_owned());
}
epoch_num
}
})
}
pub fn get_block_epoch_number(&self, hash: &H256) -> Option<u64> {
if let Some(e) =
self.inner.read_recursive().get_block_epoch_number(hash)
{
return Some(e);
}
self.data_man.block_epoch_number(hash)
}
pub fn get_block_hashes_by_epoch(
&self, epoch_number: EpochNumber,
) -> Result<Vec<H256>, String> {
self.get_height_from_epoch_number(epoch_number)
.and_then(|height| {
self.inner.read_recursive().block_hashes_by_epoch(height)
})
}
pub fn get_hash_from_epoch_number(
&self, epoch_number: EpochNumber,
) -> Result<H256, String> {
self.get_height_from_epoch_number(epoch_number)
.and_then(|height| {
self.inner.read().get_pivot_hash_from_epoch_number(height)
})
}
pub fn get_skipped_block_hashes_by_epoch(
&self, epoch_number: EpochNumber,
) -> Result<Vec<H256>, String> {
self.get_height_from_epoch_number(epoch_number)
.and_then(|height| {
self.inner
.read_recursive()
.skipped_block_hashes_by_epoch(height)
})
}
pub fn get_block_epoch_number_with_pivot_check(
&self, hash: &H256, require_pivot: bool,
) -> CoreResult<u64> {
let inner = &*self.inner.read();
let epoch_number =
inner.get_block_epoch_number(&hash).ok_or(invalid_params(
"epoch parameter",
format!("block's epoch number is not found: {:?}", hash),
))?;
if require_pivot {
if let Err(..) =
inner.check_block_pivot_assumption(&hash, epoch_number)
{
bail!(invalid_params(
"epoch parameter",
format!(
"should receive a pivot block hash, receives: {:?}",
hash
),
))
}
}
Ok(epoch_number)
}
pub fn get_block_number(
&self, block_hash: &H256,
) -> Result<Option<u64>, String> {
let inner = self.inner.read_recursive();
let epoch_number = match inner
.get_block_epoch_number(block_hash)
.or_else(|| self.data_man.block_epoch_number(&block_hash))
{
None => return Ok(None),
Some(epoch_number) => epoch_number,
};
let blocks = match self
.get_block_hashes_by_epoch(EpochNumber::Number(epoch_number))
.ok()
.or_else(|| {
self.data_man
.executed_epoch_set_hashes_from_db(epoch_number)
}) {
None => return Ok(None),
Some(hashes) => hashes,
};
let epoch_hash = blocks.last().expect("Epoch not empty");
let start_block_number =
match self.data_man.get_epoch_execution_context(&epoch_hash) {
None => return Ok(None),
Some(ctx) => ctx.start_block_number,
};
let index_of_block = match blocks.iter().position(|x| x == block_hash) {
None => return Ok(None),
Some(index) => index as u64,
};
return Ok(Some(compute_block_number(
start_block_number,
index_of_block,
)));
}
pub fn validate_stated_epoch(
&self, epoch_number: &EpochNumber,
) -> Result<(), String> {
match epoch_number {
EpochNumber::LatestMined => {
return Err("Latest mined epoch is not executed".into());
}
EpochNumber::Number(num) => {
let latest_state_epoch =
self.best_executed_state_epoch_number();
if *num > latest_state_epoch {
return Err(format!("Specified epoch {} is not executed, the latest state epoch is {}", num, latest_state_epoch));
}
}
_ => {}
}
Ok(())
}
pub fn best_executed_state_epoch_number(&self) -> u64 {
let state_upper_bound =
self.data_man.state_availability_boundary.read().upper_bound;
let best_epoch_number = self.best_info.read().best_epoch_number;
let deferred_state_height =
if best_epoch_number < DEFERRED_STATE_EPOCH_COUNT {
0
} else {
best_epoch_number - DEFERRED_STATE_EPOCH_COUNT + 1
};
min(state_upper_bound, deferred_state_height)
}
}