use brk_error::Result;
use brk_rpc::Client;
use brk_types::{BlockHash, Height};
use rustc_hash::FxHashMap;
pub struct CanonicalRange {
pub start: Height,
anchor: Option<BlockHash>,
by_hash: FxHashMap<BlockHash, u32>,
}
impl CanonicalRange {
pub fn walk(client: &Client, anchor: Option<&BlockHash>, tip: Height) -> Result<Self> {
let start = match anchor {
Some(hash) => Height::from(client.get_block_header_info(hash)?.height + 1),
None => Height::ZERO,
};
let mut range = Self::between(client, start, tip)?;
range.anchor = anchor.cloned();
Ok(range)
}
pub fn between(client: &Client, start: Height, end: Height) -> Result<Self> {
if start > end {
return Ok(Self {
start,
anchor: None,
by_hash: FxHashMap::default(),
});
}
let by_hash = client
.get_block_hashes_range(*start, *end)?
.into_iter()
.enumerate()
.map(|(i, h)| (h, i as u32))
.collect();
Ok(Self {
start,
anchor: None,
by_hash,
})
}
pub fn len(&self) -> usize {
self.by_hash.len()
}
pub fn is_empty(&self) -> bool {
self.by_hash.is_empty()
}
#[inline]
pub(crate) fn offset_of(&self, hash: &BlockHash) -> Option<u32> {
self.by_hash.get(hash).copied()
}
#[inline]
pub(crate) fn verify_prev(&self, offset: u32, prev_hash: &BlockHash) -> bool {
match offset {
0 => self.anchor.as_ref().is_none_or(|a| a == prev_hash),
_ => self.offset_of(prev_hash) == Some(offset - 1),
}
}
}