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