Skip to main content

mithril_common/test/builder/
certificate_chain_builder.rs

1use std::cmp::min;
2use std::collections::{BTreeSet, HashMap};
3use std::iter::repeat_n;
4use std::ops::{Deref, DerefMut};
5use std::sync::Arc;
6
7use mithril_stm::AggregateSignatureType;
8
9use crate::{
10    certificate_chain::CertificateGenesisProducer,
11    crypto_helper::{
12        ProtocolAggregateVerificationKey, ProtocolClerk, ProtocolGenesisSigner,
13        ProtocolGenesisVerifier, ProtocolParameters,
14    },
15    entities::{
16        CardanoDbBeacon, Certificate, CertificateMetadata, CertificateSignature, Epoch,
17        ProtocolMessage, ProtocolMessagePartKey, SignedEntityType, SupportedEra,
18    },
19    test::{
20        builder::{MithrilFixture, MithrilFixtureBuilder, SignerFixture},
21        double::fake_data,
22    },
23};
24
25/// Genesis certificate processor function type. For tests only.
26type GenesisCertificateProcessorFunc =
27    dyn Fn(Certificate, &CertificateChainBuilderContext, &ProtocolGenesisSigner) -> Certificate;
28
29/// Standard certificate processor function type. For tests only.
30type StandardCertificateProcessorFunc =
31    dyn Fn(Certificate, &CertificateChainBuilderContext) -> Certificate;
32
33/// Total signers per epoch processor function type. For tests only.
34type TotalSignersPerEpochProcessorFunc = dyn Fn(Epoch) -> usize;
35
36/// Context used while building a certificate chain. For tests only.
37pub struct CertificateChainBuilderContext<'a> {
38    /// The index of the certificate in the chain.
39    pub index_certificate: usize,
40    /// The total number of certificates in the chain.
41    #[allow(dead_code)]
42    pub total_certificates: usize,
43    /// The epoch of the certificate.
44    pub epoch: Epoch,
45    /// The fixture of the epoch.
46    pub fixture: &'a MithrilFixture,
47    /// The fixture of the next epoch.
48    pub next_fixture: &'a MithrilFixture,
49}
50
51impl<'a> CertificateChainBuilderContext<'a> {
52    fn new(
53        index_certificate: usize,
54        total_certificates: usize,
55        epoch: Epoch,
56        fixture: &'a MithrilFixture,
57        next_fixture: &'a MithrilFixture,
58    ) -> Self {
59        Self {
60            index_certificate,
61            total_certificates,
62            epoch,
63            fixture,
64            next_fixture,
65        }
66    }
67
68    /// Computes the protocol message seed.
69    pub fn compute_protocol_message_seed(&self) -> ProtocolMessage {
70        let mut protocol_message = ProtocolMessage::new();
71        protocol_message.set_message_part(
72            ProtocolMessagePartKey::NextAggregateVerificationKey,
73            self.next_fixture
74                .compute_and_encode_concatenation_aggregate_verification_key(),
75        );
76
77        #[cfg(feature = "future_snark")]
78        if let Some(snark_avk) = self
79            .next_fixture
80            .compute_and_encode_snark_aggregate_verification_key()
81        {
82            protocol_message.set_message_part(
83                ProtocolMessagePartKey::NextSnarkAggregateVerificationKey,
84                snark_avk,
85            );
86        }
87
88        protocol_message.set_message_part(
89            ProtocolMessagePartKey::NextProtocolParameters,
90            self.next_fixture.protocol_parameters().compute_hash(),
91        );
92        protocol_message
93            .set_message_part(ProtocolMessagePartKey::CurrentEpoch, self.epoch.to_string());
94
95        protocol_message
96    }
97
98    /// Checks if the current certificate is the last one.
99    pub fn is_last_certificate(&self) -> bool {
100        self.index_certificate == self.total_certificates - 1
101    }
102}
103
104/// Chaining method to use when building a certificate chain with the [CertificateChainBuilder]. For tests only.
105#[derive(Debug, Clone, Copy, PartialEq, Default)]
106pub enum CertificateChainingMethod {
107    /// `default` Chain certificates to the 'master' certificate of the epoch or if it's the 'master'
108    /// certificate, chain it to the 'master' certificate of the previous epoch.
109    ///
110    /// The 'master' certificate of an epoch is the first certificate of the epoch.
111    #[default]
112    ToMasterCertificate,
113
114    /// Chain certificates sequentially.
115    Sequential,
116}
117
118/// Fixture built from a [CertificateChainBuilder], certificates are ordered from latest to genesis
119#[derive(Debug, Clone)]
120pub struct CertificateChainFixture {
121    /// The full certificates list, ordered from latest to genesis
122    pub certificates_chained: Vec<Certificate>,
123    /// The genesis verifier associated with this chain genesis certificate
124    pub genesis_verifier: ProtocolGenesisVerifier,
125}
126
127impl Deref for CertificateChainFixture {
128    type Target = [Certificate];
129
130    fn deref(&self) -> &Self::Target {
131        &self.certificates_chained
132    }
133}
134
135impl DerefMut for CertificateChainFixture {
136    fn deref_mut(&mut self) -> &mut Self::Target {
137        &mut self.certificates_chained
138    }
139}
140
141impl<C: TryFrom<Certificate>> TryFrom<CertificateChainFixture> for Vec<C> {
142    type Error = C::Error;
143
144    fn try_from(fixture: CertificateChainFixture) -> Result<Self, Self::Error> {
145        fixture.certificates_chained.into_iter().map(C::try_from).collect()
146    }
147}
148
149impl CertificateChainFixture {
150    /// Return the genesis certificate of this chain
151    pub fn genesis_certificate(&self) -> &Certificate {
152        &self.certificates_chained[self.certificates_chained.len() - 1]
153    }
154
155    /// Return the latest certificate of this chain
156    pub fn latest_certificate(&self) -> &Certificate {
157        &self.certificates_chained[0]
158    }
159
160    /// Return a copy of the chain but with reversed order (from genesis to last)
161    pub fn reversed_chain(&self) -> Vec<Certificate> {
162        self.certificates_chained.iter().rev().cloned().collect()
163    }
164
165    /// Extract the chain starting from the certificate with the given hash and all its parents
166    /// until the genesis certificate.
167    pub fn certificate_path_to_genesis<H: AsRef<str>>(
168        &self,
169        certificate_hash: H,
170    ) -> Vec<Certificate> {
171        let mut subchain = Vec::new();
172        let mut hash_to_search = certificate_hash.as_ref().to_string();
173
174        // takes advantage of the fact that chained certificates are ordered from last to genesis
175        for certificate in &self.certificates_chained {
176            if certificate.hash == hash_to_search {
177                subchain.push(certificate.clone());
178                hash_to_search = certificate.previous_hash.clone();
179            }
180        }
181
182        subchain
183    }
184}
185
186/// A builder for creating a certificate chain. For tests only.
187///
188/// # Simple example usage for building a fully valid certificate chain
189///
190/// ```
191///     use mithril_common::crypto_helper::ProtocolParameters;
192///     use mithril_common::test::builder::CertificateChainBuilder;
193///
194///     let certificate_chain_fixture = CertificateChainBuilder::new()
195///         .with_total_certificates(5)
196///         .with_certificates_per_epoch(2)
197///         .build();
198///
199///     assert_eq!(5, certificate_chain_fixture.len());
200/// ```
201///
202/// # More complex example usage for building a fully valid certificate chain
203///
204/// ```
205///     use std::cmp::min;
206///     use mithril_common::crypto_helper::ProtocolParameters;
207///     use mithril_common::test::builder::CertificateChainBuilder;
208///
209///     let certificate_chain_fixture = CertificateChainBuilder::new()
210///         .with_total_certificates(5)
211///         .with_certificates_per_epoch(2)
212///         .with_protocol_parameters(ProtocolParameters {
213///             m: 100,
214///             k: 5,
215///             phi_f: 0.65,
216///         })
217///         .with_total_signers_per_epoch_processor(&|epoch| min(1 + *epoch as usize, 10))
218///         .build();
219///
220///     assert_eq!(5, certificate_chain_fixture.len());
221/// ```
222///
223/// # Advanced example usage for building an adversarial certificate chain
224///
225/// ```
226///     use mithril_common::entities::Epoch;
227///     use mithril_common::crypto_helper::ProtocolParameters;
228///     use mithril_common::test::builder::CertificateChainBuilder;
229///
230///     let certificate_chain_fixture = CertificateChainBuilder::new()
231///         .with_total_certificates(5)
232///         .with_certificates_per_epoch(2)
233///         .with_standard_certificate_processor(&|certificate, context| {
234///             let mut certificate = certificate;
235///             // Alter the epoch of the last certificate
236///             if context.is_last_certificate() {
237///                 certificate.epoch = Epoch(123);
238///             }
239///
240///             certificate
241///        })
242///        .build();
243///
244///     assert_eq!(5, certificate_chain_fixture.len());
245/// ```
246pub struct CertificateChainBuilder<'a> {
247    total_certificates: u64,
248    certificates_per_epoch: u64,
249    protocol_parameters: ProtocolParameters,
250    total_signers_per_epoch_processor: &'a TotalSignersPerEpochProcessorFunc,
251    genesis_certificate_processor: &'a GenesisCertificateProcessorFunc,
252    standard_certificate_processor: &'a StandardCertificateProcessorFunc,
253    certificate_chaining_method: CertificateChainingMethod,
254    aggregate_signature_type: AggregateSignatureType,
255    mithril_era: SupportedEra,
256}
257
258impl<'a> CertificateChainBuilder<'a> {
259    /// Create a new [CertificateChainBuilder] instance.
260    pub fn new() -> Self {
261        let protocol_parameters = ProtocolParameters {
262            m: 100,
263            k: 5,
264            phi_f: 0.65,
265        };
266        Self {
267            total_certificates: 5,
268            certificates_per_epoch: 1,
269            protocol_parameters,
270            total_signers_per_epoch_processor: &|epoch| min(2 + *epoch as usize, 5),
271            genesis_certificate_processor: &|certificate, _, _| certificate,
272            standard_certificate_processor: &|certificate, _| certificate,
273            certificate_chaining_method: Default::default(),
274            aggregate_signature_type: Default::default(),
275            mithril_era: *SupportedEra::eras().first().unwrap(),
276        }
277    }
278
279    /// Set the total number of certificates to generate.
280    pub fn with_total_certificates(mut self, total_certificates: u64) -> Self {
281        self.total_certificates = total_certificates;
282
283        self
284    }
285
286    /// Set the number of certificates per epoch.
287    pub fn with_certificates_per_epoch(mut self, certificates_per_epoch: u64) -> Self {
288        self.certificates_per_epoch = certificates_per_epoch;
289
290        self
291    }
292
293    /// Set the protocol parameters.
294    pub fn with_protocol_parameters(mut self, protocol_parameters: ProtocolParameters) -> Self {
295        self.protocol_parameters = protocol_parameters;
296
297        self
298    }
299
300    /// Set the total signers per epoch processor.
301    pub fn with_total_signers_per_epoch_processor(
302        mut self,
303        total_signers_per_epoch_processor: &'a TotalSignersPerEpochProcessorFunc,
304    ) -> Self {
305        self.total_signers_per_epoch_processor = total_signers_per_epoch_processor;
306
307        self
308    }
309
310    /// Set the genesis certificate processor.
311    pub fn with_genesis_certificate_processor(
312        mut self,
313        genesis_certificate_processor: &'a GenesisCertificateProcessorFunc,
314    ) -> Self {
315        self.genesis_certificate_processor = genesis_certificate_processor;
316
317        self
318    }
319
320    /// Set the standard certificate processor.
321    pub fn with_standard_certificate_processor(
322        mut self,
323        standard_certificate_processor: &'a StandardCertificateProcessorFunc,
324    ) -> Self {
325        self.standard_certificate_processor = standard_certificate_processor;
326
327        self
328    }
329
330    /// Set the chaining method to use when building the certificate chain.
331    pub fn with_certificate_chaining_method(
332        mut self,
333        certificate_chaining_method: CertificateChainingMethod,
334    ) -> Self {
335        self.certificate_chaining_method = certificate_chaining_method;
336
337        self
338    }
339
340    /// Set the aggregate signature type to use when building the certificate chain.
341    pub fn with_aggregate_signature_type(
342        mut self,
343        aggregate_signature_type: AggregateSignatureType,
344    ) -> Self {
345        self.aggregate_signature_type = aggregate_signature_type;
346
347        self
348    }
349
350    /// Set the Mithril era to use for genesis certificate creation.
351    pub fn with_mithril_era(mut self, mithril_era: SupportedEra) -> Self {
352        self.mithril_era = mithril_era;
353
354        self
355    }
356
357    /// Build the certificate chain.
358    pub fn build(self) -> CertificateChainFixture {
359        let (genesis_signer, genesis_verifier) = CertificateChainBuilder::setup_genesis();
360        let genesis_certificate_processor = self.genesis_certificate_processor;
361        let standard_certificate_processor = self.standard_certificate_processor;
362        let total_certificates = self.total_certificates as usize;
363        let fixtures_per_epoch = self.build_fixtures_for_epochs();
364        let certificates = self.build_certificate_index_and_epoch_sequence()
365            .map(|(index_certificate, epoch)| {
366                let fixture = fixtures_per_epoch.get(&epoch).unwrap_or_else(|| panic!("Fixture not found at epoch {epoch:?} with {} total certificates and {} certificates per epoch", self.total_certificates, self.certificates_per_epoch));
367                let next_fixture = fixtures_per_epoch.get(&epoch.next()).unwrap_or_else(|| panic!("Next fixture not found at epoch {epoch:?} with {} total certificates and {} certificates per epoch", self.total_certificates, self.certificates_per_epoch));
368                let context = CertificateChainBuilderContext::new(
369                    index_certificate,
370                    total_certificates,
371                    epoch,
372                    fixture,
373                    next_fixture,
374                );
375                match index_certificate {
376                    0 => genesis_certificate_processor(
377                        self.build_genesis_certificate(&context, &genesis_signer, self.mithril_era),
378                        &context,
379                        &genesis_signer,
380                    ),
381                    _ => standard_certificate_processor(
382                        self.build_standard_certificate(&context),
383                        &context,
384                    ),
385                }
386            })
387            .collect::<Vec<Certificate>>();
388        let certificates_chained = self.compute_chained_certificates(certificates);
389
390        CertificateChainFixture {
391            certificates_chained,
392            genesis_verifier,
393        }
394    }
395
396    fn compute_clerk_for_signers(signers: &[SignerFixture]) -> ProtocolClerk {
397        let first_signer = &signers[0].protocol_signer;
398
399        ProtocolClerk::new_clerk_from_signer(first_signer)
400    }
401
402    fn compute_avk_for_signers(signers: &[SignerFixture]) -> ProtocolAggregateVerificationKey {
403        let clerk = Self::compute_clerk_for_signers(signers);
404
405        clerk.compute_aggregate_verification_key()
406    }
407
408    fn setup_genesis() -> (ProtocolGenesisSigner, ProtocolGenesisVerifier) {
409        let genesis_signer = ProtocolGenesisSigner::create_deterministic_signer();
410        let genesis_verifier = genesis_signer.create_verifier();
411
412        (genesis_signer, genesis_verifier)
413    }
414
415    fn build_epochs_sequence(&self) -> impl Iterator<Item = Epoch> + use<> {
416        let total_certificates = self.total_certificates;
417        let certificates_per_epoch = self.certificates_per_epoch;
418        assert!(
419            certificates_per_epoch > 0,
420            "Certificates per epoch must be greater than 0"
421        );
422        assert!(
423            total_certificates >= certificates_per_epoch,
424            "Total certificates must be greater or equal to certificates per epoch"
425        );
426        // The total number of epochs in the sequence is the total number of standard certificates (total number of certificates minus one genesis certificate)
427        // divided by the number of certificates per epoch plus two (one for the genesis epoch and one to compute the next fixtures of the last epoch)
428        const TOTAL_GENESIS_CERTIFICATES: u64 = 1;
429        const TOTAL_EXTRA_EPOCHS_FOR_FIXTURES_COMPUTATION: u64 = 1;
430        let total_epochs_in_sequence = (total_certificates - TOTAL_GENESIS_CERTIFICATES)
431            .div_ceil(certificates_per_epoch)
432            + TOTAL_GENESIS_CERTIFICATES
433            + TOTAL_EXTRA_EPOCHS_FOR_FIXTURES_COMPUTATION;
434        (1..=total_epochs_in_sequence).map(Epoch)
435    }
436
437    fn build_certificate_index_and_epoch_sequence(
438        &self,
439    ) -> impl Iterator<Item = (usize, Epoch)> + use<> {
440        let total_certificates = self.total_certificates as usize;
441        let certificates_per_epoch = self.certificates_per_epoch as usize;
442
443        self.build_epochs_sequence()
444            .flat_map(move |epoch| {
445                let repeat_epoch = if epoch == 1 {
446                    // No need to repeat with the genesis epoch
447                    1
448                } else {
449                    certificates_per_epoch
450                };
451                repeat_n(Epoch(*epoch), repeat_epoch)
452            })
453            .take(total_certificates)
454            .enumerate()
455    }
456
457    fn build_fixtures_for_epochs(&self) -> HashMap<Epoch, MithrilFixture> {
458        self.build_epochs_sequence()
459            .collect::<BTreeSet<_>>()
460            .into_iter()
461            .map(|epoch| {
462                let total_signers = (self.total_signers_per_epoch_processor)(epoch);
463                let protocol_parameters = self.protocol_parameters.to_owned().into();
464                (
465                    epoch,
466                    MithrilFixtureBuilder::default()
467                        .with_protocol_parameters(protocol_parameters)
468                        .with_signers(total_signers)
469                        .build(),
470                )
471            })
472            .collect::<HashMap<_, _>>()
473    }
474
475    fn build_base_certificate(&self, context: &CertificateChainBuilderContext) -> Certificate {
476        let index_certificate = context.index_certificate;
477        let epoch = context.epoch;
478        let certificate_hash = format!("certificate_hash-{index_certificate}");
479        let avk = Self::compute_avk_for_signers(&context.fixture.signers_fixture());
480        let protocol_parameters = context.fixture.protocol_parameters().to_owned();
481        let base_certificate = fake_data::certificate(certificate_hash);
482        let protocol_message = context.compute_protocol_message_seed();
483        let signed_message = protocol_message.compute_hash();
484
485        Certificate {
486            epoch,
487            aggregate_verification_key: avk
488                .to_concatenation_aggregate_verification_key()
489                .to_owned()
490                .into(),
491            #[cfg(feature = "future_snark")]
492            aggregate_verification_key_snark: avk
493                .to_snark_aggregate_verification_key()
494                .map(|snark_avk| snark_avk.to_owned().into()),
495            previous_hash: "".to_string(),
496            protocol_message,
497            signed_message,
498            metadata: CertificateMetadata {
499                protocol_parameters,
500                ..base_certificate.metadata
501            },
502            ..base_certificate
503        }
504    }
505
506    fn build_genesis_certificate(
507        &self,
508        context: &CertificateChainBuilderContext,
509        genesis_signer: &ProtocolGenesisSigner,
510        mithril_era: SupportedEra,
511    ) -> Certificate {
512        let epoch = context.epoch;
513        let certificate = self.build_base_certificate(context);
514        let next_avk = Self::compute_avk_for_signers(&context.next_fixture.signers_fixture());
515        let next_protocol_parameters = &context.next_fixture.protocol_parameters();
516        let genesis_producer =
517            CertificateGenesisProducer::new(Some(Arc::new(genesis_signer.to_owned())));
518        let genesis_protocol_message = genesis_producer
519            .create_genesis_protocol_message(
520                next_protocol_parameters,
521                &next_avk,
522                &epoch,
523                mithril_era,
524            )
525            .unwrap();
526        let genesis_signature = genesis_producer
527            .sign_genesis_protocol_message(genesis_protocol_message)
528            .unwrap();
529
530        genesis_producer
531            .create_genesis_certificate(
532                certificate.metadata.protocol_parameters,
533                certificate.metadata.network,
534                certificate.epoch,
535                next_avk,
536                genesis_signature,
537                mithril_era,
538            )
539            .unwrap()
540    }
541
542    fn build_standard_certificate(&self, context: &CertificateChainBuilderContext) -> Certificate {
543        let fixture = context.fixture;
544        let mut certificate = self.build_base_certificate(context);
545        certificate.metadata.signers = fixture.stake_distribution_parties();
546        let mut protocol_message = certificate.protocol_message.clone();
547        protocol_message.set_message_part(
548            ProtocolMessagePartKey::SnapshotDigest,
549            format!("digest-{}", context.index_certificate),
550        );
551        certificate.protocol_message = protocol_message;
552        certificate.signed_message = certificate.protocol_message.compute_hash();
553        let single_signatures = fixture
554            .signers_fixture()
555            .iter()
556            .filter_map(|s| s.protocol_signer.sign(certificate.signed_message.as_bytes()))
557            .collect::<Vec<_>>();
558        let clerk = CertificateChainBuilder::compute_clerk_for_signers(&fixture.signers_fixture());
559        let multi_signature = clerk
560            .aggregate_signatures_with_type(
561                &single_signatures,
562                certificate.signed_message.as_bytes(),
563                self.aggregate_signature_type,
564            )
565            .unwrap();
566        certificate.signature = CertificateSignature::MultiSignature(
567            SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(
568                *context.epoch,
569                context.index_certificate as u64,
570            )),
571            multi_signature.into(),
572        );
573
574        certificate
575    }
576
577    fn update_certificate_previous_hash(
578        &self,
579        certificate: Certificate,
580        previous_certificate: Option<&Certificate>,
581    ) -> Certificate {
582        let mut certificate = certificate;
583        certificate.previous_hash =
584            previous_certificate.map(|c| c.hash.to_string()).unwrap_or_default();
585        certificate.hash = certificate.compute_hash();
586
587        certificate
588    }
589
590    fn fetch_previous_certificate_from_chain<'b>(
591        &self,
592        certificate: &Certificate,
593        certificates_chained: &'b [Certificate],
594    ) -> Option<&'b Certificate> {
595        match self.certificate_chaining_method {
596            CertificateChainingMethod::ToMasterCertificate => {
597                let is_certificate_first_of_epoch = certificates_chained
598                    .last()
599                    .map(|c| c.epoch != certificate.epoch)
600                    .unwrap_or(true);
601
602                certificates_chained.iter().rev().rfind(|c| {
603                    if is_certificate_first_of_epoch {
604                        // The previous certificate of the first certificate of an epoch
605                        // is the first certificate of the previous epoch
606                        c.epoch == certificate.epoch.previous().unwrap()
607                    } else {
608                        // The previous certificate of not the first certificate of an epoch
609                        // is the first certificate of the epoch
610                        c.epoch == certificate.epoch
611                    }
612                })
613            }
614            CertificateChainingMethod::Sequential => certificates_chained.last(),
615        }
616    }
617
618    // Returns the chained certificates in reverse order
619    // The latest certificate of the chain is the first in the vector
620    fn compute_chained_certificates(&self, certificates: Vec<Certificate>) -> Vec<Certificate> {
621        let mut certificates_chained: Vec<Certificate> =
622            certificates
623                .into_iter()
624                .fold(Vec::new(), |mut certificates_chained, certificate| {
625                    let previous_certificate_maybe = self
626                        .fetch_previous_certificate_from_chain(&certificate, &certificates_chained);
627                    let certificate = self
628                        .update_certificate_previous_hash(certificate, previous_certificate_maybe);
629                    certificates_chained.push(certificate);
630
631                    certificates_chained
632                });
633        certificates_chained.reverse();
634
635        certificates_chained
636    }
637}
638
639impl Default for CertificateChainBuilder<'_> {
640    fn default() -> Self {
641        Self::new()
642    }
643}
644
645#[cfg(test)]
646mod test {
647    use std::collections::BTreeMap;
648
649    use super::*;
650
651    fn build_epoch_numbers_sequence(
652        total_certificates: u64,
653        certificates_per_epoch: u64,
654    ) -> Vec<u64> {
655        CertificateChainBuilder::default()
656            .with_total_certificates(total_certificates)
657            .with_certificates_per_epoch(certificates_per_epoch)
658            .build_epochs_sequence()
659            .map(|epoch| *epoch)
660            .collect::<Vec<_>>()
661    }
662
663    fn build_certificate_index_and_epoch_numbers_sequence(
664        total_certificates: u64,
665        certificates_per_epoch: u64,
666    ) -> Vec<(usize, u64)> {
667        CertificateChainBuilder::default()
668            .with_total_certificates(total_certificates)
669            .with_certificates_per_epoch(certificates_per_epoch)
670            .build_certificate_index_and_epoch_sequence()
671            .map(|(certificate_index, epoch)| (certificate_index, *epoch))
672            .collect::<Vec<_>>()
673    }
674
675    fn build_epoch_numbers_sequence_in_certificate_chain(
676        total_certificates: u64,
677        certificates_per_epoch: u64,
678    ) -> Vec<u64> {
679        build_certificate_index_and_epoch_numbers_sequence(
680            total_certificates,
681            certificates_per_epoch,
682        )
683        .iter()
684        .map(|(_certificate_index, epoch)| *epoch)
685        .collect::<Vec<_>>()
686    }
687
688    fn build_certificate_chain(
689        total_certificates: u64,
690        certificates_per_epoch: u64,
691    ) -> Vec<Certificate> {
692        let certificate_chain_fixture = CertificateChainBuilder::default()
693            .with_total_certificates(total_certificates)
694            .with_certificates_per_epoch(certificates_per_epoch)
695            .build();
696
697        certificate_chain_fixture.certificates_chained
698    }
699
700    #[test]
701    fn certificate_chain_builder_context_computes_correct_protocol_message_seed() {
702        let protocol_parameters = ProtocolParameters {
703            m: 123,
704            k: 45,
705            phi_f: 0.67,
706        };
707        let next_protocol_parameters = ProtocolParameters {
708            m: 100,
709            k: 10,
710            phi_f: 0.10,
711        };
712        let fixture = MithrilFixtureBuilder::default()
713            .with_protocol_parameters(protocol_parameters.into())
714            .with_signers(2)
715            .build();
716        let next_fixture = MithrilFixtureBuilder::default()
717            .with_protocol_parameters(next_protocol_parameters.into())
718            .with_signers(3)
719            .build();
720        let context = CertificateChainBuilderContext {
721            index_certificate: 2,
722            total_certificates: 5,
723            epoch: Epoch(1),
724            fixture: &fixture,
725            next_fixture: &next_fixture,
726        };
727        let expected_next_avk_part_value =
728            next_fixture.compute_and_encode_concatenation_aggregate_verification_key();
729        let expected_next_protocol_parameters_part_value =
730            next_fixture.protocol_parameters().compute_hash();
731
732        let expected_current_epoch_part_value = context.epoch.to_string();
733
734        let protocol_message = context.compute_protocol_message_seed();
735
736        let mut expected_protocol_message = ProtocolMessage::new();
737        expected_protocol_message.set_message_part(
738            ProtocolMessagePartKey::NextAggregateVerificationKey,
739            expected_next_avk_part_value,
740        );
741        #[cfg(feature = "future_snark")]
742        if let Some(snark_avk) = next_fixture.compute_and_encode_snark_aggregate_verification_key()
743        {
744            expected_protocol_message.set_message_part(
745                ProtocolMessagePartKey::NextSnarkAggregateVerificationKey,
746                snark_avk,
747            );
748        }
749        expected_protocol_message.set_message_part(
750            ProtocolMessagePartKey::NextProtocolParameters,
751            expected_next_protocol_parameters_part_value,
752        );
753        expected_protocol_message.set_message_part(
754            ProtocolMessagePartKey::CurrentEpoch,
755            expected_current_epoch_part_value,
756        );
757
758        assert_eq!(expected_protocol_message, protocol_message);
759    }
760
761    #[test]
762    fn certificate_chain_builder_context_checks_correctly_if_certificate_is_last() {
763        let fixture = MithrilFixtureBuilder::default().with_signers(2).build();
764        let context = CertificateChainBuilderContext {
765            index_certificate: 4,
766            total_certificates: 5,
767            epoch: Epoch(1),
768            fixture: &fixture,
769            next_fixture: &fixture,
770        };
771
772        assert!(context.is_last_certificate());
773    }
774
775    #[test]
776    fn builds_certificate_chain_with_correct_length() {
777        assert_eq!(4, build_certificate_chain(4, 1).len());
778        assert_eq!(4, build_certificate_chain(4, 2).len());
779        assert_eq!(4, build_certificate_chain(4, 3).len());
780        assert_eq!(4, build_certificate_chain(4, 4).len());
781        assert_eq!(5, build_certificate_chain(5, 1).len());
782        assert_eq!(5, build_certificate_chain(5, 2).len());
783        assert_eq!(5, build_certificate_chain(5, 3).len());
784        assert_eq!(7, build_certificate_chain(7, 3).len());
785        assert_eq!(15, build_certificate_chain(15, 3).len());
786    }
787
788    #[test]
789    fn builds_valid_epochs_sequence() {
790        assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(3, 1));
791        assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(4, 2));
792        assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(5, 2));
793        assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(7, 3),);
794        assert_eq!(
795            vec![1, 2, 3, 4, 5, 6, 7],
796            build_epoch_numbers_sequence(15, 3),
797        );
798    }
799
800    #[test]
801    fn builds_valid_certificate_index_and_epoch_numbers_sequence() {
802        assert_eq!(
803            vec![1, 2, 3],
804            build_epoch_numbers_sequence_in_certificate_chain(3, 1)
805        );
806        assert_eq!(
807            vec![1, 2, 2, 3],
808            build_epoch_numbers_sequence_in_certificate_chain(4, 2)
809        );
810        assert_eq!(
811            vec![1, 2, 2, 3, 3],
812            build_epoch_numbers_sequence_in_certificate_chain(5, 2)
813        );
814        assert_eq!(
815            vec![1, 2, 2, 2, 3, 3, 3],
816            build_epoch_numbers_sequence_in_certificate_chain(7, 3),
817        );
818        assert_eq!(
819            vec![1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6],
820            build_epoch_numbers_sequence_in_certificate_chain(15, 3),
821        );
822    }
823
824    #[test]
825    #[should_panic]
826    fn panics_building_invalid_epochs_sequence_no_certificates_per_epoch() {
827        build_epoch_numbers_sequence(3, 0);
828    }
829
830    #[test]
831    #[should_panic]
832    fn panics_building_invalid_epochs_sequence_less_total_certificates_than_certificates_per_epoch()
833    {
834        build_epoch_numbers_sequence(3, 5);
835    }
836
837    #[test]
838    fn builds_valid_fixtures_per_epochs() {
839        let expected_total_signers = (1..=6).collect::<Vec<_>>();
840        let certificate_chain_builder = CertificateChainBuilder::default()
841            .with_total_certificates(5)
842            .with_certificates_per_epoch(1)
843            .with_total_signers_per_epoch_processor(&|epoch| *epoch as usize);
844
845        let epoch_fixtures =
846            BTreeMap::from_iter(certificate_chain_builder.build_fixtures_for_epochs());
847
848        let total_signers = epoch_fixtures
849            .into_values()
850            .map(|fixture| fixture.signers().len())
851            .collect::<Vec<_>>();
852        assert_eq!(expected_total_signers, total_signers);
853    }
854
855    #[test]
856    fn builds_valid_genesis_certificate() {
857        let expected_protocol_parameters = ProtocolParameters {
858            m: 123,
859            k: 45,
860            phi_f: 0.67,
861        };
862        let fixture = MithrilFixtureBuilder::default()
863            .with_protocol_parameters(expected_protocol_parameters.into())
864            .with_signers(2)
865            .build();
866        let next_fixture = MithrilFixtureBuilder::default()
867            .with_protocol_parameters(expected_protocol_parameters.into())
868            .with_signers(3)
869            .build();
870        let context = CertificateChainBuilderContext {
871            index_certificate: 0,
872            total_certificates: 5,
873            epoch: Epoch(1),
874            fixture: &fixture,
875            next_fixture: &next_fixture,
876        };
877        let expected_protocol_message = context.compute_protocol_message_seed();
878        let expected_signed_message = expected_protocol_message.compute_hash();
879        let (protocol_genesis_signer, _) = CertificateChainBuilder::setup_genesis();
880
881        let mithril_era = if cfg!(feature = "future_snark") {
882            SupportedEra::Lagrange
883        } else {
884            SupportedEra::Pythagoras
885        };
886        let genesis_certificate = CertificateChainBuilder::default()
887            .with_protocol_parameters(expected_protocol_parameters)
888            .build_genesis_certificate(&context, &protocol_genesis_signer, mithril_era);
889
890        assert!(genesis_certificate.is_genesis());
891        assert_eq!(
892            SignedEntityType::genesis(Epoch(1)),
893            genesis_certificate.signed_entity_type()
894        );
895        assert_eq!(Epoch(1), genesis_certificate.epoch);
896        assert_eq!(
897            expected_protocol_parameters,
898            genesis_certificate.metadata.protocol_parameters.into()
899        );
900        assert_eq!(0, genesis_certificate.metadata.signers.len());
901        assert_eq!(
902            expected_protocol_message,
903            genesis_certificate.protocol_message
904        );
905        assert_eq!(expected_signed_message, genesis_certificate.signed_message);
906    }
907
908    #[test]
909    fn builds_valid_standard_certificate() {
910        let expected_protocol_parameters = ProtocolParameters {
911            m: 123,
912            k: 45,
913            phi_f: 0.67,
914        };
915        let fixture = MithrilFixtureBuilder::default()
916            .with_protocol_parameters(expected_protocol_parameters.into())
917            .with_signers(2)
918            .build();
919        let next_fixture = MithrilFixtureBuilder::default()
920            .with_protocol_parameters(expected_protocol_parameters.into())
921            .with_signers(3)
922            .build();
923        let avk = fixture.compute_and_encode_concatenation_aggregate_verification_key();
924        let context = CertificateChainBuilderContext {
925            index_certificate: 2,
926            total_certificates: 5,
927            epoch: Epoch(1),
928            fixture: &fixture,
929            next_fixture: &next_fixture,
930        };
931        let mut expected_protocol_message = context.compute_protocol_message_seed();
932        expected_protocol_message.set_message_part(
933            ProtocolMessagePartKey::SnapshotDigest,
934            format!("digest-{}", context.index_certificate),
935        );
936        let expected_signed_message = expected_protocol_message.compute_hash();
937
938        let standard_certificate = CertificateChainBuilder::default()
939            .with_protocol_parameters(expected_protocol_parameters)
940            .build_standard_certificate(&context);
941
942        assert!(!standard_certificate.is_genesis());
943        assert_eq!(
944            SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(1, 2)),
945            standard_certificate.signed_entity_type()
946        );
947        assert_eq!(Epoch(1), standard_certificate.epoch);
948        assert_eq!(
949            expected_protocol_parameters,
950            standard_certificate.metadata.protocol_parameters.into()
951        );
952        assert_eq!(2, standard_certificate.metadata.signers.len());
953        assert_eq!(
954            expected_protocol_message,
955            standard_certificate.protocol_message
956        );
957        assert_eq!(expected_signed_message, standard_certificate.signed_message);
958        assert_eq!(
959            avk,
960            standard_certificate.aggregate_verification_key.to_json_hex().unwrap()
961        );
962    }
963
964    #[test]
965    fn builds_certificate_chain_chained_by_default_to_master_certificates() {
966        fn create_fake_certificate(epoch: Epoch, index_in_epoch: u64) -> Certificate {
967            Certificate {
968                epoch,
969                signed_message: format!("certificate-{}-{index_in_epoch}", *epoch),
970                ..fake_data::certificate("cert-fake".to_string())
971            }
972        }
973
974        let certificates = vec![
975            create_fake_certificate(Epoch(1), 1),
976            create_fake_certificate(Epoch(2), 1),
977            create_fake_certificate(Epoch(2), 2),
978            create_fake_certificate(Epoch(3), 1),
979            create_fake_certificate(Epoch(4), 1),
980            create_fake_certificate(Epoch(4), 2),
981            create_fake_certificate(Epoch(4), 3),
982        ];
983
984        let mut certificates_chained =
985            CertificateChainBuilder::default().compute_chained_certificates(certificates);
986        certificates_chained.reverse();
987
988        let certificate_chained_1_1 = &certificates_chained[0];
989        let certificate_chained_2_1 = &certificates_chained[1];
990        let certificate_chained_2_2 = &certificates_chained[2];
991        let certificate_chained_3_1 = &certificates_chained[3];
992        let certificate_chained_4_1 = &certificates_chained[4];
993        let certificate_chained_4_2 = &certificates_chained[5];
994        let certificate_chained_4_3 = &certificates_chained[6];
995        assert_eq!("", certificate_chained_1_1.previous_hash);
996        assert_eq!(
997            certificate_chained_2_1.previous_hash,
998            certificate_chained_1_1.hash
999        );
1000        assert_eq!(
1001            certificate_chained_2_2.previous_hash,
1002            certificate_chained_2_1.hash
1003        );
1004        assert_eq!(
1005            certificate_chained_3_1.previous_hash,
1006            certificate_chained_2_1.hash
1007        );
1008        assert_eq!(
1009            certificate_chained_4_1.previous_hash,
1010            certificate_chained_3_1.hash
1011        );
1012        assert_eq!(
1013            certificate_chained_4_2.previous_hash,
1014            certificate_chained_4_1.hash
1015        );
1016        assert_eq!(
1017            certificate_chained_4_3.previous_hash,
1018            certificate_chained_4_1.hash
1019        );
1020    }
1021
1022    #[test]
1023    fn builds_certificate_chain_chained_sequentially() {
1024        fn create_fake_certificate(epoch: Epoch, index_in_epoch: u64) -> Certificate {
1025            Certificate {
1026                epoch,
1027                signed_message: format!("certificate-{}-{index_in_epoch}", *epoch),
1028                ..fake_data::certificate("cert-fake".to_string())
1029            }
1030        }
1031
1032        let certificates = vec![
1033            create_fake_certificate(Epoch(1), 1),
1034            create_fake_certificate(Epoch(2), 1),
1035            create_fake_certificate(Epoch(2), 2),
1036            create_fake_certificate(Epoch(3), 1),
1037            create_fake_certificate(Epoch(4), 1),
1038            create_fake_certificate(Epoch(4), 2),
1039            create_fake_certificate(Epoch(4), 3),
1040        ];
1041
1042        let mut certificates_chained = CertificateChainBuilder::default()
1043            .with_certificate_chaining_method(CertificateChainingMethod::Sequential)
1044            .compute_chained_certificates(certificates);
1045        certificates_chained.reverse();
1046
1047        let certificate_chained_1_1 = &certificates_chained[0];
1048        let certificate_chained_2_1 = &certificates_chained[1];
1049        let certificate_chained_2_2 = &certificates_chained[2];
1050        let certificate_chained_3_1 = &certificates_chained[3];
1051        let certificate_chained_4_1 = &certificates_chained[4];
1052        let certificate_chained_4_2 = &certificates_chained[5];
1053        let certificate_chained_4_3 = &certificates_chained[6];
1054        assert_eq!("", certificate_chained_1_1.previous_hash);
1055        assert_eq!(
1056            certificate_chained_2_1.previous_hash,
1057            certificate_chained_1_1.hash
1058        );
1059        assert_eq!(
1060            certificate_chained_2_2.previous_hash,
1061            certificate_chained_2_1.hash
1062        );
1063        assert_eq!(
1064            certificate_chained_3_1.previous_hash,
1065            certificate_chained_2_2.hash
1066        );
1067        assert_eq!(
1068            certificate_chained_4_1.previous_hash,
1069            certificate_chained_3_1.hash
1070        );
1071        assert_eq!(
1072            certificate_chained_4_2.previous_hash,
1073            certificate_chained_4_1.hash
1074        );
1075        assert_eq!(
1076            certificate_chained_4_3.previous_hash,
1077            certificate_chained_4_2.hash
1078        );
1079    }
1080
1081    #[test]
1082    fn builds_certificate_chain_with_alteration_on_genesis_certificate() {
1083        let certificate_chain_fixture = CertificateChainBuilder::new()
1084            .with_total_certificates(5)
1085            .with_genesis_certificate_processor(&|certificate, _, _| {
1086                let mut certificate = certificate;
1087                certificate.signed_message = "altered_msg".to_string();
1088
1089                certificate
1090            })
1091            .build();
1092
1093        assert_eq!(
1094            "altered_msg".to_string(),
1095            certificate_chain_fixture.last().unwrap().signed_message
1096        );
1097    }
1098
1099    #[test]
1100    fn builds_certificate_chain_with_alteration_on_standard_certificates() {
1101        let total_certificates = 5;
1102        let expected_signed_messages = (1..total_certificates)
1103            .rev()
1104            .map(|i| format!("altered-msg-{i}"))
1105            .collect::<Vec<_>>();
1106
1107        let certificate_chain_fixture = CertificateChainBuilder::new()
1108            .with_total_certificates(total_certificates)
1109            .with_standard_certificate_processor(&|certificate, context| {
1110                let mut certificate = certificate;
1111                certificate.signed_message = format!("altered-msg-{}", context.index_certificate);
1112
1113                certificate
1114            })
1115            .build();
1116
1117        let signed_message = certificate_chain_fixture
1118            .certificates_chained
1119            .into_iter()
1120            .take(total_certificates as usize - 1)
1121            .map(|certificate| certificate.signed_message)
1122            .collect::<Vec<_>>();
1123        assert_eq!(expected_signed_messages, signed_message);
1124    }
1125
1126    mod certificate_chain_fixture {
1127        use super::*;
1128
1129        #[test]
1130        fn get_genesis_certificate() {
1131            let chain_with_only_a_genesis =
1132                CertificateChainBuilder::new().with_total_certificates(1).build();
1133            assert!(chain_with_only_a_genesis.genesis_certificate().is_genesis());
1134
1135            let chain_with_multiple_certificates =
1136                CertificateChainBuilder::new().with_total_certificates(10).build();
1137            assert!(chain_with_multiple_certificates.genesis_certificate().is_genesis());
1138        }
1139
1140        #[test]
1141        fn get_latest_certificate() {
1142            let chain_with_only_a_genesis =
1143                CertificateChainBuilder::new().with_total_certificates(1).build();
1144            assert!(chain_with_only_a_genesis.latest_certificate().is_genesis());
1145
1146            let chain_with_multiple_certificates =
1147                CertificateChainBuilder::new().with_total_certificates(10).build();
1148            assert_eq!(
1149                chain_with_multiple_certificates.latest_certificate(),
1150                chain_with_multiple_certificates.first().unwrap()
1151            );
1152        }
1153
1154        #[test]
1155        fn path_to_genesis_from_a_chain_with_one_certificate_per_epoch() {
1156            let chain = CertificateChainBuilder::new()
1157                .with_total_certificates(5)
1158                .with_certificates_per_epoch(1)
1159                .build();
1160
1161            assert_eq!(
1162                chain.certificate_path_to_genesis(&chain[0].hash),
1163                chain.certificates_chained
1164            );
1165        }
1166
1167        #[test]
1168        fn path_to_genesis_from_a_chain_with_multiple_certificates_per_epoch() {
1169            let chain = CertificateChainBuilder::new()
1170                .with_total_certificates(9)
1171                .with_certificates_per_epoch(3)
1172                .build();
1173
1174            let expected_subchain =
1175                vec![chain[1].clone(), chain[4].clone(), chain[7].clone(), chain[8].clone()];
1176            assert_eq!(
1177                chain.certificate_path_to_genesis(&chain[1].hash),
1178                expected_subchain
1179            );
1180        }
1181
1182        #[test]
1183        fn reversed_chain() {
1184            let chain = CertificateChainBuilder::new()
1185                .with_total_certificates(5)
1186                .with_certificates_per_epoch(2)
1187                .build();
1188
1189            let expected: Vec<Certificate> =
1190                chain.certificates_chained.clone().into_iter().rev().collect();
1191            assert_eq!(chain.reversed_chain(), expected);
1192        }
1193    }
1194}