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
569 .iter()
570 .rev()
571 .filter(|c| {
572 if is_certificate_first_of_epoch {
573 c.epoch == certificate.epoch.previous().unwrap()
576 } else {
577 c.epoch == certificate.epoch
580 }
581 })
582 .next_back()
583 }
584 CertificateChainingMethod::Sequential => certificates_chained.last(),
585 }
586 }
587
588 fn compute_chained_certificates(&self, certificates: Vec<Certificate>) -> Vec<Certificate> {
591 let mut certificates_chained: Vec<Certificate> =
592 certificates
593 .into_iter()
594 .fold(Vec::new(), |mut certificates_chained, certificate| {
595 let previous_certificate_maybe = self
596 .fetch_previous_certificate_from_chain(&certificate, &certificates_chained);
597 let certificate = self
598 .update_certificate_previous_hash(certificate, previous_certificate_maybe);
599 certificates_chained.push(certificate);
600
601 certificates_chained
602 });
603 certificates_chained.reverse();
604
605 certificates_chained
606 }
607}
608
609impl Default for CertificateChainBuilder<'_> {
610 fn default() -> Self {
611 Self::new()
612 }
613}
614
615#[cfg(test)]
616mod test {
617 use std::collections::BTreeMap;
618
619 use super::*;
620
621 fn build_epoch_numbers_sequence(
622 total_certificates: u64,
623 certificates_per_epoch: u64,
624 ) -> Vec<u64> {
625 CertificateChainBuilder::default()
626 .with_total_certificates(total_certificates)
627 .with_certificates_per_epoch(certificates_per_epoch)
628 .build_epochs_sequence()
629 .map(|epoch| *epoch)
630 .collect::<Vec<_>>()
631 }
632
633 fn build_certificate_index_and_epoch_numbers_sequence(
634 total_certificates: u64,
635 certificates_per_epoch: u64,
636 ) -> Vec<(usize, u64)> {
637 CertificateChainBuilder::default()
638 .with_total_certificates(total_certificates)
639 .with_certificates_per_epoch(certificates_per_epoch)
640 .build_certificate_index_and_epoch_sequence()
641 .map(|(certificate_index, epoch)| (certificate_index, *epoch))
642 .collect::<Vec<_>>()
643 }
644
645 fn build_epoch_numbers_sequence_in_certificate_chain(
646 total_certificates: u64,
647 certificates_per_epoch: u64,
648 ) -> Vec<u64> {
649 build_certificate_index_and_epoch_numbers_sequence(
650 total_certificates,
651 certificates_per_epoch,
652 )
653 .iter()
654 .map(|(_certificate_index, epoch)| *epoch)
655 .collect::<Vec<_>>()
656 }
657
658 fn build_certificate_chain(
659 total_certificates: u64,
660 certificates_per_epoch: u64,
661 ) -> Vec<Certificate> {
662 let certificate_chain_fixture = CertificateChainBuilder::default()
663 .with_total_certificates(total_certificates)
664 .with_certificates_per_epoch(certificates_per_epoch)
665 .build();
666
667 certificate_chain_fixture.certificates_chained
668 }
669
670 #[test]
671 fn certificate_chain_builder_context_computes_correct_protocol_message_seed() {
672 let protocol_parameters = ProtocolParameters {
673 m: 123,
674 k: 45,
675 phi_f: 0.67,
676 };
677 let next_protocol_parameters = ProtocolParameters {
678 m: 100,
679 k: 10,
680 phi_f: 0.10,
681 };
682 let fixture = MithrilFixtureBuilder::default()
683 .with_protocol_parameters(protocol_parameters.into())
684 .with_signers(2)
685 .build();
686 let next_fixture = MithrilFixtureBuilder::default()
687 .with_protocol_parameters(next_protocol_parameters.into())
688 .with_signers(3)
689 .build();
690 let context = CertificateChainBuilderContext {
691 index_certificate: 2,
692 total_certificates: 5,
693 epoch: Epoch(1),
694 fixture: &fixture,
695 next_fixture: &next_fixture,
696 };
697 let expected_next_avk_part_value = next_fixture.compute_and_encode_avk();
698 let expected_next_protocol_parameters_part_value =
699 next_fixture.protocol_parameters().compute_hash();
700
701 let expected_current_epoch_part_value = context.epoch.to_string();
702
703 let protocol_message = context.compute_protocol_message_seed();
704
705 let mut expected_protocol_message = ProtocolMessage::new();
706 expected_protocol_message.set_message_part(
707 ProtocolMessagePartKey::NextAggregateVerificationKey,
708 expected_next_avk_part_value,
709 );
710 expected_protocol_message.set_message_part(
711 ProtocolMessagePartKey::NextProtocolParameters,
712 expected_next_protocol_parameters_part_value,
713 );
714 expected_protocol_message.set_message_part(
715 ProtocolMessagePartKey::CurrentEpoch,
716 expected_current_epoch_part_value,
717 );
718
719 assert_eq!(expected_protocol_message, protocol_message);
720 }
721
722 #[test]
723 fn certificate_chain_builder_context_checks_correctly_if_certificate_is_last() {
724 let fixture = MithrilFixtureBuilder::default().with_signers(2).build();
725 let context = CertificateChainBuilderContext {
726 index_certificate: 4,
727 total_certificates: 5,
728 epoch: Epoch(1),
729 fixture: &fixture,
730 next_fixture: &fixture,
731 };
732
733 assert!(context.is_last_certificate());
734 }
735
736 #[test]
737 fn builds_certificate_chain_with_correct_length() {
738 assert_eq!(4, build_certificate_chain(4, 1).len());
739 assert_eq!(4, build_certificate_chain(4, 2).len());
740 assert_eq!(4, build_certificate_chain(4, 3).len());
741 assert_eq!(4, build_certificate_chain(4, 4).len());
742 assert_eq!(5, build_certificate_chain(5, 1).len());
743 assert_eq!(5, build_certificate_chain(5, 2).len());
744 assert_eq!(5, build_certificate_chain(5, 3).len());
745 assert_eq!(7, build_certificate_chain(7, 3).len());
746 assert_eq!(15, build_certificate_chain(15, 3).len());
747 }
748
749 #[test]
750 fn builds_valid_epochs_sequence() {
751 assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(3, 1));
752 assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(4, 2));
753 assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(5, 2));
754 assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(7, 3),);
755 assert_eq!(
756 vec![1, 2, 3, 4, 5, 6, 7],
757 build_epoch_numbers_sequence(15, 3),
758 );
759 }
760
761 #[test]
762 fn builds_valid_certificate_index_and_epoch_numbers_sequence() {
763 assert_eq!(
764 vec![1, 2, 3],
765 build_epoch_numbers_sequence_in_certificate_chain(3, 1)
766 );
767 assert_eq!(
768 vec![1, 2, 2, 3],
769 build_epoch_numbers_sequence_in_certificate_chain(4, 2)
770 );
771 assert_eq!(
772 vec![1, 2, 2, 3, 3],
773 build_epoch_numbers_sequence_in_certificate_chain(5, 2)
774 );
775 assert_eq!(
776 vec![1, 2, 2, 2, 3, 3, 3],
777 build_epoch_numbers_sequence_in_certificate_chain(7, 3),
778 );
779 assert_eq!(
780 vec![1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6],
781 build_epoch_numbers_sequence_in_certificate_chain(15, 3),
782 );
783 }
784
785 #[test]
786 #[should_panic]
787 fn panics_building_invalid_epochs_sequence_no_certificates_per_epoch() {
788 build_epoch_numbers_sequence(3, 0);
789 }
790
791 #[test]
792 #[should_panic]
793 fn panics_building_invalid_epochs_sequence_less_total_certificates_than_certificates_per_epoch()
794 {
795 build_epoch_numbers_sequence(3, 5);
796 }
797
798 #[test]
799 fn builds_valid_fixtures_per_epochs() {
800 let expected_total_signers = (1..=6).collect::<Vec<_>>();
801 let certificate_chain_builder = CertificateChainBuilder::default()
802 .with_total_certificates(5)
803 .with_certificates_per_epoch(1)
804 .with_total_signers_per_epoch_processor(&|epoch| *epoch as usize);
805
806 let epoch_fixtures =
807 BTreeMap::from_iter(certificate_chain_builder.build_fixtures_for_epochs());
808
809 let total_signers = epoch_fixtures
810 .into_values()
811 .map(|fixture| fixture.signers().len())
812 .collect::<Vec<_>>();
813 assert_eq!(expected_total_signers, total_signers);
814 }
815
816 #[test]
817 fn builds_valid_genesis_certificate() {
818 let expected_protocol_parameters = ProtocolParameters {
819 m: 123,
820 k: 45,
821 phi_f: 0.67,
822 };
823 let fixture = MithrilFixtureBuilder::default()
824 .with_protocol_parameters(expected_protocol_parameters.into())
825 .with_signers(2)
826 .build();
827 let next_fixture = MithrilFixtureBuilder::default()
828 .with_protocol_parameters(expected_protocol_parameters.into())
829 .with_signers(3)
830 .build();
831 let context = CertificateChainBuilderContext {
832 index_certificate: 0,
833 total_certificates: 5,
834 epoch: Epoch(1),
835 fixture: &fixture,
836 next_fixture: &next_fixture,
837 };
838 let expected_protocol_message = context.compute_protocol_message_seed();
839 let expected_signed_message = expected_protocol_message.compute_hash();
840 let (protocol_genesis_signer, _) = CertificateChainBuilder::setup_genesis();
841
842 let genesis_certificate = CertificateChainBuilder::default()
843 .with_protocol_parameters(expected_protocol_parameters)
844 .build_genesis_certificate(&context, &protocol_genesis_signer);
845
846 assert!(genesis_certificate.is_genesis());
847 assert_eq!(
848 SignedEntityType::genesis(Epoch(1)),
849 genesis_certificate.signed_entity_type()
850 );
851 assert_eq!(Epoch(1), genesis_certificate.epoch);
852 assert_eq!(
853 expected_protocol_parameters,
854 genesis_certificate.metadata.protocol_parameters.into()
855 );
856 assert_eq!(0, genesis_certificate.metadata.signers.len());
857 assert_eq!(
858 expected_protocol_message,
859 genesis_certificate.protocol_message
860 );
861 assert_eq!(expected_signed_message, genesis_certificate.signed_message);
862 }
863
864 #[test]
865 fn builds_valid_standard_certificate() {
866 let expected_protocol_parameters = ProtocolParameters {
867 m: 123,
868 k: 45,
869 phi_f: 0.67,
870 };
871 let fixture = MithrilFixtureBuilder::default()
872 .with_protocol_parameters(expected_protocol_parameters.into())
873 .with_signers(2)
874 .build();
875 let next_fixture = MithrilFixtureBuilder::default()
876 .with_protocol_parameters(expected_protocol_parameters.into())
877 .with_signers(3)
878 .build();
879 let avk = fixture.compute_and_encode_avk();
880 let context = CertificateChainBuilderContext {
881 index_certificate: 2,
882 total_certificates: 5,
883 epoch: Epoch(1),
884 fixture: &fixture,
885 next_fixture: &next_fixture,
886 };
887 let mut expected_protocol_message = context.compute_protocol_message_seed();
888 expected_protocol_message.set_message_part(
889 ProtocolMessagePartKey::SnapshotDigest,
890 format!("digest-{}", context.index_certificate),
891 );
892 let expected_signed_message = expected_protocol_message.compute_hash();
893
894 let standard_certificate = CertificateChainBuilder::default()
895 .with_protocol_parameters(expected_protocol_parameters)
896 .build_standard_certificate(&context);
897
898 assert!(!standard_certificate.is_genesis());
899 assert_eq!(
900 SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(1, 2)),
901 standard_certificate.signed_entity_type()
902 );
903 assert_eq!(Epoch(1), standard_certificate.epoch);
904 assert_eq!(
905 expected_protocol_parameters,
906 standard_certificate.metadata.protocol_parameters.into()
907 );
908 assert_eq!(2, standard_certificate.metadata.signers.len());
909 assert_eq!(
910 expected_protocol_message,
911 standard_certificate.protocol_message
912 );
913 assert_eq!(expected_signed_message, standard_certificate.signed_message);
914 assert_eq!(
915 avk,
916 standard_certificate.aggregate_verification_key.to_json_hex().unwrap()
917 );
918 }
919
920 #[test]
921 fn builds_certificate_chain_chained_by_default_to_master_certificates() {
922 fn create_fake_certificate(epoch: Epoch, index_in_epoch: u64) -> Certificate {
923 Certificate {
924 epoch,
925 signed_message: format!("certificate-{}-{index_in_epoch}", *epoch),
926 ..fake_data::certificate("cert-fake".to_string())
927 }
928 }
929
930 let certificates = vec![
931 create_fake_certificate(Epoch(1), 1),
932 create_fake_certificate(Epoch(2), 1),
933 create_fake_certificate(Epoch(2), 2),
934 create_fake_certificate(Epoch(3), 1),
935 create_fake_certificate(Epoch(4), 1),
936 create_fake_certificate(Epoch(4), 2),
937 create_fake_certificate(Epoch(4), 3),
938 ];
939
940 let mut certificates_chained =
941 CertificateChainBuilder::default().compute_chained_certificates(certificates);
942 certificates_chained.reverse();
943
944 let certificate_chained_1_1 = &certificates_chained[0];
945 let certificate_chained_2_1 = &certificates_chained[1];
946 let certificate_chained_2_2 = &certificates_chained[2];
947 let certificate_chained_3_1 = &certificates_chained[3];
948 let certificate_chained_4_1 = &certificates_chained[4];
949 let certificate_chained_4_2 = &certificates_chained[5];
950 let certificate_chained_4_3 = &certificates_chained[6];
951 assert_eq!("", certificate_chained_1_1.previous_hash);
952 assert_eq!(
953 certificate_chained_2_1.previous_hash,
954 certificate_chained_1_1.hash
955 );
956 assert_eq!(
957 certificate_chained_2_2.previous_hash,
958 certificate_chained_2_1.hash
959 );
960 assert_eq!(
961 certificate_chained_3_1.previous_hash,
962 certificate_chained_2_1.hash
963 );
964 assert_eq!(
965 certificate_chained_4_1.previous_hash,
966 certificate_chained_3_1.hash
967 );
968 assert_eq!(
969 certificate_chained_4_2.previous_hash,
970 certificate_chained_4_1.hash
971 );
972 assert_eq!(
973 certificate_chained_4_3.previous_hash,
974 certificate_chained_4_1.hash
975 );
976 }
977
978 #[test]
979 fn builds_certificate_chain_chained_sequentially() {
980 fn create_fake_certificate(epoch: Epoch, index_in_epoch: u64) -> Certificate {
981 Certificate {
982 epoch,
983 signed_message: format!("certificate-{}-{index_in_epoch}", *epoch),
984 ..fake_data::certificate("cert-fake".to_string())
985 }
986 }
987
988 let certificates = vec![
989 create_fake_certificate(Epoch(1), 1),
990 create_fake_certificate(Epoch(2), 1),
991 create_fake_certificate(Epoch(2), 2),
992 create_fake_certificate(Epoch(3), 1),
993 create_fake_certificate(Epoch(4), 1),
994 create_fake_certificate(Epoch(4), 2),
995 create_fake_certificate(Epoch(4), 3),
996 ];
997
998 let mut certificates_chained = CertificateChainBuilder::default()
999 .with_certificate_chaining_method(CertificateChainingMethod::Sequential)
1000 .compute_chained_certificates(certificates);
1001 certificates_chained.reverse();
1002
1003 let certificate_chained_1_1 = &certificates_chained[0];
1004 let certificate_chained_2_1 = &certificates_chained[1];
1005 let certificate_chained_2_2 = &certificates_chained[2];
1006 let certificate_chained_3_1 = &certificates_chained[3];
1007 let certificate_chained_4_1 = &certificates_chained[4];
1008 let certificate_chained_4_2 = &certificates_chained[5];
1009 let certificate_chained_4_3 = &certificates_chained[6];
1010 assert_eq!("", certificate_chained_1_1.previous_hash);
1011 assert_eq!(
1012 certificate_chained_2_1.previous_hash,
1013 certificate_chained_1_1.hash
1014 );
1015 assert_eq!(
1016 certificate_chained_2_2.previous_hash,
1017 certificate_chained_2_1.hash
1018 );
1019 assert_eq!(
1020 certificate_chained_3_1.previous_hash,
1021 certificate_chained_2_2.hash
1022 );
1023 assert_eq!(
1024 certificate_chained_4_1.previous_hash,
1025 certificate_chained_3_1.hash
1026 );
1027 assert_eq!(
1028 certificate_chained_4_2.previous_hash,
1029 certificate_chained_4_1.hash
1030 );
1031 assert_eq!(
1032 certificate_chained_4_3.previous_hash,
1033 certificate_chained_4_2.hash
1034 );
1035 }
1036
1037 #[test]
1038 fn builds_certificate_chain_with_alteration_on_genesis_certificate() {
1039 let certificate_chain_fixture = CertificateChainBuilder::new()
1040 .with_total_certificates(5)
1041 .with_genesis_certificate_processor(&|certificate, _, _| {
1042 let mut certificate = certificate;
1043 certificate.signed_message = "altered_msg".to_string();
1044
1045 certificate
1046 })
1047 .build();
1048
1049 assert_eq!(
1050 "altered_msg".to_string(),
1051 certificate_chain_fixture.last().unwrap().signed_message
1052 );
1053 }
1054
1055 #[test]
1056 fn builds_certificate_chain_with_alteration_on_standard_certificates() {
1057 let total_certificates = 5;
1058 let expected_signed_messages = (1..total_certificates)
1059 .rev()
1060 .map(|i| format!("altered-msg-{i}"))
1061 .collect::<Vec<_>>();
1062
1063 let certificate_chain_fixture = CertificateChainBuilder::new()
1064 .with_total_certificates(total_certificates)
1065 .with_standard_certificate_processor(&|certificate, context| {
1066 let mut certificate = certificate;
1067 certificate.signed_message = format!("altered-msg-{}", context.index_certificate);
1068
1069 certificate
1070 })
1071 .build();
1072
1073 let signed_message = certificate_chain_fixture
1074 .certificates_chained
1075 .into_iter()
1076 .take(total_certificates as usize - 1)
1077 .map(|certificate| certificate.signed_message)
1078 .collect::<Vec<_>>();
1079 assert_eq!(expected_signed_messages, signed_message);
1080 }
1081
1082 mod certificate_chain_fixture {
1083 use super::*;
1084
1085 #[test]
1086 fn get_genesis_certificate() {
1087 let chain_with_only_a_genesis =
1088 CertificateChainBuilder::new().with_total_certificates(1).build();
1089 assert!(chain_with_only_a_genesis.genesis_certificate().is_genesis());
1090
1091 let chain_with_multiple_certificates =
1092 CertificateChainBuilder::new().with_total_certificates(10).build();
1093 assert!(chain_with_multiple_certificates.genesis_certificate().is_genesis());
1094 }
1095
1096 #[test]
1097 fn get_latest_certificate() {
1098 let chain_with_only_a_genesis =
1099 CertificateChainBuilder::new().with_total_certificates(1).build();
1100 assert!(chain_with_only_a_genesis.latest_certificate().is_genesis());
1101
1102 let chain_with_multiple_certificates =
1103 CertificateChainBuilder::new().with_total_certificates(10).build();
1104 assert_eq!(
1105 chain_with_multiple_certificates.latest_certificate(),
1106 chain_with_multiple_certificates.first().unwrap()
1107 );
1108 }
1109
1110 #[test]
1111 fn path_to_genesis_from_a_chain_with_one_certificate_per_epoch() {
1112 let chain = CertificateChainBuilder::new()
1113 .with_total_certificates(5)
1114 .with_certificates_per_epoch(1)
1115 .build();
1116
1117 assert_eq!(
1118 chain.certificate_path_to_genesis(&chain[0].hash),
1119 chain.certificates_chained
1120 );
1121 }
1122
1123 #[test]
1124 fn path_to_genesis_from_a_chain_with_multiple_certificates_per_epoch() {
1125 let chain = CertificateChainBuilder::new()
1126 .with_total_certificates(9)
1127 .with_certificates_per_epoch(3)
1128 .build();
1129
1130 let expected_subchain =
1131 vec![chain[1].clone(), chain[4].clone(), chain[7].clone(), chain[8].clone()];
1132 assert_eq!(
1133 chain.certificate_path_to_genesis(&chain[1].hash),
1134 expected_subchain
1135 );
1136 }
1137
1138 #[test]
1139 fn reversed_chain() {
1140 let chain = CertificateChainBuilder::new()
1141 .with_total_certificates(5)
1142 .with_certificates_per_epoch(2)
1143 .build();
1144
1145 let expected: Vec<Certificate> =
1146 chain.certificates_chained.clone().into_iter().rev().collect();
1147 assert_eq!(chain.reversed_chain(), expected);
1148 }
1149 }
1150}