mithril_aggregator/services/
message.rs

1//! This service is responsible for providing HTTP server with messages as fast as possible.
2
3use std::collections::BTreeSet;
4use std::sync::Arc;
5
6use async_trait::async_trait;
7
8use mithril_common::{
9    StdResult,
10    entities::{Epoch, SignedEntityTypeDiscriminants},
11    messages::{
12        CardanoDatabaseDigestListItemMessage, CardanoDatabaseDigestListMessage,
13        CardanoDatabaseSnapshotListMessage, CardanoDatabaseSnapshotMessage,
14        CardanoStakeDistributionListMessage, CardanoStakeDistributionMessage,
15        CardanoTransactionSnapshotListMessage, CardanoTransactionSnapshotMessage,
16        CertificateListMessage, CertificateMessage, EpochSettingsMessage,
17        MithrilStakeDistributionListMessage, MithrilStakeDistributionMessage, SignerMessagePart,
18        SnapshotListMessage, SnapshotMessage,
19    },
20};
21
22use crate::{
23    ImmutableFileDigestMapper,
24    database::repository::{CertificateRepository, SignedEntityStorer},
25    dependency_injection::EpochServiceWrapper,
26};
27
28/// HTTP Message service trait.
29#[cfg_attr(test, mockall::automock)]
30#[async_trait]
31pub trait MessageService: Sync + Send {
32    /// Return the epoch settings message if it exists.
33    async fn get_epoch_settings_message(
34        &self,
35        allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
36    ) -> StdResult<EpochSettingsMessage>;
37
38    /// Return the message representation of a certificate if it exists.
39    async fn get_certificate_message(
40        &self,
41        certificate_hash: &str,
42    ) -> StdResult<Option<CertificateMessage>>;
43
44    /// Return the message representation of the latest genesis certificate.
45    async fn get_latest_genesis_certificate_message(&self)
46    -> StdResult<Option<CertificateMessage>>;
47
48    /// Return the message representation of the last N certificates.
49    async fn get_certificate_list_message(&self, limit: usize)
50    -> StdResult<CertificateListMessage>;
51
52    /// Return the information regarding the given snapshot.
53    async fn get_snapshot_message(
54        &self,
55        signed_entity_id: &str,
56    ) -> StdResult<Option<SnapshotMessage>>;
57
58    /// Return the list of the last signed snapshots. The limit of the list is
59    /// passed as argument.
60    async fn get_snapshot_list_message(&self, limit: usize) -> StdResult<SnapshotListMessage>;
61
62    /// Return the information regarding the Cardano database for the given identifier.
63    async fn get_cardano_database_message(
64        &self,
65        signed_entity_id: &str,
66    ) -> StdResult<Option<CardanoDatabaseSnapshotMessage>>;
67
68    /// Return the list of the last Cardano database message.
69    async fn get_cardano_database_list_message(
70        &self,
71        limit: usize,
72    ) -> StdResult<CardanoDatabaseSnapshotListMessage>;
73
74    /// Return the list of the Cardano database immutable file names and their digests.
75    async fn get_cardano_database_digest_list_message(
76        &self,
77    ) -> StdResult<CardanoDatabaseDigestListMessage>;
78
79    /// Return the information regarding the Mithril stake distribution for the given identifier.
80    async fn get_mithril_stake_distribution_message(
81        &self,
82        signed_entity_id: &str,
83    ) -> StdResult<Option<MithrilStakeDistributionMessage>>;
84
85    /// Return the list of the last Mithril stake distributions message.
86    async fn get_mithril_stake_distribution_list_message(
87        &self,
88        limit: usize,
89    ) -> StdResult<MithrilStakeDistributionListMessage>;
90
91    /// Return the information regarding the Cardano transactions set for the given identifier.
92    async fn get_cardano_transaction_message(
93        &self,
94        signed_entity_id: &str,
95    ) -> StdResult<Option<CardanoTransactionSnapshotMessage>>;
96
97    /// Return the list of the last Cardano transactions set message.
98    async fn get_cardano_transaction_list_message(
99        &self,
100        limit: usize,
101    ) -> StdResult<CardanoTransactionSnapshotListMessage>;
102
103    /// Return the information regarding the Cardano stake distribution for the given identifier.
104    async fn get_cardano_stake_distribution_message(
105        &self,
106        signed_entity_id: &str,
107    ) -> StdResult<Option<CardanoStakeDistributionMessage>>;
108
109    /// Return the information regarding the Cardano stake distribution for the given epoch.
110    async fn get_cardano_stake_distribution_message_by_epoch(
111        &self,
112        epoch: Epoch,
113    ) -> StdResult<Option<CardanoStakeDistributionMessage>>;
114
115    /// Return the list of the last Cardano stake distributions message.
116    async fn get_cardano_stake_distribution_list_message(
117        &self,
118        limit: usize,
119    ) -> StdResult<CardanoStakeDistributionListMessage>;
120}
121
122/// Implementation of the [MessageService]
123pub struct MithrilMessageService {
124    certificate_repository: Arc<CertificateRepository>,
125    signed_entity_storer: Arc<dyn SignedEntityStorer>,
126    immutable_file_digest_mapper: Arc<dyn ImmutableFileDigestMapper>,
127    epoch_service: EpochServiceWrapper,
128}
129
130impl MithrilMessageService {
131    /// Constructor
132    pub fn new(
133        certificate_repository: Arc<CertificateRepository>,
134        signed_entity_storer: Arc<dyn SignedEntityStorer>,
135        immutable_file_digest_mapper: Arc<dyn ImmutableFileDigestMapper>,
136        epoch_service: EpochServiceWrapper,
137    ) -> Self {
138        Self {
139            certificate_repository,
140            signed_entity_storer,
141            immutable_file_digest_mapper,
142            epoch_service,
143        }
144    }
145}
146
147#[async_trait]
148impl MessageService for MithrilMessageService {
149    async fn get_epoch_settings_message(
150        &self,
151        allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
152    ) -> StdResult<EpochSettingsMessage> {
153        let epoch_service = self.epoch_service.read().await;
154
155        let epoch = epoch_service.epoch_of_current_data()?;
156        let signer_registration_protocol_parameters =
157            epoch_service.signer_registration_protocol_parameters()?.clone();
158        let current_signers = epoch_service.current_signers()?;
159        let next_signers = epoch_service.next_signers()?;
160
161        let cardano_transactions_discriminant =
162            allowed_discriminants.get(&SignedEntityTypeDiscriminants::CardanoTransactions);
163
164        let cardano_transactions_signing_config = cardano_transactions_discriminant
165            .map(|_| epoch_service.current_cardano_transactions_signing_config())
166            .transpose()?
167            .cloned();
168        let next_cardano_transactions_signing_config = cardano_transactions_discriminant
169            .map(|_| epoch_service.next_cardano_transactions_signing_config())
170            .transpose()?
171            .cloned();
172
173        let epoch_settings_message = EpochSettingsMessage {
174            epoch,
175            signer_registration_protocol_parameters,
176            current_signers: SignerMessagePart::from_signers(current_signers.to_vec()),
177            next_signers: SignerMessagePart::from_signers(next_signers.to_vec()),
178            cardano_transactions_signing_config,
179            next_cardano_transactions_signing_config,
180        };
181
182        Ok(epoch_settings_message)
183    }
184
185    async fn get_certificate_message(
186        &self,
187        certificate_hash: &str,
188    ) -> StdResult<Option<CertificateMessage>> {
189        self.certificate_repository.get_certificate(certificate_hash).await
190    }
191
192    async fn get_latest_genesis_certificate_message(
193        &self,
194    ) -> StdResult<Option<CertificateMessage>> {
195        self.certificate_repository.get_latest_genesis_certificate().await
196    }
197
198    async fn get_certificate_list_message(
199        &self,
200        limit: usize,
201    ) -> StdResult<CertificateListMessage> {
202        self.certificate_repository.get_latest_certificates(limit).await
203    }
204
205    async fn get_snapshot_message(
206        &self,
207        signed_entity_id: &str,
208    ) -> StdResult<Option<SnapshotMessage>> {
209        let signed_entity = self.signed_entity_storer.get_signed_entity(signed_entity_id).await?;
210
211        signed_entity.map(|s| s.try_into()).transpose()
212    }
213
214    async fn get_snapshot_list_message(&self, limit: usize) -> StdResult<SnapshotListMessage> {
215        let signed_entity_type_id = SignedEntityTypeDiscriminants::CardanoImmutableFilesFull;
216        let entities = self
217            .signed_entity_storer
218            .get_last_signed_entities_by_type(&signed_entity_type_id, limit)
219            .await?;
220
221        entities.into_iter().map(|i| i.try_into()).collect()
222    }
223
224    async fn get_cardano_database_message(
225        &self,
226        signed_entity_id: &str,
227    ) -> StdResult<Option<CardanoDatabaseSnapshotMessage>> {
228        let signed_entity = self.signed_entity_storer.get_signed_entity(signed_entity_id).await?;
229
230        signed_entity.map(|v| v.try_into()).transpose()
231    }
232
233    async fn get_cardano_database_list_message(
234        &self,
235        limit: usize,
236    ) -> StdResult<CardanoDatabaseSnapshotListMessage> {
237        let signed_entity_type_id = SignedEntityTypeDiscriminants::CardanoDatabase;
238        let entities = self
239            .signed_entity_storer
240            .get_last_signed_entities_by_type(&signed_entity_type_id, limit)
241            .await?;
242
243        entities.into_iter().map(|i| i.try_into()).collect()
244    }
245
246    async fn get_cardano_database_digest_list_message(
247        &self,
248    ) -> StdResult<CardanoDatabaseDigestListMessage> {
249        Ok(self
250            .immutable_file_digest_mapper
251            .get_immutable_file_digest_map()
252            .await?
253            .into_iter()
254            .map(
255                |(immutable_file_name, digest)| CardanoDatabaseDigestListItemMessage {
256                    immutable_file_name,
257                    digest,
258                },
259            )
260            .collect::<Vec<_>>())
261    }
262
263    async fn get_mithril_stake_distribution_message(
264        &self,
265        signed_entity_id: &str,
266    ) -> StdResult<Option<MithrilStakeDistributionMessage>> {
267        let signed_entity = self.signed_entity_storer.get_signed_entity(signed_entity_id).await?;
268
269        signed_entity.map(|v| v.try_into()).transpose()
270    }
271
272    async fn get_mithril_stake_distribution_list_message(
273        &self,
274        limit: usize,
275    ) -> StdResult<MithrilStakeDistributionListMessage> {
276        let signed_entity_type_id = SignedEntityTypeDiscriminants::MithrilStakeDistribution;
277        let entities = self
278            .signed_entity_storer
279            .get_last_signed_entities_by_type(&signed_entity_type_id, limit)
280            .await?;
281
282        entities.into_iter().map(|i| i.try_into()).collect()
283    }
284
285    async fn get_cardano_transaction_message(
286        &self,
287        signed_entity_id: &str,
288    ) -> StdResult<Option<CardanoTransactionSnapshotMessage>> {
289        let signed_entity = self.signed_entity_storer.get_signed_entity(signed_entity_id).await?;
290
291        signed_entity.map(|v| v.try_into()).transpose()
292    }
293
294    async fn get_cardano_transaction_list_message(
295        &self,
296        limit: usize,
297    ) -> StdResult<CardanoTransactionSnapshotListMessage> {
298        let signed_entity_type_id = SignedEntityTypeDiscriminants::CardanoTransactions;
299        let entities = self
300            .signed_entity_storer
301            .get_last_signed_entities_by_type(&signed_entity_type_id, limit)
302            .await?;
303
304        entities.into_iter().map(|i| i.try_into()).collect()
305    }
306
307    async fn get_cardano_stake_distribution_message(
308        &self,
309        signed_entity_id: &str,
310    ) -> StdResult<Option<CardanoStakeDistributionMessage>> {
311        let signed_entity = self.signed_entity_storer.get_signed_entity(signed_entity_id).await?;
312
313        signed_entity.map(|v| v.try_into()).transpose()
314    }
315
316    async fn get_cardano_stake_distribution_message_by_epoch(
317        &self,
318        epoch: Epoch,
319    ) -> StdResult<Option<CardanoStakeDistributionMessage>> {
320        let signed_entity = self
321            .signed_entity_storer
322            .get_cardano_stake_distribution_signed_entity_by_epoch(epoch)
323            .await?;
324
325        signed_entity.map(|v| v.try_into()).transpose()
326    }
327
328    async fn get_cardano_stake_distribution_list_message(
329        &self,
330        limit: usize,
331    ) -> StdResult<CardanoStakeDistributionListMessage> {
332        let signed_entity_type_id = SignedEntityTypeDiscriminants::CardanoStakeDistribution;
333        let entities = self
334            .signed_entity_storer
335            .get_last_signed_entities_by_type(&signed_entity_type_id, limit)
336            .await?;
337
338        entities.into_iter().map(|i| i.try_into()).collect()
339    }
340}
341
342#[cfg(test)]
343mod tests {
344    use mithril_common::entities::{BlockNumber, Certificate, SignedEntityType};
345    use mithril_common::test::double::{Dummy, fake_data};
346    use tokio::sync::RwLock;
347
348    use crate::database::record::SignedEntityRecord;
349    use crate::database::repository::{ImmutableFileDigestRepository, SignedEntityStore};
350    use crate::database::test_helper::main_db_connection;
351    use crate::services::FakeEpochService;
352
353    use super::*;
354
355    struct MessageServiceBuilder {
356        certificates: Vec<Certificate>,
357        signed_entity_records: Vec<SignedEntityRecord>,
358        immutable_file_digest_messages: Vec<CardanoDatabaseDigestListItemMessage>,
359        epoch_service: Option<FakeEpochService>,
360    }
361
362    impl MessageServiceBuilder {
363        fn new() -> Self {
364            Self {
365                certificates: Vec::new(),
366                signed_entity_records: Vec::new(),
367                immutable_file_digest_messages: Vec::new(),
368                epoch_service: None,
369            }
370        }
371
372        fn with_certificates(mut self, certificates: &[Certificate]) -> Self {
373            self.certificates.extend_from_slice(certificates);
374
375            self
376        }
377
378        fn with_signed_entity_records(
379            mut self,
380            signed_entity_record: &[SignedEntityRecord],
381        ) -> Self {
382            self.signed_entity_records.extend_from_slice(signed_entity_record);
383
384            self
385        }
386
387        fn with_immutable_file_digest_messages(
388            mut self,
389            digests: &[CardanoDatabaseDigestListItemMessage],
390        ) -> Self {
391            self.immutable_file_digest_messages.extend_from_slice(digests);
392
393            self
394        }
395
396        fn with_epoch_service(mut self, epoch_service: FakeEpochService) -> Self {
397            self.epoch_service = Some(epoch_service);
398
399            self
400        }
401
402        async fn build(self) -> MithrilMessageService {
403            let connection = Arc::new(main_db_connection().unwrap());
404            let certificate_repository = CertificateRepository::new(connection.clone());
405            let signed_entity_store = SignedEntityStore::new(connection.clone());
406            let immutable_file_digest_mapper =
407                ImmutableFileDigestRepository::new(connection.clone());
408            let epoch_service = self.epoch_service.unwrap_or(FakeEpochService::without_data());
409
410            certificate_repository
411                .create_many_certificates(self.certificates)
412                .await
413                .unwrap();
414            for record in self.signed_entity_records {
415                signed_entity_store.store_signed_entity(&record).await.unwrap();
416            }
417
418            for digest_message in self.immutable_file_digest_messages {
419                immutable_file_digest_mapper
420                    .upsert_immutable_file_digest(
421                        &digest_message.immutable_file_name,
422                        &digest_message.digest,
423                    )
424                    .await
425                    .unwrap();
426            }
427
428            MithrilMessageService::new(
429                Arc::new(certificate_repository),
430                Arc::new(signed_entity_store),
431                Arc::new(immutable_file_digest_mapper),
432                Arc::new(RwLock::new(epoch_service)),
433            )
434        }
435    }
436
437    mod epoch_settings {
438        use mithril_common::{
439            entities::{CardanoTransactionsSigningConfig, ProtocolParameters},
440            test::builder::MithrilFixtureBuilder,
441        };
442
443        use crate::{entities::AggregatorEpochSettings, services::FakeEpochServiceBuilder};
444
445        use super::*;
446
447        #[tokio::test]
448        async fn get_epoch_settings_message() {
449            let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
450            let epoch_service = FakeEpochService::from_fixture(Epoch(4), &fixture);
451            let message_service = MessageServiceBuilder::new()
452                .with_epoch_service(epoch_service)
453                .build()
454                .await;
455
456            let message = message_service
457                .get_epoch_settings_message(SignedEntityTypeDiscriminants::all())
458                .await
459                .unwrap();
460
461            assert_eq!(message.epoch, Epoch(4));
462            assert_eq!(
463                message.signer_registration_protocol_parameters,
464                ProtocolParameters::new(5, 100, 0.65)
465            );
466            assert_eq!(message.current_signers.len(), 3);
467            assert_eq!(message.next_signers.len(), 3);
468            assert_eq!(
469                message.cardano_transactions_signing_config,
470                Some(CardanoTransactionsSigningConfig {
471                    security_parameter: BlockNumber(0),
472                    step: BlockNumber(15)
473                })
474            );
475            assert_eq!(
476                message.next_cardano_transactions_signing_config,
477                Some(CardanoTransactionsSigningConfig {
478                    security_parameter: BlockNumber(0),
479                    step: BlockNumber(15)
480                })
481            );
482        }
483
484        #[tokio::test]
485        async fn get_epoch_settings_message_with_cardano_transactions_enabled() {
486            let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
487            let epoch_service = FakeEpochService::from_fixture(Epoch(4), &fixture);
488            let message_service = MessageServiceBuilder::new()
489                .with_epoch_service(epoch_service)
490                .build()
491                .await;
492
493            let message = message_service
494                .get_epoch_settings_message(BTreeSet::from([
495                    SignedEntityTypeDiscriminants::CardanoTransactions,
496                ]))
497                .await
498                .unwrap();
499
500            assert!(message.cardano_transactions_signing_config.is_some());
501            assert!(message.next_cardano_transactions_signing_config.is_some(),);
502        }
503
504        #[tokio::test]
505        async fn get_epoch_settings_message_with_cardano_transactions_not_enabled() {
506            let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
507            let epoch_service = FakeEpochService::from_fixture(Epoch(4), &fixture);
508            let message_service = MessageServiceBuilder::new()
509                .with_epoch_service(epoch_service)
510                .build()
511                .await;
512
513            let message = message_service
514                .get_epoch_settings_message(BTreeSet::new())
515                .await
516                .unwrap();
517
518            assert_eq!(message.cardano_transactions_signing_config, None);
519            assert_eq!(message.next_cardano_transactions_signing_config, None);
520        }
521
522        #[tokio::test]
523        async fn get_epoch_settings_message_retrieves_protocol_parameters_from_epoch_service() {
524            let current_epoch_settings = AggregatorEpochSettings {
525                protocol_parameters: ProtocolParameters::new(101, 10, 0.5),
526                ..AggregatorEpochSettings::dummy()
527            };
528            let next_epoch_settings = AggregatorEpochSettings {
529                protocol_parameters: ProtocolParameters::new(102, 20, 0.5),
530                ..AggregatorEpochSettings::dummy()
531            };
532            let signer_registration_epoch_settings = AggregatorEpochSettings {
533                protocol_parameters: ProtocolParameters::new(103, 30, 0.5),
534                ..AggregatorEpochSettings::dummy()
535            };
536            let epoch_service = FakeEpochServiceBuilder {
537                current_epoch_settings,
538                next_epoch_settings: next_epoch_settings.clone(),
539                signer_registration_epoch_settings: signer_registration_epoch_settings.clone(),
540                current_signers_with_stake: fake_data::signers_with_stakes(5),
541                next_signers_with_stake: fake_data::signers_with_stakes(3),
542                ..FakeEpochServiceBuilder::dummy(Epoch(1))
543            }
544            .build();
545            let message_service = MessageServiceBuilder::new()
546                .with_epoch_service(epoch_service)
547                .build()
548                .await;
549
550            let message = message_service
551                .get_epoch_settings_message(SignedEntityTypeDiscriminants::all())
552                .await
553                .unwrap();
554
555            assert_eq!(
556                message.signer_registration_protocol_parameters,
557                signer_registration_epoch_settings.protocol_parameters
558            );
559        }
560
561        #[tokio::test]
562        async fn get_epoch_settings_message_retrieves_signing_configuration_from_epoch_service() {
563            let current_epoch_settings = AggregatorEpochSettings {
564                cardano_transactions_signing_config: CardanoTransactionsSigningConfig {
565                    security_parameter: BlockNumber(100),
566                    step: BlockNumber(15),
567                },
568                ..AggregatorEpochSettings::dummy()
569            };
570            let next_epoch_settings = AggregatorEpochSettings {
571                cardano_transactions_signing_config: CardanoTransactionsSigningConfig {
572                    security_parameter: BlockNumber(200),
573                    step: BlockNumber(15),
574                },
575                ..AggregatorEpochSettings::dummy()
576            };
577            let epoch_service = FakeEpochServiceBuilder {
578                current_epoch_settings: current_epoch_settings.clone(),
579                next_epoch_settings: next_epoch_settings.clone(),
580                signer_registration_epoch_settings: AggregatorEpochSettings::dummy(),
581                current_signers_with_stake: fake_data::signers_with_stakes(5),
582                next_signers_with_stake: fake_data::signers_with_stakes(3),
583                ..FakeEpochServiceBuilder::dummy(Epoch(1))
584            }
585            .build();
586            let message_service = MessageServiceBuilder::new()
587                .with_epoch_service(epoch_service)
588                .build()
589                .await;
590
591            let message = message_service
592                .get_epoch_settings_message(SignedEntityTypeDiscriminants::all())
593                .await
594                .unwrap();
595
596            assert_eq!(
597                message.cardano_transactions_signing_config,
598                Some(current_epoch_settings.cardano_transactions_signing_config),
599            );
600            assert_eq!(
601                message.next_cardano_transactions_signing_config,
602                Some(next_epoch_settings.cardano_transactions_signing_config),
603            );
604        }
605    }
606
607    mod certificate {
608        use super::*;
609
610        #[tokio::test]
611        async fn get_no_certificate() {
612            let service = MessageServiceBuilder::new().build().await;
613
614            let certificate_hash = "whatever";
615            let certificate_message =
616                service.get_certificate_message(certificate_hash).await.unwrap();
617            assert!(certificate_message.is_none());
618        }
619
620        #[tokio::test]
621        async fn get_certificate() {
622            let genesis_certificate = fake_data::genesis_certificate("genesis_hash");
623            let service = MessageServiceBuilder::new()
624                .with_certificates(&[genesis_certificate.clone()])
625                .build()
626                .await;
627
628            let certificate_message = service
629                .get_certificate_message(&genesis_certificate.hash)
630                .await
631                .unwrap()
632                .expect("There should be a certificate.");
633            assert_eq!(genesis_certificate.hash, certificate_message.hash);
634        }
635
636        #[tokio::test]
637        async fn get_no_latest_genesis_certificate() {
638            let service = MessageServiceBuilder::new().build().await;
639
640            let certificate_message =
641                service.get_latest_genesis_certificate_message().await.unwrap();
642            assert_eq!(None, certificate_message);
643        }
644
645        #[tokio::test]
646        async fn get_latest_genesis_certificate() {
647            let certificates = [
648                fake_data::genesis_certificate("certificate_1"),
649                fake_data::genesis_certificate("certificate_2"),
650                fake_data::certificate("certificate_3"),
651            ];
652            let last_genesis_hash = certificates[1].hash.clone();
653            let service = MessageServiceBuilder::new()
654                .with_certificates(&certificates)
655                .build()
656                .await;
657
658            let certificate_message = service
659                .get_latest_genesis_certificate_message()
660                .await
661                .unwrap()
662                .expect("There should be a genesis certificate.");
663            assert_eq!(last_genesis_hash, certificate_message.hash);
664        }
665
666        #[tokio::test]
667        async fn get_last_certificates() {
668            let certificates = [
669                fake_data::genesis_certificate("certificate_1"),
670                fake_data::genesis_certificate("certificate_2"),
671            ];
672            let last_certificate_hash = certificates[1].hash.clone();
673            let service = MessageServiceBuilder::new()
674                .with_certificates(&certificates)
675                .build()
676                .await;
677
678            let certificate_messages = service.get_certificate_list_message(5).await.unwrap();
679
680            assert_eq!(2, certificate_messages.len());
681            assert_eq!(last_certificate_hash, certificate_messages[0].hash);
682        }
683    }
684
685    mod snapshot {
686        use super::*;
687
688        #[tokio::test]
689        async fn get_snapshot_not_exist() {
690            let service = MessageServiceBuilder::new().build().await;
691            let snapshot = service.get_snapshot_message("whatever").await.unwrap();
692
693            assert!(snapshot.is_none());
694        }
695
696        #[tokio::test]
697        async fn get_snapshot() {
698            let record = SignedEntityRecord {
699                signed_entity_id: "signed_entity_id".to_string(),
700                signed_entity_type: SignedEntityType::CardanoImmutableFilesFull(fake_data::beacon()),
701                certificate_id: "cert_id".to_string(),
702                artifact: serde_json::to_string(&fake_data::snapshots(1)[0]).unwrap(),
703                created_at: Default::default(),
704            };
705            let message: SnapshotMessage = record.clone().try_into().unwrap();
706
707            let service = MessageServiceBuilder::new()
708                .with_signed_entity_records(&[record.clone()])
709                .build()
710                .await;
711
712            let response = service
713                .get_snapshot_message(&record.signed_entity_id)
714                .await
715                .unwrap()
716                .expect("A SnapshotMessage was expected.");
717
718            assert_eq!(message, response);
719        }
720
721        #[tokio::test]
722        async fn get_snapshot_list_message() {
723            let records = vec![
724                SignedEntityRecord {
725                    signed_entity_id: "signed_entity_id-1".to_string(),
726                    signed_entity_type: SignedEntityType::CardanoImmutableFilesFull(
727                        fake_data::beacon(),
728                    ),
729                    certificate_id: "cert_id-1".to_string(),
730                    artifact: serde_json::to_string(&fake_data::snapshots(1)[0]).unwrap(),
731                    created_at: Default::default(),
732                },
733                SignedEntityRecord {
734                    signed_entity_id: "signed_entity_id-2".to_string(),
735                    signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
736                    certificate_id: "cert_id-2".to_string(),
737                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshots(1)[0])
738                        .unwrap(),
739                    created_at: Default::default(),
740                },
741            ];
742            let message: SnapshotListMessage = vec![records[0].clone().try_into().unwrap()];
743
744            let service = MessageServiceBuilder::new()
745                .with_signed_entity_records(&records)
746                .build()
747                .await;
748
749            let response = service.get_snapshot_list_message(0).await.unwrap();
750            assert!(response.is_empty());
751
752            let response = service.get_snapshot_list_message(3).await.unwrap();
753            assert_eq!(message, response);
754        }
755    }
756
757    mod cardano_database {
758        use super::*;
759
760        #[tokio::test]
761        async fn get_cardano_database_when_record_does_not_exist() {
762            let service = MessageServiceBuilder::new().build().await;
763            let snapshot = service.get_cardano_database_message("whatever").await.unwrap();
764
765            assert!(snapshot.is_none());
766        }
767
768        #[tokio::test]
769        async fn get_cardano_database() {
770            let record = SignedEntityRecord {
771                signed_entity_id: "signed_entity_id".to_string(),
772                signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
773                certificate_id: "cert_id".to_string(),
774                artifact: serde_json::to_string(&fake_data::cardano_database_snapshots(1)[0])
775                    .unwrap(),
776                created_at: Default::default(),
777            };
778            let message: CardanoDatabaseSnapshotMessage = record.clone().try_into().unwrap();
779
780            let service = MessageServiceBuilder::new()
781                .with_signed_entity_records(&[record.clone()])
782                .build()
783                .await;
784
785            let response = service
786                .get_cardano_database_message(&record.signed_entity_id)
787                .await
788                .unwrap()
789                .expect("A CardanoDatabaseSnapshotMessage was expected.");
790
791            assert_eq!(message, response);
792        }
793
794        #[tokio::test]
795        async fn get_cardano_database_list_message() {
796            let records = vec![
797                SignedEntityRecord {
798                    signed_entity_id: "signed_entity_id-1".to_string(),
799                    signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
800                    certificate_id: "cert_id-1".to_string(),
801                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshots(1)[0])
802                        .unwrap(),
803                    created_at: Default::default(),
804                },
805                SignedEntityRecord {
806                    signed_entity_id: "signed_entity_id-2".to_string(),
807                    signed_entity_type: SignedEntityType::CardanoImmutableFilesFull(
808                        fake_data::beacon(),
809                    ),
810                    certificate_id: "cert_id-2".to_string(),
811                    artifact: serde_json::to_string(&fake_data::snapshots(1)[0]).unwrap(),
812                    created_at: Default::default(),
813                },
814            ];
815            let message: CardanoDatabaseSnapshotListMessage =
816                vec![records[0].clone().try_into().unwrap()];
817
818            let service = MessageServiceBuilder::new()
819                .with_signed_entity_records(&records)
820                .build()
821                .await;
822
823            let response = service.get_cardano_database_list_message(0).await.unwrap();
824            assert!(response.is_empty());
825
826            let response = service.get_cardano_database_list_message(3).await.unwrap();
827            assert_eq!(message, response);
828        }
829
830        #[tokio::test]
831        async fn get_cardano_database_digest_list_message() {
832            let messages: CardanoDatabaseDigestListMessage = vec![
833                CardanoDatabaseDigestListItemMessage {
834                    immutable_file_name: "06685.chunk".to_string(),
835                    digest: "0af556ab2620dd9363bf76963a231abe8948a500ea6be31b131d87907ab09b1e"
836                        .to_string(),
837                },
838                CardanoDatabaseDigestListItemMessage {
839                    immutable_file_name: "06685.primary".to_string(),
840                    digest: "32dfd6b722d87f253e78eb8b478fb94f1e13463826e674d6ec7b6bf0892b2e39"
841                        .to_string(),
842                },
843            ];
844
845            let service = MessageServiceBuilder::new()
846                .with_immutable_file_digest_messages(&messages)
847                .build()
848                .await;
849
850            let response = service.get_cardano_database_digest_list_message().await.unwrap();
851
852            assert_eq!(messages, response);
853        }
854    }
855
856    mod mithril_stake_distribution {
857        use super::*;
858
859        #[tokio::test]
860        async fn get_mithril_stake_distribution() {
861            let record = SignedEntityRecord {
862                signed_entity_id: "signed_entity_id".to_string(),
863                signed_entity_type: SignedEntityType::MithrilStakeDistribution(Epoch(18)),
864                certificate_id: "cert_id".to_string(),
865                artifact: serde_json::to_string(&fake_data::mithril_stake_distributions(1)[0])
866                    .unwrap(),
867                created_at: Default::default(),
868            };
869            let message: MithrilStakeDistributionMessage = record.clone().try_into().unwrap();
870
871            let service = MessageServiceBuilder::new()
872                .with_signed_entity_records(&[record.clone()])
873                .build()
874                .await;
875
876            let response = service
877                .get_mithril_stake_distribution_message(&record.signed_entity_id)
878                .await
879                .unwrap()
880                .expect("A MithrilStakeDistributionMessage was expected.");
881
882            assert_eq!(message, response);
883        }
884
885        #[tokio::test]
886        async fn get_mithril_stake_distribution_not_exist() {
887            let service = MessageServiceBuilder::new().build().await;
888
889            let response = service
890                .get_mithril_stake_distribution_message("whatever")
891                .await
892                .unwrap();
893
894            assert!(response.is_none());
895        }
896
897        #[tokio::test]
898        async fn get_mithril_stake_distribution_list_message() {
899            let records = vec![
900                SignedEntityRecord {
901                    signed_entity_id: "signed_entity_id-1".to_string(),
902                    signed_entity_type: SignedEntityType::MithrilStakeDistribution(Epoch(18)),
903                    certificate_id: "cert_id-1".to_string(),
904                    artifact: serde_json::to_string(&fake_data::mithril_stake_distributions(1)[0])
905                        .unwrap(),
906                    created_at: Default::default(),
907                },
908                SignedEntityRecord {
909                    signed_entity_id: "signed_entity_id-2".to_string(),
910                    signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
911                    certificate_id: "cert_id-2".to_string(),
912                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshots(1)[0])
913                        .unwrap(),
914                    created_at: Default::default(),
915                },
916            ];
917            let message: MithrilStakeDistributionListMessage =
918                vec![records[0].clone().try_into().unwrap()];
919
920            let service = MessageServiceBuilder::new()
921                .with_signed_entity_records(&records)
922                .build()
923                .await;
924
925            let response = service.get_mithril_stake_distribution_list_message(0).await.unwrap();
926            assert!(response.is_empty());
927
928            let response = service.get_mithril_stake_distribution_list_message(3).await.unwrap();
929            assert_eq!(message, response);
930        }
931    }
932
933    mod cardano_transaction {
934        use super::*;
935
936        #[tokio::test]
937        async fn get_cardano_transaction() {
938            let record = SignedEntityRecord {
939                signed_entity_id: "signed_entity_id".to_string(),
940                signed_entity_type: SignedEntityType::CardanoTransactions(
941                    Epoch(18),
942                    BlockNumber(120),
943                ),
944                certificate_id: "cert_id".to_string(),
945                artifact: serde_json::to_string(&fake_data::cardano_transactions_snapshot(1)[0])
946                    .unwrap(),
947                created_at: Default::default(),
948            };
949            let message: CardanoTransactionSnapshotMessage = record.clone().try_into().unwrap();
950
951            let service = MessageServiceBuilder::new()
952                .with_signed_entity_records(&[record.clone()])
953                .build()
954                .await;
955
956            let response = service
957                .get_cardano_transaction_message(&record.signed_entity_id)
958                .await
959                .unwrap()
960                .expect("A CardanoTransactionMessage was expected.");
961
962            assert_eq!(message, response);
963        }
964
965        #[tokio::test]
966        async fn get_cardano_transaction_not_exist() {
967            let service = MessageServiceBuilder::new().build().await;
968
969            let response = service.get_cardano_transaction_message("whatever").await.unwrap();
970
971            assert!(response.is_none());
972        }
973
974        #[tokio::test]
975        async fn get_cardano_transaction_list_message() {
976            let records = vec![
977                SignedEntityRecord {
978                    signed_entity_id: "signed_entity_id-1".to_string(),
979                    signed_entity_type: SignedEntityType::CardanoTransactions(
980                        Epoch(18),
981                        BlockNumber(120),
982                    ),
983                    certificate_id: "cert_id-1".to_string(),
984                    artifact: serde_json::to_string(
985                        &fake_data::cardano_transactions_snapshot(1)[0],
986                    )
987                    .unwrap(),
988                    created_at: Default::default(),
989                },
990                SignedEntityRecord {
991                    signed_entity_id: "signed_entity_id-2".to_string(),
992                    signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
993                    certificate_id: "cert_id-2".to_string(),
994                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshots(1)[0])
995                        .unwrap(),
996                    created_at: Default::default(),
997                },
998            ];
999            let message: CardanoTransactionSnapshotListMessage =
1000                vec![records[0].clone().try_into().unwrap()];
1001
1002            let service = MessageServiceBuilder::new()
1003                .with_signed_entity_records(&records)
1004                .build()
1005                .await;
1006
1007            let response = service.get_cardano_transaction_list_message(0).await.unwrap();
1008            assert!(response.is_empty());
1009
1010            let response = service.get_cardano_transaction_list_message(3).await.unwrap();
1011            assert_eq!(message, response);
1012        }
1013    }
1014
1015    mod cardano_stake_distribution {
1016        use super::*;
1017
1018        #[tokio::test]
1019        async fn get_cardano_stake_distribution() {
1020            let record = SignedEntityRecord {
1021                signed_entity_id: "signed_entity_id".to_string(),
1022                signed_entity_type: SignedEntityType::CardanoStakeDistribution(Epoch(18)),
1023                certificate_id: "cert_id".to_string(),
1024                artifact: serde_json::to_string(&fake_data::cardano_stake_distributions(1)[0])
1025                    .unwrap(),
1026                created_at: Default::default(),
1027            };
1028            let message: CardanoStakeDistributionMessage = record.clone().try_into().unwrap();
1029
1030            let service = MessageServiceBuilder::new()
1031                .with_signed_entity_records(&[record.clone()])
1032                .build()
1033                .await;
1034
1035            let response = service
1036                .get_cardano_stake_distribution_message(&record.signed_entity_id)
1037                .await
1038                .unwrap()
1039                .expect("A CardanoStakeDistributionMessage was expected.");
1040
1041            assert_eq!(message, response);
1042        }
1043
1044        #[tokio::test]
1045        async fn get_cardano_stake_distribution_not_exist() {
1046            let service = MessageServiceBuilder::new().build().await;
1047
1048            let response = service
1049                .get_cardano_stake_distribution_message("whatever")
1050                .await
1051                .unwrap();
1052
1053            assert!(response.is_none());
1054        }
1055
1056        #[tokio::test]
1057        async fn get_cardano_stake_distribution_by_epoch() {
1058            let record = SignedEntityRecord {
1059                signed_entity_id: "signed_entity_id".to_string(),
1060                signed_entity_type: SignedEntityType::CardanoStakeDistribution(Epoch(18)),
1061                certificate_id: "cert_id".to_string(),
1062                artifact: serde_json::to_string(&fake_data::cardano_stake_distributions(1)[0])
1063                    .unwrap(),
1064                created_at: Default::default(),
1065            };
1066            let message: CardanoStakeDistributionMessage = record.clone().try_into().unwrap();
1067
1068            let service = MessageServiceBuilder::new()
1069                .with_signed_entity_records(&[record.clone()])
1070                .build()
1071                .await;
1072
1073            let response = service
1074                .get_cardano_stake_distribution_message_by_epoch(
1075                    record.signed_entity_type.get_epoch(),
1076                )
1077                .await
1078                .unwrap()
1079                .expect("A CardanoStakeDistributionMessage was expected.");
1080
1081            assert_eq!(message, response);
1082        }
1083
1084        #[tokio::test]
1085        async fn get_cardano_stake_distribution_by_epoch_not_exist() {
1086            let service = MessageServiceBuilder::new().build().await;
1087
1088            let response = service
1089                .get_cardano_stake_distribution_message_by_epoch(Epoch(999))
1090                .await
1091                .unwrap();
1092
1093            assert!(response.is_none());
1094        }
1095
1096        #[tokio::test]
1097        async fn get_cardano_stake_distribution_list_message() {
1098            let records = vec![
1099                SignedEntityRecord {
1100                    signed_entity_id: "signed_entity_id-1".to_string(),
1101                    signed_entity_type: SignedEntityType::CardanoStakeDistribution(Epoch(18)),
1102                    certificate_id: "cert_id-1".to_string(),
1103                    artifact: serde_json::to_string(&fake_data::cardano_stake_distributions(1)[0])
1104                        .unwrap(),
1105                    created_at: Default::default(),
1106                },
1107                SignedEntityRecord {
1108                    signed_entity_id: "signed_entity_id-2".to_string(),
1109                    signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
1110                    certificate_id: "cert_id-2".to_string(),
1111                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshots(1)[0])
1112                        .unwrap(),
1113                    created_at: Default::default(),
1114                },
1115            ];
1116            let message: CardanoStakeDistributionListMessage =
1117                vec![records[0].clone().try_into().unwrap()];
1118
1119            let service = MessageServiceBuilder::new()
1120                .with_signed_entity_records(&records)
1121                .build()
1122                .await;
1123
1124            let response = service.get_cardano_stake_distribution_list_message(0).await.unwrap();
1125            assert!(response.is_empty());
1126
1127            let response = service.get_cardano_stake_distribution_list_message(3).await.unwrap();
1128            assert_eq!(message, response);
1129        }
1130    }
1131}