mithril_signer/services/
epoch_service.rs

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