mithril_common/entities/
cardano_chain_point.rs

1use std::cmp::Ordering;
2use std::fmt::{Display, Formatter};
3
4use serde::{Deserialize, Serialize};
5
6use crate::entities::{BlockNumber, SlotNumber};
7
8/// Hash of a Cardano Block
9pub type BlockHash = String;
10
11/// The Cardano chain point which is used to identify a specific point in the Cardano chain.
12#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
13pub struct ChainPoint {
14    /// The [slot number](https://docs.cardano.org/learn/cardano-node/#slotsandepochs)
15    pub slot_number: SlotNumber,
16
17    ///  The block number
18    pub block_number: BlockNumber,
19
20    /// The hex encoded block hash
21    pub block_hash: BlockHash,
22}
23
24impl ChainPoint {
25    /// [ChainPoint] factory
26    pub fn new<T: Into<BlockHash>>(
27        slot_number: SlotNumber,
28        block_number: BlockNumber,
29        block_hash: T,
30    ) -> ChainPoint {
31        ChainPoint {
32            slot_number,
33            block_number,
34            block_hash: block_hash.into(),
35        }
36    }
37}
38
39impl Display for ChainPoint {
40    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
41        write!(
42            f,
43            "ChainPoint (slot_number: {}, block_number: {}, block_hash: {})",
44            self.slot_number, self.block_number, self.block_hash
45        )
46    }
47}
48
49impl PartialOrd for ChainPoint {
50    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
51        Some(self.cmp(other))
52    }
53}
54
55impl Ord for ChainPoint {
56    fn cmp(&self, other: &Self) -> Ordering {
57        self.block_number
58            .cmp(&other.block_number)
59            .then(self.slot_number.cmp(&other.slot_number))
60            .then(self.block_hash.cmp(&other.block_hash))
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use std::cmp::Ordering;
67
68    use super::*;
69
70    #[test]
71    fn chain_point_ord_cmp_block_number_take_precedence_over_other_fields() {
72        let chain_point1 = ChainPoint {
73            slot_number: SlotNumber(15),
74            block_number: BlockNumber(10),
75            block_hash: "hash2".to_string(),
76        };
77        let chain_point2 = ChainPoint {
78            slot_number: SlotNumber(5),
79            block_number: BlockNumber(20),
80            block_hash: "hash1".to_string(),
81        };
82
83        assert_eq!(Ordering::Less, chain_point1.cmp(&chain_point2));
84    }
85
86    #[test]
87    fn chain_point_ord_cmp_if_block_number_equals_then_compare_slot_numbers() {
88        let chain_point1 = ChainPoint {
89            slot_number: SlotNumber(15),
90            block_number: BlockNumber(0),
91            block_hash: "hash2".to_string(),
92        };
93        let chain_point2 = ChainPoint {
94            slot_number: SlotNumber(5),
95            block_number: BlockNumber(0),
96            block_hash: "hash1".to_string(),
97        };
98
99        assert_eq!(Ordering::Greater, chain_point1.cmp(&chain_point2));
100    }
101
102    #[test]
103    fn chain_point_ord_cmp_if_block_number_and_slot_number_equals_then_compare_block_hash() {
104        let chain_point1 = ChainPoint {
105            slot_number: SlotNumber(5),
106            block_number: BlockNumber(10),
107            block_hash: "hash1".to_string(),
108        };
109        let chain_point2 = ChainPoint {
110            slot_number: SlotNumber(5),
111            block_number: BlockNumber(10),
112            block_hash: "hash2".to_string(),
113        };
114
115        assert_eq!(Ordering::Less, chain_point1.cmp(&chain_point2));
116    }
117}