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