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,
18        ProtocolConfigurationMessage, SignerMessagePart, SnapshotListMessage, SnapshotMessage,
19    },
20};
21
22use crate::{
23    EpochSettingsStorer, 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 protocol configuration message for the given epoch if it exists.
39    async fn get_protocol_configuration_message(
40        &self,
41        epoch: Epoch,
42        allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
43    ) -> StdResult<Option<ProtocolConfigurationMessage>>;
44
45    /// Return the message representation of a certificate if it exists.
46    async fn get_certificate_message(
47        &self,
48        certificate_hash: &str,
49    ) -> StdResult<Option<CertificateMessage>>;
50
51    /// Return the message representation of the latest genesis certificate.
52    async fn get_latest_genesis_certificate_message(&self)
53    -> StdResult<Option<CertificateMessage>>;
54
55    /// Return the message representation of the last N certificates.
56    async fn get_certificate_list_message(&self, limit: usize)
57    -> StdResult<CertificateListMessage>;
58
59    /// Return the information regarding the given snapshot.
60    async fn get_snapshot_message(
61        &self,
62        signed_entity_id: &str,
63    ) -> StdResult<Option<SnapshotMessage>>;
64
65    /// Return the list of the last signed snapshots. The limit of the list is
66    /// passed as argument.
67    async fn get_snapshot_list_message(&self, limit: usize) -> StdResult<SnapshotListMessage>;
68
69    /// Return the information regarding the Cardano database for the given identifier.
70    async fn get_cardano_database_message(
71        &self,
72        signed_entity_id: &str,
73    ) -> StdResult<Option<CardanoDatabaseSnapshotMessage>>;
74
75    /// Return the list of the last Cardano database message.
76    async fn get_cardano_database_list_message(
77        &self,
78        limit: usize,
79    ) -> StdResult<CardanoDatabaseSnapshotListMessage>;
80
81    /// Return the list of the last Cardano database message.
82    async fn get_cardano_database_list_message_by_epoch(
83        &self,
84        limit: usize,
85        epoch: Epoch,
86    ) -> StdResult<CardanoDatabaseSnapshotListMessage>;
87
88    /// Return the list of the Cardano database immutable file names and their digests.
89    async fn get_cardano_database_digest_list_message(
90        &self,
91    ) -> StdResult<CardanoDatabaseDigestListMessage>;
92
93    /// Return the information regarding the Mithril stake distribution for the given identifier.
94    async fn get_mithril_stake_distribution_message(
95        &self,
96        signed_entity_id: &str,
97    ) -> StdResult<Option<MithrilStakeDistributionMessage>>;
98
99    /// Return the list of the last Mithril stake distributions message.
100    async fn get_mithril_stake_distribution_list_message(
101        &self,
102        limit: usize,
103    ) -> StdResult<MithrilStakeDistributionListMessage>;
104
105    /// Return the information regarding the Cardano transactions set for the given identifier.
106    async fn get_cardano_transaction_message(
107        &self,
108        signed_entity_id: &str,
109    ) -> StdResult<Option<CardanoTransactionSnapshotMessage>>;
110
111    /// Return the list of the last Cardano transactions set message.
112    async fn get_cardano_transaction_list_message(
113        &self,
114        limit: usize,
115    ) -> StdResult<CardanoTransactionSnapshotListMessage>;
116
117    /// Return the information regarding the Cardano stake distribution for the given identifier.
118    async fn get_cardano_stake_distribution_message(
119        &self,
120        signed_entity_id: &str,
121    ) -> StdResult<Option<CardanoStakeDistributionMessage>>;
122
123    /// Return the information regarding the Cardano stake distribution for the given epoch.
124    async fn get_cardano_stake_distribution_message_by_epoch(
125        &self,
126        epoch: Epoch,
127    ) -> StdResult<Option<CardanoStakeDistributionMessage>>;
128
129    /// Return the list of the last Cardano stake distributions message.
130    async fn get_cardano_stake_distribution_list_message(
131        &self,
132        limit: usize,
133    ) -> StdResult<CardanoStakeDistributionListMessage>;
134}
135
136/// Implementation of the [MessageService]
137pub struct MithrilMessageService {
138    certificate_repository: Arc<CertificateRepository>,
139    signed_entity_storer: Arc<dyn SignedEntityStorer>,
140    epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
141    immutable_file_digest_mapper: Arc<dyn ImmutableFileDigestMapper>,
142    epoch_service: EpochServiceWrapper,
143}
144
145impl MithrilMessageService {
146    /// Constructor
147    pub fn new(
148        certificate_repository: Arc<CertificateRepository>,
149        signed_entity_storer: Arc<dyn SignedEntityStorer>,
150        epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
151        immutable_file_digest_mapper: Arc<dyn ImmutableFileDigestMapper>,
152        epoch_service: EpochServiceWrapper,
153    ) -> Self {
154        Self {
155            certificate_repository,
156            signed_entity_storer,
157            epoch_settings_storer,
158            immutable_file_digest_mapper,
159            epoch_service,
160        }
161    }
162}
163
164#[async_trait]
165impl MessageService for MithrilMessageService {
166    async fn get_epoch_settings_message(
167        &self,
168        allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
169    ) -> StdResult<EpochSettingsMessage> {
170        let epoch_service = self.epoch_service.read().await;
171
172        let epoch = epoch_service.epoch_of_current_data()?;
173        let signer_registration_protocol_parameters =
174            epoch_service.signer_registration_protocol_parameters()?.clone();
175        let current_signers = epoch_service.current_signers()?;
176        let next_signers = epoch_service.next_signers()?;
177
178        let cardano_transactions_discriminant =
179            allowed_discriminants.get(&SignedEntityTypeDiscriminants::CardanoTransactions);
180
181        let signed_entity_config = cardano_transactions_discriminant
182            .map(|_| epoch_service.signed_entity_config())
183            .transpose()?
184            .cloned();
185
186        #[allow(deprecated)]
187        let epoch_settings_message = EpochSettingsMessage {
188            epoch,
189            signer_registration_protocol_parameters: Some(signer_registration_protocol_parameters),
190            current_signers: SignerMessagePart::from_signers(current_signers.to_vec()),
191            next_signers: SignerMessagePart::from_signers(next_signers.to_vec()),
192            cardano_transactions_signing_config: signed_entity_config
193                .and_then(|c| c.cardano_transactions_signing_config),
194        };
195
196        Ok(epoch_settings_message)
197    }
198
199    async fn get_protocol_configuration_message(
200        &self,
201        epoch: Epoch,
202        enabled_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
203    ) -> StdResult<Option<ProtocolConfigurationMessage>> {
204        let epoch_settings = match self.epoch_settings_storer.get_epoch_settings(epoch).await? {
205            Some(settings) => settings,
206            None => return Ok(None),
207        };
208
209        let cardano_transactions_discriminant =
210            enabled_discriminants.get(&SignedEntityTypeDiscriminants::CardanoTransactions);
211
212        let cardano_transactions_signing_config = cardano_transactions_discriminant
213            .and(epoch_settings.cardano_transactions_signing_config);
214
215        let protocol_configuration_message = ProtocolConfigurationMessage {
216            protocol_parameters: epoch_settings.protocol_parameters,
217            cardano_transactions_signing_config,
218            available_signed_entity_types: enabled_discriminants,
219        };
220        Ok(Some(protocol_configuration_message))
221    }
222
223    async fn get_certificate_message(
224        &self,
225        certificate_hash: &str,
226    ) -> StdResult<Option<CertificateMessage>> {
227        self.certificate_repository.get_certificate(certificate_hash).await
228    }
229
230    async fn get_latest_genesis_certificate_message(
231        &self,
232    ) -> StdResult<Option<CertificateMessage>> {
233        self.certificate_repository.get_latest_genesis_certificate().await
234    }
235
236    async fn get_certificate_list_message(
237        &self,
238        limit: usize,
239    ) -> StdResult<CertificateListMessage> {
240        self.certificate_repository.get_latest_certificates(limit).await
241    }
242
243    async fn get_snapshot_message(
244        &self,
245        signed_entity_id: &str,
246    ) -> StdResult<Option<SnapshotMessage>> {
247        let signed_entity = self.signed_entity_storer.get_signed_entity(signed_entity_id).await?;
248
249        signed_entity.map(|s| s.try_into()).transpose()
250    }
251
252    async fn get_snapshot_list_message(&self, limit: usize) -> StdResult<SnapshotListMessage> {
253        let signed_entity_type_id = SignedEntityTypeDiscriminants::CardanoImmutableFilesFull;
254        let entities = self
255            .signed_entity_storer
256            .get_last_signed_entities_by_type(&signed_entity_type_id, limit)
257            .await?;
258
259        entities.into_iter().map(|i| i.try_into()).collect()
260    }
261
262    async fn get_cardano_database_message(
263        &self,
264        signed_entity_id: &str,
265    ) -> StdResult<Option<CardanoDatabaseSnapshotMessage>> {
266        let signed_entity = self.signed_entity_storer.get_signed_entity(signed_entity_id).await?;
267
268        signed_entity.map(|v| v.try_into()).transpose()
269    }
270
271    async fn get_cardano_database_list_message(
272        &self,
273        limit: usize,
274    ) -> StdResult<CardanoDatabaseSnapshotListMessage> {
275        let signed_entity_type_id = SignedEntityTypeDiscriminants::CardanoDatabase;
276        let entities = self
277            .signed_entity_storer
278            .get_last_signed_entities_by_type(&signed_entity_type_id, limit)
279            .await?;
280
281        entities.into_iter().map(|i| i.try_into()).collect()
282    }
283
284    async fn get_cardano_database_list_message_by_epoch(
285        &self,
286        limit: usize,
287        epoch: Epoch,
288    ) -> StdResult<CardanoDatabaseSnapshotListMessage> {
289        let signed_entity_type_id = SignedEntityTypeDiscriminants::CardanoDatabase;
290        let entities = self
291            .signed_entity_storer
292            .get_last_signed_entities_by_type_and_epoch(&signed_entity_type_id, epoch, limit)
293            .await?;
294
295        entities.into_iter().map(|i| i.try_into()).collect()
296    }
297
298    async fn get_cardano_database_digest_list_message(
299        &self,
300    ) -> StdResult<CardanoDatabaseDigestListMessage> {
301        Ok(self
302            .immutable_file_digest_mapper
303            .get_immutable_file_digest_map()
304            .await?
305            .into_iter()
306            .map(
307                |(immutable_file_name, digest)| CardanoDatabaseDigestListItemMessage {
308                    immutable_file_name,
309                    digest,
310                },
311            )
312            .collect::<Vec<_>>())
313    }
314
315    async fn get_mithril_stake_distribution_message(
316        &self,
317        signed_entity_id: &str,
318    ) -> StdResult<Option<MithrilStakeDistributionMessage>> {
319        let signed_entity = self.signed_entity_storer.get_signed_entity(signed_entity_id).await?;
320
321        signed_entity.map(|v| v.try_into()).transpose()
322    }
323
324    async fn get_mithril_stake_distribution_list_message(
325        &self,
326        limit: usize,
327    ) -> StdResult<MithrilStakeDistributionListMessage> {
328        let signed_entity_type_id = SignedEntityTypeDiscriminants::MithrilStakeDistribution;
329        let entities = self
330            .signed_entity_storer
331            .get_last_signed_entities_by_type(&signed_entity_type_id, limit)
332            .await?;
333
334        entities.into_iter().map(|i| i.try_into()).collect()
335    }
336
337    async fn get_cardano_transaction_message(
338        &self,
339        signed_entity_id: &str,
340    ) -> StdResult<Option<CardanoTransactionSnapshotMessage>> {
341        let signed_entity = self.signed_entity_storer.get_signed_entity(signed_entity_id).await?;
342
343        signed_entity.map(|v| v.try_into()).transpose()
344    }
345
346    async fn get_cardano_transaction_list_message(
347        &self,
348        limit: usize,
349    ) -> StdResult<CardanoTransactionSnapshotListMessage> {
350        let signed_entity_type_id = SignedEntityTypeDiscriminants::CardanoTransactions;
351        let entities = self
352            .signed_entity_storer
353            .get_last_signed_entities_by_type(&signed_entity_type_id, limit)
354            .await?;
355
356        entities.into_iter().map(|i| i.try_into()).collect()
357    }
358
359    async fn get_cardano_stake_distribution_message(
360        &self,
361        signed_entity_id: &str,
362    ) -> StdResult<Option<CardanoStakeDistributionMessage>> {
363        let signed_entity = self.signed_entity_storer.get_signed_entity(signed_entity_id).await?;
364
365        signed_entity.map(|v| v.try_into()).transpose()
366    }
367
368    async fn get_cardano_stake_distribution_message_by_epoch(
369        &self,
370        epoch: Epoch,
371    ) -> StdResult<Option<CardanoStakeDistributionMessage>> {
372        let signed_entity = self
373            .signed_entity_storer
374            .get_cardano_stake_distribution_signed_entity_by_epoch(epoch)
375            .await?;
376
377        signed_entity.map(|v| v.try_into()).transpose()
378    }
379
380    async fn get_cardano_stake_distribution_list_message(
381        &self,
382        limit: usize,
383    ) -> StdResult<CardanoStakeDistributionListMessage> {
384        let signed_entity_type_id = SignedEntityTypeDiscriminants::CardanoStakeDistribution;
385        let entities = self
386            .signed_entity_storer
387            .get_last_signed_entities_by_type(&signed_entity_type_id, limit)
388            .await?;
389
390        entities.into_iter().map(|i| i.try_into()).collect()
391    }
392}
393
394#[cfg(test)]
395mod tests {
396    use std::collections::BTreeMap;
397
398    use mithril_common::entities::{BlockNumber, CardanoDbBeacon, Certificate, SignedEntityType};
399    use mithril_common::test::double::{Dummy, fake_data};
400    use tokio::sync::RwLock;
401
402    use crate::database::record::SignedEntityRecord;
403    use crate::database::repository::{
404        EpochSettingsStore, ImmutableFileDigestRepository, SignedEntityStore,
405    };
406    use crate::database::test_helper::main_db_connection;
407    use crate::entities::AggregatorEpochSettings;
408    use crate::services::FakeEpochService;
409
410    use super::*;
411
412    struct MessageServiceBuilder {
413        certificates: Vec<Certificate>,
414        signed_entity_records: Vec<SignedEntityRecord>,
415        epoch_settings_map: BTreeMap<Epoch, AggregatorEpochSettings>,
416        immutable_file_digest_messages: Vec<CardanoDatabaseDigestListItemMessage>,
417        epoch_service: Option<FakeEpochService>,
418    }
419
420    impl MessageServiceBuilder {
421        fn new() -> Self {
422            Self {
423                certificates: Vec::new(),
424                signed_entity_records: Vec::new(),
425                epoch_settings_map: BTreeMap::new(),
426                immutable_file_digest_messages: Vec::new(),
427                epoch_service: None,
428            }
429        }
430
431        fn with_certificates(mut self, certificates: &[Certificate]) -> Self {
432            self.certificates.extend_from_slice(certificates);
433
434            self
435        }
436
437        fn with_signed_entity_records(
438            mut self,
439            signed_entity_record: &[SignedEntityRecord],
440        ) -> Self {
441            self.signed_entity_records.extend_from_slice(signed_entity_record);
442
443            self
444        }
445
446        fn with_epoch_settings(
447            mut self,
448            epoch_settings_map: BTreeMap<Epoch, AggregatorEpochSettings>,
449        ) -> Self {
450            self.epoch_settings_map = epoch_settings_map;
451
452            self
453        }
454
455        fn with_immutable_file_digest_messages(
456            mut self,
457            digests: &[CardanoDatabaseDigestListItemMessage],
458        ) -> Self {
459            self.immutable_file_digest_messages.extend_from_slice(digests);
460
461            self
462        }
463
464        fn with_epoch_service(mut self, epoch_service: FakeEpochService) -> Self {
465            self.epoch_service = Some(epoch_service);
466
467            self
468        }
469
470        async fn build(self) -> MithrilMessageService {
471            let connection = Arc::new(main_db_connection().unwrap());
472            let certificate_repository = CertificateRepository::new(connection.clone());
473            let signed_entity_store = SignedEntityStore::new(connection.clone());
474            let epoch_settings_store = EpochSettingsStore::new(connection.clone(), None);
475            let immutable_file_digest_mapper =
476                ImmutableFileDigestRepository::new(connection.clone());
477            let epoch_service = self.epoch_service.unwrap_or(FakeEpochService::without_data());
478
479            certificate_repository
480                .create_many_certificates(self.certificates)
481                .await
482                .unwrap();
483            for record in self.signed_entity_records {
484                signed_entity_store.store_signed_entity(&record).await.unwrap();
485            }
486
487            for (epoch, epoch_settings) in self.epoch_settings_map {
488                epoch_settings_store
489                    .save_epoch_settings(epoch, epoch_settings)
490                    .await
491                    .unwrap();
492            }
493
494            for digest_message in self.immutable_file_digest_messages {
495                immutable_file_digest_mapper
496                    .upsert_immutable_file_digest(
497                        &digest_message.immutable_file_name,
498                        &digest_message.digest,
499                    )
500                    .await
501                    .unwrap();
502            }
503
504            MithrilMessageService::new(
505                Arc::new(certificate_repository),
506                Arc::new(signed_entity_store),
507                Arc::new(epoch_settings_store),
508                Arc::new(immutable_file_digest_mapper),
509                Arc::new(RwLock::new(epoch_service)),
510            )
511        }
512    }
513
514    #[allow(deprecated)]
515    mod epoch_settings {
516        use mithril_common::{
517            entities::{CardanoTransactionsSigningConfig, ProtocolParameters, SignedEntityConfig},
518            test::builder::MithrilFixtureBuilder,
519        };
520
521        use crate::{entities::AggregatorEpochSettings, services::FakeEpochServiceBuilder};
522
523        use super::*;
524
525        #[tokio::test]
526        async fn get_epoch_settings_message() {
527            let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
528            let epoch_service = FakeEpochService::from_fixture(Epoch(4), &fixture);
529            let message_service = MessageServiceBuilder::new()
530                .with_epoch_service(epoch_service)
531                .build()
532                .await;
533
534            let message = message_service
535                .get_epoch_settings_message(SignedEntityTypeDiscriminants::all())
536                .await
537                .unwrap();
538
539            assert_eq!(message.epoch, Epoch(4));
540            assert_eq!(
541                message.signer_registration_protocol_parameters,
542                Some(ProtocolParameters::new(5, 100, 0.65))
543            );
544            assert_eq!(message.current_signers.len(), 3);
545            assert_eq!(message.next_signers.len(), 3);
546            assert_eq!(
547                message.cardano_transactions_signing_config,
548                Some(CardanoTransactionsSigningConfig {
549                    security_parameter: BlockNumber(0),
550                    step: BlockNumber(15)
551                })
552            );
553        }
554
555        #[tokio::test]
556        async fn get_epoch_settings_message_with_cardano_transactions_enabled() {
557            let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
558            let epoch_service = FakeEpochService::from_fixture(Epoch(4), &fixture);
559            let message_service = MessageServiceBuilder::new()
560                .with_epoch_service(epoch_service)
561                .build()
562                .await;
563
564            let message = message_service
565                .get_epoch_settings_message(BTreeSet::from([
566                    SignedEntityTypeDiscriminants::CardanoTransactions,
567                ]))
568                .await
569                .unwrap();
570
571            assert!(message.cardano_transactions_signing_config.is_some());
572        }
573
574        #[tokio::test]
575        async fn get_epoch_settings_message_with_cardano_transactions_not_enabled() {
576            let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
577            let epoch_service = FakeEpochService::from_fixture(Epoch(4), &fixture);
578            let message_service = MessageServiceBuilder::new()
579                .with_epoch_service(epoch_service)
580                .build()
581                .await;
582
583            let message = message_service
584                .get_epoch_settings_message(BTreeSet::new())
585                .await
586                .unwrap();
587
588            assert_eq!(message.cardano_transactions_signing_config, None);
589        }
590
591        #[tokio::test]
592        async fn get_epoch_settings_message_retrieves_protocol_parameters_from_epoch_service() {
593            let current_epoch_settings = AggregatorEpochSettings {
594                protocol_parameters: ProtocolParameters::new(101, 10, 0.5),
595                ..AggregatorEpochSettings::dummy()
596            };
597            let next_epoch_settings = AggregatorEpochSettings {
598                protocol_parameters: ProtocolParameters::new(102, 20, 0.5),
599                ..AggregatorEpochSettings::dummy()
600            };
601            let signer_registration_epoch_settings = AggregatorEpochSettings {
602                protocol_parameters: ProtocolParameters::new(103, 30, 0.5),
603                ..AggregatorEpochSettings::dummy()
604            };
605            let epoch_service = FakeEpochServiceBuilder {
606                current_epoch_settings,
607                next_epoch_settings: next_epoch_settings.clone(),
608                signer_registration_epoch_settings: signer_registration_epoch_settings.clone(),
609                current_signers_with_stake: fake_data::signers_with_stakes(5),
610                next_signers_with_stake: fake_data::signers_with_stakes(3),
611                ..FakeEpochServiceBuilder::dummy(Epoch(1))
612            }
613            .build();
614            let message_service = MessageServiceBuilder::new()
615                .with_epoch_service(epoch_service)
616                .build()
617                .await;
618
619            let message = message_service
620                .get_epoch_settings_message(SignedEntityTypeDiscriminants::all())
621                .await
622                .unwrap();
623
624            assert_eq!(
625                message.signer_registration_protocol_parameters,
626                Some(signer_registration_epoch_settings.protocol_parameters)
627            );
628        }
629
630        #[tokio::test]
631        async fn get_epoch_settings_message_retrieves_signing_configuration_from_epoch_service() {
632            let expected_ctx_config = CardanoTransactionsSigningConfig {
633                security_parameter: BlockNumber(100),
634                step: BlockNumber(15),
635            };
636            let epoch_service = FakeEpochServiceBuilder {
637                signed_entity_config: SignedEntityConfig {
638                    cardano_transactions_signing_config: Some(expected_ctx_config.clone()),
639                    ..Dummy::dummy()
640                },
641                ..FakeEpochServiceBuilder::dummy(Epoch(1))
642            }
643            .build();
644            let message_service = MessageServiceBuilder::new()
645                .with_epoch_service(epoch_service)
646                .build()
647                .await;
648
649            let message = message_service
650                .get_epoch_settings_message(SignedEntityTypeDiscriminants::all())
651                .await
652                .unwrap();
653
654            assert_eq!(
655                message.cardano_transactions_signing_config,
656                Some(expected_ctx_config),
657            );
658        }
659    }
660
661    mod protocol_configuration {
662        use super::*;
663
664        use mithril_common::entities::{CardanoTransactionsSigningConfig, ProtocolParameters};
665
666        use crate::entities::AggregatorEpochSettings;
667
668        #[tokio::test]
669        async fn get_protocol_configuration_message() {
670            let epoch = Epoch(4);
671            let aggregator_epoch_settings = AggregatorEpochSettings {
672                protocol_parameters: ProtocolParameters::new(5, 100, 0.65),
673                cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig {
674                    security_parameter: BlockNumber(0),
675                    step: BlockNumber(15),
676                }),
677            };
678            let message_service = MessageServiceBuilder::new()
679                .with_epoch_settings(BTreeMap::from([(epoch, aggregator_epoch_settings)]))
680                .build()
681                .await;
682
683            let message = message_service
684                .get_protocol_configuration_message(epoch, SignedEntityTypeDiscriminants::all())
685                .await
686                .unwrap()
687                .expect("Protocol configuration message should exist.");
688
689            assert_eq!(
690                message.protocol_parameters,
691                ProtocolParameters::new(5, 100, 0.65)
692            );
693            assert_eq!(
694                message.cardano_transactions_signing_config,
695                Some(CardanoTransactionsSigningConfig {
696                    security_parameter: BlockNumber(0),
697                    step: BlockNumber(15)
698                })
699            );
700            assert_eq!(
701                message.available_signed_entity_types,
702                SignedEntityTypeDiscriminants::all()
703            );
704        }
705
706        #[tokio::test]
707        async fn get_protocol_configuration_message_with_multiple_epochs_settings_stored() {
708            let message_service = MessageServiceBuilder::new()
709                .with_epoch_settings(BTreeMap::from([
710                    (
711                        Epoch(7),
712                        AggregatorEpochSettings {
713                            protocol_parameters: ProtocolParameters::new(1, 10, 0.11),
714                            ..Dummy::dummy()
715                        },
716                    ),
717                    (
718                        Epoch(8),
719                        AggregatorEpochSettings {
720                            protocol_parameters: ProtocolParameters::new(2, 20, 0.22),
721                            ..Dummy::dummy()
722                        },
723                    ),
724                    (
725                        Epoch(9),
726                        AggregatorEpochSettings {
727                            protocol_parameters: ProtocolParameters::new(3, 30, 0.33),
728                            ..Dummy::dummy()
729                        },
730                    ),
731                ]))
732                .build()
733                .await;
734
735            let message = message_service
736                .get_protocol_configuration_message(Epoch(8), SignedEntityTypeDiscriminants::all())
737                .await
738                .unwrap()
739                .expect("Protocol configuration message should exist.");
740
741            assert_eq!(
742                message.protocol_parameters,
743                ProtocolParameters::new(2, 20, 0.22)
744            );
745        }
746
747        #[tokio::test]
748        async fn get_protocol_configuration_message_with_cardano_transactions_enabled() {
749            let epoch = Epoch(4);
750            let message_service = MessageServiceBuilder::new()
751                .with_epoch_settings(BTreeMap::from([(epoch, AggregatorEpochSettings::dummy())]))
752                .build()
753                .await;
754
755            let message = message_service
756                .get_protocol_configuration_message(
757                    epoch,
758                    BTreeSet::from([SignedEntityTypeDiscriminants::CardanoTransactions]),
759                )
760                .await
761                .unwrap()
762                .expect("Protocol configuration message should exist.");
763
764            assert!(message.cardano_transactions_signing_config.is_some());
765        }
766
767        #[tokio::test]
768        async fn get_protocol_configuration_message_without_cardano_transactions_does_not_return_signing_config()
769         {
770            let epoch = Epoch(4);
771            let message_service = MessageServiceBuilder::new()
772                .with_epoch_settings(BTreeMap::from([(epoch, AggregatorEpochSettings::dummy())]))
773                .build()
774                .await;
775
776            let message = message_service
777                .get_protocol_configuration_message(epoch, BTreeSet::new())
778                .await
779                .unwrap()
780                .expect("Protocol configuration message should exist.");
781
782            assert_eq!(message.cardano_transactions_signing_config, None);
783        }
784
785        #[tokio::test]
786        async fn get_protocol_configuration_message_return_none_if_epoch_not_found() {
787            let epoch_number = 7;
788            let epoch_without_correspondence = epoch_number + 42;
789            let message_service = MessageServiceBuilder::new()
790                .with_epoch_settings(BTreeMap::from([(
791                    Epoch(epoch_number),
792                    AggregatorEpochSettings::dummy(),
793                )]))
794                .build()
795                .await;
796
797            let message = message_service
798                .get_protocol_configuration_message(
799                    Epoch(epoch_without_correspondence),
800                    SignedEntityTypeDiscriminants::all(),
801                )
802                .await
803                .unwrap();
804
805            assert_eq!(message, None);
806        }
807    }
808
809    mod certificate {
810        use super::*;
811
812        #[tokio::test]
813        async fn get_no_certificate() {
814            let service = MessageServiceBuilder::new().build().await;
815
816            let certificate_hash = "whatever";
817            let certificate_message =
818                service.get_certificate_message(certificate_hash).await.unwrap();
819            assert!(certificate_message.is_none());
820        }
821
822        #[tokio::test]
823        async fn get_certificate() {
824            let genesis_certificate = fake_data::genesis_certificate("genesis_hash");
825            let service = MessageServiceBuilder::new()
826                .with_certificates(std::slice::from_ref(&genesis_certificate))
827                .build()
828                .await;
829
830            let certificate_message = service
831                .get_certificate_message(&genesis_certificate.hash)
832                .await
833                .unwrap()
834                .expect("There should be a certificate.");
835            assert_eq!(genesis_certificate.hash, certificate_message.hash);
836        }
837
838        #[tokio::test]
839        async fn get_no_latest_genesis_certificate() {
840            let service = MessageServiceBuilder::new().build().await;
841
842            let certificate_message =
843                service.get_latest_genesis_certificate_message().await.unwrap();
844            assert_eq!(None, certificate_message);
845        }
846
847        #[tokio::test]
848        async fn get_latest_genesis_certificate() {
849            let certificates = [
850                fake_data::genesis_certificate("certificate_1"),
851                fake_data::genesis_certificate("certificate_2"),
852                fake_data::certificate("certificate_3"),
853            ];
854            let last_genesis_hash = certificates[1].hash.clone();
855            let service = MessageServiceBuilder::new()
856                .with_certificates(&certificates)
857                .build()
858                .await;
859
860            let certificate_message = service
861                .get_latest_genesis_certificate_message()
862                .await
863                .unwrap()
864                .expect("There should be a genesis certificate.");
865            assert_eq!(last_genesis_hash, certificate_message.hash);
866        }
867
868        #[tokio::test]
869        async fn get_last_certificates() {
870            let certificates = [
871                fake_data::genesis_certificate("certificate_1"),
872                fake_data::genesis_certificate("certificate_2"),
873            ];
874            let last_certificate_hash = certificates[1].hash.clone();
875            let service = MessageServiceBuilder::new()
876                .with_certificates(&certificates)
877                .build()
878                .await;
879
880            let certificate_messages = service.get_certificate_list_message(5).await.unwrap();
881
882            assert_eq!(2, certificate_messages.len());
883            assert_eq!(last_certificate_hash, certificate_messages[0].hash);
884        }
885    }
886
887    mod snapshot {
888        use super::*;
889
890        #[tokio::test]
891        async fn get_snapshot_not_exist() {
892            let service = MessageServiceBuilder::new().build().await;
893            let snapshot = service.get_snapshot_message("whatever").await.unwrap();
894
895            assert!(snapshot.is_none());
896        }
897
898        #[tokio::test]
899        async fn get_snapshot() {
900            let record = SignedEntityRecord {
901                signed_entity_id: "signed_entity_id".to_string(),
902                signed_entity_type: SignedEntityType::CardanoImmutableFilesFull(fake_data::beacon()),
903                certificate_id: "cert_id".to_string(),
904                artifact: serde_json::to_string(&fake_data::snapshot(1)).unwrap(),
905                created_at: Default::default(),
906            };
907            let message: SnapshotMessage = record.clone().try_into().unwrap();
908
909            let service = MessageServiceBuilder::new()
910                .with_signed_entity_records(std::slice::from_ref(&record))
911                .build()
912                .await;
913
914            let response = service
915                .get_snapshot_message(&record.signed_entity_id)
916                .await
917                .unwrap()
918                .expect("A SnapshotMessage was expected.");
919
920            assert_eq!(message, response);
921        }
922
923        #[tokio::test]
924        async fn get_snapshot_list_message() {
925            let records = vec![
926                SignedEntityRecord {
927                    signed_entity_id: "signed_entity_id-1".to_string(),
928                    signed_entity_type: SignedEntityType::CardanoImmutableFilesFull(
929                        fake_data::beacon(),
930                    ),
931                    certificate_id: "cert_id-1".to_string(),
932                    artifact: serde_json::to_string(&fake_data::snapshot(1)).unwrap(),
933                    created_at: Default::default(),
934                },
935                SignedEntityRecord {
936                    signed_entity_id: "signed_entity_id-2".to_string(),
937                    signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
938                    certificate_id: "cert_id-2".to_string(),
939                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshot(1))
940                        .unwrap(),
941                    created_at: Default::default(),
942                },
943            ];
944            let message: SnapshotListMessage = vec![records[0].clone().try_into().unwrap()];
945
946            let service = MessageServiceBuilder::new()
947                .with_signed_entity_records(&records)
948                .build()
949                .await;
950
951            let response = service.get_snapshot_list_message(0).await.unwrap();
952            assert!(response.is_empty());
953
954            let response = service.get_snapshot_list_message(3).await.unwrap();
955            assert_eq!(message, response);
956        }
957    }
958
959    mod cardano_database {
960        use super::*;
961
962        #[tokio::test]
963        async fn get_cardano_database_when_record_does_not_exist() {
964            let service = MessageServiceBuilder::new().build().await;
965            let snapshot = service.get_cardano_database_message("whatever").await.unwrap();
966
967            assert!(snapshot.is_none());
968        }
969
970        #[tokio::test]
971        async fn get_cardano_database() {
972            let record = SignedEntityRecord {
973                signed_entity_id: "signed_entity_id".to_string(),
974                signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
975                certificate_id: "cert_id".to_string(),
976                artifact: serde_json::to_string(&fake_data::cardano_database_snapshot(1)).unwrap(),
977                created_at: Default::default(),
978            };
979            let message: CardanoDatabaseSnapshotMessage = record.clone().try_into().unwrap();
980
981            let service = MessageServiceBuilder::new()
982                .with_signed_entity_records(std::slice::from_ref(&record))
983                .build()
984                .await;
985
986            let response = service
987                .get_cardano_database_message(&record.signed_entity_id)
988                .await
989                .unwrap()
990                .expect("A CardanoDatabaseSnapshotMessage was expected.");
991
992            assert_eq!(message, response);
993        }
994
995        #[tokio::test]
996        async fn get_cardano_database_list_message() {
997            let records = vec![
998                SignedEntityRecord {
999                    signed_entity_id: "signed_entity_id-1".to_string(),
1000                    signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
1001                    certificate_id: "cert_id-1".to_string(),
1002                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshot(1))
1003                        .unwrap(),
1004                    created_at: Default::default(),
1005                },
1006                SignedEntityRecord {
1007                    signed_entity_id: "signed_entity_id-2".to_string(),
1008                    signed_entity_type: SignedEntityType::CardanoImmutableFilesFull(
1009                        fake_data::beacon(),
1010                    ),
1011                    certificate_id: "cert_id-2".to_string(),
1012                    artifact: serde_json::to_string(&fake_data::snapshot(1)).unwrap(),
1013                    created_at: Default::default(),
1014                },
1015            ];
1016            let message: CardanoDatabaseSnapshotListMessage =
1017                vec![records[0].clone().try_into().unwrap()];
1018
1019            let service = MessageServiceBuilder::new()
1020                .with_signed_entity_records(&records)
1021                .build()
1022                .await;
1023
1024            let response = service.get_cardano_database_list_message(0).await.unwrap();
1025            assert!(response.is_empty());
1026
1027            let response = service.get_cardano_database_list_message(3).await.unwrap();
1028            assert_eq!(message, response);
1029        }
1030
1031        #[tokio::test]
1032        async fn get_cardano_database_list_message_by_epoch() {
1033            let records = vec![
1034                // Cardano database on epoch 3
1035                SignedEntityRecord {
1036                    signed_entity_id: "signed_entity_id-1".to_string(),
1037                    signed_entity_type: SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(
1038                        3, 100,
1039                    )),
1040                    certificate_id: "cert_id-1".to_string(),
1041                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshot(100))
1042                        .unwrap(),
1043                    created_at: Default::default(),
1044                },
1045                // Another signed entity type on the same epoch
1046                SignedEntityRecord {
1047                    signed_entity_id: "signed_entity_id-2".to_string(),
1048                    signed_entity_type: SignedEntityType::CardanoImmutableFilesFull(
1049                        CardanoDbBeacon::new(3, 100),
1050                    ),
1051                    certificate_id: "cert_id-2".to_string(),
1052                    artifact: serde_json::to_string(&fake_data::snapshot(1)).unwrap(),
1053                    created_at: Default::default(),
1054                },
1055                // Cardano database also on epoch 3
1056                SignedEntityRecord {
1057                    signed_entity_id: "signed_entity_id-3".to_string(),
1058                    signed_entity_type: SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(
1059                        3, 102,
1060                    )),
1061                    certificate_id: "cert_id-3".to_string(),
1062                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshot(102))
1063                        .unwrap(),
1064                    created_at: Default::default(),
1065                },
1066                // Cardano database also another epoch
1067                SignedEntityRecord {
1068                    signed_entity_id: "signed_entity_id-4".to_string(),
1069                    signed_entity_type: SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(
1070                        4, 104,
1071                    )),
1072                    certificate_id: "cert_id-4".to_string(),
1073                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshot(104))
1074                        .unwrap(),
1075                    created_at: Default::default(),
1076                },
1077            ];
1078            let message: CardanoDatabaseSnapshotListMessage = vec![
1079                records[2].clone().try_into().unwrap(),
1080                records[0].clone().try_into().unwrap(),
1081            ];
1082
1083            let service = MessageServiceBuilder::new()
1084                .with_signed_entity_records(&records)
1085                .build()
1086                .await;
1087
1088            let response = service
1089                .get_cardano_database_list_message_by_epoch(0, Epoch(3))
1090                .await
1091                .unwrap();
1092            assert!(response.is_empty());
1093
1094            let response = service
1095                .get_cardano_database_list_message_by_epoch(3, Epoch(3))
1096                .await
1097                .unwrap();
1098            assert_eq!(message, response);
1099        }
1100
1101        #[tokio::test]
1102        async fn get_cardano_database_digest_list_message() {
1103            let messages: CardanoDatabaseDigestListMessage = vec![
1104                CardanoDatabaseDigestListItemMessage {
1105                    immutable_file_name: "06685.chunk".to_string(),
1106                    digest: "0af556ab2620dd9363bf76963a231abe8948a500ea6be31b131d87907ab09b1e"
1107                        .to_string(),
1108                },
1109                CardanoDatabaseDigestListItemMessage {
1110                    immutable_file_name: "06685.primary".to_string(),
1111                    digest: "32dfd6b722d87f253e78eb8b478fb94f1e13463826e674d6ec7b6bf0892b2e39"
1112                        .to_string(),
1113                },
1114            ];
1115
1116            let service = MessageServiceBuilder::new()
1117                .with_immutable_file_digest_messages(&messages)
1118                .build()
1119                .await;
1120
1121            let response = service.get_cardano_database_digest_list_message().await.unwrap();
1122
1123            assert_eq!(messages, response);
1124        }
1125    }
1126
1127    mod mithril_stake_distribution {
1128        use super::*;
1129
1130        #[tokio::test]
1131        async fn get_mithril_stake_distribution() {
1132            let record = SignedEntityRecord {
1133                signed_entity_id: "signed_entity_id".to_string(),
1134                signed_entity_type: SignedEntityType::MithrilStakeDistribution(Epoch(18)),
1135                certificate_id: "cert_id".to_string(),
1136                artifact: serde_json::to_string(&fake_data::mithril_stake_distribution(
1137                    Epoch(1),
1138                    vec![],
1139                ))
1140                .unwrap(),
1141                created_at: Default::default(),
1142            };
1143            let message: MithrilStakeDistributionMessage = record.clone().try_into().unwrap();
1144
1145            let service = MessageServiceBuilder::new()
1146                .with_signed_entity_records(std::slice::from_ref(&record))
1147                .build()
1148                .await;
1149
1150            let response = service
1151                .get_mithril_stake_distribution_message(&record.signed_entity_id)
1152                .await
1153                .unwrap()
1154                .expect("A MithrilStakeDistributionMessage was expected.");
1155
1156            assert_eq!(message, response);
1157        }
1158
1159        #[tokio::test]
1160        async fn get_mithril_stake_distribution_not_exist() {
1161            let service = MessageServiceBuilder::new().build().await;
1162
1163            let response = service
1164                .get_mithril_stake_distribution_message("whatever")
1165                .await
1166                .unwrap();
1167
1168            assert!(response.is_none());
1169        }
1170
1171        #[tokio::test]
1172        async fn get_mithril_stake_distribution_list_message() {
1173            let records = vec![
1174                SignedEntityRecord {
1175                    signed_entity_id: "signed_entity_id-1".to_string(),
1176                    signed_entity_type: SignedEntityType::MithrilStakeDistribution(Epoch(18)),
1177                    certificate_id: "cert_id-1".to_string(),
1178                    artifact: serde_json::to_string(&fake_data::mithril_stake_distribution(
1179                        Epoch(1),
1180                        vec![],
1181                    ))
1182                    .unwrap(),
1183                    created_at: Default::default(),
1184                },
1185                SignedEntityRecord {
1186                    signed_entity_id: "signed_entity_id-2".to_string(),
1187                    signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
1188                    certificate_id: "cert_id-2".to_string(),
1189                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshot(1))
1190                        .unwrap(),
1191                    created_at: Default::default(),
1192                },
1193            ];
1194            let message: MithrilStakeDistributionListMessage =
1195                vec![records[0].clone().try_into().unwrap()];
1196
1197            let service = MessageServiceBuilder::new()
1198                .with_signed_entity_records(&records)
1199                .build()
1200                .await;
1201
1202            let response = service.get_mithril_stake_distribution_list_message(0).await.unwrap();
1203            assert!(response.is_empty());
1204
1205            let response = service.get_mithril_stake_distribution_list_message(3).await.unwrap();
1206            assert_eq!(message, response);
1207        }
1208    }
1209
1210    mod cardano_transaction {
1211        use super::*;
1212
1213        #[tokio::test]
1214        async fn get_cardano_transaction() {
1215            let record = SignedEntityRecord {
1216                signed_entity_id: "signed_entity_id".to_string(),
1217                signed_entity_type: SignedEntityType::CardanoTransactions(
1218                    Epoch(18),
1219                    BlockNumber(120),
1220                ),
1221                certificate_id: "cert_id".to_string(),
1222                artifact: serde_json::to_string(&fake_data::cardano_transactions_snapshot(
1223                    BlockNumber(1),
1224                ))
1225                .unwrap(),
1226                created_at: Default::default(),
1227            };
1228            let message: CardanoTransactionSnapshotMessage = record.clone().try_into().unwrap();
1229
1230            let service = MessageServiceBuilder::new()
1231                .with_signed_entity_records(std::slice::from_ref(&record))
1232                .build()
1233                .await;
1234
1235            let response = service
1236                .get_cardano_transaction_message(&record.signed_entity_id)
1237                .await
1238                .unwrap()
1239                .expect("A CardanoTransactionMessage was expected.");
1240
1241            assert_eq!(message, response);
1242        }
1243
1244        #[tokio::test]
1245        async fn get_cardano_transaction_not_exist() {
1246            let service = MessageServiceBuilder::new().build().await;
1247
1248            let response = service.get_cardano_transaction_message("whatever").await.unwrap();
1249
1250            assert!(response.is_none());
1251        }
1252
1253        #[tokio::test]
1254        async fn get_cardano_transaction_list_message() {
1255            let records = vec![
1256                SignedEntityRecord {
1257                    signed_entity_id: "signed_entity_id-1".to_string(),
1258                    signed_entity_type: SignedEntityType::CardanoTransactions(
1259                        Epoch(18),
1260                        BlockNumber(120),
1261                    ),
1262                    certificate_id: "cert_id-1".to_string(),
1263                    artifact: serde_json::to_string(&fake_data::cardano_transactions_snapshot(
1264                        BlockNumber(1),
1265                    ))
1266                    .unwrap(),
1267                    created_at: Default::default(),
1268                },
1269                SignedEntityRecord {
1270                    signed_entity_id: "signed_entity_id-2".to_string(),
1271                    signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
1272                    certificate_id: "cert_id-2".to_string(),
1273                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshot(1))
1274                        .unwrap(),
1275                    created_at: Default::default(),
1276                },
1277            ];
1278            let message: CardanoTransactionSnapshotListMessage =
1279                vec![records[0].clone().try_into().unwrap()];
1280
1281            let service = MessageServiceBuilder::new()
1282                .with_signed_entity_records(&records)
1283                .build()
1284                .await;
1285
1286            let response = service.get_cardano_transaction_list_message(0).await.unwrap();
1287            assert!(response.is_empty());
1288
1289            let response = service.get_cardano_transaction_list_message(3).await.unwrap();
1290            assert_eq!(message, response);
1291        }
1292    }
1293
1294    mod cardano_stake_distribution {
1295        use super::*;
1296
1297        #[tokio::test]
1298        async fn get_cardano_stake_distribution() {
1299            let record = SignedEntityRecord {
1300                signed_entity_id: "signed_entity_id".to_string(),
1301                signed_entity_type: SignedEntityType::CardanoStakeDistribution(Epoch(18)),
1302                certificate_id: "cert_id".to_string(),
1303                artifact: serde_json::to_string(&fake_data::cardano_stake_distribution(Epoch(1)))
1304                    .unwrap(),
1305                created_at: Default::default(),
1306            };
1307            let message: CardanoStakeDistributionMessage = record.clone().try_into().unwrap();
1308
1309            let service = MessageServiceBuilder::new()
1310                .with_signed_entity_records(std::slice::from_ref(&record))
1311                .build()
1312                .await;
1313
1314            let response = service
1315                .get_cardano_stake_distribution_message(&record.signed_entity_id)
1316                .await
1317                .unwrap()
1318                .expect("A CardanoStakeDistributionMessage was expected.");
1319
1320            assert_eq!(message, response);
1321        }
1322
1323        #[tokio::test]
1324        async fn get_cardano_stake_distribution_not_exist() {
1325            let service = MessageServiceBuilder::new().build().await;
1326
1327            let response = service
1328                .get_cardano_stake_distribution_message("whatever")
1329                .await
1330                .unwrap();
1331
1332            assert!(response.is_none());
1333        }
1334
1335        #[tokio::test]
1336        async fn get_cardano_stake_distribution_by_epoch() {
1337            let record = SignedEntityRecord {
1338                signed_entity_id: "signed_entity_id".to_string(),
1339                signed_entity_type: SignedEntityType::CardanoStakeDistribution(Epoch(18)),
1340                certificate_id: "cert_id".to_string(),
1341                artifact: serde_json::to_string(&fake_data::cardano_stake_distribution(Epoch(1)))
1342                    .unwrap(),
1343                created_at: Default::default(),
1344            };
1345            let message: CardanoStakeDistributionMessage = record.clone().try_into().unwrap();
1346
1347            let service = MessageServiceBuilder::new()
1348                .with_signed_entity_records(std::slice::from_ref(&record))
1349                .build()
1350                .await;
1351
1352            let response = service
1353                .get_cardano_stake_distribution_message_by_epoch(
1354                    record.signed_entity_type.get_epoch(),
1355                )
1356                .await
1357                .unwrap()
1358                .expect("A CardanoStakeDistributionMessage was expected.");
1359
1360            assert_eq!(message, response);
1361        }
1362
1363        #[tokio::test]
1364        async fn get_cardano_stake_distribution_by_epoch_not_exist() {
1365            let service = MessageServiceBuilder::new().build().await;
1366
1367            let response = service
1368                .get_cardano_stake_distribution_message_by_epoch(Epoch(999))
1369                .await
1370                .unwrap();
1371
1372            assert!(response.is_none());
1373        }
1374
1375        #[tokio::test]
1376        async fn get_cardano_stake_distribution_list_message() {
1377            let records = vec![
1378                SignedEntityRecord {
1379                    signed_entity_id: "signed_entity_id-1".to_string(),
1380                    signed_entity_type: SignedEntityType::CardanoStakeDistribution(Epoch(18)),
1381                    certificate_id: "cert_id-1".to_string(),
1382                    artifact: serde_json::to_string(&fake_data::cardano_stake_distribution(Epoch(
1383                        1,
1384                    )))
1385                    .unwrap(),
1386                    created_at: Default::default(),
1387                },
1388                SignedEntityRecord {
1389                    signed_entity_id: "signed_entity_id-2".to_string(),
1390                    signed_entity_type: SignedEntityType::CardanoDatabase(fake_data::beacon()),
1391                    certificate_id: "cert_id-2".to_string(),
1392                    artifact: serde_json::to_string(&fake_data::cardano_database_snapshot(1))
1393                        .unwrap(),
1394                    created_at: Default::default(),
1395                },
1396            ];
1397            let message: CardanoStakeDistributionListMessage =
1398                vec![records[0].clone().try_into().unwrap()];
1399
1400            let service = MessageServiceBuilder::new()
1401                .with_signed_entity_records(&records)
1402                .build()
1403                .await;
1404
1405            let response = service.get_cardano_stake_distribution_list_message(0).await.unwrap();
1406            assert!(response.is_empty());
1407
1408            let response = service.get_cardano_stake_distribution_list_message(3).await.unwrap();
1409            assert_eq!(message, response);
1410        }
1411    }
1412}