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