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