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