1use std::cmp::Ordering;
2
3use crate::crypto_helper::MKTreeNode;
4use crate::entities::{BlockHash, BlockNumber, CardanoTransaction, SlotNumber, TransactionHash};
5
6#[derive(Debug, Clone, PartialEq)]
8pub struct CardanoBlock {
9 pub block_hash: BlockHash,
11 pub block_number: BlockNumber,
13 pub slot_number: SlotNumber,
15}
16
17impl CardanoBlock {
18 pub fn new<U: Into<BlockHash>>(
20 block_hash: U,
21 block_number: BlockNumber,
22 slot_number: SlotNumber,
23 ) -> Self {
24 Self {
25 block_hash: block_hash.into(),
26 block_number,
27 slot_number,
28 }
29 }
30}
31
32#[derive(Debug, Clone, PartialEq)]
34pub struct CardanoBlockWithTransactions {
35 pub block_hash: BlockHash,
37 pub block_number: BlockNumber,
39 pub slot_number: SlotNumber,
41 pub transactions_hashes: Vec<TransactionHash>,
43}
44
45impl CardanoBlockWithTransactions {
46 pub fn new<U: Into<BlockHash>, T: Into<TransactionHash>>(
48 block_hash: U,
49 block_number: BlockNumber,
50 slot_number: SlotNumber,
51 tx_hashes: Vec<T>,
52 ) -> Self {
53 Self {
54 block_hash: block_hash.into(),
55 block_number,
56 slot_number,
57 transactions_hashes: tx_hashes.into_iter().map(Into::into).collect(),
58 }
59 }
60
61 pub fn into_transactions(self) -> Vec<CardanoTransaction> {
63 self.transactions_hashes
64 .into_iter()
65 .map(|tx_hash| {
66 CardanoTransaction::new(
67 tx_hash,
68 self.block_number,
69 self.slot_number,
70 self.block_hash.clone(),
71 )
72 })
73 .collect()
74 }
75
76 pub fn into_mk_tree_node(self) -> Vec<CardanoBlockTransactionMkTreeNode> {
78 let mut result = Vec::with_capacity(self.transactions_hashes.len() + 1);
79 result.push(CardanoBlockTransactionMkTreeNode::Block {
80 block_hash: self.block_hash.clone(),
81 block_number: self.block_number,
82 slot_number: self.slot_number,
83 });
84 result.extend(self.transactions_hashes.into_iter().map(|tx_hash| {
85 CardanoBlockTransactionMkTreeNode::Transaction {
86 transaction_hash: tx_hash,
87 block_hash: self.block_hash.clone(),
88 block_number: self.block_number,
89 slot_number: self.slot_number,
90 }
91 }));
92
93 result
94 }
95
96 pub fn transactions_count(&self) -> usize {
98 self.transactions_hashes.len()
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Eq)]
109pub enum CardanoBlockTransactionMkTreeNode {
110 Block {
112 block_hash: BlockHash,
114 block_number: BlockNumber,
116 slot_number: SlotNumber,
118 },
119 Transaction {
121 transaction_hash: TransactionHash,
123 block_number: BlockNumber,
125 slot_number: SlotNumber,
127 block_hash: BlockHash,
129 },
130}
131
132impl CardanoBlockTransactionMkTreeNode {
133 fn leaf_identifier(&self) -> Vec<u8> {
134 match self {
135 Self::Block {
136 block_hash,
137 block_number,
138 slot_number,
139 } => format!("Block/{block_hash}/{block_number}/{slot_number}").into_bytes(),
140 Self::Transaction {
141 transaction_hash,
142 block_hash,
143 block_number,
144 slot_number,
145 } => format!("Tx/{transaction_hash}/{block_hash}/{block_number}/{slot_number}",)
146 .into_bytes(),
147 }
148 }
149}
150
151impl From<CardanoBlockTransactionMkTreeNode> for MKTreeNode {
152 fn from(value: CardanoBlockTransactionMkTreeNode) -> Self {
153 MKTreeNode::new(value.leaf_identifier())
154 }
155}
156
157impl PartialOrd for CardanoBlockTransactionMkTreeNode {
158 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
159 Some(Ord::cmp(self, other))
160 }
161}
162
163impl Ord for CardanoBlockTransactionMkTreeNode {
164 fn cmp(&self, other: &Self) -> Ordering {
165 use CardanoBlockTransactionMkTreeNode::{Block, Transaction};
166
167 match (self, other) {
168 (Block { .. }, Transaction { .. }) => Ordering::Less,
169 (Transaction { .. }, Block { .. }) => Ordering::Greater,
170 (
171 Block {
172 block_number,
173 block_hash,
174 slot_number,
175 },
176 Block {
177 block_number: other_block_number,
178 block_hash: other_block_hash,
179 slot_number: other_slot_number,
180 },
181 ) => block_number
182 .cmp(other_block_number)
183 .then(slot_number.cmp(other_slot_number))
184 .then(block_hash.cmp(other_block_hash)),
185 (
186 Transaction {
187 block_number,
188 slot_number,
189 block_hash,
190 transaction_hash,
191 },
192 Transaction {
193 block_number: other_block_number,
194 slot_number: other_slot_number,
195 block_hash: other_block_hash,
196 transaction_hash: other_transaction_hash,
197 },
198 ) => block_number
199 .cmp(other_block_number)
200 .then(slot_number.cmp(other_slot_number))
201 .then(block_hash.cmp(other_block_hash))
202 .then(transaction_hash.cmp(other_transaction_hash)),
203 }
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 macro_rules! block_node {
212 (num: $block_number:expr, slot: $slot_number:expr) => {
213 block_node!(hash:format!("block_hash-{}", $block_number), num: $block_number, slot: $slot_number)
214 };
215 (hash: $block_hash:expr, num: $block_number:expr, slot: $slot_number:expr) => {
216 CardanoBlockTransactionMkTreeNode::Block {
217 block_hash: $block_hash.to_string(),
218 block_number: BlockNumber($block_number),
219 slot_number: SlotNumber($slot_number),
220 }
221 };
222 }
223
224 macro_rules! tx_node {
225 (hash: $tx_hash:expr, block: $block_number:expr, slot: $slot_number:expr) => {
226 tx_node!(hash: $tx_hash, block_hash: format!("block_hash-{}", $tx_hash), block: $block_number, slot: $slot_number)
227 };
228 (hash: $tx_hash:expr, block_hash: $block_hash:expr, block: $block_number:expr, slot: $slot_number:expr) => {
229 CardanoBlockTransactionMkTreeNode::Transaction {
230 transaction_hash: $tx_hash.to_string(),
231 block_hash: $block_hash.to_string(),
232 block_number: BlockNumber($block_number),
233 slot_number: SlotNumber($slot_number),
234 }
235 };
236 }
237
238 #[test]
239 fn block_node_leaf_identifier() {
240 assert_eq!(
242 "Block/block_hash-5/5/6".to_string().into_bytes(),
243 block_node!(hash: "block_hash-5", num: 5, slot: 6).leaf_identifier()
244 );
245 assert_eq!(
246 "Block/block_hash-10/9/13".to_string().into_bytes(),
247 block_node!(hash: "block_hash-10", num: 9, slot: 13).leaf_identifier()
248 );
249 }
250
251 #[test]
252 fn transaction_node_leaf_identifier() {
253 assert_eq!(
255 "Tx/tx_hash-5/block_hash-5/5/6".to_string().into_bytes(),
256 tx_node!(hash: "tx_hash-5", block_hash: "block_hash-5", block: 5, slot: 6)
257 .leaf_identifier()
258 );
259 assert_eq!(
260 "Tx/tx_hash-10/block_hash-10/9/13".to_string().into_bytes(),
261 tx_node!(hash: "tx_hash-10", block_hash: "block_hash-10", block: 9, slot: 13)
262 .leaf_identifier()
263 );
264 }
265
266 #[test]
267 fn convert_block_node_into_mktree_nodes() {
268 let block = block_node!(hash: "block_hash-5", num: 5, slot: 6);
269 let expected: MKTreeNode = MKTreeNode::new(block.leaf_identifier());
270
271 let computed_node: MKTreeNode = block.into();
272 assert_eq!(expected, computed_node);
273 assert_ne!(
274 computed_node,
275 block_node!(hash: "other", num: 5, slot: 6).into()
276 );
277 assert_ne!(
278 computed_node,
279 block_node!(hash: "block_hash-10", num: 1000, slot: 6).into()
280 );
281 assert_ne!(
282 computed_node,
283 block_node!(hash: "block_hash-10", num: 5, slot: 1000).into()
284 );
285 }
286
287 #[test]
288 fn convert_tx_node_into_mktree_nodes() {
289 let transaction =
290 tx_node!(hash: "tx_hash-5", block_hash: "block_hash-5", block: 5, slot: 6);
291 let expected: MKTreeNode = MKTreeNode::new(transaction.leaf_identifier());
292
293 let computed_node: MKTreeNode = transaction.into();
294 assert_eq!(expected, computed_node);
295 assert_ne!(
296 computed_node,
297 tx_node!(hash: "other", block_hash: "block_hash-5", block: 5, slot: 6).into(),
298 );
299 assert_ne!(
300 computed_node,
301 tx_node!(hash: "tx_hash-5", block_hash: "other", block: 5, slot: 6).into(),
302 );
303 assert_ne!(
304 computed_node,
305 tx_node!(hash: "tx_hash-5", block_hash: "block_hash-5", block: 1000, slot: 6).into(),
306 );
307 assert_ne!(
308 computed_node,
309 tx_node!(hash: "tx_hash-5", block_hash: "block_hash-5", block: 5, slot: 1000).into(),
310 );
311 }
312
313 mod mk_tree_node_ordering {
314 use super::*;
315
316 #[test]
317 fn same_value_yield_equal_order() {
318 let block = block_node!(num: 5, slot: 6);
319 let tx = tx_node!(hash: "tx_hash-1", block: 5, slot: 6);
320
321 assert_eq!(Ordering::Equal, block.cmp(&block));
322 assert_eq!(Ordering::Equal, tx.cmp(&tx));
323 }
324
325 #[test]
326 fn order_block_nodes_first_then_transaction_nodes() {
327 let block = block_node!(num: 5, slot: 6);
328 let tx = tx_node!(hash: "tx_hash-1", block: 5, slot: 6);
329
330 assert_eq!(Ordering::Less, block.cmp(&tx));
331 assert_eq!(Ordering::Greater, tx.cmp(&block));
332 }
333
334 #[test]
335 fn order_blocks_by_block_number_first() {
336 let block = block_node!(hash: "block_hash-5", num: 5, slot: 6);
337
338 assert_eq!(
339 Ordering::Less,
340 block.cmp(&block_node!(hash: "block_hash-1", num: 10, slot: 1))
341 );
342 assert_eq!(
343 Ordering::Greater,
344 block.cmp(&block_node!(hash: "block_hash-9", num: 1, slot: 9))
345 );
346 }
347
348 #[test]
349 fn order_blocks_by_slot_number_second() {
350 let block = block_node!(hash: "block_hash-5", num: 5, slot: 6);
351
352 assert_eq!(
353 Ordering::Less,
354 block.cmp(&block_node!(hash: "block_hash-1", num: 5, slot: 9))
355 );
356 assert_eq!(
357 Ordering::Greater,
358 block.cmp(&block_node!(hash: "block_hash-9", num: 5, slot: 1))
359 );
360 }
361
362 #[test]
363 fn order_blocks_by_block_hash_third() {
364 let block = block_node!(hash: "block_hash-5", num: 5, slot: 6);
365
366 assert_eq!(
367 Ordering::Less,
368 block.cmp(&block_node!(hash: "block_hash-9", num: 5, slot: 6))
369 );
370 assert_eq!(
371 Ordering::Greater,
372 block.cmp(&block_node!(hash: "block_hash-1", num: 5, slot: 6))
373 );
374 }
375
376 #[test]
377 fn order_transactions_by_block_number_first() {
378 let tx = tx_node!(hash: "tx_hash-5", block_hash: "block_hash-5", block: 5, slot: 6);
379
380 assert_eq!(
381 Ordering::Less,
382 tx.cmp(
383 &tx_node!(hash: "tx_hash-1", block_hash: "block_hash-1", block: 10, slot: 1)
384 )
385 );
386 assert_eq!(
387 Ordering::Greater,
388 tx.cmp(&tx_node!(hash: "tx_hash-9", block_hash: "block_hash-9", block: 1, slot: 9))
389 );
390 }
391
392 #[test]
393 fn order_transactions_by_slot_number_second() {
394 let tx = tx_node!(hash: "tx_hash-5", block_hash: "block_hash-5", block: 5, slot: 6);
395
396 assert_eq!(
397 Ordering::Less,
398 tx.cmp(&tx_node!(hash: "tx_hash-1", block_hash: "block_hash-1", block: 5, slot: 9))
399 );
400 assert_eq!(
401 Ordering::Greater,
402 tx.cmp(&tx_node!(hash: "tx_hash-9", block_hash: "block_hash-9", block: 5, slot: 1))
403 );
404 }
405
406 #[test]
407 fn order_transactions_by_block_hash_third() {
408 let tx = tx_node!(hash: "tx_hash-5", block_hash: "block_hash-5", block: 5, slot: 6);
409
410 assert_eq!(
411 Ordering::Less,
412 tx.cmp(&tx_node!(hash: "tx_hash-1", block_hash: "block_hash-9", block: 5, slot: 6))
413 );
414 assert_eq!(
415 Ordering::Greater,
416 tx.cmp(&tx_node!(hash: "tx_hash-9", block_hash: "block_hash-1", block: 5, slot: 6))
417 );
418 }
419
420 #[test]
421 fn order_transactions_by_transaction_hash_fourth() {
422 let tx = tx_node!(hash: "tx_hash-5", block_hash: "block_hash-5", block: 5, slot: 6);
423
424 assert_eq!(
425 Ordering::Less,
426 tx.cmp(&tx_node!(hash: "tx_hash-9", block_hash: "block_hash-5", block: 5, slot: 6))
427 );
428 assert_eq!(
429 Ordering::Greater,
430 tx.cmp(&tx_node!(hash: "tx_hash-1", block_hash: "block_hash-5", block: 5, slot: 6))
431 );
432 }
433
434 #[test]
435 fn sorting_a_vec() {
436 let mut list = vec![
437 tx_node!(hash: "tx_hash-70", block: 300, slot: 35),
438 tx_node!(hash: "tx_hash-200", block: 100, slot: 100),
439 tx_node!(hash: "tx_hash-50", block: 200, slot: 25),
440 block_node!(num: 100, slot: 100),
441 tx_node!(hash: "tx_hash-100", block: 100, slot: 100),
442 block_node!(num: 200, slot: 35),
443 block_node!(num: 200, slot: 25),
444 ];
445 list.sort();
446
447 assert_eq!(
448 list,
449 vec![
450 block_node!(num: 100, slot: 100),
451 block_node!(num: 200, slot: 25),
452 block_node!(num: 200, slot: 35),
453 tx_node!(hash: "tx_hash-100", block: 100, slot: 100),
454 tx_node!(hash: "tx_hash-200", block: 100, slot: 100),
455 tx_node!(hash: "tx_hash-50", block: 200, slot: 25),
456 tx_node!(hash: "tx_hash-70", block: 300, slot: 35),
457 ]
458 );
459 }
460 }
461}