mithril_common/entities/
cardano_stake_distribution.rs

1use serde::{Deserialize, Serialize};
2use sha2::{Digest, Sha256};
3
4use super::{Epoch, StakeDistribution};
5
6/// Cardano Stake Distribution
7#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
8pub struct CardanoStakeDistribution {
9    /// Unique hash of the Cardano Stake Distribution
10    pub hash: String,
11
12    /// Epoch at which the Cardano Stake Distribution is computed
13    pub epoch: Epoch,
14
15    /// StakeDistribution represents the list of participants in the Cardano chain with their associated stake
16    pub stake_distribution: StakeDistribution,
17}
18
19impl CardanoStakeDistribution {
20    /// Constructor
21    pub fn new(epoch: Epoch, stake_distribution: StakeDistribution) -> CardanoStakeDistribution {
22        let mut cardano_stake_distribution = CardanoStakeDistribution {
23            hash: "".to_string(),
24            epoch,
25            stake_distribution,
26        };
27        cardano_stake_distribution.hash = cardano_stake_distribution.compute_hash();
28
29        cardano_stake_distribution
30    }
31
32    /// Cardano stake distribution hash computation
33    fn compute_hash(&self) -> String {
34        let mut hasher = Sha256::new();
35        hasher.update(self.epoch.to_be_bytes());
36        self.stake_distribution.iter().for_each(|(k, v)| {
37            hasher.update(k.as_bytes());
38            hasher.update(v.to_be_bytes());
39        });
40
41        hex::encode(hasher.finalize())
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn test_compute_hash() {
51        assert_eq!(
52            "7144dda132aa4fe758df8da5f85a774cef602ce85c8554baeeea006866710154",
53            CardanoStakeDistribution::new(
54                Epoch(1),
55                StakeDistribution::from([("pool-1".to_string(), 100), ("pool-2".to_string(), 200)])
56            )
57            .compute_hash()
58        );
59    }
60
61    #[test]
62    fn compute_hash_returns_same_hash_with_same_cardano_stake_distribution() {
63        let epoch = Epoch(1);
64
65        assert_eq!(
66            CardanoStakeDistribution::new(
67                epoch,
68                StakeDistribution::from([("pool-1".to_string(), 100), ("pool-2".to_string(), 200)])
69            )
70            .compute_hash(),
71            CardanoStakeDistribution::new(
72                epoch,
73                StakeDistribution::from([("pool-1".to_string(), 100), ("pool-2".to_string(), 200)])
74            )
75            .compute_hash()
76        );
77    }
78
79    #[test]
80    fn compute_hash_returns_same_hash_whatever_the_stake_distribution_order() {
81        let epoch = Epoch(1);
82
83        assert_eq!(
84            CardanoStakeDistribution::new(
85                epoch,
86                StakeDistribution::from([("pool-1".to_string(), 100), ("pool-2".to_string(), 200)])
87            )
88            .compute_hash(),
89            CardanoStakeDistribution::new(
90                epoch,
91                StakeDistribution::from([("pool-2".to_string(), 200), ("pool-1".to_string(), 100)])
92            )
93            .compute_hash()
94        );
95    }
96
97    #[test]
98    fn compute_hash_returns_different_hash_with_different_epoch() {
99        assert_ne!(
100            CardanoStakeDistribution::new(
101                Epoch(1),
102                StakeDistribution::from([("pool-1".to_string(), 100)])
103            )
104            .compute_hash(),
105            CardanoStakeDistribution::new(
106                Epoch(2),
107                StakeDistribution::from([("pool-1".to_string(), 100)])
108            )
109            .compute_hash()
110        );
111    }
112
113    #[test]
114    fn compute_hash_returns_different_hash_with_different_stake_distribution_pool_id() {
115        let epoch = Epoch(1);
116
117        assert_ne!(
118            CardanoStakeDistribution::new(
119                epoch,
120                StakeDistribution::from([("pool-1".to_string(), 100)])
121            )
122            .compute_hash(),
123            CardanoStakeDistribution::new(
124                epoch,
125                StakeDistribution::from([("pool-2".to_string(), 100)])
126            )
127            .compute_hash()
128        );
129    }
130
131    #[test]
132    fn compute_hash_returns_different_hash_with_different_stake_distribution_stakes() {
133        let epoch = Epoch(1);
134
135        assert_ne!(
136            CardanoStakeDistribution::new(
137                epoch,
138                StakeDistribution::from([("pool-1".to_string(), 100)])
139            )
140            .compute_hash(),
141            CardanoStakeDistribution::new(
142                epoch,
143                StakeDistribution::from([("pool-1".to_string(), 150)])
144            )
145            .compute_hash()
146        );
147    }
148
149    #[test]
150    fn compute_hash_returns_different_hash_with_different_stake_distribution_size() {
151        let epoch = Epoch(1);
152
153        assert_ne!(
154            CardanoStakeDistribution::new(
155                epoch,
156                StakeDistribution::from([("pool-1".to_string(), 100)])
157            )
158            .compute_hash(),
159            CardanoStakeDistribution::new(
160                epoch,
161                StakeDistribution::from([("pool-1".to_string(), 100), ("pool-2".to_string(), 150)])
162            )
163            .compute_hash()
164        );
165    }
166}