mithril_aggregator/services/
message.rs

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