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    cfg_test_tools! {
39        /// Create a dummy ChainPoint
40        pub fn dummy() -> Self {
41            Self {
42                slot_number: SlotNumber(100),
43                block_number: BlockNumber(0),
44                block_hash: "block_hash-50".to_string(),
45            }
46        }
47    }
48}
49
50impl Display for ChainPoint {
51    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
52        write!(
53            f,
54            "ChainPoint (slot_number: {}, block_number: {}, block_hash: {})",
55            self.slot_number, self.block_number, self.block_hash
56        )
57    }
58}
59
60impl PartialOrd for ChainPoint {
61    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
62        Some(self.cmp(other))
63    }
64}
65
66impl Ord for ChainPoint {
67    fn cmp(&self, other: &Self) -> Ordering {
68        self.block_number
69            .cmp(&other.block_number)
70            .then(self.slot_number.cmp(&other.slot_number))
71            .then(self.block_hash.cmp(&other.block_hash))
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use std::cmp::Ordering;
78
79    use super::*;
80
81    #[test]
82    fn chain_point_ord_cmp_block_number_take_precedence_over_other_fields() {
83        let chain_point1 = ChainPoint {
84            slot_number: SlotNumber(15),
85            block_number: BlockNumber(10),
86            block_hash: "hash2".to_string(),
87        };
88        let chain_point2 = ChainPoint {
89            slot_number: SlotNumber(5),
90            block_number: BlockNumber(20),
91            block_hash: "hash1".to_string(),
92        };
93
94        assert_eq!(Ordering::Less, chain_point1.cmp(&chain_point2));
95    }
96
97    #[test]
98    fn chain_point_ord_cmp_if_block_number_equals_then_compare_slot_numbers() {
99        let chain_point1 = ChainPoint {
100            slot_number: SlotNumber(15),
101            block_number: BlockNumber(0),
102            block_hash: "hash2".to_string(),
103        };
104        let chain_point2 = ChainPoint {
105            slot_number: SlotNumber(5),
106            block_number: BlockNumber(0),
107            block_hash: "hash1".to_string(),
108        };
109
110        assert_eq!(Ordering::Greater, chain_point1.cmp(&chain_point2));
111    }
112
113    #[test]
114    fn chain_point_ord_cmp_if_block_number_and_slot_number_equals_then_compare_block_hash() {
115        let chain_point1 = ChainPoint {
116            slot_number: SlotNumber(5),
117            block_number: BlockNumber(10),
118            block_hash: "hash1".to_string(),
119        };
120        let chain_point2 = ChainPoint {
121            slot_number: SlotNumber(5),
122            block_number: BlockNumber(10),
123            block_hash: "hash2".to_string(),
124        };
125
126        assert_eq!(Ordering::Less, chain_point1.cmp(&chain_point2));
127    }
128}