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