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