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