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