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            .signer_registration_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 =
192            mithril_network_configuration.available_signed_entity_types.clone();
193
194        let cardano_transactions_signing_config = mithril_network_configuration
195            .signed_entity_types_config
196            .cardano_transactions
197            .clone();
198
199        self.epoch_data = Some(EpochData {
200            epoch: aggregator_signer_registration_epoch,
201            registration_protocol_parameters,
202            protocol_initializer,
203            current_signers,
204            next_signers,
205            allowed_discriminants,
206            cardano_transactions_signing_config,
207        });
208
209        Ok(())
210    }
211
212    fn epoch_of_current_data(&self) -> StdResult<Epoch> {
213        Ok(self.unwrap_data()?.epoch)
214    }
215
216    fn registration_protocol_parameters(&self) -> StdResult<&ProtocolParameters> {
217        Ok(&self.unwrap_data()?.registration_protocol_parameters)
218    }
219
220    fn protocol_initializer(&self) -> StdResult<&Option<ProtocolInitializer>> {
221        Ok(&self.unwrap_data()?.protocol_initializer)
222    }
223
224    fn current_signers(&self) -> StdResult<&Vec<Signer>> {
225        Ok(&self.unwrap_data()?.current_signers)
226    }
227
228    fn next_signers(&self) -> StdResult<&Vec<Signer>> {
229        Ok(&self.unwrap_data()?.next_signers)
230    }
231
232    async fn current_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_signer_retrieval_epoch()?,
236            self.current_signers()?,
237        )
238        .await
239    }
240
241    async fn next_signers_with_stake(&self) -> StdResult<Vec<SignerWithStake>> {
242        let current_epoch = self.epoch_of_current_data()?;
243        self.associate_signers_with_stake(
244            current_epoch.offset_to_next_signer_retrieval_epoch(),
245            self.next_signers()?,
246        )
247        .await
248    }
249
250    fn allowed_discriminants(&self) -> StdResult<&BTreeSet<SignedEntityTypeDiscriminants>> {
251        Ok(&self.unwrap_data()?.allowed_discriminants)
252    }
253
254    fn cardano_transactions_signing_config(
255        &self,
256    ) -> StdResult<&Option<CardanoTransactionsSigningConfig>> {
257        Ok(&self.unwrap_data()?.cardano_transactions_signing_config)
258    }
259
260    fn can_signer_sign_current_epoch(&self, party_id: PartyId) -> StdResult<bool> {
261        let epoch = self.epoch_of_current_data()?;
262        if let Some(protocol_initializer) = self.protocol_initializer()? {
263            debug!(
264                self.logger,
265                " > Got protocol initializer for this epoch ({epoch})"
266            );
267            if self
268                .is_signer_included_in_current_stake_distribution(party_id, protocol_initializer)?
269            {
270                return Ok(true);
271            } else {
272                debug!(
273                    self.logger,
274                    " > Signer not in current stake distribution. Can NOT sign"
275                );
276            }
277        } else {
278            warn!(
279                self.logger,
280                " > NO protocol initializer found for this epoch ({epoch})",
281            );
282        }
283
284        Ok(false)
285    }
286}
287
288/// Simple wrapper to the [EpochService] to implement the [SignedEntityConfigProvider] trait.
289///
290/// Needed because the epoch service is wrapped in an Arc<RwLock<>> in the dependencies, making
291/// direct usage of implemented traits methods difficult.
292pub struct SignerSignedEntityConfigProvider {
293    epoch_service: EpochServiceWrapper,
294}
295
296impl SignerSignedEntityConfigProvider {
297    /// Create a new instance of the `SignerSignedEntityConfigProvider`.
298    pub fn new(epoch_service: EpochServiceWrapper) -> Self {
299        Self { epoch_service }
300    }
301}
302
303#[async_trait]
304impl SignedEntityConfigProvider for SignerSignedEntityConfigProvider {
305    async fn get(&self) -> StdResult<SignedEntityConfig> {
306        let epoch_service = self.epoch_service.read().await;
307        let cardano_transactions_signing_config =
308            match epoch_service.cardano_transactions_signing_config()? {
309                Some(config) => Ok(config.clone()),
310                None => Err(anyhow!("No cardano transaction signing config available")),
311            }?;
312
313        Ok(SignedEntityConfig {
314            allowed_discriminants: epoch_service.allowed_discriminants()?.clone(),
315            cardano_transactions_signing_config,
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::SignedEntityTypeConfiguration;
437
438    use crate::database::repository::{ProtocolInitializerRepository, StakePoolStore};
439    use crate::database::test_helper::main_db_connection;
440    use crate::services::MithrilProtocolInitializerBuilder;
441    use crate::test_tools::TestLogger;
442
443    use super::*;
444
445    #[test]
446    fn test_is_signer_included_in_current_stake_distribution_returns_error_when_epoch_settings_is_not_set()
447     {
448        let party_id = "party_id".to_string();
449        let protocol_initializer = MithrilProtocolInitializerBuilder::build(
450            &100,
451            &fake_data::protocol_parameters(),
452            None,
453            None,
454        )
455        .unwrap();
456        let connection = Arc::new(main_db_connection().unwrap());
457        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
458        let protocol_initializer_store =
459            Arc::new(ProtocolInitializerRepository::new(connection, None));
460        let service = MithrilEpochService::new(
461            stake_store,
462            protocol_initializer_store,
463            TestLogger::stdout(),
464        );
465
466        service
467            .is_signer_included_in_current_stake_distribution(party_id, &protocol_initializer)
468            .expect_err("can_signer_sign should return error when epoch settings is not set");
469    }
470
471    #[tokio::test]
472    async fn test_is_signer_included_in_current_stake_distribution_returns_true_when_signer_verification_key_and_pool_id_found()
473     {
474        let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
475        let protocol_initializer = fixtures.signers_fixture()[0].protocol_initializer.to_owned();
476
477        let connection = Arc::new(main_db_connection().unwrap());
478        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
479        let protocol_initializer_store =
480            Arc::new(ProtocolInitializerRepository::new(connection, None));
481
482        let epoch = Epoch(12);
483        let mithril_network_configuration = MithrilNetworkConfiguration {
484            epoch,
485            available_signed_entity_types: BTreeSet::new(),
486            ..MithrilNetworkConfiguration::dummy().clone()
487        };
488
489        let signers = fixtures.signers();
490        let current_signers = signers[..5].to_vec();
491        let next_signers = signers[2..5].to_vec();
492
493        let mut service = MithrilEpochService::new(
494            stake_store,
495            protocol_initializer_store,
496            TestLogger::stdout(),
497        );
498        service
499            .inform_epoch_settings(
500                epoch,
501                mithril_network_configuration.clone(),
502                current_signers.clone(),
503                next_signers.clone(),
504            )
505            .await
506            .unwrap();
507
508        let party_id = fixtures.signers_fixture()[0].party_id();
509        assert!(
510            service
511                .is_signer_included_in_current_stake_distribution(
512                    party_id.clone(),
513                    &protocol_initializer
514                )
515                .unwrap()
516        );
517
518        let party_id_not_included = fixtures.signers_fixture()[6].party_id();
519        assert!(
520            !service
521                .is_signer_included_in_current_stake_distribution(
522                    party_id_not_included,
523                    &protocol_initializer
524                )
525                .unwrap()
526        );
527
528        let protocol_initializer_not_included =
529            fixtures.signers_fixture()[6].protocol_initializer.to_owned();
530        assert!(
531            !service
532                .is_signer_included_in_current_stake_distribution(
533                    party_id,
534                    &protocol_initializer_not_included
535                )
536                .unwrap()
537        );
538    }
539
540    mod can_signer_sign_current_epoch {
541        use super::*;
542
543        #[test]
544        fn cant_sign_if_no_protocol_initializer_are_stored() {
545            let epoch = Epoch(12);
546            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
547
548            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
549                .set_data_to_default_or_fake(epoch)
550                .alter_data(|data| {
551                    data.protocol_initializer = None;
552                    data.current_signers = fixtures.signers();
553                });
554
555            let can_sign_current_epoch = epoch_service
556                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
557                .unwrap();
558            assert!(!can_sign_current_epoch);
559        }
560
561        #[test]
562        fn cant_sign_if_stored_protocol_initializer_belong_to_another_party() {
563            let epoch = Epoch(12);
564            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
565
566            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
567                .set_data_to_default_or_fake(epoch)
568                .alter_data(|data| {
569                    data.protocol_initializer =
570                        Some(fixtures.signers_fixture()[2].protocol_initializer.clone());
571                    data.current_signers = fixtures.signers();
572                });
573
574            let can_sign_current_epoch = epoch_service
575                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
576                .unwrap();
577            assert!(!can_sign_current_epoch);
578        }
579
580        #[test]
581        fn cant_sign_if_not_in_current_signers() {
582            let epoch = Epoch(12);
583            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
584
585            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
586                .set_data_to_default_or_fake(epoch)
587                .alter_data(|data| {
588                    data.protocol_initializer =
589                        Some(fixtures.signers_fixture()[0].protocol_initializer.clone());
590                    data.current_signers = fixtures.signers()[2..].to_vec();
591                });
592
593            let can_sign_current_epoch = epoch_service
594                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
595                .unwrap();
596            assert!(!can_sign_current_epoch);
597        }
598
599        #[test]
600        fn can_sign_if_in_current_signers_and_use_the_stored_protocol_initializer() {
601            let epoch = Epoch(12);
602            let fixtures = MithrilFixtureBuilder::default().with_signers(10).build();
603
604            let epoch_service = MithrilEpochService::new_with_dumb_dependencies()
605                .set_data_to_default_or_fake(epoch)
606                .alter_data(|data| {
607                    data.protocol_initializer =
608                        Some(fixtures.signers_fixture()[0].protocol_initializer.clone());
609                    data.current_signers = fixtures.signers();
610                });
611
612            let can_sign_current_epoch = epoch_service
613                .can_signer_sign_current_epoch(fixtures.signers()[0].party_id.clone())
614                .unwrap();
615            assert!(can_sign_current_epoch);
616        }
617    }
618
619    #[tokio::test]
620    async fn test_retrieve_data_return_error_before_register_epoch_settings_was_call() {
621        let epoch = Epoch(12);
622        // Signers and stake distribution
623        let signers = fake_data::signers(10);
624        let stake_distribution: StakeDistribution = signers
625            .iter()
626            .enumerate()
627            .map(|(i, signer)| (signer.party_id.clone(), (i + 1) as u64 * 100))
628            .collect();
629
630        // Init stores
631        let connection = Arc::new(main_db_connection().unwrap());
632        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
633        stake_store
634            .save_stakes(epoch, stake_distribution.clone())
635            .await
636            .expect("save_stakes should not fail");
637        let protocol_initializer_store =
638            Arc::new(ProtocolInitializerRepository::new(connection, None));
639
640        // Build service and register epoch settings
641        let service = MithrilEpochService::new(
642            stake_store,
643            protocol_initializer_store,
644            TestLogger::stdout(),
645        );
646        assert!(service.epoch_of_current_data().is_err());
647        assert!(service.registration_protocol_parameters().is_err());
648        assert!(service.protocol_initializer().is_err());
649        assert!(service.current_signers().is_err());
650        assert!(service.next_signers().is_err());
651        assert!(service.current_signers_with_stake().await.is_err());
652        assert!(service.next_signers_with_stake().await.is_err());
653        assert!(service.cardano_transactions_signing_config().is_err());
654    }
655
656    #[tokio::test]
657    async fn test_data_are_available_after_register_epoch_settings_call() {
658        // Signers and stake distribution
659
660        // Init stores
661        let connection = Arc::new(main_db_connection().unwrap());
662        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
663        let protocol_initializer_store =
664            Arc::new(ProtocolInitializerRepository::new(connection, None));
665
666        let epoch = Epoch(12);
667        let mithril_network_configuration = MithrilNetworkConfiguration {
668            epoch,
669            available_signed_entity_types: BTreeSet::from([
670                SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
671            ]),
672            ..MithrilNetworkConfiguration::dummy().clone()
673        };
674
675        let signers = fake_data::signers(10);
676        let current_signers = signers[2..5].to_vec();
677        let next_signers = signers[3..7].to_vec();
678
679        // Build service and register epoch settings
680        let mut service = MithrilEpochService::new(
681            stake_store,
682            protocol_initializer_store,
683            TestLogger::stdout(),
684        );
685
686        service
687            .inform_epoch_settings(
688                epoch,
689                mithril_network_configuration.clone(),
690                current_signers.clone(),
691                next_signers.clone(),
692            )
693            .await
694            .unwrap();
695
696        // Check current_signers
697        {
698            let current_signers = service.current_signers().unwrap();
699            let expected_current_signers = current_signers.clone();
700            assert_eq!(expected_current_signers, *current_signers);
701        }
702        // Check next_signers
703        {
704            let next_signers = service.next_signers().unwrap();
705            let expected_next_signers = next_signers.clone();
706            assert_eq!(expected_next_signers, *next_signers);
707        }
708
709        // Check other data
710        assert_eq!(
711            mithril_network_configuration.epoch,
712            service.epoch_of_current_data().unwrap()
713        );
714        assert_eq!(
715            mithril_network_configuration.signer_registration_protocol_parameters,
716            *service.registration_protocol_parameters().unwrap()
717        );
718        assert!(
719            service.protocol_initializer().unwrap().is_none(),
720            "protocol_initializer should be None since nothing was in store"
721        );
722
723        // Check allowed_discriminants
724        assert_eq!(
725            BTreeSet::from([SignedEntityTypeDiscriminants::CardanoImmutableFilesFull]),
726            *service.allowed_discriminants().unwrap()
727        );
728
729        // Check cardano_transactions_signing_config
730        assert_eq!(
731            mithril_network_configuration
732                .signed_entity_types_config
733                .cardano_transactions,
734            *service.cardano_transactions_signing_config().unwrap()
735        );
736    }
737
738    #[tokio::test]
739    async fn test_signers_with_stake_are_available_after_register_epoch_settings_call() {
740        fn build_stake_distribution(signers: &[Signer], first_stake: u64) -> StakeDistribution {
741            signers
742                .iter()
743                .enumerate()
744                .map(|(i, signer)| (signer.party_id.clone(), first_stake + i as u64))
745                .collect()
746        }
747
748        let epoch = Epoch(12);
749
750        // Signers and stake distribution
751        let signers = fake_data::signers(10);
752        let stake_distribution: StakeDistribution = build_stake_distribution(&signers, 100);
753        let next_stake_distribution: StakeDistribution = build_stake_distribution(&signers, 500);
754
755        let connection = Arc::new(main_db_connection().unwrap());
756        let stake_store = {
757            let store = Arc::new(StakePoolStore::new(connection.clone(), None));
758            store
759                .save_stakes(
760                    epoch.offset_to_signer_retrieval_epoch().unwrap(),
761                    stake_distribution.clone(),
762                )
763                .await
764                .unwrap();
765            store
766                .save_stakes(
767                    epoch.offset_to_next_signer_retrieval_epoch(),
768                    next_stake_distribution.clone(),
769                )
770                .await
771                .unwrap();
772            store
773        };
774        let protocol_initializer_store =
775            Arc::new(ProtocolInitializerRepository::new(connection, None));
776
777        // MithrilNetworkConfiguration
778        let mithril_network_configuration = MithrilNetworkConfiguration {
779            epoch,
780            available_signed_entity_types: BTreeSet::new(),
781            ..MithrilNetworkConfiguration::dummy().clone()
782        };
783
784        let current_signers = signers[2..5].to_vec();
785        let next_signers = signers[3..7].to_vec();
786
787        // Build service and register epoch settings
788        let mut service = MithrilEpochService::new(
789            stake_store,
790            protocol_initializer_store,
791            TestLogger::stdout(),
792        );
793        service
794            .inform_epoch_settings(
795                epoch,
796                mithril_network_configuration,
797                current_signers.clone(),
798                next_signers.clone(),
799            )
800            .await
801            .unwrap();
802
803        // Check current signers with stake
804        {
805            let actual_current_signers = service.current_signers_with_stake().await.unwrap();
806
807            assert_eq!(current_signers.len(), actual_current_signers.len());
808            for signer in actual_current_signers {
809                let expected_stake = stake_distribution.get(&signer.party_id).unwrap();
810                assert_eq!(expected_stake, &signer.stake);
811            }
812        }
813
814        // Check next signers with stake
815        {
816            let actual_next_signers = service.next_signers_with_stake().await.unwrap();
817
818            assert_eq!(next_signers.len(), actual_next_signers.len());
819            for signer in actual_next_signers {
820                let expected_stake = next_stake_distribution.get(&signer.party_id).unwrap();
821                assert_eq!(expected_stake, &signer.stake);
822            }
823        }
824    }
825
826    #[tokio::test]
827    async fn test_protocol_initializer_is_available_after_register_epoch_settings_call_if_in_store()
828    {
829        let epoch = Epoch(12);
830        let connection = Arc::new(main_db_connection().unwrap());
831        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
832        let protocol_initializer_store =
833            Arc::new(ProtocolInitializerRepository::new(connection, None));
834        protocol_initializer_store
835            .save_protocol_initializer(
836                epoch.offset_to_signer_retrieval_epoch().unwrap(),
837                fake_data::protocol_initializer("seed", 1245),
838            )
839            .await
840            .unwrap();
841
842        let mut service = MithrilEpochService::new(
843            stake_store,
844            protocol_initializer_store,
845            TestLogger::stdout(),
846        );
847
848        let mithril_network_configuration = MithrilNetworkConfiguration {
849            epoch,
850            available_signed_entity_types: BTreeSet::new(),
851            ..MithrilNetworkConfiguration::dummy().clone()
852        };
853
854        service
855            .inform_epoch_settings(epoch, mithril_network_configuration, Vec::new(), Vec::new())
856            .await
857            .unwrap();
858
859        let protocol_initializer = service.protocol_initializer().unwrap();
860        assert_eq!(
861            Some(1245),
862            protocol_initializer.as_ref().map(|p| p.get_stake()),
863        );
864    }
865
866    #[tokio::test]
867    async fn is_source_of_signed_entity_config() {
868        let connection = Arc::new(main_db_connection().unwrap());
869        let stake_store = Arc::new(StakePoolStore::new(connection.clone(), None));
870        let protocol_initializer_store =
871            Arc::new(ProtocolInitializerRepository::new(connection, None));
872        let epoch_service = Arc::new(RwLock::new(MithrilEpochService::new(
873            stake_store,
874            protocol_initializer_store,
875            TestLogger::stdout(),
876        )));
877        let config_provider = SignerSignedEntityConfigProvider {
878            epoch_service: epoch_service.clone(),
879        };
880
881        // Fail before the first `inform_epoch_settings`
882        {
883            config_provider.get().await.expect_err(
884                "Should fail since sources data are not set before the first inform_epoch_settings",
885            );
886        }
887        // Fail after `inform_epoch_settings` if `cardano_transactions_signing_config` is not set
888        {
889            let signers = fake_data::signers(5);
890            let current_signers = signers[1..3].to_vec();
891            let next_signers = signers[2..5].to_vec();
892
893            epoch_service
894                .write()
895                .await
896                .inform_epoch_settings(
897                    fake_data::beacon().epoch,
898                    MithrilNetworkConfiguration {
899                        available_signed_entity_types: BTreeSet::new(),
900                        signed_entity_types_config: SignedEntityTypeConfiguration {
901                            cardano_transactions: None,
902                        },
903                        ..MithrilNetworkConfiguration::dummy()
904                    },
905                    current_signers,
906                    next_signers,
907                )
908                .await
909                .unwrap();
910
911            config_provider
912                .get()
913                .await
914                .expect_err("Should fail since cardano_transactions_signing_config is not set");
915        }
916        // Success after `inform_epoch_settings` if `cardano_transactions_signing_config` is set
917        {
918            let allowed_discriminants =
919                BTreeSet::from([SignedEntityTypeDiscriminants::CardanoImmutableFilesFull]);
920
921            let signed_entity_types_config = SignedEntityTypeConfiguration {
922                cardano_transactions: Some(CardanoTransactionsSigningConfig::dummy()),
923            };
924
925            let signers = fake_data::signers(5);
926            let current_signers = signers[1..3].to_vec();
927            let next_signers = signers[2..5].to_vec();
928
929            epoch_service
930                .write()
931                .await
932                .inform_epoch_settings(
933                    fake_data::beacon().epoch,
934                    MithrilNetworkConfiguration {
935                        available_signed_entity_types: allowed_discriminants.clone(),
936                        signed_entity_types_config,
937                        ..MithrilNetworkConfiguration::dummy()
938                    },
939                    current_signers,
940                    next_signers,
941                )
942                .await
943                .unwrap();
944
945            let config = config_provider.get().await.unwrap();
946
947            assert_eq!(
948                SignedEntityConfig {
949                    allowed_discriminants,
950                    cardano_transactions_signing_config: CardanoTransactionsSigningConfig::dummy(),
951                },
952                config
953            );
954        }
955    }
956}