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