tycho_block_util/block/
top_blocks.rs1use anyhow::Result;
2use tycho_types::models::*;
3use tycho_util::FastHashMap;
4
5use crate::block::BlockStuff;
6
7#[derive(Debug, Default, Clone)]
9pub struct ShardHeights(FastHashMap<ShardIdent, u32>);
10
11impl ShardHeights {
12 pub fn contains(&self, block_id: &BlockId) -> bool {
15 self.contains_shard_seqno(&block_id.shard, block_id.seqno)
16 }
17
18 pub fn contains_ext<F>(&self, block_id: &BlockId, f: F) -> bool
22 where
23 F: Fn(u32, u32) -> bool,
24 {
25 self.contains_shard_seqno_ext(&block_id.shard, block_id.seqno, f)
26 }
27
28 pub fn contains_shard_seqno(&self, shard_ident: &ShardIdent, seqno: u32) -> bool {
33 self.contains_shard_seqno_ext(shard_ident, seqno, |top_seqno, seqno| top_seqno <= seqno)
34 }
35
36 pub fn contains_shard_seqno_ext<F>(&self, shard_ident: &ShardIdent, seqno: u32, f: F) -> bool
43 where
44 F: Fn(u32, u32) -> bool,
45 {
46 match self.0.get(shard_ident) {
47 Some(&top_seqno) => f(top_seqno, seqno),
48 None => self
49 .0
50 .iter()
51 .find(|&(shard, _)| shard_ident.intersects(shard))
52 .map(|(_, &top_seqno)| f(top_seqno, seqno))
53 .unwrap_or_default(),
54 }
55 }
56
57 pub fn len(&self) -> usize {
59 self.0.len()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.0.is_empty()
65 }
66
67 pub fn iter(&self) -> impl ExactSizeIterator<Item = BlockIdShort> + Clone + '_ {
68 self.0
69 .iter()
70 .map(|(shard, seqno)| BlockIdShort::from((*shard, *seqno)))
71 }
72}
73
74impl<const N: usize> From<[(ShardIdent, u32); N]> for ShardHeights {
75 fn from(value: [(ShardIdent, u32); N]) -> Self {
76 Self(FastHashMap::from_iter(value))
77 }
78}
79
80impl FromIterator<(ShardIdent, u32)> for ShardHeights {
81 #[inline]
82 fn from_iter<T: IntoIterator<Item = (ShardIdent, u32)>>(iter: T) -> Self {
83 Self(FastHashMap::from_iter(iter))
84 }
85}
86
87impl FromIterator<BlockIdShort> for ShardHeights {
88 fn from_iter<T: IntoIterator<Item = BlockIdShort>>(iter: T) -> Self {
89 Self(
90 iter.into_iter()
91 .map(|block_id| (block_id.shard, block_id.seqno))
92 .collect(),
93 )
94 }
95}
96
97impl From<FastHashMap<ShardIdent, u32>> for ShardHeights {
98 #[inline]
99 fn from(map: FastHashMap<ShardIdent, u32>) -> Self {
100 Self(map)
101 }
102}
103
104#[derive(Debug, Clone)]
106pub struct TopBlocks {
107 pub mc_block: BlockIdShort,
108 pub shard_heights: ShardHeights,
109}
110
111impl TopBlocks {
112 pub fn from_mc_block(mc_block_data: &BlockStuff) -> Result<Self> {
114 let block_id = mc_block_data.id();
115 debug_assert!(block_id.shard.is_masterchain());
116
117 Ok(Self {
118 mc_block: block_id.as_short_id(),
119 shard_heights: ShardHeights(mc_block_data.shard_blocks_seqno()?),
120 })
121 }
122
123 pub fn mc_seqno(&self) -> u32 {
125 self.mc_block.seqno
126 }
127
128 pub fn shard_heights(&self) -> &ShardHeights {
129 &self.shard_heights
130 }
131
132 pub fn count(&self) -> usize {
134 1 + self.shard_heights.len()
135 }
136
137 pub fn contains(&self, block_id: &BlockId) -> bool {
140 self.contains_shard_seqno(&block_id.shard, block_id.seqno)
141 }
142
143 pub fn contains_shard_seqno(&self, shard_ident: &ShardIdent, seqno: u32) -> bool {
148 if shard_ident.is_masterchain() {
149 seqno >= self.mc_block.seqno
150 } else {
151 self.shard_heights.contains_shard_seqno(shard_ident, seqno)
152 }
153 }
154
155 pub fn short_ids(&self) -> TopBlocksShortIdsIter<'_> {
157 TopBlocksShortIdsIter {
158 top_blocks: self,
159 shards_iter: None,
160 }
161 }
162}
163
164pub struct TopBlocksShortIdsIter<'a> {
166 top_blocks: &'a TopBlocks,
167 shards_iter: Option<std::collections::hash_map::Iter<'a, ShardIdent, u32>>,
168}
169
170impl Iterator for TopBlocksShortIdsIter<'_> {
171 type Item = BlockIdShort;
172
173 fn next(&mut self) -> Option<Self::Item> {
174 match &mut self.shards_iter {
175 None => {
176 self.shards_iter = Some(self.top_blocks.shard_heights.0.iter());
177 Some(self.top_blocks.mc_block)
178 }
179 Some(iter) => {
180 let (shard_ident, seqno) = iter.next()?;
181 Some(BlockIdShort::from((*shard_ident, *seqno)))
182 }
183 }
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn test_split_shards() {
193 let mut shard_heights = FastHashMap::default();
194
195 let main_shard = ShardIdent::new_full(0);
196
197 let (left_shard, right_shard) = main_shard.split().unwrap();
198 shard_heights.insert(left_shard, 1000);
199 shard_heights.insert(right_shard, 1001);
200
201 let top_blocks = TopBlocks {
202 mc_block: (ShardIdent::MASTERCHAIN, 100).into(),
203 shard_heights: shard_heights.into(),
204 };
205
206 assert!(!top_blocks.contains(&BlockId {
207 shard: right_shard,
208 seqno: 100,
209 ..Default::default()
210 }));
211
212 assert!(!top_blocks.contains(&BlockId {
214 shard: main_shard,
215 seqno: 100,
216 ..Default::default()
217 }));
218 assert!(top_blocks.contains(&BlockId {
219 shard: main_shard,
220 seqno: 10000,
221 ..Default::default()
222 }));
223
224 let (right_left_shard, _) = right_shard.split().unwrap();
226 assert!(!top_blocks.contains(&BlockId {
227 shard: right_left_shard,
228 seqno: 100,
229 ..Default::default()
230 }));
231 assert!(top_blocks.contains(&BlockId {
232 shard: right_left_shard,
233 seqno: 10000,
234 ..Default::default()
235 }));
236 }
237}