mithril_signer/services/
epoch_service.rs

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