1use crate::{
2 certificate_chain::CertificateGenesisProducer,
3 crypto_helper::{
4 ProtocolAggregateVerificationKey, ProtocolClerk, ProtocolGenesisSigner,
5 ProtocolGenesisVerifier, ProtocolParameters,
6 },
7 entities::{Certificate, CertificateSignature, Epoch, ProtocolMessage, ProtocolMessagePartKey},
8 test_utils::{fake_data, MithrilFixture, MithrilFixtureBuilder, SignerFixture},
9};
10
11use crate::entities::CertificateMetadata;
12use std::{
13 cmp::min,
14 collections::{BTreeSet, HashMap},
15 iter::repeat_n,
16 sync::Arc,
17};
18
19type GenesisCertificateProcessorFunc =
21 dyn Fn(Certificate, &CertificateChainBuilderContext, &ProtocolGenesisSigner) -> Certificate;
22
23type StandardCertificateProcessorFunc =
25 dyn Fn(Certificate, &CertificateChainBuilderContext) -> Certificate;
26
27type TotalSignersPerEpochProcessorFunc = dyn Fn(Epoch) -> usize;
29
30pub struct CertificateChainBuilderContext<'a> {
32 pub index_certificate: usize,
34 #[allow(dead_code)]
36 pub total_certificates: usize,
37 pub epoch: Epoch,
39 pub fixture: &'a MithrilFixture,
41 pub next_fixture: &'a MithrilFixture,
43}
44
45impl<'a> CertificateChainBuilderContext<'a> {
46 fn new(
47 index_certificate: usize,
48 total_certificates: usize,
49 epoch: Epoch,
50 fixture: &'a MithrilFixture,
51 next_fixture: &'a MithrilFixture,
52 ) -> Self {
53 Self {
54 index_certificate,
55 total_certificates,
56 epoch,
57 fixture,
58 next_fixture,
59 }
60 }
61
62 pub fn compute_protocol_message_seed(&self) -> ProtocolMessage {
64 let mut protocol_message = ProtocolMessage::new();
65 protocol_message.set_message_part(
66 ProtocolMessagePartKey::NextAggregateVerificationKey,
67 self.next_fixture.compute_and_encode_avk(),
68 );
69 protocol_message.set_message_part(
70 ProtocolMessagePartKey::NextProtocolParameters,
71 self.next_fixture.protocol_parameters().compute_hash(),
72 );
73 protocol_message
74 .set_message_part(ProtocolMessagePartKey::CurrentEpoch, self.epoch.to_string());
75
76 protocol_message
77 }
78
79 pub fn is_last_certificate(&self) -> bool {
81 self.index_certificate == self.total_certificates - 1
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Default)]
87pub enum CertificateChainingMethod {
88 #[default]
93 ToMasterCertificate,
94
95 Sequential,
97}
98
99pub struct CertificateChainBuilder<'a> {
160 total_certificates: u64,
161 certificates_per_epoch: u64,
162 protocol_parameters: ProtocolParameters,
163 total_signers_per_epoch_processor: &'a TotalSignersPerEpochProcessorFunc,
164 genesis_certificate_processor: &'a GenesisCertificateProcessorFunc,
165 standard_certificate_processor: &'a StandardCertificateProcessorFunc,
166 certificate_chaining_method: CertificateChainingMethod,
167}
168
169impl<'a> CertificateChainBuilder<'a> {
170 pub fn new() -> Self {
172 let protocol_parameters = ProtocolParameters {
173 m: 100,
174 k: 5,
175 phi_f: 0.65,
176 };
177 Self {
178 total_certificates: 5,
179 certificates_per_epoch: 1,
180 protocol_parameters,
181 total_signers_per_epoch_processor: &|epoch| min(2 + *epoch as usize, 5),
182 genesis_certificate_processor: &|certificate, _, _| certificate,
183 standard_certificate_processor: &|certificate, _| certificate,
184 certificate_chaining_method: Default::default(),
185 }
186 }
187
188 pub fn with_total_certificates(mut self, total_certificates: u64) -> Self {
190 self.total_certificates = total_certificates;
191
192 self
193 }
194
195 pub fn with_certificates_per_epoch(mut self, certificates_per_epoch: u64) -> Self {
197 self.certificates_per_epoch = certificates_per_epoch;
198
199 self
200 }
201
202 pub fn with_protocol_parameters(mut self, protocol_parameters: ProtocolParameters) -> Self {
204 self.protocol_parameters = protocol_parameters;
205
206 self
207 }
208
209 pub fn with_total_signers_per_epoch_processor(
211 mut self,
212 total_signers_per_epoch_processor: &'a TotalSignersPerEpochProcessorFunc,
213 ) -> Self {
214 self.total_signers_per_epoch_processor = total_signers_per_epoch_processor;
215
216 self
217 }
218
219 pub fn with_genesis_certificate_processor(
221 mut self,
222 genesis_certificate_processor: &'a GenesisCertificateProcessorFunc,
223 ) -> Self {
224 self.genesis_certificate_processor = genesis_certificate_processor;
225
226 self
227 }
228
229 pub fn with_standard_certificate_processor(
231 mut self,
232 standard_certificate_processor: &'a StandardCertificateProcessorFunc,
233 ) -> Self {
234 self.standard_certificate_processor = standard_certificate_processor;
235
236 self
237 }
238
239 pub fn with_certificate_chaining_method(
241 mut self,
242 certificate_chaining_method: CertificateChainingMethod,
243 ) -> Self {
244 self.certificate_chaining_method = certificate_chaining_method;
245
246 self
247 }
248
249 pub fn build(self) -> (Vec<Certificate>, ProtocolGenesisVerifier) {
251 let (genesis_signer, genesis_verifier) = CertificateChainBuilder::setup_genesis();
252 let genesis_certificate_processor = self.genesis_certificate_processor;
253 let standard_certificate_processor = self.standard_certificate_processor;
254 let total_certificates = self.total_certificates as usize;
255 let fixtures_per_epoch = self.build_fixtures_for_epochs();
256 let certificates = self.build_certificate_index_and_epoch_sequence()
257 .map(|(index_certificate, epoch)| {
258 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));
259 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));
260 let context = CertificateChainBuilderContext::new(
261 index_certificate,
262 total_certificates,
263 epoch,
264 fixture,
265 next_fixture,
266 );
267 match index_certificate {
268 0 => genesis_certificate_processor(
269 self.build_genesis_certificate(&context, &genesis_signer),
270 &context,
271 &genesis_signer,
272 ),
273 _ => standard_certificate_processor(
274 self.build_standard_certificate(&context),
275 &context,
276 ),
277 }
278 })
279 .collect::<Vec<Certificate>>();
280 let certificates_chained = self.compute_chained_certificates(certificates);
281
282 (certificates_chained, genesis_verifier)
283 }
284
285 fn compute_clerk_for_signers(signers: &[SignerFixture]) -> ProtocolClerk {
286 let first_signer = &signers[0].protocol_signer;
287
288 ProtocolClerk::from_signer(first_signer)
289 }
290
291 fn compute_avk_for_signers(signers: &[SignerFixture]) -> ProtocolAggregateVerificationKey {
292 let clerk = Self::compute_clerk_for_signers(signers);
293
294 clerk.compute_avk().into()
295 }
296
297 fn setup_genesis() -> (ProtocolGenesisSigner, ProtocolGenesisVerifier) {
298 let genesis_signer = ProtocolGenesisSigner::create_deterministic_signer();
299 let genesis_verifier = genesis_signer.create_verifier();
300
301 (genesis_signer, genesis_verifier)
302 }
303
304 fn build_epochs_sequence(&self) -> impl Iterator<Item = Epoch> {
305 let total_certificates = self.total_certificates;
306 let certificates_per_epoch = self.certificates_per_epoch;
307 assert!(
308 certificates_per_epoch > 0,
309 "Certificates per epoch must be greater than 0"
310 );
311 assert!(
312 total_certificates >= certificates_per_epoch,
313 "Total certificates must be greater or equal to certificates per epoch"
314 );
315 const TOTAL_GENESIS_CERTIFICATES: u64 = 1;
318 const TOTAL_EXTRA_EPOCHS_FOR_FIXTURES_COMPUTATION: u64 = 1;
319 let total_epochs_in_sequence = (total_certificates - TOTAL_GENESIS_CERTIFICATES)
320 .div_ceil(certificates_per_epoch)
321 + TOTAL_GENESIS_CERTIFICATES
322 + TOTAL_EXTRA_EPOCHS_FOR_FIXTURES_COMPUTATION;
323 (1..=total_epochs_in_sequence).map(Epoch)
324 }
325
326 fn build_certificate_index_and_epoch_sequence(&self) -> impl Iterator<Item = (usize, Epoch)> {
327 let total_certificates = self.total_certificates as usize;
328 let certificates_per_epoch = self.certificates_per_epoch as usize;
329
330 self.build_epochs_sequence()
331 .flat_map(move |epoch| {
332 let repeat_epoch = if epoch == 1 {
333 1
335 } else {
336 certificates_per_epoch
337 };
338 repeat_n(Epoch(*epoch), repeat_epoch)
339 })
340 .take(total_certificates)
341 .enumerate()
342 }
343
344 fn build_fixtures_for_epochs(&self) -> HashMap<Epoch, MithrilFixture> {
345 self.build_epochs_sequence()
346 .collect::<BTreeSet<_>>()
347 .into_iter()
348 .map(|epoch| {
349 let total_signers = (self.total_signers_per_epoch_processor)(epoch);
350 let protocol_parameters = self.protocol_parameters.to_owned().into();
351 (
352 epoch,
353 MithrilFixtureBuilder::default()
354 .with_protocol_parameters(protocol_parameters)
355 .with_signers(total_signers)
356 .build(),
357 )
358 })
359 .collect::<HashMap<_, _>>()
360 }
361
362 fn build_base_certificate(&self, context: &CertificateChainBuilderContext) -> Certificate {
363 let index_certificate = context.index_certificate;
364 let epoch = context.epoch;
365 let certificate_hash = format!("certificate_hash-{index_certificate}");
366 let avk = Self::compute_avk_for_signers(&context.fixture.signers_fixture());
367 let protocol_parameters = context.fixture.protocol_parameters().to_owned();
368 let base_certificate = fake_data::certificate(certificate_hash);
369 let protocol_message = context.compute_protocol_message_seed();
370 let signed_message = protocol_message.compute_hash();
371
372 Certificate {
373 epoch,
374 aggregate_verification_key: avk.to_owned(),
375 previous_hash: "".to_string(),
376 protocol_message,
377 signed_message,
378 metadata: CertificateMetadata {
379 protocol_parameters,
380 ..base_certificate.metadata
381 },
382 ..base_certificate
383 }
384 }
385
386 fn build_genesis_certificate(
387 &self,
388 context: &CertificateChainBuilderContext,
389 genesis_signer: &ProtocolGenesisSigner,
390 ) -> Certificate {
391 let epoch = context.epoch;
392 let certificate = self.build_base_certificate(context);
393 let next_avk = Self::compute_avk_for_signers(&context.next_fixture.signers_fixture());
394 let next_protocol_parameters = &context.next_fixture.protocol_parameters();
395 let genesis_producer =
396 CertificateGenesisProducer::new(Some(Arc::new(genesis_signer.to_owned())));
397 let genesis_protocol_message = CertificateGenesisProducer::create_genesis_protocol_message(
398 next_protocol_parameters,
399 &next_avk,
400 &epoch,
401 )
402 .unwrap();
403 let genesis_signature = genesis_producer
404 .sign_genesis_protocol_message(genesis_protocol_message)
405 .unwrap();
406
407 CertificateGenesisProducer::create_genesis_certificate(
408 certificate.metadata.protocol_parameters,
409 certificate.metadata.network,
410 certificate.epoch,
411 next_avk,
412 genesis_signature,
413 )
414 .unwrap()
415 }
416
417 fn build_standard_certificate(&self, context: &CertificateChainBuilderContext) -> Certificate {
418 let fixture = context.fixture;
419 let mut certificate = self.build_base_certificate(context);
420 certificate.metadata.signers = fixture.stake_distribution_parties();
421 let mut protocol_message = certificate.protocol_message.clone();
422 protocol_message.set_message_part(
423 ProtocolMessagePartKey::SnapshotDigest,
424 format!("digest-{}", context.index_certificate),
425 );
426 certificate.protocol_message = protocol_message;
427 certificate.signed_message = certificate.protocol_message.compute_hash();
428 let single_signatures = fixture
429 .signers_fixture()
430 .iter()
431 .filter_map(|s| {
432 s.protocol_signer
433 .sign(certificate.signed_message.as_bytes())
434 })
435 .collect::<Vec<_>>();
436 let clerk = CertificateChainBuilder::compute_clerk_for_signers(&fixture.signers_fixture());
437 let multi_signature = clerk
438 .aggregate(&single_signatures, certificate.signed_message.as_bytes())
439 .unwrap();
440 certificate.signature = CertificateSignature::MultiSignature(
441 certificate.signed_entity_type(),
442 multi_signature.into(),
443 );
444
445 certificate
446 }
447
448 fn update_certificate_previous_hash(
449 &self,
450 certificate: Certificate,
451 previous_certificate: Option<&Certificate>,
452 ) -> Certificate {
453 let mut certificate = certificate;
454 certificate.previous_hash = previous_certificate
455 .map(|c| c.hash.to_string())
456 .unwrap_or_default();
457 certificate.hash = certificate.compute_hash();
458
459 certificate
460 }
461
462 fn fetch_previous_certificate_from_chain<'b>(
463 &self,
464 certificate: &Certificate,
465 certificates_chained: &'b [Certificate],
466 ) -> Option<&'b Certificate> {
467 match self.certificate_chaining_method {
468 CertificateChainingMethod::ToMasterCertificate => {
469 let is_certificate_first_of_epoch = certificates_chained
470 .last()
471 .map(|c| c.epoch != certificate.epoch)
472 .unwrap_or(true);
473
474 certificates_chained
475 .iter()
476 .rev()
477 .filter(|c| {
478 if is_certificate_first_of_epoch {
479 c.epoch == certificate.epoch.previous().unwrap()
482 } else {
483 c.epoch == certificate.epoch
486 }
487 })
488 .next_back()
489 }
490 CertificateChainingMethod::Sequential => certificates_chained.last(),
491 }
492 }
493
494 fn compute_chained_certificates(&self, certificates: Vec<Certificate>) -> Vec<Certificate> {
497 let mut certificates_chained: Vec<Certificate> =
498 certificates
499 .into_iter()
500 .fold(Vec::new(), |mut certificates_chained, certificate| {
501 let previous_certificate_maybe = self
502 .fetch_previous_certificate_from_chain(&certificate, &certificates_chained);
503 let certificate = self
504 .update_certificate_previous_hash(certificate, previous_certificate_maybe);
505 certificates_chained.push(certificate);
506
507 certificates_chained
508 });
509 certificates_chained.reverse();
510
511 certificates_chained
512 }
513}
514
515impl Default for CertificateChainBuilder<'_> {
516 fn default() -> Self {
517 Self::new()
518 }
519}
520
521#[cfg(test)]
522mod test {
523 use std::collections::BTreeMap;
524
525 use super::*;
526
527 fn build_epoch_numbers_sequence(
528 total_certificates: u64,
529 certificates_per_epoch: u64,
530 ) -> Vec<u64> {
531 CertificateChainBuilder::default()
532 .with_total_certificates(total_certificates)
533 .with_certificates_per_epoch(certificates_per_epoch)
534 .build_epochs_sequence()
535 .map(|epoch| *epoch)
536 .collect::<Vec<_>>()
537 }
538
539 fn build_certificate_index_and_epoch_numbers_sequence(
540 total_certificates: u64,
541 certificates_per_epoch: u64,
542 ) -> Vec<(usize, u64)> {
543 CertificateChainBuilder::default()
544 .with_total_certificates(total_certificates)
545 .with_certificates_per_epoch(certificates_per_epoch)
546 .build_certificate_index_and_epoch_sequence()
547 .map(|(certificate_index, epoch)| (certificate_index, *epoch))
548 .collect::<Vec<_>>()
549 }
550
551 fn build_epoch_numbers_sequence_in_certificate_chain(
552 total_certificates: u64,
553 certificates_per_epoch: u64,
554 ) -> Vec<u64> {
555 build_certificate_index_and_epoch_numbers_sequence(
556 total_certificates,
557 certificates_per_epoch,
558 )
559 .iter()
560 .map(|(_certificate_index, epoch)| *epoch)
561 .collect::<Vec<_>>()
562 }
563
564 fn build_certificate_chain(
565 total_certificates: u64,
566 certificates_per_epoch: u64,
567 ) -> Vec<Certificate> {
568 let (certificate_chain, _) = CertificateChainBuilder::default()
569 .with_total_certificates(total_certificates)
570 .with_certificates_per_epoch(certificates_per_epoch)
571 .build();
572
573 certificate_chain
574 }
575
576 #[test]
577 fn certificate_chain_builder_context_computes_correct_protocol_message_seed() {
578 let protocol_parameters = ProtocolParameters {
579 m: 123,
580 k: 45,
581 phi_f: 0.67,
582 };
583 let next_protocol_parameters = ProtocolParameters {
584 m: 100,
585 k: 10,
586 phi_f: 0.10,
587 };
588 let fixture = MithrilFixtureBuilder::default()
589 .with_protocol_parameters(protocol_parameters.into())
590 .with_signers(2)
591 .build();
592 let next_fixture = MithrilFixtureBuilder::default()
593 .with_protocol_parameters(next_protocol_parameters.into())
594 .with_signers(3)
595 .build();
596 let context = CertificateChainBuilderContext {
597 index_certificate: 2,
598 total_certificates: 5,
599 epoch: Epoch(1),
600 fixture: &fixture,
601 next_fixture: &next_fixture,
602 };
603 let expected_next_avk_part_value = next_fixture.compute_and_encode_avk();
604 let expected_next_protocol_parameters_part_value =
605 next_fixture.protocol_parameters().compute_hash();
606
607 let expected_current_epoch_part_value = context.epoch.to_string();
608
609 let protocol_message = context.compute_protocol_message_seed();
610
611 let mut expected_protocol_message = ProtocolMessage::new();
612 expected_protocol_message.set_message_part(
613 ProtocolMessagePartKey::NextAggregateVerificationKey,
614 expected_next_avk_part_value,
615 );
616 expected_protocol_message.set_message_part(
617 ProtocolMessagePartKey::NextProtocolParameters,
618 expected_next_protocol_parameters_part_value,
619 );
620 expected_protocol_message.set_message_part(
621 ProtocolMessagePartKey::CurrentEpoch,
622 expected_current_epoch_part_value,
623 );
624
625 assert_eq!(expected_protocol_message, protocol_message);
626 }
627
628 #[test]
629 fn certificate_chain_builder_context_checks_correctly_if_certificate_is_last() {
630 let fixture = MithrilFixtureBuilder::default().with_signers(2).build();
631 let context = CertificateChainBuilderContext {
632 index_certificate: 4,
633 total_certificates: 5,
634 epoch: Epoch(1),
635 fixture: &fixture,
636 next_fixture: &fixture,
637 };
638
639 assert!(context.is_last_certificate());
640 }
641
642 #[test]
643 fn builds_certificate_chain_with_correct_length() {
644 assert_eq!(4, build_certificate_chain(4, 1).len());
645 assert_eq!(4, build_certificate_chain(4, 2).len());
646 assert_eq!(4, build_certificate_chain(4, 3).len());
647 assert_eq!(4, build_certificate_chain(4, 4).len());
648 assert_eq!(5, build_certificate_chain(5, 1).len());
649 assert_eq!(5, build_certificate_chain(5, 2).len());
650 assert_eq!(5, build_certificate_chain(5, 3).len());
651 assert_eq!(7, build_certificate_chain(7, 3).len());
652 assert_eq!(15, build_certificate_chain(15, 3).len());
653 }
654
655 #[test]
656 fn builds_valid_epochs_sequence() {
657 assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(3, 1));
658 assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(4, 2));
659 assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(5, 2));
660 assert_eq!(vec![1, 2, 3, 4], build_epoch_numbers_sequence(7, 3),);
661 assert_eq!(
662 vec![1, 2, 3, 4, 5, 6, 7],
663 build_epoch_numbers_sequence(15, 3),
664 );
665 }
666
667 #[test]
668 fn builds_valid_certificate_index_and_epoch_numbers_sequence() {
669 assert_eq!(
670 vec![1, 2, 3],
671 build_epoch_numbers_sequence_in_certificate_chain(3, 1)
672 );
673 assert_eq!(
674 vec![1, 2, 2, 3],
675 build_epoch_numbers_sequence_in_certificate_chain(4, 2)
676 );
677 assert_eq!(
678 vec![1, 2, 2, 3, 3],
679 build_epoch_numbers_sequence_in_certificate_chain(5, 2)
680 );
681 assert_eq!(
682 vec![1, 2, 2, 2, 3, 3, 3],
683 build_epoch_numbers_sequence_in_certificate_chain(7, 3),
684 );
685 assert_eq!(
686 vec![1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6],
687 build_epoch_numbers_sequence_in_certificate_chain(15, 3),
688 );
689 }
690
691 #[test]
692 #[should_panic]
693 fn panics_building_invalid_epochs_sequence_no_certificates_per_epoch() {
694 build_epoch_numbers_sequence(3, 0);
695 }
696
697 #[test]
698 #[should_panic]
699 fn panics_building_invalid_epochs_sequence_less_total_certificates_than_certificates_per_epoch()
700 {
701 build_epoch_numbers_sequence(3, 5);
702 }
703
704 #[test]
705 fn builds_valid_fixtures_per_epochs() {
706 let expected_total_signers = (1..=6).collect::<Vec<_>>();
707 let certificate_chain_builder = CertificateChainBuilder::default()
708 .with_total_certificates(5)
709 .with_certificates_per_epoch(1)
710 .with_total_signers_per_epoch_processor(&|epoch| *epoch as usize);
711
712 let epoch_fixtures =
713 BTreeMap::from_iter(certificate_chain_builder.build_fixtures_for_epochs());
714
715 let total_signers = epoch_fixtures
716 .into_values()
717 .map(|fixture| fixture.signers().len())
718 .collect::<Vec<_>>();
719 assert_eq!(expected_total_signers, total_signers);
720 }
721
722 #[test]
723 fn builds_valid_genesis_certificate() {
724 let expected_protocol_parameters = ProtocolParameters {
725 m: 123,
726 k: 45,
727 phi_f: 0.67,
728 };
729 let fixture = MithrilFixtureBuilder::default()
730 .with_protocol_parameters(expected_protocol_parameters.into())
731 .with_signers(2)
732 .build();
733 let next_fixture = MithrilFixtureBuilder::default()
734 .with_protocol_parameters(expected_protocol_parameters.into())
735 .with_signers(3)
736 .build();
737 let context = CertificateChainBuilderContext {
738 index_certificate: 0,
739 total_certificates: 5,
740 epoch: Epoch(1),
741 fixture: &fixture,
742 next_fixture: &next_fixture,
743 };
744 let expected_protocol_message = context.compute_protocol_message_seed();
745 let expected_signed_message = expected_protocol_message.compute_hash();
746 let (protocol_genesis_signer, _) = CertificateChainBuilder::setup_genesis();
747
748 let genesis_certificate = CertificateChainBuilder::default()
749 .with_protocol_parameters(expected_protocol_parameters)
750 .build_genesis_certificate(&context, &protocol_genesis_signer);
751
752 assert!(genesis_certificate.is_genesis());
753 assert_eq!(Epoch(1), genesis_certificate.epoch);
754 assert_eq!(
755 expected_protocol_parameters,
756 genesis_certificate.metadata.protocol_parameters.into()
757 );
758 assert_eq!(0, genesis_certificate.metadata.signers.len());
759 assert_eq!(
760 expected_protocol_message,
761 genesis_certificate.protocol_message
762 );
763 assert_eq!(expected_signed_message, genesis_certificate.signed_message);
764 }
765
766 #[test]
767 fn builds_valid_standard_certificate() {
768 let expected_protocol_parameters = ProtocolParameters {
769 m: 123,
770 k: 45,
771 phi_f: 0.67,
772 };
773 let fixture = MithrilFixtureBuilder::default()
774 .with_protocol_parameters(expected_protocol_parameters.into())
775 .with_signers(2)
776 .build();
777 let next_fixture = MithrilFixtureBuilder::default()
778 .with_protocol_parameters(expected_protocol_parameters.into())
779 .with_signers(3)
780 .build();
781 let avk = fixture.compute_and_encode_avk();
782 let context = CertificateChainBuilderContext {
783 index_certificate: 2,
784 total_certificates: 5,
785 epoch: Epoch(1),
786 fixture: &fixture,
787 next_fixture: &next_fixture,
788 };
789 let mut expected_protocol_message = context.compute_protocol_message_seed();
790 expected_protocol_message.set_message_part(
791 ProtocolMessagePartKey::SnapshotDigest,
792 format!("digest-{}", context.index_certificate),
793 );
794 let expected_signed_message = expected_protocol_message.compute_hash();
795
796 let standard_certificate = CertificateChainBuilder::default()
797 .with_protocol_parameters(expected_protocol_parameters)
798 .build_standard_certificate(&context);
799
800 assert!(!standard_certificate.is_genesis());
801 assert_eq!(Epoch(1), standard_certificate.epoch);
802 assert_eq!(
803 expected_protocol_parameters,
804 standard_certificate.metadata.protocol_parameters.into()
805 );
806 assert_eq!(2, standard_certificate.metadata.signers.len());
807 assert_eq!(
808 expected_protocol_message,
809 standard_certificate.protocol_message
810 );
811 assert_eq!(expected_signed_message, standard_certificate.signed_message);
812 assert_eq!(
813 avk,
814 standard_certificate
815 .aggregate_verification_key
816 .to_json_hex()
817 .unwrap()
818 );
819 }
820
821 #[test]
822 fn builds_certificate_chain_chained_by_default_to_master_certificates() {
823 fn create_fake_certificate(epoch: Epoch, index_in_epoch: u64) -> Certificate {
824 Certificate {
825 epoch,
826 signed_message: format!("certificate-{}-{index_in_epoch}", *epoch),
827 ..fake_data::certificate("cert-fake".to_string())
828 }
829 }
830
831 let certificates = vec![
832 create_fake_certificate(Epoch(1), 1),
833 create_fake_certificate(Epoch(2), 1),
834 create_fake_certificate(Epoch(2), 2),
835 create_fake_certificate(Epoch(3), 1),
836 create_fake_certificate(Epoch(4), 1),
837 create_fake_certificate(Epoch(4), 2),
838 create_fake_certificate(Epoch(4), 3),
839 ];
840
841 let mut certificates_chained =
842 CertificateChainBuilder::default().compute_chained_certificates(certificates);
843 certificates_chained.reverse();
844
845 let certificate_chained_1_1 = &certificates_chained[0];
846 let certificate_chained_2_1 = &certificates_chained[1];
847 let certificate_chained_2_2 = &certificates_chained[2];
848 let certificate_chained_3_1 = &certificates_chained[3];
849 let certificate_chained_4_1 = &certificates_chained[4];
850 let certificate_chained_4_2 = &certificates_chained[5];
851 let certificate_chained_4_3 = &certificates_chained[6];
852 assert_eq!("", certificate_chained_1_1.previous_hash);
853 assert_eq!(
854 certificate_chained_2_1.previous_hash,
855 certificate_chained_1_1.hash
856 );
857 assert_eq!(
858 certificate_chained_2_2.previous_hash,
859 certificate_chained_2_1.hash
860 );
861 assert_eq!(
862 certificate_chained_3_1.previous_hash,
863 certificate_chained_2_1.hash
864 );
865 assert_eq!(
866 certificate_chained_4_1.previous_hash,
867 certificate_chained_3_1.hash
868 );
869 assert_eq!(
870 certificate_chained_4_2.previous_hash,
871 certificate_chained_4_1.hash
872 );
873 assert_eq!(
874 certificate_chained_4_3.previous_hash,
875 certificate_chained_4_1.hash
876 );
877 }
878
879 #[test]
880 fn builds_certificate_chain_chained_sequentially() {
881 fn create_fake_certificate(epoch: Epoch, index_in_epoch: u64) -> Certificate {
882 Certificate {
883 epoch,
884 signed_message: format!("certificate-{}-{index_in_epoch}", *epoch),
885 ..fake_data::certificate("cert-fake".to_string())
886 }
887 }
888
889 let certificates = vec![
890 create_fake_certificate(Epoch(1), 1),
891 create_fake_certificate(Epoch(2), 1),
892 create_fake_certificate(Epoch(2), 2),
893 create_fake_certificate(Epoch(3), 1),
894 create_fake_certificate(Epoch(4), 1),
895 create_fake_certificate(Epoch(4), 2),
896 create_fake_certificate(Epoch(4), 3),
897 ];
898
899 let mut certificates_chained = CertificateChainBuilder::default()
900 .with_certificate_chaining_method(CertificateChainingMethod::Sequential)
901 .compute_chained_certificates(certificates);
902 certificates_chained.reverse();
903
904 let certificate_chained_1_1 = &certificates_chained[0];
905 let certificate_chained_2_1 = &certificates_chained[1];
906 let certificate_chained_2_2 = &certificates_chained[2];
907 let certificate_chained_3_1 = &certificates_chained[3];
908 let certificate_chained_4_1 = &certificates_chained[4];
909 let certificate_chained_4_2 = &certificates_chained[5];
910 let certificate_chained_4_3 = &certificates_chained[6];
911 assert_eq!("", certificate_chained_1_1.previous_hash);
912 assert_eq!(
913 certificate_chained_2_1.previous_hash,
914 certificate_chained_1_1.hash
915 );
916 assert_eq!(
917 certificate_chained_2_2.previous_hash,
918 certificate_chained_2_1.hash
919 );
920 assert_eq!(
921 certificate_chained_3_1.previous_hash,
922 certificate_chained_2_2.hash
923 );
924 assert_eq!(
925 certificate_chained_4_1.previous_hash,
926 certificate_chained_3_1.hash
927 );
928 assert_eq!(
929 certificate_chained_4_2.previous_hash,
930 certificate_chained_4_1.hash
931 );
932 assert_eq!(
933 certificate_chained_4_3.previous_hash,
934 certificate_chained_4_2.hash
935 );
936 }
937
938 #[test]
939 fn builds_certificate_chain_with_alteration_on_genesis_certificate() {
940 let (certificates, _) = CertificateChainBuilder::new()
941 .with_total_certificates(5)
942 .with_genesis_certificate_processor(&|certificate, _, _| {
943 let mut certificate = certificate;
944 certificate.signed_message = "altered_msg".to_string();
945
946 certificate
947 })
948 .build();
949
950 assert_eq!(
951 "altered_msg".to_string(),
952 certificates.last().unwrap().signed_message
953 );
954 }
955
956 #[test]
957 fn builds_certificate_chain_with_alteration_on_standard_certificates() {
958 let total_certificates = 5;
959 let expected_signed_messages = (1..total_certificates)
960 .rev()
961 .map(|i| format!("altered-msg-{}", i))
962 .collect::<Vec<_>>();
963
964 let (certificates, _) = CertificateChainBuilder::new()
965 .with_total_certificates(total_certificates)
966 .with_standard_certificate_processor(&|certificate, context| {
967 let mut certificate = certificate;
968 certificate.signed_message = format!("altered-msg-{}", context.index_certificate);
969
970 certificate
971 })
972 .build();
973
974 let signed_message = certificates
975 .into_iter()
976 .take(total_certificates as usize - 1)
977 .map(|certificate| certificate.signed_message)
978 .collect::<Vec<_>>();
979 assert_eq!(expected_signed_messages, signed_message);
980 }
981}