use anyhow::Result;
use tycho_types::models::*;
use tycho_util::FastHashMap;
use crate::block::BlockStuff;
#[derive(Debug, Default, Clone)]
pub struct ShardHeights(FastHashMap<ShardIdent, u32>);
impl ShardHeights {
pub fn contains(&self, block_id: &BlockId) -> bool {
self.contains_shard_seqno(&block_id.shard, block_id.seqno)
}
pub fn contains_ext<F>(&self, block_id: &BlockId, f: F) -> bool
where
F: Fn(u32, u32) -> bool,
{
self.contains_shard_seqno_ext(&block_id.shard, block_id.seqno, f)
}
pub fn contains_shard_seqno(&self, shard_ident: &ShardIdent, seqno: u32) -> bool {
self.contains_shard_seqno_ext(shard_ident, seqno, |top_seqno, seqno| top_seqno <= seqno)
}
pub fn contains_shard_seqno_ext<F>(&self, shard_ident: &ShardIdent, seqno: u32, f: F) -> bool
where
F: Fn(u32, u32) -> bool,
{
match self.0.get(shard_ident) {
Some(&top_seqno) => f(top_seqno, seqno),
None => self
.0
.iter()
.find(|&(shard, _)| shard_ident.intersects(shard))
.map(|(_, &top_seqno)| f(top_seqno, seqno))
.unwrap_or_default(),
}
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = BlockIdShort> + Clone + '_ {
self.0
.iter()
.map(|(shard, seqno)| BlockIdShort::from((*shard, *seqno)))
}
}
impl<const N: usize> From<[(ShardIdent, u32); N]> for ShardHeights {
fn from(value: [(ShardIdent, u32); N]) -> Self {
Self(FastHashMap::from_iter(value))
}
}
impl FromIterator<(ShardIdent, u32)> for ShardHeights {
#[inline]
fn from_iter<T: IntoIterator<Item = (ShardIdent, u32)>>(iter: T) -> Self {
Self(FastHashMap::from_iter(iter))
}
}
impl FromIterator<BlockIdShort> for ShardHeights {
fn from_iter<T: IntoIterator<Item = BlockIdShort>>(iter: T) -> Self {
Self(
iter.into_iter()
.map(|block_id| (block_id.shard, block_id.seqno))
.collect(),
)
}
}
impl From<FastHashMap<ShardIdent, u32>> for ShardHeights {
#[inline]
fn from(map: FastHashMap<ShardIdent, u32>) -> Self {
Self(map)
}
}
#[derive(Debug, Clone)]
pub struct TopBlocks {
pub mc_block: BlockIdShort,
pub shard_heights: ShardHeights,
}
impl TopBlocks {
pub fn from_mc_block(mc_block_data: &BlockStuff) -> Result<Self> {
let block_id = mc_block_data.id();
debug_assert!(block_id.shard.is_masterchain());
Ok(Self {
mc_block: block_id.as_short_id(),
shard_heights: ShardHeights(mc_block_data.shard_blocks_seqno()?),
})
}
pub fn mc_seqno(&self) -> u32 {
self.mc_block.seqno
}
pub fn shard_heights(&self) -> &ShardHeights {
&self.shard_heights
}
pub fn count(&self) -> usize {
1 + self.shard_heights.len()
}
pub fn contains(&self, block_id: &BlockId) -> bool {
self.contains_shard_seqno(&block_id.shard, block_id.seqno)
}
pub fn contains_shard_seqno(&self, shard_ident: &ShardIdent, seqno: u32) -> bool {
if shard_ident.is_masterchain() {
seqno >= self.mc_block.seqno
} else {
self.shard_heights.contains_shard_seqno(shard_ident, seqno)
}
}
pub fn short_ids(&self) -> TopBlocksShortIdsIter<'_> {
TopBlocksShortIdsIter {
top_blocks: self,
shards_iter: None,
}
}
}
pub struct TopBlocksShortIdsIter<'a> {
top_blocks: &'a TopBlocks,
shards_iter: Option<std::collections::hash_map::Iter<'a, ShardIdent, u32>>,
}
impl Iterator for TopBlocksShortIdsIter<'_> {
type Item = BlockIdShort;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.shards_iter {
None => {
self.shards_iter = Some(self.top_blocks.shard_heights.0.iter());
Some(self.top_blocks.mc_block)
}
Some(iter) => {
let (shard_ident, seqno) = iter.next()?;
Some(BlockIdShort::from((*shard_ident, *seqno)))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_split_shards() {
let mut shard_heights = FastHashMap::default();
let main_shard = ShardIdent::new_full(0);
let (left_shard, right_shard) = main_shard.split().unwrap();
shard_heights.insert(left_shard, 1000);
shard_heights.insert(right_shard, 1001);
let top_blocks = TopBlocks {
mc_block: (ShardIdent::MASTERCHAIN, 100).into(),
shard_heights: shard_heights.into(),
};
assert!(!top_blocks.contains(&BlockId {
shard: right_shard,
seqno: 100,
..Default::default()
}));
assert!(!top_blocks.contains(&BlockId {
shard: main_shard,
seqno: 100,
..Default::default()
}));
assert!(top_blocks.contains(&BlockId {
shard: main_shard,
seqno: 10000,
..Default::default()
}));
let (right_left_shard, _) = right_shard.split().unwrap();
assert!(!top_blocks.contains(&BlockId {
shard: right_left_shard,
seqno: 100,
..Default::default()
}));
assert!(top_blocks.contains(&BlockId {
shard: right_left_shard,
seqno: 10000,
..Default::default()
}));
}
}