saito_core/core/consensus/
blockring.rs1use log::{debug, trace};
2
3use crate::core::consensus::block::Block;
4use crate::core::consensus::ringitem::RingItem;
5use crate::core::defs::{BlockId, PrintForLog, SaitoHash};
6
7#[derive(Debug)]
13pub struct BlockRing {
14 pub ring: Vec<RingItem>,
19 pub lc_pos: Option<usize>,
20 pub empty: bool,
21 pub genesis_period: BlockId,
22}
23
24impl BlockRing {
25 #[allow(clippy::new_without_default)]
27 pub fn new(genesis_period: BlockId) -> Self {
28 let mut init_ring: Vec<RingItem> = vec![];
29 for _i in 0..(genesis_period * 2) {
30 init_ring.push(RingItem::default());
31 }
32
33 BlockRing {
34 ring: init_ring,
35 lc_pos: None,
36 empty: true,
37 genesis_period,
38 }
39 }
40 pub fn get_ring_buffer_size(&self) -> BlockId {
41 self.genesis_period * 2
42 }
43 pub fn add_block(&mut self, block: &Block) {
44 let insert_pos = block.id % self.get_ring_buffer_size();
45 trace!(
46 "blockring.add_block : {:?} at pos = {:?}",
47 block.hash.to_hex(),
48 insert_pos
49 );
50 self.ring[insert_pos as usize].add_block(block.id, block.hash);
51 }
52
53 pub fn contains_block_hash_at_block_id(&self, block_id: u64, block_hash: SaitoHash) -> bool {
54 let insert_pos = block_id % self.get_ring_buffer_size();
55 self.ring[insert_pos as usize].contains_block_hash(block_hash)
56 }
57
58 pub fn get_latest_block_hash(&self) -> SaitoHash {
59 match self.lc_pos {
60 Some(lc_pos_block_ring) => match self.ring[lc_pos_block_ring].lc_pos {
61 Some(lc_pos_block_item) => {
62 self.ring[lc_pos_block_ring].block_hashes[lc_pos_block_item]
63 }
64 None => [0; 32],
65 },
66 None => [0; 32],
67 }
68 }
69
70 pub fn get_latest_block_id(&self) -> BlockId {
71 match self.lc_pos {
72 Some(lc_pos_block_ring) => match self.ring[lc_pos_block_ring].lc_pos {
73 Some(lc_pos_block_item) => {
74 self.ring[lc_pos_block_ring].block_ids[lc_pos_block_item]
75 }
76 None => 0,
77 },
78 None => 0,
79 }
80 }
81
82 pub fn get_longest_chain_block_hash_at_block_id(&self, id: u64) -> Option<SaitoHash> {
83 let insert_pos = (id % self.get_ring_buffer_size()) as usize;
84 trace!(
85 "blockring -> insert_pos : {:?}, id : {:?}, ring_length : {:?}",
86 insert_pos,
87 id,
88 self.get_ring_buffer_size()
89 );
90 match self.ring[insert_pos].lc_pos {
91 Some(lc_pos) => {
92 trace!(
93 "lc_pos : {:?}, ring_size : {:?} hash_count : {:?}",
94 lc_pos,
95 self.ring.len(),
96 self.ring[insert_pos].block_hashes.len()
97 );
98 let ring_item = self.ring.get(insert_pos)?;
99 let item_id = ring_item.block_ids.get(lc_pos)?;
100 if item_id != &id {
101 trace!(
102 "get_longest_chain_block_hash_by_block_id : {:?} insert_pos = {:?} is not set",
103 id,
104 insert_pos
105 );
106 return None;
107 }
108 let hash = ring_item.block_hashes.get(lc_pos)?;
109 Some(*hash)
110 }
111 None => {
112 trace!(
113 "get_longest_chain_block_hash_by_block_id : {:?} insert_pos = {:?} is not set",
114 id,
115 insert_pos
116 );
117 None
118 }
119 }
120 }
121
122 pub fn is_block_hash_at_block_id(&self, block_id: u64, block_hash: SaitoHash) -> bool {
123 let insert_pos = block_id % self.get_ring_buffer_size();
124 for i in 0..self.ring[insert_pos as usize].block_hashes.len() {
125 if self.ring[insert_pos as usize].block_hashes[i] == block_hash {
126 return true;
127 }
128 }
129 false
130 }
131
132 pub fn is_empty(&self) -> bool {
133 self.empty
134 }
135
136 pub fn delete_block(&mut self, block_id: u64, block_hash: SaitoHash) {
137 debug!(
138 "blockring.delete_block : block_id = {:?}, block_hash = {:?}",
139 block_id,
140 block_hash.to_hex()
141 );
142 let insert_pos = block_id % self.get_ring_buffer_size();
143 self.ring[insert_pos as usize].delete_block(block_id, block_hash);
144 }
145
146 pub fn get_block_hashes_at_block_id(&self, block_id: u64) -> Vec<SaitoHash> {
147 let insert_pos = block_id % self.get_ring_buffer_size();
148 let mut v: Vec<SaitoHash> = vec![];
149 for i in 0..self.ring[insert_pos as usize].block_hashes.len() {
150 if self.ring[insert_pos as usize].block_ids[i] == block_id {
151 v.push(self.ring[insert_pos as usize].block_hashes[i]);
152 }
153 }
154 v
155 }
156
157 pub fn on_chain_reorganization(&mut self, block_id: u64, hash: SaitoHash, lc: bool) -> bool {
158 let insert_pos = block_id % self.get_ring_buffer_size();
159 self.ring[insert_pos as usize].on_chain_reorganization(hash, lc);
160 trace!(
161 "blockring.on_chain_reorg : block_id = {:?}, hash = {:?}, insert_pos = {:?}, lc_pos = {:?}",
162 block_id,
163 hash.to_hex(),
164 insert_pos,
165 self.ring[insert_pos as usize].lc_pos
166 );
167 if lc {
168 self.lc_pos = Some(insert_pos as usize);
169 } else {
170 if let Some(lc_pos) = self.lc_pos {
180 if lc_pos == insert_pos as usize {
181 let previous_block_index: usize = if lc_pos > 0 {
182 lc_pos - 1
183 } else {
184 self.get_ring_buffer_size() as usize - 1
185 };
186
187 self.lc_pos = None;
189
190 if let Some(previous_block_index_lc_pos) =
193 self.ring[previous_block_index].lc_pos
194 {
195 if self.ring[previous_block_index].block_ids.len()
196 > previous_block_index_lc_pos
197 {
198 if self.ring[previous_block_index].block_ids
199 [previous_block_index_lc_pos]
200 == block_id - 1
201 {
202 self.lc_pos = Some(previous_block_index);
203 }
204 }
205 }
206 }
207 }
208 }
209 true
210 }
211
212 pub fn print_lc(&self) {
213 for i in 0..self.genesis_period {
214 if !self.ring[i as usize].block_hashes.is_empty() {
215 trace!(
216 "Block {:?}: {:?}",
217 i,
218 self.get_longest_chain_block_hash_at_block_id(i)
219 );
220 }
221 }
222 }
223
224 pub fn get_block_hash_by_block_id(&self, block_id: u64) -> Option<SaitoHash> {
225 let insert_pos = (block_id % self.get_ring_buffer_size()) as usize;
226 self.ring[insert_pos]
227 .block_ids
228 .iter()
229 .position(|&id| id == block_id)
230 .map(|pos| self.ring[insert_pos].block_hashes[pos])
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use crate::core::consensus::block::Block;
237 use crate::core::consensus::blockring::BlockRing;
238
239 #[test]
240 fn blockring_new_test() {
241 let blockring = BlockRing::new(1_000);
242 assert_eq!(blockring.ring.len() as u64, 2_000);
243 assert_eq!(blockring.lc_pos, None);
244 }
245
246 #[test]
247 fn blockring_add_block_test() {
248 let mut blockring = BlockRing::new(1_000);
249 let mut block = Block::new();
250 block.id = 1;
251 block.generate_hash();
252 let block_hash = block.hash;
253 let block_id = block.id;
254
255 assert_eq!(blockring.is_empty(), true);
257 assert_eq!(blockring.get_latest_block_hash(), [0; 32]);
258 assert_eq!(blockring.get_latest_block_id(), 0);
259 assert_eq!(blockring.get_longest_chain_block_hash_at_block_id(0), None);
260 assert_eq!(
261 blockring.contains_block_hash_at_block_id(block.id, block.hash),
262 false
263 );
264
265 blockring.add_block(&block);
266 blockring.on_chain_reorganization(block.id, block.hash, true);
267
268 assert_eq!(blockring.get_latest_block_hash(), block_hash);
270 assert_eq!(blockring.get_latest_block_id(), block_id);
271 assert_eq!(
272 blockring
273 .get_longest_chain_block_hash_at_block_id(block_id)
274 .unwrap(),
275 block_hash
276 );
277 assert_eq!(
278 blockring.contains_block_hash_at_block_id(block.id, block.hash),
279 true
280 );
281 }
282
283 #[test]
284 fn blockring_delete_block_test() {
285 let mut blockring = BlockRing::new(1_000);
286 let mut block = Block::new();
287 block.generate_hash();
288 let block_hash = block.hash;
289 let block_id = block.id;
290
291 assert_eq!(blockring.is_empty(), true);
293 assert_eq!(blockring.get_latest_block_hash(), [0; 32]);
294 assert_eq!(blockring.get_latest_block_id(), 0);
295 assert_eq!(blockring.get_longest_chain_block_hash_at_block_id(0), None);
296 assert_eq!(
297 blockring.contains_block_hash_at_block_id(block.id, block.hash),
298 false
299 );
300
301 blockring.add_block(&block);
302 blockring.on_chain_reorganization(block.id, block.hash, true);
303
304 assert_eq!(blockring.get_latest_block_hash(), block_hash);
306 assert_eq!(blockring.get_latest_block_id(), block_id);
307 assert_eq!(
308 blockring
309 .get_longest_chain_block_hash_at_block_id(block_id)
310 .unwrap(),
311 block_hash
312 );
313 assert_eq!(
314 blockring.contains_block_hash_at_block_id(block.id, block.hash),
315 true
316 );
317
318 blockring.delete_block(block.id, block.hash);
319 assert_eq!(
320 blockring.contains_block_hash_at_block_id(block.id, block.hash),
321 false
322 );
323 }
324
325 #[tokio::test]
326 #[serial_test::serial]
327 async fn blockring_manual_reorganization_test() {
331 let mut block1 = Block::new();
332 let mut block2 = Block::new();
333 let mut block3 = Block::new();
334 let mut block4 = Block::new();
335 let mut block5 = Block::new();
336
337 block1.id = 1;
338 block2.id = 2;
339 block3.id = 3;
340 block4.id = 4;
341 block5.id = 5;
342
343 block1.generate().unwrap();
344 block2.generate().unwrap();
345 block3.generate().unwrap();
346 block4.generate().unwrap();
347 block5.generate().unwrap();
348
349 let mut blockring = BlockRing::new(1_000);
350
351 blockring.add_block(&block1);
352 blockring.add_block(&block2);
353 blockring.add_block(&block3);
354 blockring.add_block(&block4);
355 blockring.add_block(&block5);
356
357 assert_eq!(
359 blockring.contains_block_hash_at_block_id(1, block1.hash),
360 true
361 );
362 assert_eq!(
363 blockring.contains_block_hash_at_block_id(2, block2.hash),
364 true
365 );
366 assert_eq!(
367 blockring.contains_block_hash_at_block_id(3, block3.hash),
368 true
369 );
370 assert_eq!(
371 blockring.contains_block_hash_at_block_id(4, block4.hash),
372 true
373 );
374 assert_eq!(
375 blockring.contains_block_hash_at_block_id(5, block5.hash),
376 true
377 );
378 assert_eq!(
379 blockring.contains_block_hash_at_block_id(2, block4.hash),
380 false
381 );
382
383 blockring.on_chain_reorganization(1, block1.hash, true);
385 assert_eq!(blockring.get_latest_block_id(), 1);
386 blockring.on_chain_reorganization(2, block2.hash, true);
387 assert_eq!(blockring.get_latest_block_id(), 2);
388 blockring.on_chain_reorganization(3, block3.hash, true);
389 assert_eq!(blockring.get_latest_block_id(), 3);
390 blockring.on_chain_reorganization(4, block4.hash, true);
391 assert_eq!(blockring.get_latest_block_id(), 4);
392 blockring.on_chain_reorganization(5, block5.hash, true);
393 assert_eq!(blockring.get_latest_block_id(), 5);
394 blockring.on_chain_reorganization(5, block5.hash, false);
395 assert_eq!(blockring.get_latest_block_id(), 4);
396 blockring.on_chain_reorganization(4, block4.hash, false);
397 assert_eq!(blockring.get_latest_block_id(), 3);
398 blockring.on_chain_reorganization(3, block3.hash, false);
399 assert_eq!(blockring.get_latest_block_id(), 2);
400
401 blockring.on_chain_reorganization(532, block5.hash, false);
403 assert_eq!(blockring.get_latest_block_id(), 2);
404
405 blockring.on_chain_reorganization(2, block2.hash, true);
407 blockring.on_chain_reorganization(2, block2.hash, true);
408 assert_eq!(blockring.get_latest_block_id(), 2);
409 }
410}