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