use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use crate::{
entities::{Epoch, SignerWithStake},
signable_builder::Artifact,
};
use super::ProtocolParameters;
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct MithrilStakeDistribution {
pub epoch: Epoch,
pub signers_with_stake: Vec<SignerWithStake>,
pub hash: String,
pub protocol_parameters: ProtocolParameters,
}
impl MithrilStakeDistribution {
pub fn new(
epoch: Epoch,
signers_with_stake: Vec<SignerWithStake>,
protocol_parameters: &ProtocolParameters,
) -> Self {
let mut signers_with_stake_sorted = signers_with_stake;
signers_with_stake_sorted.sort();
let mut mithril_stake_distribution: MithrilStakeDistribution = Self {
epoch,
signers_with_stake: signers_with_stake_sorted,
hash: "".to_string(),
protocol_parameters: protocol_parameters.to_owned(),
};
mithril_stake_distribution.hash = mithril_stake_distribution.compute_hash();
mithril_stake_distribution
}
fn compute_hash(&self) -> String {
let mut hasher = Sha256::new();
hasher.update(self.epoch.to_be_bytes());
for signer_with_stake in &self.signers_with_stake {
hasher.update(signer_with_stake.compute_hash().as_bytes());
}
hex::encode(hasher.finalize())
}
}
#[typetag::serde]
impl Artifact for MithrilStakeDistribution {
fn get_id(&self) -> String {
self.hash.clone()
}
}
#[cfg(test)]
mod tests {
use crate::test_utils::{fake_data, MithrilFixtureBuilder};
use super::*;
const EXPECTED_HASH: &str = "47675a6fad57040b194655b103bc0439ff9d6920a7ba86bd53756db7ce2952f5";
#[test]
fn test_compute_hash() {
let fixtures = MithrilFixtureBuilder::default().with_signers(100).build();
let stake_distribution = MithrilStakeDistribution::new(
Epoch(1),
fixtures.signers_with_stake(),
&fake_data::protocol_parameters(),
);
assert_eq!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
}
#[test]
fn test_hash_fail_for_different_stake() {
let fixtures = MithrilFixtureBuilder::default().with_signers(100).build();
let mut signers = fixtures.signers_with_stake();
signers[0].stake += 1;
let stake_distribution =
MithrilStakeDistribution::new(Epoch(1), signers, &fake_data::protocol_parameters());
assert_ne!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
}
#[test]
fn test_hash_fail_for_different_epoch() {
let fixtures = MithrilFixtureBuilder::default().with_signers(100).build();
let stake_distribution = MithrilStakeDistribution::new(
Epoch(2),
fixtures.signers_with_stake(),
&fake_data::protocol_parameters(),
);
assert_ne!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
}
#[test]
fn test_independence_protocol_parameters() {
let signers = MithrilFixtureBuilder::default()
.with_signers(100)
.build()
.signers_with_stake();
let protocol_parameters = ProtocolParameters {
k: 100,
m: 0,
phi_f: 0.0,
};
let stake_distribution =
MithrilStakeDistribution::new(Epoch(1), signers, &protocol_parameters);
assert_eq!(EXPECTED_HASH.to_owned(), stake_distribution.compute_hash());
}
}