1use anyhow::{Context, anyhow};
24use async_trait::async_trait;
25use chrono::Utc;
26use slog::{Logger, debug, info, warn};
27use std::collections::VecDeque;
28use std::sync::Arc;
29
30use mithril_common::StdResult;
31use mithril_common::certificate_chain::CertificateVerifier;
32use mithril_common::crypto_helper::ProtocolGenesisVerifier;
33use mithril_common::entities::{Certificate, SignedEntityType};
34use mithril_common::logging::LoggerExtensions;
35
36use crate::EpochSettingsStorer;
37use crate::entities::OpenMessage;
38
39use super::{
40 CertificateChainSynchronizer, OpenMessageStorer, RemoteCertificateRetriever,
41 SynchronizedCertificateStorer,
42};
43
44pub struct MithrilCertificateChainSynchronizer {
46 remote_certificate_retriever: Arc<dyn RemoteCertificateRetriever>,
47 certificate_storer: Arc<dyn SynchronizedCertificateStorer>,
48 certificate_verifier: Arc<dyn CertificateVerifier>,
49 genesis_verifier: Arc<ProtocolGenesisVerifier>,
50 open_message_storer: Arc<dyn OpenMessageStorer>,
51 epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
52 logger: Logger,
53}
54
55#[derive(Debug, Copy, Clone, PartialEq, Eq)]
56enum SyncStatus {
57 Forced,
58 NoLocalGenesis,
59 RemoteGenesisMatchesLocalGenesis,
60 RemoteGenesisDoesntMatchLocalGenesis,
61}
62
63impl SyncStatus {
64 fn should_sync(&self) -> bool {
65 match self {
66 SyncStatus::Forced => true,
67 SyncStatus::NoLocalGenesis => true,
68 SyncStatus::RemoteGenesisMatchesLocalGenesis => false,
69 SyncStatus::RemoteGenesisDoesntMatchLocalGenesis => true,
70 }
71 }
72}
73
74impl MithrilCertificateChainSynchronizer {
75 pub fn new(
77 remote_certificate_retriever: Arc<dyn RemoteCertificateRetriever>,
78 certificate_storer: Arc<dyn SynchronizedCertificateStorer>,
79 certificate_verifier: Arc<dyn CertificateVerifier>,
80 genesis_verifier: Arc<ProtocolGenesisVerifier>,
81 open_message_storer: Arc<dyn OpenMessageStorer>,
82 epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
83 logger: Logger,
84 ) -> Self {
85 Self {
86 remote_certificate_retriever,
87 certificate_storer,
88 certificate_verifier,
89 genesis_verifier,
90 open_message_storer,
91 epoch_settings_storer,
92 logger: logger.new_with_component_name::<Self>(),
93 }
94 }
95
96 async fn check_sync_state(&self, force: bool) -> StdResult<SyncStatus> {
97 if force {
98 return Ok(SyncStatus::Forced);
99 }
100
101 match self.certificate_storer.get_latest_genesis().await? {
102 Some(local_genesis) => {
103 match self
104 .remote_certificate_retriever
105 .get_genesis_certificate_details()
106 .await?
107 {
108 Some(remote_genesis) if (local_genesis == remote_genesis) => {
109 Ok(SyncStatus::RemoteGenesisMatchesLocalGenesis)
110 }
111 Some(_) => Ok(SyncStatus::RemoteGenesisDoesntMatchLocalGenesis),
112 None => Err(anyhow!("Remote aggregator doesn't have a chain yet")),
114 }
115 }
116 None => Ok(SyncStatus::NoLocalGenesis),
117 }
118 }
119
120 async fn retrieve_and_validate_remote_certificate_chain(
121 &self,
122 starting_point: Certificate,
123 ) -> StdResult<Vec<Certificate>> {
124 let mut validated_certificates = VecDeque::new();
127 let mut certificate = starting_point;
128
129 loop {
130 let parent_certificate = self
131 .certificate_verifier
132 .verify_certificate(&certificate, &self.genesis_verifier.to_verification_key())
133 .await
134 .with_context(
135 || format!("Failed to verify certificate: `{}`", certificate.hash,),
136 )?;
137
138 match parent_certificate {
139 None => {
140 validated_certificates.push_front(certificate);
141 break;
142 }
143 Some(parent) => {
144 if !validated_certificates.is_empty() || parent.epoch != certificate.epoch {
147 validated_certificates.push_front(certificate);
148 }
149
150 certificate = parent;
151 }
152 }
153 }
154
155 Ok(validated_certificates.into())
156 }
157
158 async fn store_certificate_chain(&self, certificate_chain: Vec<Certificate>) -> StdResult<()> {
159 self.certificate_storer
160 .insert_or_replace_many(certificate_chain)
161 .await?;
162 Ok(())
163 }
164}
165
166#[async_trait]
167impl CertificateChainSynchronizer for MithrilCertificateChainSynchronizer {
168 async fn synchronize_certificate_chain(&self, force: bool) -> StdResult<()> {
169 debug!(self.logger, ">> synchronize_certificate_chain"; "force" => force);
170
171 let sync_state = self.check_sync_state(force).await.with_context(|| {
172 format!("Failed to check if certificate chain should be sync (force: `{force}`)")
173 })?;
174 if sync_state.should_sync() {
175 info!(self.logger, "Start synchronizing certificate chain"; "sync_state" => ?sync_state);
176 } else {
177 info!(self.logger, "No need to synchronize certificate chain"; "sync_state" => ?sync_state);
178 return Ok(());
179 }
180
181 let starting_point = self
182 .remote_certificate_retriever
183 .get_latest_certificate_details()
184 .await?
185 .with_context(|| "Failed to retrieve latest remote certificate details")
186 .with_context(|| "Remote aggregator doesn't have a chain yet")?;
187 let remote_certificate_chain = self
188 .retrieve_and_validate_remote_certificate_chain(starting_point)
189 .await
190 .with_context(|| "Failed to retrieve and validate remote certificate chain")?;
191 let latest_certificate = remote_certificate_chain
192 .last()
193 .with_context(|| "Retrieved certificate chain is empty")?
194 .clone();
195
196 self.store_certificate_chain(remote_certificate_chain)
197 .await
198 .with_context(|| "Failed to store remote retrieved certificate chain")?;
199
200 let epoch_settings_exist = self
205 .epoch_settings_storer
206 .get_epoch_settings(latest_certificate.epoch)
207 .await?
208 .is_some();
209 if epoch_settings_exist {
210 let open_message = prepare_open_message_to_store(&latest_certificate);
211 self.open_message_storer
212 .insert_or_replace_open_message(open_message)
213 .await
214 .with_context(
215 || "Failed to store open message when synchronizing certificate chain",
216 )?;
217 } else {
218 warn!(
219 self.logger,
220 "Skipping open message creation: no epoch settings for epoch {}",
221 latest_certificate.epoch
222 );
223 }
224
225 info!(
226 self.logger,
227 "Certificate chain synchronized with remote source"
228 );
229 Ok(())
230 }
231}
232
233fn prepare_open_message_to_store(latest_certificate: &Certificate) -> OpenMessage {
234 OpenMessage {
235 epoch: latest_certificate.epoch,
236 signed_entity_type: SignedEntityType::MithrilStakeDistribution(latest_certificate.epoch),
237 protocol_message: latest_certificate.protocol_message.clone(),
238 is_certified: true,
239 is_expired: false,
240 single_signatures: Vec::new(),
241 created_at: Utc::now(),
242 expires_at: None,
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use anyhow::anyhow;
249 use std::sync::RwLock;
250
251 use mithril_common::certificate_chain::MithrilCertificateVerifier;
252 use mithril_common::entities::Epoch;
253 use mithril_common::test::{
254 builder::{CertificateChainBuilder, CertificateChainFixture},
255 double::{Dummy, FakeCertificaterRetriever, fake_data, fake_keys},
256 mock_extensions::MockBuilder,
257 };
258
259 use crate::entities::AggregatorEpochSettings;
260 use crate::services::{
261 MockOpenMessageStorer, MockRemoteCertificateRetriever, MockSynchronizedCertificateStorer,
262 };
263 use crate::store::FakeEpochSettingsStorer;
264 use crate::test::TestLogger;
265 use crate::test::double::mocks::MockCertificateVerifier;
266
267 use super::*;
268
269 impl MithrilCertificateChainSynchronizer {
270 fn default_for_test() -> Self {
271 Self::default_for_test_with_epoch_settings(vec![])
272 }
273
274 fn default_for_test_with_epoch_settings(
275 epoch_settings: Vec<(Epoch, AggregatorEpochSettings)>,
276 ) -> Self {
277 let genesis_verification_key =
278 fake_keys::genesis_verification_key()[0].try_into().unwrap();
279 Self::new(
280 Arc::new(MockRemoteCertificateRetriever::new()),
281 Arc::new(MockSynchronizedCertificateStorer::new()),
282 Arc::new(MockCertificateVerifier::new()),
283 Arc::new(ProtocolGenesisVerifier::from_verification_key(
284 genesis_verification_key,
285 )),
286 Arc::new(MockOpenMessageStorer::new()),
287 Arc::new(FakeEpochSettingsStorer::new(epoch_settings)),
288 TestLogger::stdout(),
289 )
290 }
291 }
292
293 macro_rules! mocked_synchronizer {
294 (with_remote_genesis: $remote_genesis_result:expr) => {
295 MithrilCertificateChainSynchronizer {
296 remote_certificate_retriever:
297 MockBuilder::<MockRemoteCertificateRetriever>::configure(|retriever| {
298 retriever
299 .expect_get_genesis_certificate_details()
300 .return_once(move || $remote_genesis_result);
301 }),
302 ..MithrilCertificateChainSynchronizer::default_for_test()
303 }
304 };
305 (with_local_genesis: $local_genesis_result:expr) => {
306 MithrilCertificateChainSynchronizer {
307 certificate_storer: MockBuilder::<MockSynchronizedCertificateStorer>::configure(
308 |storer| {
309 storer
310 .expect_get_latest_genesis()
311 .return_once(move || $local_genesis_result);
312 },
313 ),
314 ..MithrilCertificateChainSynchronizer::default_for_test()
315 }
316 };
317 (with_remote_genesis: $remote_genesis_result:expr, with_local_genesis: $local_genesis_result:expr) => {
318 MithrilCertificateChainSynchronizer {
319 remote_certificate_retriever:
320 MockBuilder::<MockRemoteCertificateRetriever>::configure(|retriever| {
321 retriever
322 .expect_get_genesis_certificate_details()
323 .return_once(move || $remote_genesis_result);
324 }),
325 certificate_storer: MockBuilder::<MockSynchronizedCertificateStorer>::configure(
326 |storer| {
327 storer
328 .expect_get_latest_genesis()
329 .return_once(move || $local_genesis_result);
330 },
331 ),
332 ..MithrilCertificateChainSynchronizer::default_for_test()
333 }
334 };
335 (with_verify_certificate_result: $verify_certificate_result:expr) => {
336 MithrilCertificateChainSynchronizer {
337 certificate_verifier: MockBuilder::<MockCertificateVerifier>::configure(
338 |verifier| {
339 verifier
340 .expect_verify_certificate()
341 .return_once(move |_, _| $verify_certificate_result);
342 },
343 ),
344 ..MithrilCertificateChainSynchronizer::default_for_test()
345 }
346 };
347 }
348
349 fn fake_verifier(remote_certificate_chain: &[Certificate]) -> Arc<dyn CertificateVerifier> {
350 let verifier = MithrilCertificateVerifier::new(
351 TestLogger::stdout(),
352 Arc::new(FakeCertificaterRetriever::from_certificates(
353 remote_certificate_chain,
354 )),
355 );
356 Arc::new(verifier)
357 }
358
359 #[derive(Default)]
360 struct DumbCertificateStorer {
361 certificates: RwLock<Vec<Certificate>>,
362 genesis_certificate: Option<Certificate>,
363 }
364
365 impl DumbCertificateStorer {
366 fn new(genesis: Certificate, already_stored: Vec<Certificate>) -> Self {
367 Self {
368 certificates: RwLock::new(already_stored),
369 genesis_certificate: Some(genesis),
370 }
371 }
372
373 fn stored_certificates(&self) -> Vec<Certificate> {
374 self.certificates.read().unwrap().clone()
375 }
376 }
377
378 #[async_trait]
379 impl SynchronizedCertificateStorer for DumbCertificateStorer {
380 async fn insert_or_replace_many(
381 &self,
382 certificates_chain: Vec<Certificate>,
383 ) -> StdResult<()> {
384 let mut certificates = self.certificates.write().unwrap();
385 *certificates = certificates_chain;
386 Ok(())
387 }
388
389 async fn get_latest_genesis(&self) -> StdResult<Option<Certificate>> {
390 Ok(self.genesis_certificate.clone())
391 }
392 }
393
394 mod check_sync_state {
395 use super::*;
396
397 #[test]
398 fn sync_state_should_sync() {
399 assert!(SyncStatus::Forced.should_sync());
400 assert!(!SyncStatus::RemoteGenesisMatchesLocalGenesis.should_sync());
401 assert!(SyncStatus::RemoteGenesisDoesntMatchLocalGenesis.should_sync());
402 assert!(SyncStatus::NoLocalGenesis.should_sync());
403 }
404
405 #[tokio::test]
406 async fn state_when_force_true() {
407 let synchronizer = MithrilCertificateChainSynchronizer::default_for_test();
408
409 let sync_state = synchronizer.check_sync_state(true).await.unwrap();
410 assert_eq!(SyncStatus::Forced, sync_state);
411 }
412
413 #[tokio::test]
414 async fn state_when_force_false_and_no_local_genesis_certificate_found() {
415 let synchronizer = mocked_synchronizer!(with_local_genesis: Ok(None));
416
417 let sync_state = synchronizer.check_sync_state(false).await.unwrap();
418 assert_eq!(SyncStatus::NoLocalGenesis, sync_state);
419 }
420
421 #[tokio::test]
422 async fn state_when_force_false_and_remote_genesis_dont_matches_local_genesis() {
423 let synchronizer = mocked_synchronizer!(
424 with_remote_genesis: Ok(Some(fake_data::genesis_certificate("remote_genesis"))),
425 with_local_genesis: Ok(Some(fake_data::genesis_certificate("local_genesis")))
426 );
427
428 let sync_state = synchronizer.check_sync_state(false).await.unwrap();
429 assert_eq!(SyncStatus::RemoteGenesisDoesntMatchLocalGenesis, sync_state);
430 }
431
432 #[tokio::test]
433 async fn state_when_force_false_and_remote_genesis_matches_local_genesis() {
434 let remote_genesis = fake_data::genesis_certificate("genesis");
435 let local_genesis = remote_genesis.clone();
436 let synchronizer = mocked_synchronizer!(
437 with_remote_genesis: Ok(Some(remote_genesis)),
438 with_local_genesis: Ok(Some(local_genesis))
439 );
440
441 let sync_state = synchronizer.check_sync_state(false).await.unwrap();
442 assert_eq!(SyncStatus::RemoteGenesisMatchesLocalGenesis, sync_state);
443 }
444
445 #[tokio::test]
446 async fn if_force_true_it_should_not_fetch_remote_genesis_certificate() {
447 let synchronizer = mocked_synchronizer!(with_remote_genesis: Err(anyhow!(
448 "should not fetch genesis"
449 )));
450
451 synchronizer.check_sync_state(true).await.unwrap();
452 }
453
454 #[tokio::test]
455 async fn should_abort_with_error_if_force_false_and_fails_to_retrieve_local_genesis() {
456 let synchronizer = mocked_synchronizer!(with_local_genesis: Err(anyhow!("failure")));
457 synchronizer
458 .check_sync_state(false)
459 .await
460 .expect_err("Expected an error but was:");
461 }
462
463 #[tokio::test]
464 async fn should_abort_with_error_if_force_false_and_fails_to_retrieve_remote_genesis() {
465 let synchronizer = mocked_synchronizer!(
466 with_remote_genesis: Err(anyhow!("failure")),
467 with_local_genesis: Ok(Some(fake_data::genesis_certificate("local_genesis")))
468 );
469 synchronizer
470 .check_sync_state(false)
471 .await
472 .expect_err("Expected an error but was:");
473 }
474
475 #[tokio::test]
476 async fn should_abort_with_error_if_force_false_and_remote_genesis_is_none() {
477 let synchronizer = mocked_synchronizer!(
478 with_remote_genesis: Ok(None),
479 with_local_genesis: Ok(Some(fake_data::genesis_certificate("local_genesis")))
480 );
481 let error = synchronizer
482 .check_sync_state(false)
483 .await
484 .expect_err("Expected an error but was:");
485
486 assert!(
487 error
488 .to_string()
489 .contains("Remote aggregator doesn't have a chain yet"),
490 "Unexpected error:\n{error:?}"
491 );
492 }
493 }
494
495 mod retrieve_validate_remote_certificate_chain {
496 use mockall::predicate::{always, eq};
497
498 use mithril_common::entities::Epoch;
499
500 use super::*;
501
502 #[tokio::test]
503 async fn succeed_if_the_remote_chain_only_contains_a_genesis_certificate() {
504 let chain = CertificateChainBuilder::new().with_total_certificates(1).build();
505 let synchronizer = MithrilCertificateChainSynchronizer {
506 certificate_verifier: fake_verifier(&chain),
507 genesis_verifier: Arc::new(chain.genesis_verifier.clone()),
508 ..MithrilCertificateChainSynchronizer::default_for_test()
509 };
510
511 let starting_point = chain[0].clone();
512 let remote_certificate_chain = synchronizer
513 .retrieve_and_validate_remote_certificate_chain(starting_point)
514 .await
515 .unwrap();
516
517 assert_eq!(remote_certificate_chain, chain.certificates_chained);
518 }
519
520 #[tokio::test]
521 async fn abort_with_error_if_a_certificate_is_invalid() {
522 let synchronizer = mocked_synchronizer!(with_verify_certificate_result: Err(anyhow!("invalid certificate")));
523
524 let starting_point = fake_data::certificate("certificate");
525 synchronizer
526 .retrieve_and_validate_remote_certificate_chain(starting_point)
527 .await
528 .expect_err("Expected an error but was:");
529 }
530
531 #[tokio::test]
532 async fn succeed_with_a_valid_certificate_chain_and_only_get_first_certificate_of_each_epoch_plus_genesis()
533 {
534 let chain = CertificateChainBuilder::new()
538 .with_total_certificates(9)
539 .with_certificates_per_epoch(2)
540 .build();
541 let synchronizer = MithrilCertificateChainSynchronizer {
542 certificate_verifier: fake_verifier(&chain),
543 genesis_verifier: Arc::new(chain.genesis_verifier.clone()),
544 ..MithrilCertificateChainSynchronizer::default_for_test()
545 };
546
547 let starting_point = chain[0].clone();
548 let remote_certificate_chain = synchronizer
549 .retrieve_and_validate_remote_certificate_chain(starting_point.clone())
550 .await
551 .unwrap();
552
553 let mut expected = chain.certificate_path_to_genesis(&starting_point.hash);
554 expected.reverse();
556 expected.pop();
558 assert_eq!(remote_certificate_chain, expected);
559 }
560
561 #[tokio::test]
562 async fn return_chain_ordered_from_genesis_to_latest() {
563 let base_certificate = fake_data::certificate("whatever");
564 let chain = [
565 Certificate {
566 epoch: Epoch(2),
567 ..fake_data::genesis_certificate("genesis")
568 },
569 Certificate {
570 epoch: Epoch(3),
571 hash: "hash1".to_string(),
572 previous_hash: "genesis".to_string(),
573 ..base_certificate.clone()
574 },
575 Certificate {
576 epoch: Epoch(4),
577 hash: "hash2".to_string(),
578 previous_hash: "hash1".to_string(),
579 ..base_certificate
580 },
581 ];
582 let synchronizer = MithrilCertificateChainSynchronizer {
583 certificate_verifier: MockBuilder::<MockCertificateVerifier>::configure(|mock| {
584 let cert_1 = chain[1].clone();
585 mock.expect_verify_certificate()
586 .with(eq(chain[2].clone()), always())
587 .return_once(move |_, _| Ok(Some(cert_1)));
588 let genesis = chain[0].clone();
589 mock.expect_verify_certificate()
590 .with(eq(chain[1].clone()), always())
591 .return_once(move |_, _| Ok(Some(genesis)));
592 mock.expect_verify_certificate()
593 .with(eq(chain[0].clone()), always())
594 .return_once(move |_, _| Ok(None));
595 }),
596 ..MithrilCertificateChainSynchronizer::default_for_test()
597 };
598
599 let starting_point = chain[2].clone();
600 let remote_certificate_chain = synchronizer
601 .retrieve_and_validate_remote_certificate_chain(starting_point.clone())
602 .await
603 .unwrap();
604
605 assert_eq!(
606 remote_certificate_chain
607 .into_iter()
608 .map(|c| c.hash)
609 .collect::<Vec<_>>(),
610 vec!["genesis".to_string(), "hash1".to_string(), "hash2".to_string()]
611 );
612 }
613 }
614
615 mod store_remote_certificate_chain {
616 use super::*;
617
618 #[tokio::test]
619 async fn do_store_given_certificates() {
620 let certificates_chain = vec![
621 fake_data::genesis_certificate("genesis"),
622 fake_data::certificate("certificate1"),
623 fake_data::certificate("certificate2"),
624 ];
625 let storer = Arc::new(DumbCertificateStorer::default());
626 let synchronizer = MithrilCertificateChainSynchronizer {
627 certificate_storer: storer.clone(),
628 ..MithrilCertificateChainSynchronizer::default_for_test()
629 };
630
631 assert_eq!(Vec::<Certificate>::new(), storer.stored_certificates());
632
633 synchronizer
634 .store_certificate_chain(certificates_chain.clone())
635 .await
636 .unwrap();
637
638 assert_eq!(certificates_chain, storer.stored_certificates());
639 }
640
641 #[tokio::test]
642 async fn fail_on_storer_error() {
643 let synchronizer = MithrilCertificateChainSynchronizer {
644 certificate_storer: MockBuilder::<MockSynchronizedCertificateStorer>::configure(
645 |mock| {
646 mock.expect_insert_or_replace_many()
647 .return_once(move |_| Err(anyhow!("failure")));
648 },
649 ),
650 ..MithrilCertificateChainSynchronizer::default_for_test()
651 };
652
653 synchronizer
654 .store_certificate_chain(vec![fake_data::certificate("certificate")])
655 .await
656 .unwrap_err();
657 }
658 }
659
660 mod synchronize_certificate_chain {
661 use mockall::predicate::function;
662
663 use super::*;
664
665 fn build_synchronizer(
666 remote_chain: &CertificateChainFixture,
667 storer: Arc<dyn SynchronizedCertificateStorer>,
668 ) -> MithrilCertificateChainSynchronizer {
669 build_synchronizer_with_epoch_settings(remote_chain, storer, true)
670 }
671
672 fn build_synchronizer_with_epoch_settings(
673 remote_chain: &CertificateChainFixture,
674 storer: Arc<dyn SynchronizedCertificateStorer>,
675 expect_open_message_creation: bool,
676 ) -> MithrilCertificateChainSynchronizer {
677 let latest_epoch = remote_chain.latest_certificate().epoch;
678
679 let epoch_settings = if expect_open_message_creation {
680 vec![(latest_epoch, AggregatorEpochSettings::dummy())]
681 } else {
682 vec![]
683 };
684
685 MithrilCertificateChainSynchronizer {
686 certificate_storer: storer.clone(),
687 remote_certificate_retriever:
688 MockBuilder::<MockRemoteCertificateRetriever>::configure(|mock| {
689 let genesis = remote_chain.genesis_certificate().clone();
690 mock.expect_get_genesis_certificate_details()
691 .return_once(move || Ok(Some(genesis)));
692 let latest = remote_chain.latest_certificate().clone();
693 mock.expect_get_latest_certificate_details()
694 .return_once(move || Ok(Some(latest)));
695 }),
696 certificate_verifier: fake_verifier(remote_chain),
697 open_message_storer: MockBuilder::<MockOpenMessageStorer>::configure(|mock| {
698 if expect_open_message_creation {
699 let expected_msd_epoch = latest_epoch;
700 mock.expect_insert_or_replace_open_message()
701 .with(function(move |open_message: &OpenMessage| {
702 open_message.signed_entity_type
703 == SignedEntityType::MithrilStakeDistribution(
704 expected_msd_epoch,
705 )
706 }))
707 .times(1..)
708 .returning(|_| Ok(()));
709 } else {
710 mock.expect_insert_or_replace_open_message().never();
711 }
712 }),
713 ..MithrilCertificateChainSynchronizer::default_for_test_with_epoch_settings(
714 epoch_settings,
715 )
716 }
717 }
718
719 #[tokio::test]
720 async fn store_all() {
721 let remote_chain = CertificateChainBuilder::default()
722 .with_certificates_per_epoch(3)
723 .with_total_certificates(8)
724 .build();
725 let storer = Arc::new(DumbCertificateStorer::default());
726 let synchronizer = build_synchronizer(&remote_chain, storer.clone());
727
728 synchronizer.synchronize_certificate_chain(false).await.unwrap();
730
731 let mut expected =
732 remote_chain.certificate_path_to_genesis(&remote_chain.latest_certificate().hash);
733 expected.reverse();
734 assert_eq!(expected, storer.stored_certificates());
735 }
736
737 #[tokio::test]
738 async fn store_partial() {
739 let remote_chain = CertificateChainBuilder::default()
740 .with_certificates_per_epoch(1)
741 .with_total_certificates(8)
742 .build();
743 let existing_certificates =
744 remote_chain.certificate_path_to_genesis(&remote_chain[5].hash);
745 let storer = Arc::new(DumbCertificateStorer::new(
746 remote_chain.genesis_certificate().clone(),
747 existing_certificates.clone(),
748 ));
749 let synchronizer = build_synchronizer(&remote_chain, storer.clone());
750
751 synchronizer.synchronize_certificate_chain(false).await.unwrap();
753
754 assert_eq!(&existing_certificates, &storer.stored_certificates());
755
756 synchronizer.synchronize_certificate_chain(true).await.unwrap();
758
759 let mut expected =
760 remote_chain.certificate_path_to_genesis(&remote_chain.latest_certificate().hash);
761 expected.reverse();
762 assert_eq!(expected, storer.stored_certificates());
763 }
764
765 #[tokio::test]
766 async fn skips_open_message_creation_when_epoch_settings_do_not_exist() {
767 let remote_chain = CertificateChainBuilder::default()
768 .with_certificates_per_epoch(1)
769 .with_total_certificates(5)
770 .build();
771 let storer = Arc::new(DumbCertificateStorer::default());
772 let synchronizer =
773 build_synchronizer_with_epoch_settings(&remote_chain, storer.clone(), false);
774
775 synchronizer.synchronize_certificate_chain(false).await.unwrap();
776
777 let mut expected =
778 remote_chain.certificate_path_to_genesis(&remote_chain.latest_certificate().hash);
779 expected.reverse();
780 assert_eq!(expected, storer.stored_certificates());
781 }
782 }
783}