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