mithril_common/test_utils/
certificate_chain_builder.rs

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