mithril_signer/services/
epoch_service.rs

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