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