mithril_common/entities/
mithril_stake_distribution.rs

1use serde::{Deserialize, Serialize};
2use sha2::{Digest, Sha256};
3
4use crate::entities::{Epoch, SignerWithStake};
5
6use super::ProtocolParameters;
7
8/// Mithril Stake Distribution
9#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
10pub struct MithrilStakeDistribution {
11    /// Epoch at which the Mithril Stake Distribution is created
12    pub epoch: Epoch,
13
14    /// List of signers with stakes of the Mithril Stake Distribution
15    pub signers_with_stake: Vec<SignerWithStake>,
16
17    /// Hash of the Mithril Stake Distribution (different from the AVK).
18    pub hash: String,
19
20    /// Protocol parameters used to sign this stake distribution
21    pub protocol_parameters: ProtocolParameters,
22}
23
24impl MithrilStakeDistribution {
25    /// MithrilStakeDistribution artifact factory
26    pub fn new(
27        epoch: Epoch,
28        signers_with_stake: Vec<SignerWithStake>,
29        protocol_parameters: &ProtocolParameters,
30    ) -> Self {
31        let mut signers_with_stake_sorted = signers_with_stake;
32        signers_with_stake_sorted.sort();
33        let mut mithril_stake_distribution: MithrilStakeDistribution = Self {
34            epoch,
35            signers_with_stake: signers_with_stake_sorted,
36            hash: "".to_string(),
37            protocol_parameters: protocol_parameters.to_owned(),
38        };
39        mithril_stake_distribution.hash = mithril_stake_distribution.compute_hash();
40        mithril_stake_distribution
41    }
42
43    /// Do not add other parameters to the compute hash.
44    /// Mithril Stake Distribution is defined by the epoch and signers
45    fn compute_hash(&self) -> String {
46        let mut hasher = Sha256::new();
47        hasher.update(self.epoch.to_be_bytes());
48
49        for signer_with_stake in &self.signers_with_stake {
50            hasher.update(signer_with_stake.compute_hash().as_bytes());
51        }
52
53        hex::encode(hasher.finalize())
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use crate::test_utils::{fake_data, MithrilFixtureBuilder};
60
61    use super::*;
62
63    const EXPECTED_HASH: &str = "c5c1ff02e37c751329e3db7625c77fa2a24e86b2a75422c54f1b9f9232374d6f";
64
65    #[test]
66    fn test_compute_hash() {
67        let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
68        let stake_distribution = MithrilStakeDistribution::new(
69            Epoch(1),
70            fixtures.signers_with_stake(),
71            &fake_data::protocol_parameters(),
72        );
73
74        assert_eq!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
75    }
76
77    #[test]
78    fn test_hash_fail_for_different_stake() {
79        let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
80        let mut signers = fixtures.signers_with_stake();
81        signers[0].stake += 1;
82        let stake_distribution =
83            MithrilStakeDistribution::new(Epoch(1), signers, &fake_data::protocol_parameters());
84
85        assert_ne!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
86    }
87
88    #[test]
89    fn test_hash_fail_for_different_epoch() {
90        let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
91        let stake_distribution = MithrilStakeDistribution::new(
92            Epoch(2),
93            fixtures.signers_with_stake(),
94            &fake_data::protocol_parameters(),
95        );
96
97        assert_ne!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
98    }
99
100    #[test]
101    fn test_independence_protocol_parameters() {
102        let signers = MithrilFixtureBuilder::default()
103            .with_signers(10)
104            .build()
105            .signers_with_stake();
106        let protocol_parameters = ProtocolParameters {
107            k: 100,
108            m: 0,
109            phi_f: 0.0,
110        };
111        let stake_distribution =
112            MithrilStakeDistribution::new(Epoch(1), signers, &protocol_parameters);
113
114        assert_eq!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
115    }
116}