mithril_signer/services/
epoch_service.rs

1use anyhow::anyhow;
2use async_trait::async_trait;
3use slog::{debug, trace, warn, Logger};
4use std::collections::BTreeSet;
5use std::sync::Arc;
6use thiserror::Error;
7
8use crate::dependency_injection::EpochServiceWrapper;
9use crate::entities::SignerEpochSettings;
10use crate::services::SignedEntityConfigProvider;
11use crate::store::ProtocolInitializerStorer;
12use crate::RunnerError;
13use mithril_common::crypto_helper::ProtocolInitializer;
14use mithril_common::entities::{
15    CardanoTransactionsSigningConfig, Epoch, PartyId, ProtocolParameters, SignedEntityConfig,
16    SignedEntityTypeDiscriminants, Signer, SignerWithStake,
17};
18use mithril_common::logging::LoggerExtensions;
19use mithril_common::StdResult;
20use mithril_persistence::store::StakeStorer;
21
22/// Errors dedicated to the EpochService.
23#[derive(Debug, Error)]
24pub enum EpochServiceError {
25    /// Raised when service has not collected data at least once.
26    #[error("Epoch service was not initialized, the function `inform_epoch_settings` must be called first")]
27    NotYetInitialized,
28}
29
30/// Service that aggregates all data that don't change in a given epoch.
31#[async_trait]
32pub trait EpochService: Sync + Send {
33    /// Inform the service a new epoch has been detected, telling it to update its
34    /// internal state for the new epoch.
35    async fn inform_epoch_settings(
36        &mut self,
37        epoch_settings: SignerEpochSettings,
38        allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
39    ) -> StdResult<()>;
40
41    /// Get the current epoch for which the data stored in this service are computed.
42    fn epoch_of_current_data(&self) -> StdResult<Epoch>;
43
44    /// Get protocol parameters for registration.
45    fn registration_protocol_parameters(&self) -> StdResult<&ProtocolParameters>;
46
47    /// Get the protocol initializer for the current epoch if any
48    ///
49    /// `None` if the signer can't sign for the current epoch.
50    fn protocol_initializer(&self) -> StdResult<&Option<ProtocolInitializer>>;
51
52    /// Get signers for the current epoch
53    fn current_signers(&self) -> StdResult<&Vec<Signer>>;
54
55    /// Get signers for the next epoch
56    fn next_signers(&self) -> StdResult<&Vec<Signer>>;
57
58    /// Get signers with stake for the current epoch
59    async fn current_signers_with_stake(&self) -> StdResult<Vec<SignerWithStake>>;
60
61    /// Get signers with stake for the next epoch
62    async fn next_signers_with_stake(&self) -> StdResult<Vec<SignerWithStake>>;
63
64    /// Get the list of signed entity types that are allowed to sign for the current epoch
65    fn allowed_discriminants(&self) -> StdResult<&BTreeSet<SignedEntityTypeDiscriminants>>;
66
67    /// Get the cardano transactions signing configuration for the current epoch
68    fn cardano_transactions_signing_config(
69        &self,
70    ) -> StdResult<&Option<CardanoTransactionsSigningConfig>>;
71
72    /// Get the cardano transactions signing configuration for the next epoch
73    fn next_cardano_transactions_signing_config(
74        &self,
75    ) -> StdResult<&Option<CardanoTransactionsSigningConfig>>;
76
77    /// Check if the given signer can sign for the current epoch
78    fn can_signer_sign_current_epoch(&self, party_id: PartyId) -> StdResult<bool>;
79}
80
81pub(crate) struct EpochData {
82    pub epoch: Epoch,
83    pub registration_protocol_parameters: ProtocolParameters,
84    pub protocol_initializer: Option<ProtocolInitializer>,
85    pub current_signers: Vec<Signer>,
86    pub next_signers: Vec<Signer>,
87    pub allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
88    pub cardano_transactions_signing_config: Option<CardanoTransactionsSigningConfig>,
89    pub next_cardano_transactions_signing_config: Option<CardanoTransactionsSigningConfig>,
90}
91
92/// Implementation of the [epoch service][EpochService].
93pub struct MithrilEpochService {
94    stake_storer: Arc<dyn StakeStorer>,
95    protocol_initializer_store: Arc<dyn ProtocolInitializerStorer>,
96    epoch_data: Option<EpochData>,
97    logger: Logger,
98}
99
100impl MithrilEpochService {
101    /// Create a new service instance
102    pub fn new(
103        stake_storer: Arc<dyn StakeStorer>,
104        protocol_initializer_store: Arc<dyn ProtocolInitializerStorer>,
105        logger: Logger,
106    ) -> Self {
107        Self {
108            stake_storer,
109            protocol_initializer_store,
110            epoch_data: None,
111            logger: logger.new_with_component_name::<Self>(),
112        }
113    }
114
115    async fn associate_signers_with_stake(
116        &self,
117        epoch: Epoch,
118        signers: &[Signer],
119    ) -> StdResult<Vec<SignerWithStake>> {
120        debug!(
121            self.logger,
122            ">> associate_signers_with_stake(epoch:{epoch})"
123        );
124
125        let stakes = self
126            .stake_storer
127            .get_stakes(epoch)
128            .await?
129            .ok_or_else(|| RunnerError::NoValueError(format!("stakes at epoch {epoch}")))?;
130
131        let mut signers_with_stake = vec![];
132
133        for signer in signers {
134            let stake = stakes
135                .get(&*signer.party_id)
136                .ok_or_else(|| RunnerError::NoStakeForSigner(signer.party_id.to_string()))?;
137
138            signers_with_stake.push(SignerWithStake::new(
139                signer.party_id.to_owned(),
140                signer.verification_key.to_owned(),
141                signer.verification_key_signature.to_owned(),
142                signer.operational_certificate.to_owned(),
143                signer.kes_period.to_owned(),
144                *stake,
145            ));
146            trace!(
147                self.logger,
148                " > Associating signer_id {} with stake {}",
149                signer.party_id,
150                *stake
151            );
152        }
153
154        Ok(signers_with_stake)
155    }
156
157    fn is_signer_included_in_current_stake_distribution(
158        &self,
159        party_id: PartyId,
160        protocol_initializer: &ProtocolInitializer,
161    ) -> StdResult<bool> {
162        Ok(self.current_signers()?.iter().any(|s| {
163            s.party_id == party_id
164                && s.verification_key == protocol_initializer.verification_key().into()
165        }))
166    }
167
168    fn unwrap_data(&self) -> Result<&EpochData, EpochServiceError> {
169        self.epoch_data
170            .as_ref()
171            .ok_or(EpochServiceError::NotYetInitialized)
172    }
173}
174
175#[async_trait]
176impl EpochService for MithrilEpochService {
177    async fn inform_epoch_settings(
178        &mut self,
179        epoch_settings: SignerEpochSettings,
180        allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
181    ) -> StdResult<()> {
182        debug!(self.logger, ">> inform_epoch_settings"; "epoch_settings" => ?epoch_settings);
183
184        let epoch = epoch_settings.epoch;
185        let protocol_initializer = self
186            .protocol_initializer_store
187            .get_protocol_initializer(epoch.offset_to_signer_retrieval_epoch()?)
188            .await?;
189
190        self.epoch_data = Some(EpochData {
191            epoch,
192            registration_protocol_parameters: epoch_settings.registration_protocol_parameters,
193            protocol_initializer,
194            current_signers: epoch_settings.current_signers,
195            next_signers: epoch_settings.next_signers,
196            allowed_discriminants,
197            cardano_transactions_signing_config: epoch_settings.cardano_transactions_signing_config,
198            next_cardano_transactions_signing_config: epoch_settings
199                .next_cardano_transactions_signing_config,
200        });
201
202        Ok(())
203    }
204
205    fn epoch_of_current_data(&self) -> StdResult<Epoch> {
206        Ok(self.unwrap_data()?.epoch)
207    }
208
209    fn registration_protocol_parameters(&self) -> StdResult<&ProtocolParameters> {
210        Ok(&self.unwrap_data()?.registration_protocol_parameters)
211    }
212
213    fn protocol_initializer(&self) -> StdResult<&Option<ProtocolInitializer>> {
214        Ok(&self.unwrap_data()?.protocol_initializer)
215    }
216
217    fn current_signers(&self) -> StdResult<&Vec<Signer>> {
218        Ok(&self.unwrap_data()?.current_signers)
219    }
220
221    fn next_signers(&self) -> StdResult<&Vec<Signer>> {
222        Ok(&self.unwrap_data()?.next_signers)
223    }
224
225    async fn current_signers_with_stake(&self) -> StdResult<Vec<SignerWithStake>> {
226        let current_epoch = self.epoch_of_current_data()?;
227        self.associate_signers_with_stake(
228            current_epoch.offset_to_signer_retrieval_epoch()?,
229            self.current_signers()?,
230        )
231        .await
232    }
233
234    async fn next_signers_with_stake(&self) -> StdResult<Vec<SignerWithStake>> {
235        let current_epoch = self.epoch_of_current_data()?;
236        self.associate_signers_with_stake(
237            current_epoch.offset_to_next_signer_retrieval_epoch(),
238            self.next_signers()?,
239        )
240        .await
241    }
242
243    fn allowed_discriminants(&self) -> StdResult<&BTreeSet<SignedEntityTypeDiscriminants>> {
244        Ok(&self.unwrap_data()?.allowed_discriminants)
245    }
246
247    fn cardano_transactions_signing_config(
248        &self,
249    ) -> StdResult<&Option<CardanoTransactionsSigningConfig>> {
250        Ok(&self.unwrap_data()?.cardano_transactions_signing_config)
251    }
252
253    fn next_cardano_transactions_signing_config(
254        &self,
255    ) -> StdResult<&Option<CardanoTransactionsSigningConfig>> {
256        Ok(&self.unwrap_data()?.next_cardano_transactions_signing_config)
257    }
258
259    fn can_signer_sign_current_epoch(&self, party_id: PartyId) -> StdResult<bool> {
260        let epoch = self.epoch_of_current_data()?;
261        if let Some(protocol_initializer) = self.protocol_initializer()? {
262            debug!(
263                self.logger,
264                " > Got protocol initializer for this epoch ({epoch})"
265            );
266            if self
267                .is_signer_included_in_current_stake_distribution(party_id, protocol_initializer)?
268            {
269                return Ok(true);
270            } else {
271                debug!(
272                    self.logger,
273                    " > Signer not in current stake distribution. Can NOT sign"
274                );
275            }
276        } else {
277            warn!(
278                self.logger,
279                " > NO protocol initializer found for this epoch ({epoch})",
280            );
281        }
282
283        Ok(false)
284    }
285}
286
287/// Simple wrapper to the [EpochService] to implement the [SignedEntityConfigProvider] trait.
288///
289/// Needed because the epoch service is wrapped in an Arc<RwLock<>> in the dependencies, making
290/// direct usage of implemented traits methods difficult.
291pub struct SignerSignedEntityConfigProvider {
292    epoch_service: EpochServiceWrapper,
293}
294
295impl SignerSignedEntityConfigProvider {
296    /// Create a new instance of the `SignerSignedEntityConfigProvider`.
297    pub fn new(epoch_service: EpochServiceWrapper) -> Self {
298        Self { epoch_service }
299    }
300}
301
302#[async_trait]
303impl SignedEntityConfigProvider for SignerSignedEntityConfigProvider {
304    async fn get(&self) -> StdResult<SignedEntityConfig> {
305        let epoch_service = self.epoch_service.read().await;
306        let cardano_transactions_signing_config =
307            match epoch_service.cardano_transactions_signing_config()? {
308                Some(config) => Ok(config.clone()),
309                None => Err(anyhow!("No cardano transaction signing config available")),
310            }?;
311
312        Ok(SignedEntityConfig {
313            allowed_discriminants: epoch_service.allowed_discriminants()?.clone(),
314            cardano_transactions_signing_config,
315        })
316    }
317}
318
319#[cfg(test)]
320use crate::database::repository::ProtocolInitializerRepository;
321
322#[cfg(test)]
323impl MithrilEpochService {
324    /// `TEST ONLY` - Create a new instance of the service using dumb dependencies.
325    pub fn new_with_dumb_dependencies() -> Self {
326        use crate::database::repository::StakePoolStore;
327        use crate::database::test_helper::main_db_connection;
328        use crate::test_tools::TestLogger;
329
330        let sqlite_connection = Arc::new(main_db_connection().unwrap());
331        let stake_store = Arc::new(StakePoolStore::new(sqlite_connection.clone(), None));
332        let protocol_initializer_store =
333            Arc::new(ProtocolInitializerRepository::new(sqlite_connection, None));
334
335        Self::new(
336            stake_store,
337            protocol_initializer_store,
338            TestLogger::stdout(),
339        )
340    }
341
342    /// `TEST ONLY` - Set all data to either default values, empty values, or fake values
343    /// if no default/empty can be set.
344    pub fn set_data_to_default_or_fake(mut self, epoch: Epoch) -> Self {
345        use mithril_common::test_utils::fake_data;
346
347        let epoch_data = EpochData {
348            epoch,
349            registration_protocol_parameters: fake_data::protocol_parameters(),
350            protocol_initializer: None,
351            current_signers: vec![],
352            next_signers: vec![],
353            allowed_discriminants: BTreeSet::new(),
354            cardano_transactions_signing_config: None,
355            next_cardano_transactions_signing_config: None,
356        };
357        self.epoch_data = Some(epoch_data);
358        self
359    }
360
361    /// `TEST ONLY` - Alter the data stored in the service, won't do anything if no data is stored.
362    pub(crate) fn alter_data(mut self, data_config: impl FnOnce(&mut EpochData)) -> Self {
363        if let Some(data) = self.epoch_data.as_mut() {
364            data_config(data);
365        }
366        self
367    }
368}
369
370#[cfg(test)]
371pub(crate) mod mock_epoch_service {
372    use mockall::mock;
373
374    use super::*;
375
376    mock! {
377        pub EpochServiceImpl {}
378
379        #[async_trait]
380        impl EpochService for EpochServiceImpl {
381            async fn inform_epoch_settings(
382                &mut self,
383                epoch_settings: SignerEpochSettings,
384                allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
385            ) -> StdResult<()>;
386
387            fn epoch_of_current_data(&self) -> StdResult<Epoch>;
388
389            fn registration_protocol_parameters(&self) -> StdResult<&'static ProtocolParameters>;
390
391            fn protocol_initializer(&self) -> StdResult<&'static Option<ProtocolInitializer>>;
392
393            fn current_signers(&self) -> StdResult<&'static Vec<Signer>>;
394
395            fn next_signers(&self) -> StdResult<&'static Vec<Signer>>;
396
397            async fn current_signers_with_stake(&self) -> StdResult<Vec<SignerWithStake>>;
398
399            async fn next_signers_with_stake(&self) -> StdResult<Vec<SignerWithStake>>;
400
401            fn allowed_discriminants(&self) -> StdResult<&'static BTreeSet<SignedEntityTypeDiscriminants>>;
402
403            fn cardano_transactions_signing_config(
404                &self,
405            ) -> StdResult<&'static Option<CardanoTransactionsSigningConfig>>;
406
407            fn next_cardano_transactions_signing_config(
408                &self,
409            ) -> StdResult<&'static Option<CardanoTransactionsSigningConfig>>;
410
411            fn can_signer_sign_current_epoch(&self, party_id: PartyId) -> StdResult<bool>;
412        }
413    }
414
415    impl MockEpochServiceImpl {
416        pub fn new_with_config(
417            config: impl FnOnce(&mut MockEpochServiceImpl),
418        ) -> MockEpochServiceImpl {
419            let mut epoch_service_mock = MockEpochServiceImpl::new();
420            config(&mut epoch_service_mock);
421
422            epoch_service_mock
423        }
424    }
425}
426
427#[cfg(test)]
428mod tests {
429    use std::sync::Arc;
430    use tokio::sync::RwLock;
431
432    use mithril_common::entities::{Epoch, StakeDistribution};
433    use mithril_common::test_utils::{fake_data, MithrilFixtureBuilder};
434
435    use crate::database::repository::{ProtocolInitializerRepository, StakePoolStore};
436    use crate::database::test_helper::main_db_connection;
437    use crate::entities::SignerEpochSettings;
438    use crate::services::MithrilProtocolInitializerBuilder;
439    use crate::test_tools::TestLogger;
440
441    use super::*;
442
443    #[test]
444    fn test_is_signer_included_in_current_stake_distribution_returns_error_when_epoch_settings_is_not_set(
445    ) {
446        let party_id = "party_id".to_string();
447        let protocol_initializer = MithrilProtocolInitializerBuilder::build(
448            &100,
449            &fake_data::protocol_parameters(),
450            None,
451            None,
452        )
453        .unwrap();
454        let connection = Arc::new(main_db_connection().unwrap());
455        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
456        let protocol_initializer_store =
457            Arc::new(ProtocolInitializerRepository::new(connection, None));
458        let service = MithrilEpochService::new(
459            stake_store,
460            protocol_initializer_store,
461            TestLogger::stdout(),
462        );
463
464        service
465            .is_signer_included_in_current_stake_distribution(party_id, &protocol_initializer)
466            .expect_err("can_signer_sign should return error when epoch settings is not set");
467    }
468
469    #[tokio::test]
470    async fn test_is_signer_included_in_current_stake_distribution_returns_true_when_signer_verification_key_and_pool_id_found(
471    ) {
472        let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
473        let protocol_initializer = fixtures.signers_fixture()[0]
474            .protocol_initializer
475            .to_owned();
476        let epoch = Epoch(12);
477        let signers = fixtures.signers();
478
479        let connection = Arc::new(main_db_connection().unwrap());
480        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
481        let protocol_initializer_store =
482            Arc::new(ProtocolInitializerRepository::new(connection, None));
483
484        let epoch_settings = SignerEpochSettings {
485            epoch,
486            current_signers: signers[..5].to_vec(),
487            ..SignerEpochSettings::dummy().clone()
488        };
489
490        let mut service = MithrilEpochService::new(
491            stake_store,
492            protocol_initializer_store,
493            TestLogger::stdout(),
494        );
495        service
496            .inform_epoch_settings(epoch_settings.clone(), BTreeSet::new())
497            .await
498            .unwrap();
499
500        let party_id = fixtures.signers_fixture()[0].party_id();
501        assert!(service
502            .is_signer_included_in_current_stake_distribution(
503                party_id.clone(),
504                &protocol_initializer
505            )
506            .unwrap());
507
508        let party_id_not_included = fixtures.signers_fixture()[6].party_id();
509        assert!(!service
510            .is_signer_included_in_current_stake_distribution(
511                party_id_not_included,
512                &protocol_initializer
513            )
514            .unwrap());
515
516        let protocol_initializer_not_included = fixtures.signers_fixture()[6]
517            .protocol_initializer
518            .to_owned();
519        assert!(!service
520            .is_signer_included_in_current_stake_distribution(
521                party_id,
522                &protocol_initializer_not_included
523            )
524            .unwrap());
525    }
526
527    mod can_signer_sign_current_epoch {
528        use super::*;
529
530        #[test]
531        fn cant_sign_if_no_protocol_initializer_are_stored() {
532            let epoch = Epoch(12);
533            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
534
535            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
536                .set_data_to_default_or_fake(epoch)
537                .alter_data(|data| {
538                    data.protocol_initializer = None;
539                    data.current_signers = fixtures.signers();
540                });
541
542            let can_sign_current_epoch = epoch_service
543                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
544                .unwrap();
545            assert!(!can_sign_current_epoch);
546        }
547
548        #[test]
549        fn cant_sign_if_stored_protocol_initializer_belong_to_another_party() {
550            let epoch = Epoch(12);
551            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
552
553            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
554                .set_data_to_default_or_fake(epoch)
555                .alter_data(|data| {
556                    data.protocol_initializer =
557                        Some(fixtures.signers_fixture()[2].protocol_initializer.clone());
558                    data.current_signers = fixtures.signers();
559                });
560
561            let can_sign_current_epoch = epoch_service
562                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
563                .unwrap();
564            assert!(!can_sign_current_epoch);
565        }
566
567        #[test]
568        fn cant_sign_if_not_in_current_signers() {
569            let epoch = Epoch(12);
570            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
571
572            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
573                .set_data_to_default_or_fake(epoch)
574                .alter_data(|data| {
575                    data.protocol_initializer =
576                        Some(fixtures.signers_fixture()[0].protocol_initializer.clone());
577                    data.current_signers = fixtures.signers()[2..].to_vec();
578                });
579
580            let can_sign_current_epoch = epoch_service
581                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
582                .unwrap();
583            assert!(!can_sign_current_epoch);
584        }
585
586        #[test]
587        fn can_sign_if_in_current_signers_and_use_the_stored_protocol_initializer() {
588            let epoch = Epoch(12);
589            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
590
591            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
592                .set_data_to_default_or_fake(epoch)
593                .alter_data(|data| {
594                    data.protocol_initializer =
595                        Some(fixtures.signers_fixture()[0].protocol_initializer.clone());
596                    data.current_signers = fixtures.signers();
597                });
598
599            let can_sign_current_epoch = epoch_service
600                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
601                .unwrap();
602            assert!(can_sign_current_epoch);
603        }
604    }
605
606    #[tokio::test]
607    async fn test_retrieve_data_return_error_before_register_epoch_settings_was_call() {
608        let epoch = Epoch(12);
609        // Signers and stake distribution
610        let signers = fake_data::signers(10);
611        let stake_distribution: StakeDistribution = signers
612            .iter()
613            .enumerate()
614            .map(|(i, signer)| (signer.party_id.clone(), (i + 1) as u64 * 100))
615            .collect();
616
617        // Init stores
618        let connection = Arc::new(main_db_connection().unwrap());
619        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
620        stake_store
621            .save_stakes(epoch, stake_distribution.clone())
622            .await
623            .expect("save_stakes should not fail");
624        let protocol_initializer_store =
625            Arc::new(ProtocolInitializerRepository::new(connection, None));
626
627        // Build service and register epoch settings
628        let service = MithrilEpochService::new(
629            stake_store,
630            protocol_initializer_store,
631            TestLogger::stdout(),
632        );
633        assert!(service.epoch_of_current_data().is_err());
634        assert!(service.registration_protocol_parameters().is_err());
635        assert!(service.protocol_initializer().is_err());
636        assert!(service.current_signers().is_err());
637        assert!(service.next_signers().is_err());
638        assert!(service.current_signers_with_stake().await.is_err());
639        assert!(service.next_signers_with_stake().await.is_err());
640        assert!(service.cardano_transactions_signing_config().is_err());
641        assert!(service.next_cardano_transactions_signing_config().is_err());
642    }
643
644    #[tokio::test]
645    async fn test_data_are_available_after_register_epoch_settings_call() {
646        let epoch = Epoch(12);
647        // Signers and stake distribution
648        let signers = fake_data::signers(10);
649
650        // Init stores
651        let connection = Arc::new(main_db_connection().unwrap());
652        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
653        let protocol_initializer_store =
654            Arc::new(ProtocolInitializerRepository::new(connection, None));
655
656        // Epoch settings
657        let epoch_settings = SignerEpochSettings {
658            epoch,
659            current_signers: signers[2..5].to_vec(),
660            next_signers: signers[3..7].to_vec(),
661            ..SignerEpochSettings::dummy().clone()
662        };
663
664        // Build service and register epoch settings
665        let mut service = MithrilEpochService::new(
666            stake_store,
667            protocol_initializer_store,
668            TestLogger::stdout(),
669        );
670
671        service
672            .inform_epoch_settings(
673                epoch_settings.clone(),
674                BTreeSet::from([SignedEntityTypeDiscriminants::CardanoImmutableFilesFull]),
675            )
676            .await
677            .unwrap();
678
679        // Check current_signers
680        {
681            let current_signers = service.current_signers().unwrap();
682            let expected_current_signers = epoch_settings.current_signers.clone();
683            assert_eq!(expected_current_signers, *current_signers);
684        }
685        // Check next_signers
686        {
687            let next_signers = service.next_signers().unwrap();
688            let expected_next_signers = epoch_settings.next_signers.clone();
689            assert_eq!(expected_next_signers, *next_signers);
690        }
691
692        // Check other data
693        assert_eq!(
694            epoch_settings.epoch,
695            service.epoch_of_current_data().unwrap()
696        );
697        assert_eq!(
698            epoch_settings.registration_protocol_parameters,
699            *service.registration_protocol_parameters().unwrap()
700        );
701        assert!(
702            service.protocol_initializer().unwrap().is_none(),
703            "protocol_initializer should be None since nothing was in store"
704        );
705
706        // Check allowed_discriminants
707        assert_eq!(
708            BTreeSet::from([SignedEntityTypeDiscriminants::CardanoImmutableFilesFull]),
709            *service.allowed_discriminants().unwrap()
710        );
711
712        // Check cardano_transactions_signing_config
713        assert_eq!(
714            epoch_settings.cardano_transactions_signing_config,
715            *service.cardano_transactions_signing_config().unwrap()
716        );
717        // Check next_cardano_transactions_signing_config
718        assert_eq!(
719            epoch_settings.next_cardano_transactions_signing_config,
720            *service.next_cardano_transactions_signing_config().unwrap()
721        );
722    }
723
724    #[tokio::test]
725    async fn test_signers_with_stake_are_available_after_register_epoch_settings_call() {
726        fn build_stake_distribution(signers: &[Signer], first_stake: u64) -> StakeDistribution {
727            signers
728                .iter()
729                .enumerate()
730                .map(|(i, signer)| (signer.party_id.clone(), first_stake + i as u64))
731                .collect()
732        }
733
734        let epoch = Epoch(12);
735
736        // Signers and stake distribution
737        let signers = fake_data::signers(10);
738        let stake_distribution: StakeDistribution = build_stake_distribution(&signers, 100);
739        let next_stake_distribution: StakeDistribution = build_stake_distribution(&signers, 500);
740
741        let connection = Arc::new(main_db_connection().unwrap());
742        let stake_store = {
743            let store = Arc::new(StakePoolStore::new(connection.clone(), None));
744            store
745                .save_stakes(
746                    epoch.offset_to_signer_retrieval_epoch().unwrap(),
747                    stake_distribution.clone(),
748                )
749                .await
750                .unwrap();
751            store
752                .save_stakes(
753                    epoch.offset_to_next_signer_retrieval_epoch(),
754                    next_stake_distribution.clone(),
755                )
756                .await
757                .unwrap();
758            store
759        };
760        let protocol_initializer_store =
761            Arc::new(ProtocolInitializerRepository::new(connection, None));
762
763        // Epoch settings
764        let epoch_settings = SignerEpochSettings {
765            epoch,
766            current_signers: signers[2..5].to_vec(),
767            next_signers: signers[3..7].to_vec(),
768            ..SignerEpochSettings::dummy().clone()
769        };
770
771        // Build service and register epoch settings
772        let mut service = MithrilEpochService::new(
773            stake_store,
774            protocol_initializer_store,
775            TestLogger::stdout(),
776        );
777        service
778            .inform_epoch_settings(epoch_settings.clone(), BTreeSet::new())
779            .await
780            .unwrap();
781
782        // Check current signers with stake
783        {
784            let current_signers = service.current_signers_with_stake().await.unwrap();
785
786            assert_eq!(epoch_settings.current_signers.len(), current_signers.len());
787            for signer in current_signers {
788                let expected_stake = stake_distribution.get(&signer.party_id).unwrap();
789                assert_eq!(expected_stake, &signer.stake);
790            }
791        }
792
793        // Check next signers with stake
794        {
795            let next_signers = service.next_signers_with_stake().await.unwrap();
796
797            assert_eq!(epoch_settings.next_signers.len(), next_signers.len());
798            for signer in next_signers {
799                let expected_stake = next_stake_distribution.get(&signer.party_id).unwrap();
800                assert_eq!(expected_stake, &signer.stake);
801            }
802        }
803    }
804
805    #[tokio::test]
806    async fn test_protocol_initializer_is_available_after_register_epoch_settings_call_if_in_store()
807    {
808        let epoch = Epoch(12);
809        let connection = Arc::new(main_db_connection().unwrap());
810        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
811        let protocol_initializer_store =
812            Arc::new(ProtocolInitializerRepository::new(connection, None));
813        protocol_initializer_store
814            .save_protocol_initializer(
815                epoch.offset_to_signer_retrieval_epoch().unwrap(),
816                fake_data::protocol_initializer("seed", 1245),
817            )
818            .await
819            .unwrap();
820
821        let mut service = MithrilEpochService::new(
822            stake_store,
823            protocol_initializer_store,
824            TestLogger::stdout(),
825        );
826        let epoch_settings = SignerEpochSettings {
827            epoch,
828            ..SignerEpochSettings::dummy().clone()
829        };
830        service
831            .inform_epoch_settings(epoch_settings, BTreeSet::new())
832            .await
833            .unwrap();
834
835        let protocol_initializer = service.protocol_initializer().unwrap();
836        assert_eq!(
837            Some(1245),
838            protocol_initializer.as_ref().map(|p| p.get_stake()),
839        );
840    }
841
842    #[tokio::test]
843    async fn is_source_of_signed_entity_config() {
844        let connection = Arc::new(main_db_connection().unwrap());
845        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
846        let protocol_initializer_store =
847            Arc::new(ProtocolInitializerRepository::new(connection, None));
848        let epoch_service = Arc::new(RwLock::new(MithrilEpochService::new(
849            stake_store,
850            protocol_initializer_store,
851            TestLogger::stdout(),
852        )));
853        let config_provider = SignerSignedEntityConfigProvider {
854            epoch_service: epoch_service.clone(),
855        };
856
857        // Fail before the first `inform_epoch_settings`
858        {
859            config_provider.get().await.expect_err(
860                "Should fail since sources data are not set before the first inform_epoch_settings",
861            );
862        }
863        // Fail after `inform_epoch_settings` if `cardano_transactions_signing_config` is not set
864        {
865            let allowed_discriminants =
866                BTreeSet::from([SignedEntityTypeDiscriminants::CardanoImmutableFilesFull]);
867
868            epoch_service
869                .write()
870                .await
871                .inform_epoch_settings(
872                    SignerEpochSettings {
873                        cardano_transactions_signing_config: None,
874                        ..SignerEpochSettings::dummy()
875                    },
876                    allowed_discriminants.clone(),
877                )
878                .await
879                .unwrap();
880
881            config_provider
882                .get()
883                .await
884                .expect_err("Should fail since cardano_transactions_signing_config is not set");
885        }
886        // Success after `inform_epoch_settings` if `cardano_transactions_signing_config` is set
887        {
888            let allowed_discriminants =
889                BTreeSet::from([SignedEntityTypeDiscriminants::CardanoImmutableFilesFull]);
890            epoch_service
891                .write()
892                .await
893                .inform_epoch_settings(
894                    SignerEpochSettings {
895                        cardano_transactions_signing_config: Some(
896                            CardanoTransactionsSigningConfig::dummy(),
897                        ),
898                        ..SignerEpochSettings::dummy()
899                    },
900                    allowed_discriminants.clone(),
901                )
902                .await
903                .unwrap();
904
905            let config = config_provider.get().await.unwrap();
906
907            assert_eq!(
908                SignedEntityConfig {
909                    allowed_discriminants,
910                    cardano_transactions_signing_config: CardanoTransactionsSigningConfig::dummy(),
911                },
912                config
913            );
914        }
915    }
916}