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::{builder::MithrilFixtureBuilder, double::fake_data};
60
61    use super::*;
62
63    #[cfg(not(feature = "future_snark"))]
64    const EXPECTED_HASH: &str = "c5c1ff02e37c751329e3db7625c77fa2a24e86b2a75422c54f1b9f9232374d6f";
65    #[cfg(feature = "future_snark")]
66    const EXPECTED_HASH: &str = "aafc009f81c97622f4a4695acf218447585648c8ff56ec16837b842e9557b1aa";
67
68    #[test]
69    fn test_compute_hash() {
70        let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
71        let stake_distribution = MithrilStakeDistribution::new(
72            Epoch(1),
73            fixtures.signers_with_stake(),
74            &fake_data::protocol_parameters(),
75        );
76
77        assert_eq!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
78    }
79
80    #[test]
81    fn test_hash_fail_for_different_stake() {
82        let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
83        let mut signers = fixtures.signers_with_stake();
84        signers[0].stake += 1;
85        let stake_distribution =
86            MithrilStakeDistribution::new(Epoch(1), signers, &fake_data::protocol_parameters());
87
88        assert_ne!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
89    }
90
91    #[test]
92    fn test_hash_fail_for_different_epoch() {
93        let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
94        let stake_distribution = MithrilStakeDistribution::new(
95            Epoch(2),
96            fixtures.signers_with_stake(),
97            &fake_data::protocol_parameters(),
98        );
99
100        assert_ne!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
101    }
102
103    #[test]
104    fn test_independence_protocol_parameters() {
105        let signers = MithrilFixtureBuilder::default()
106            .with_signers(10)
107            .build()
108            .signers_with_stake();
109        let protocol_parameters = ProtocolParameters {
110            k: 100,
111            m: 0,
112            phi_f: 0.0,
113        };
114        let stake_distribution =
115            MithrilStakeDistribution::new(Epoch(1), signers, &protocol_parameters);
116
117        assert_eq!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
118    }
119}