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_utils::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_utils::{MithrilFixtureBuilder, fake_data};
432
433    use crate::database::repository::{ProtocolInitializerRepository, StakePoolStore};
434    use crate::database::test_helper::main_db_connection;
435    use crate::entities::SignerEpochSettings;
436    use crate::services::MithrilProtocolInitializerBuilder;
437    use crate::test_tools::TestLogger;
438
439    use super::*;
440
441    #[test]
442    fn test_is_signer_included_in_current_stake_distribution_returns_error_when_epoch_settings_is_not_set()
443     {
444        let party_id = "party_id".to_string();
445        let protocol_initializer = MithrilProtocolInitializerBuilder::build(
446            &100,
447            &fake_data::protocol_parameters(),
448            None,
449            None,
450        )
451        .unwrap();
452        let connection = Arc::new(main_db_connection().unwrap());
453        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
454        let protocol_initializer_store =
455            Arc::new(ProtocolInitializerRepository::new(connection, None));
456        let service = MithrilEpochService::new(
457            stake_store,
458            protocol_initializer_store,
459            TestLogger::stdout(),
460        );
461
462        service
463            .is_signer_included_in_current_stake_distribution(party_id, &protocol_initializer)
464            .expect_err("can_signer_sign should return error when epoch settings is not set");
465    }
466
467    #[tokio::test]
468    async fn test_is_signer_included_in_current_stake_distribution_returns_true_when_signer_verification_key_and_pool_id_found()
469     {
470        let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
471        let protocol_initializer = fixtures.signers_fixture()[0].protocol_initializer.to_owned();
472        let epoch = Epoch(12);
473        let signers = fixtures.signers();
474
475        let connection = Arc::new(main_db_connection().unwrap());
476        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
477        let protocol_initializer_store =
478            Arc::new(ProtocolInitializerRepository::new(connection, None));
479
480        let epoch_settings = SignerEpochSettings {
481            epoch,
482            current_signers: signers[..5].to_vec(),
483            ..SignerEpochSettings::dummy().clone()
484        };
485
486        let mut service = MithrilEpochService::new(
487            stake_store,
488            protocol_initializer_store,
489            TestLogger::stdout(),
490        );
491        service
492            .inform_epoch_settings(epoch_settings.clone(), BTreeSet::new())
493            .await
494            .unwrap();
495
496        let party_id = fixtures.signers_fixture()[0].party_id();
497        assert!(
498            service
499                .is_signer_included_in_current_stake_distribution(
500                    party_id.clone(),
501                    &protocol_initializer
502                )
503                .unwrap()
504        );
505
506        let party_id_not_included = fixtures.signers_fixture()[6].party_id();
507        assert!(
508            !service
509                .is_signer_included_in_current_stake_distribution(
510                    party_id_not_included,
511                    &protocol_initializer
512                )
513                .unwrap()
514        );
515
516        let protocol_initializer_not_included =
517            fixtures.signers_fixture()[6].protocol_initializer.to_owned();
518        assert!(
519            !service
520                .is_signer_included_in_current_stake_distribution(
521                    party_id,
522                    &protocol_initializer_not_included
523                )
524                .unwrap()
525        );
526    }
527
528    mod can_signer_sign_current_epoch {
529        use super::*;
530
531        #[test]
532        fn cant_sign_if_no_protocol_initializer_are_stored() {
533            let epoch = Epoch(12);
534            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
535
536            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
537                .set_data_to_default_or_fake(epoch)
538                .alter_data(|data| {
539                    data.protocol_initializer = None;
540                    data.current_signers = fixtures.signers();
541                });
542
543            let can_sign_current_epoch = epoch_service
544                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
545                .unwrap();
546            assert!(!can_sign_current_epoch);
547        }
548
549        #[test]
550        fn cant_sign_if_stored_protocol_initializer_belong_to_another_party() {
551            let epoch = Epoch(12);
552            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
553
554            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
555                .set_data_to_default_or_fake(epoch)
556                .alter_data(|data| {
557                    data.protocol_initializer =
558                        Some(fixtures.signers_fixture()[2].protocol_initializer.clone());
559                    data.current_signers = fixtures.signers();
560                });
561
562            let can_sign_current_epoch = epoch_service
563                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
564                .unwrap();
565            assert!(!can_sign_current_epoch);
566        }
567
568        #[test]
569        fn cant_sign_if_not_in_current_signers() {
570            let epoch = Epoch(12);
571            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
572
573            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
574                .set_data_to_default_or_fake(epoch)
575                .alter_data(|data| {
576                    data.protocol_initializer =
577                        Some(fixtures.signers_fixture()[0].protocol_initializer.clone());
578                    data.current_signers = fixtures.signers()[2..].to_vec();
579                });
580
581            let can_sign_current_epoch = epoch_service
582                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
583                .unwrap();
584            assert!(!can_sign_current_epoch);
585        }
586
587        #[test]
588        fn can_sign_if_in_current_signers_and_use_the_stored_protocol_initializer() {
589            let epoch = Epoch(12);
590            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
591
592            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
593                .set_data_to_default_or_fake(epoch)
594                .alter_data(|data| {
595                    data.protocol_initializer =
596                        Some(fixtures.signers_fixture()[0].protocol_initializer.clone());
597                    data.current_signers = fixtures.signers();
598                });
599
600            let can_sign_current_epoch = epoch_service
601                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
602                .unwrap();
603            assert!(can_sign_current_epoch);
604        }
605    }
606
607    #[tokio::test]
608    async fn test_retrieve_data_return_error_before_register_epoch_settings_was_call() {
609        let epoch = Epoch(12);
610        // Signers and stake distribution
611        let signers = fake_data::signers(10);
612        let stake_distribution: StakeDistribution = signers
613            .iter()
614            .enumerate()
615            .map(|(i, signer)| (signer.party_id.clone(), (i + 1) as u64 * 100))
616            .collect();
617
618        // Init stores
619        let connection = Arc::new(main_db_connection().unwrap());
620        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
621        stake_store
622            .save_stakes(epoch, stake_distribution.clone())
623            .await
624            .expect("save_stakes should not fail");
625        let protocol_initializer_store =
626            Arc::new(ProtocolInitializerRepository::new(connection, None));
627
628        // Build service and register epoch settings
629        let service = MithrilEpochService::new(
630            stake_store,
631            protocol_initializer_store,
632            TestLogger::stdout(),
633        );
634        assert!(service.epoch_of_current_data().is_err());
635        assert!(service.registration_protocol_parameters().is_err());
636        assert!(service.protocol_initializer().is_err());
637        assert!(service.current_signers().is_err());
638        assert!(service.next_signers().is_err());
639        assert!(service.current_signers_with_stake().await.is_err());
640        assert!(service.next_signers_with_stake().await.is_err());
641        assert!(service.cardano_transactions_signing_config().is_err());
642        assert!(service.next_cardano_transactions_signing_config().is_err());
643    }
644
645    #[tokio::test]
646    async fn test_data_are_available_after_register_epoch_settings_call() {
647        let epoch = Epoch(12);
648        // Signers and stake distribution
649        let signers = fake_data::signers(10);
650
651        // Init stores
652        let connection = Arc::new(main_db_connection().unwrap());
653        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
654        let protocol_initializer_store =
655            Arc::new(ProtocolInitializerRepository::new(connection, None));
656
657        // Epoch settings
658        let epoch_settings = SignerEpochSettings {
659            epoch,
660            current_signers: signers[2..5].to_vec(),
661            next_signers: signers[3..7].to_vec(),
662            ..SignerEpochSettings::dummy().clone()
663        };
664
665        // Build service and register epoch settings
666        let mut service = MithrilEpochService::new(
667            stake_store,
668            protocol_initializer_store,
669            TestLogger::stdout(),
670        );
671
672        service
673            .inform_epoch_settings(
674                epoch_settings.clone(),
675                BTreeSet::from([SignedEntityTypeDiscriminants::CardanoImmutableFilesFull]),
676            )
677            .await
678            .unwrap();
679
680        // Check current_signers
681        {
682            let current_signers = service.current_signers().unwrap();
683            let expected_current_signers = epoch_settings.current_signers.clone();
684            assert_eq!(expected_current_signers, *current_signers);
685        }
686        // Check next_signers
687        {
688            let next_signers = service.next_signers().unwrap();
689            let expected_next_signers = epoch_settings.next_signers.clone();
690            assert_eq!(expected_next_signers, *next_signers);
691        }
692
693        // Check other data
694        assert_eq!(
695            epoch_settings.epoch,
696            service.epoch_of_current_data().unwrap()
697        );
698        assert_eq!(
699            epoch_settings.registration_protocol_parameters,
700            *service.registration_protocol_parameters().unwrap()
701        );
702        assert!(
703            service.protocol_initializer().unwrap().is_none(),
704            "protocol_initializer should be None since nothing was in store"
705        );
706
707        // Check allowed_discriminants
708        assert_eq!(
709            BTreeSet::from([SignedEntityTypeDiscriminants::CardanoImmutableFilesFull]),
710            *service.allowed_discriminants().unwrap()
711        );
712
713        // Check cardano_transactions_signing_config
714        assert_eq!(
715            epoch_settings.cardano_transactions_signing_config,
716            *service.cardano_transactions_signing_config().unwrap()
717        );
718        // Check next_cardano_transactions_signing_config
719        assert_eq!(
720            epoch_settings.next_cardano_transactions_signing_config,
721            *service.next_cardano_transactions_signing_config().unwrap()
722        );
723    }
724
725    #[tokio::test]
726    async fn test_signers_with_stake_are_available_after_register_epoch_settings_call() {
727        fn build_stake_distribution(signers: &[Signer], first_stake: u64) -> StakeDistribution {
728            signers
729                .iter()
730                .enumerate()
731                .map(|(i, signer)| (signer.party_id.clone(), first_stake + i as u64))
732                .collect()
733        }
734
735        let epoch = Epoch(12);
736
737        // Signers and stake distribution
738        let signers = fake_data::signers(10);
739        let stake_distribution: StakeDistribution = build_stake_distribution(&signers, 100);
740        let next_stake_distribution: StakeDistribution = build_stake_distribution(&signers, 500);
741
742        let connection = Arc::new(main_db_connection().unwrap());
743        let stake_store = {
744            let store = Arc::new(StakePoolStore::new(connection.clone(), None));
745            store
746                .save_stakes(
747                    epoch.offset_to_signer_retrieval_epoch().unwrap(),
748                    stake_distribution.clone(),
749                )
750                .await
751                .unwrap();
752            store
753                .save_stakes(
754                    epoch.offset_to_next_signer_retrieval_epoch(),
755                    next_stake_distribution.clone(),
756                )
757                .await
758                .unwrap();
759            store
760        };
761        let protocol_initializer_store =
762            Arc::new(ProtocolInitializerRepository::new(connection, None));
763
764        // Epoch settings
765        let epoch_settings = SignerEpochSettings {
766            epoch,
767            current_signers: signers[2..5].to_vec(),
768            next_signers: signers[3..7].to_vec(),
769            ..SignerEpochSettings::dummy().clone()
770        };
771
772        // Build service and register epoch settings
773        let mut service = MithrilEpochService::new(
774            stake_store,
775            protocol_initializer_store,
776            TestLogger::stdout(),
777        );
778        service
779            .inform_epoch_settings(epoch_settings.clone(), BTreeSet::new())
780            .await
781            .unwrap();
782
783        // Check current signers with stake
784        {
785            let current_signers = service.current_signers_with_stake().await.unwrap();
786
787            assert_eq!(epoch_settings.current_signers.len(), current_signers.len());
788            for signer in current_signers {
789                let expected_stake = stake_distribution.get(&signer.party_id).unwrap();
790                assert_eq!(expected_stake, &signer.stake);
791            }
792        }
793
794        // Check next signers with stake
795        {
796            let next_signers = service.next_signers_with_stake().await.unwrap();
797
798            assert_eq!(epoch_settings.next_signers.len(), next_signers.len());
799            for signer in next_signers {
800                let expected_stake = next_stake_distribution.get(&signer.party_id).unwrap();
801                assert_eq!(expected_stake, &signer.stake);
802            }
803        }
804    }
805
806    #[tokio::test]
807    async fn test_protocol_initializer_is_available_after_register_epoch_settings_call_if_in_store()
808    {
809        let epoch = Epoch(12);
810        let connection = Arc::new(main_db_connection().unwrap());
811        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
812        let protocol_initializer_store =
813            Arc::new(ProtocolInitializerRepository::new(connection, None));
814        protocol_initializer_store
815            .save_protocol_initializer(
816                epoch.offset_to_signer_retrieval_epoch().unwrap(),
817                fake_data::protocol_initializer("seed", 1245),
818            )
819            .await
820            .unwrap();
821
822        let mut service = MithrilEpochService::new(
823            stake_store,
824            protocol_initializer_store,
825            TestLogger::stdout(),
826        );
827        let epoch_settings = SignerEpochSettings {
828            epoch,
829            ..SignerEpochSettings::dummy().clone()
830        };
831        service
832            .inform_epoch_settings(epoch_settings, BTreeSet::new())
833            .await
834            .unwrap();
835
836        let protocol_initializer = service.protocol_initializer().unwrap();
837        assert_eq!(
838            Some(1245),
839            protocol_initializer.as_ref().map(|p| p.get_stake()),
840        );
841    }
842
843    #[tokio::test]
844    async fn is_source_of_signed_entity_config() {
845        let connection = Arc::new(main_db_connection().unwrap());
846        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
847        let protocol_initializer_store =
848            Arc::new(ProtocolInitializerRepository::new(connection, None));
849        let epoch_service = Arc::new(RwLock::new(MithrilEpochService::new(
850            stake_store,
851            protocol_initializer_store,
852            TestLogger::stdout(),
853        )));
854        let config_provider = SignerSignedEntityConfigProvider {
855            epoch_service: epoch_service.clone(),
856        };
857
858        // Fail before the first `inform_epoch_settings`
859        {
860            config_provider.get().await.expect_err(
861                "Should fail since sources data are not set before the first inform_epoch_settings",
862            );
863        }
864        // Fail after `inform_epoch_settings` if `cardano_transactions_signing_config` is not set
865        {
866            let allowed_discriminants =
867                BTreeSet::from([SignedEntityTypeDiscriminants::CardanoImmutableFilesFull]);
868
869            epoch_service
870                .write()
871                .await
872                .inform_epoch_settings(
873                    SignerEpochSettings {
874                        cardano_transactions_signing_config: None,
875                        ..SignerEpochSettings::dummy()
876                    },
877                    allowed_discriminants.clone(),
878                )
879                .await
880                .unwrap();
881
882            config_provider
883                .get()
884                .await
885                .expect_err("Should fail since cardano_transactions_signing_config is not set");
886        }
887        // Success after `inform_epoch_settings` if `cardano_transactions_signing_config` is set
888        {
889            let allowed_discriminants =
890                BTreeSet::from([SignedEntityTypeDiscriminants::CardanoImmutableFilesFull]);
891            epoch_service
892                .write()
893                .await
894                .inform_epoch_settings(
895                    SignerEpochSettings {
896                        cardano_transactions_signing_config: Some(
897                            CardanoTransactionsSigningConfig::dummy(),
898                        ),
899                        ..SignerEpochSettings::dummy()
900                    },
901                    allowed_discriminants.clone(),
902                )
903                .await
904                .unwrap();
905
906            let config = config_provider.get().await.unwrap();
907
908            assert_eq!(
909                SignedEntityConfig {
910                    allowed_discriminants,
911                    cardano_transactions_signing_config: CardanoTransactionsSigningConfig::dummy(),
912                },
913                config
914            );
915        }
916    }
917}