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