1use 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#[cfg_attr(test, mockall::automock)]
30#[async_trait]
31pub trait MessageService: Sync + Send {
32 async fn get_epoch_settings_message(
34 &self,
35 allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
36 ) -> StdResult<EpochSettingsMessage>;
37
38 async fn get_protocol_configuration_message(
40 &self,
41 epoch: Epoch,
42 allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
43 ) -> StdResult<Option<ProtocolConfigurationMessage>>;
44
45 async fn get_certificate_message(
47 &self,
48 certificate_hash: &str,
49 ) -> StdResult<Option<CertificateMessage>>;
50
51 async fn get_latest_genesis_certificate_message(&self)
53 -> StdResult<Option<CertificateMessage>>;
54
55 async fn get_certificate_list_message(&self, limit: usize)
57 -> StdResult<CertificateListMessage>;
58
59 async fn get_snapshot_message(
61 &self,
62 signed_entity_id: &str,
63 ) -> StdResult<Option<SnapshotMessage>>;
64
65 async fn get_snapshot_list_message(&self, limit: usize) -> StdResult<SnapshotListMessage>;
68
69 async fn get_cardano_database_message(
71 &self,
72 signed_entity_id: &str,
73 ) -> StdResult<Option<CardanoDatabaseSnapshotMessage>>;
74
75 async fn get_cardano_database_list_message(
77 &self,
78 limit: usize,
79 ) -> StdResult<CardanoDatabaseSnapshotListMessage>;
80
81 async fn get_cardano_database_list_message_by_epoch(
83 &self,
84 limit: usize,
85 epoch: Epoch,
86 ) -> StdResult<CardanoDatabaseSnapshotListMessage>;
87
88 async fn get_cardano_database_digest_list_message(
90 &self,
91 ) -> StdResult<CardanoDatabaseDigestListMessage>;
92
93 async fn get_mithril_stake_distribution_message(
95 &self,
96 signed_entity_id: &str,
97 ) -> StdResult<Option<MithrilStakeDistributionMessage>>;
98
99 async fn get_mithril_stake_distribution_list_message(
101 &self,
102 limit: usize,
103 ) -> StdResult<MithrilStakeDistributionListMessage>;
104
105 async fn get_cardano_transaction_message(
107 &self,
108 signed_entity_id: &str,
109 ) -> StdResult<Option<CardanoTransactionSnapshotMessage>>;
110
111 async fn get_cardano_transaction_list_message(
113 &self,
114 limit: usize,
115 ) -> StdResult<CardanoTransactionSnapshotListMessage>;
116
117 async fn get_cardano_stake_distribution_message(
119 &self,
120 signed_entity_id: &str,
121 ) -> StdResult<Option<CardanoStakeDistributionMessage>>;
122
123 async fn get_cardano_stake_distribution_message_by_epoch(
125 &self,
126 epoch: Epoch,
127 ) -> StdResult<Option<CardanoStakeDistributionMessage>>;
128
129 async fn get_cardano_stake_distribution_list_message(
131 &self,
132 limit: usize,
133 ) -> StdResult<CardanoStakeDistributionListMessage>;
134}
135
136pub 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 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 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 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 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 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}