mithril_common/test_utils/
mithril_fixture.rs

1use rayon::prelude::*;
2use serde::{Deserialize, Serialize};
3use std::{
4    collections::HashMap,
5    path::{Path, PathBuf},
6    sync::Arc,
7};
8
9use crate::{
10    certificate_chain::CertificateGenesisProducer,
11    crypto_helper::{
12        ProtocolAggregateVerificationKey, ProtocolClosedKeyRegistration, ProtocolGenesisSigner,
13        ProtocolInitializer, ProtocolOpCert, ProtocolSigner, ProtocolSignerVerificationKey,
14        ProtocolSignerVerificationKeySignature, ProtocolStakeDistribution,
15    },
16    entities::{
17        Certificate, Epoch, HexEncodedAggregateVerificationKey, PartyId, ProtocolParameters,
18        Signer, SignerWithStake, SingleSignatures, Stake, StakeDistribution,
19        StakeDistributionParty,
20    },
21    protocol::{SignerBuilder, ToMessage},
22    StdResult,
23};
24
25/// A fixture of Mithril data types.
26#[derive(Debug, Clone)]
27pub struct MithrilFixture {
28    protocol_parameters: ProtocolParameters,
29    signers: Vec<SignerFixture>,
30    stake_distribution: ProtocolStakeDistribution,
31}
32
33/// A signer fixture, containing a [signer entity][SignerWithStake] with its
34/// corresponding protocol [signer][ProtocolSigner] and
35/// [initializer][ProtocolInitializer]
36#[derive(Debug, Clone)]
37pub struct SignerFixture {
38    /// A [SignerWithStake].
39    pub signer_with_stake: SignerWithStake,
40    /// A [ProtocolSigner].
41    pub protocol_signer: ProtocolSigner,
42    /// A [ProtocolSigner].
43    pub protocol_initializer: ProtocolInitializer,
44    /// A [ProtocolClosedKeyRegistration].
45    pub protocol_closed_key_registration: ProtocolClosedKeyRegistration,
46    /// The path to this signer kes secret key file
47    pub kes_secret_key_path: Option<PathBuf>,
48}
49
50impl SignerFixture {
51    /// Create a new SignerFixture with specific protocol parameters.
52    /// This is useful to simulate some adversarial behaviors.
53    pub fn try_new_with_protocol_parameters(
54        self,
55        protocol_parameters: ProtocolParameters,
56    ) -> StdResult<Self> {
57        let mut protocol_initializer = self.protocol_initializer.clone();
58        protocol_initializer.override_protocol_parameters(&protocol_parameters.into());
59        let protocol_signer =
60            protocol_initializer.new_signer(self.protocol_closed_key_registration.clone())?;
61        Ok(Self {
62            protocol_signer,
63            ..self
64        })
65    }
66}
67
68impl From<SignerFixture> for SignerWithStake {
69    fn from(fixture: SignerFixture) -> Self {
70        fixture.signer_with_stake
71    }
72}
73
74impl From<&SignerFixture> for SignerWithStake {
75    fn from(fixture: &SignerFixture) -> Self {
76        fixture.signer_with_stake.clone()
77    }
78}
79
80/// Represent the output of the `stake-snapshot` command of the cardano-cli
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct CardanoCliStakeDistribution {
83    #[serde(rename = "pools")]
84    pub signers: HashMap<String, CardanoCliSignerStake>,
85}
86
87/// Represent the stakes of a party in the output of the `stake-snapshot`
88/// command of the cardano-cli
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct CardanoCliSignerStake {
91    #[serde(rename = "stakeMark")]
92    actual_stake: Stake,
93    #[serde(rename = "stakeSet")]
94    previous_stake: Stake,
95    #[serde(rename = "stakeGo")]
96    penultimate_stake: Stake,
97}
98
99impl MithrilFixture {
100    /// [MithrilFixture] factory.
101    pub fn new(
102        protocol_parameters: ProtocolParameters,
103        signers: Vec<SignerFixture>,
104        stake_distribution: ProtocolStakeDistribution,
105    ) -> Self {
106        Self {
107            protocol_parameters,
108            signers,
109            stake_distribution,
110        }
111    }
112
113    /// Get the fixture protocol parameters.
114    pub fn protocol_parameters(&self) -> ProtocolParameters {
115        self.protocol_parameters.clone()
116    }
117
118    /// Get the fixture signers.
119    pub fn signers_fixture(&self) -> Vec<SignerFixture> {
120        self.signers.clone()
121    }
122
123    /// Get the fixture signers.
124    pub fn signers(&self) -> Vec<Signer> {
125        self.signers
126            .clone()
127            .into_iter()
128            .map(|s| s.signer_with_stake.into())
129            .collect()
130    }
131
132    /// Get the fixture signers with stake.
133    pub fn signers_with_stake(&self) -> Vec<SignerWithStake> {
134        self.signers
135            .iter()
136            .map(|s| &s.signer_with_stake)
137            .cloned()
138            .collect()
139    }
140
141    /// Get certificate metadata signers
142    pub fn stake_distribution_parties(&self) -> Vec<StakeDistributionParty> {
143        self.signers
144            .iter()
145            .map(|s| StakeDistributionParty {
146                party_id: s.signer_with_stake.party_id.clone(),
147                stake: s.signer_with_stake.stake,
148            })
149            .collect()
150    }
151
152    /// Get the fixture stake distribution.
153    pub fn stake_distribution(&self) -> StakeDistribution {
154        StakeDistribution::from_iter(self.stake_distribution.clone())
155    }
156
157    /// Get the fixture protocol stake distribution.
158    pub fn protocol_stake_distribution(&self) -> ProtocolStakeDistribution {
159        self.stake_distribution.clone()
160    }
161
162    /// Get the stake distribution formated as a cardano-cli `stake-snapshot` output.
163    ///
164    /// Note: will fail if the signers certification was disabled
165    pub fn cardano_cli_stake_distribution(&self) -> CardanoCliStakeDistribution {
166        let signers = HashMap::from_iter(self.signers_fixture().into_iter().map(|signer| {
167            (
168                signer.compute_protocol_party_id_as_hash(),
169                CardanoCliSignerStake {
170                    actual_stake: signer.signer_with_stake.stake,
171                    previous_stake: signer.signer_with_stake.stake,
172                    penultimate_stake: signer.signer_with_stake.stake,
173                },
174            )
175        }));
176
177        CardanoCliStakeDistribution { signers }
178    }
179
180    /// Compute the Aggregate Verification Key for this fixture.
181    pub fn compute_avk(&self) -> ProtocolAggregateVerificationKey {
182        SignerBuilder::new(&self.signers_with_stake(), &self.protocol_parameters)
183            .unwrap()
184            .compute_aggregate_verification_key()
185    }
186
187    /// Compute the Aggregate Verification Key for this fixture and returns it has a [HexEncodedAggregateVerificationKey].
188    pub fn compute_and_encode_avk(&self) -> HexEncodedAggregateVerificationKey {
189        let avk = self.compute_avk();
190        avk.to_json_hex().unwrap()
191    }
192
193    /// Create a genesis certificate using the fixture signers for the given beacon
194    pub fn create_genesis_certificate<T: Into<String>>(
195        &self,
196        network: T,
197        epoch: Epoch,
198    ) -> Certificate {
199        let genesis_avk = self.compute_avk();
200        let genesis_signer = ProtocolGenesisSigner::create_deterministic_signer();
201        let genesis_producer = CertificateGenesisProducer::new(Some(Arc::new(genesis_signer)));
202        let genesis_protocol_message = CertificateGenesisProducer::create_genesis_protocol_message(
203            &self.protocol_parameters,
204            &genesis_avk,
205            &epoch,
206        )
207        .unwrap();
208        let genesis_signature = genesis_producer
209            .sign_genesis_protocol_message(genesis_protocol_message)
210            .unwrap();
211
212        CertificateGenesisProducer::create_genesis_certificate(
213            self.protocol_parameters.clone(),
214            network,
215            epoch,
216            genesis_avk,
217            genesis_signature,
218        )
219        .unwrap()
220    }
221
222    /// Make all underlying signers sign the given message, filter the resulting list to remove
223    /// the signers that did not sign because they loosed the lottery.
224    pub fn sign_all<T: ToMessage>(&self, message: &T) -> Vec<SingleSignatures> {
225        self.signers
226            .par_iter()
227            .filter_map(|s| s.sign(message))
228            .collect()
229    }
230}
231
232impl From<MithrilFixture> for Vec<Signer> {
233    fn from(fixture: MithrilFixture) -> Self {
234        fixture.signers()
235    }
236}
237
238impl From<MithrilFixture> for Vec<SignerWithStake> {
239    fn from(fixture: MithrilFixture) -> Self {
240        fixture.signers_with_stake()
241    }
242}
243
244impl From<MithrilFixture> for Vec<SignerFixture> {
245    fn from(fixture: MithrilFixture) -> Self {
246        fixture.signers_fixture()
247    }
248}
249
250impl SignerFixture {
251    /// Sign the given protocol message.
252    pub fn sign<T: ToMessage>(&self, message: &T) -> Option<SingleSignatures> {
253        let message = message.to_message();
254        self.protocol_signer
255            .sign(message.as_bytes())
256            .map(|signature| {
257                let won_indexes = signature.indexes.clone();
258
259                SingleSignatures::new(
260                    self.signer_with_stake.party_id.to_owned(),
261                    signature.into(),
262                    won_indexes,
263                )
264            })
265    }
266
267    /// Shortcut to get the party id from the inner signer with stake
268    pub fn party_id(&self) -> PartyId {
269        self.signer_with_stake.party_id.clone()
270    }
271
272    /// Decode this signer operational certificate if any
273    pub fn operational_certificate(&self) -> Option<ProtocolOpCert> {
274        self.signer_with_stake.operational_certificate.clone()
275    }
276
277    /// Compute the party id hash
278    ///
279    /// Note: will fail if the signers certification was disabled
280    pub fn compute_protocol_party_id_as_hash(&self) -> String {
281        self.operational_certificate()
282            .unwrap()
283            .compute_protocol_party_id_as_hash()
284    }
285
286    /// Decode this signer verification key certificate
287    pub fn verification_key(&self) -> ProtocolSignerVerificationKey {
288        self.signer_with_stake.verification_key
289    }
290
291    /// Decode this signer verification key signature certificate if any
292    pub fn verification_key_signature(&self) -> Option<ProtocolSignerVerificationKeySignature> {
293        self.signer_with_stake.verification_key_signature
294    }
295
296    /// Get the path to this signer kes secret key
297    pub fn kes_secret_key_path(&self) -> Option<&Path> {
298        self.kes_secret_key_path.as_deref()
299    }
300}