mithril_aggregator/services/
epoch_service.rs

1use anyhow::Context;
2use async_trait::async_trait;
3use slog::{Logger, debug};
4use std::collections::BTreeSet;
5use std::sync::Arc;
6use thiserror::Error;
7
8use mithril_cardano_node_chain::chain_observer::ChainObserver;
9use mithril_common::StdResult;
10use mithril_common::crypto_helper::ProtocolAggregateVerificationKey;
11use mithril_common::entities::{
12    CardanoEra, Epoch, ProtocolParameters, SignedEntityConfig, SignedEntityTypeDiscriminants,
13    Signer, SignerWithStake, Stake, SupportedEra, TotalSPOs,
14};
15use mithril_common::logging::LoggerExtensions;
16use mithril_common::protocol::{MultiSigner as ProtocolMultiSigner, SignerBuilder};
17use mithril_era::EraChecker;
18use mithril_persistence::store::StakeStorer;
19use mithril_protocol_config::interface::MithrilNetworkConfigurationProvider;
20use mithril_protocol_config::model::MithrilNetworkConfiguration;
21
22use crate::{EpochSettingsStorer, VerificationKeyStorer, entities::AggregatorEpochSettings};
23
24/// Errors dedicated to the CertifierService.
25#[derive(Debug, Error)]
26pub enum EpochServiceError {
27    /// One of the data that is held for an epoch duration by the service was not available.
28    #[error("Epoch service could not obtain {1} for epoch {0}")]
29    UnavailableData(Epoch, String),
30
31    /// Raised when service has not collected data at least once.
32    #[error("Epoch service was not initialized, the function `inform_epoch` must be called first")]
33    NotYetInitialized,
34
35    /// Raised when service has not computed data for its current epoch.
36    #[error(
37        "No data computed for epoch {0}, the function `precompute_epoch_data` must be called first"
38    )]
39    NotYetComputed(Epoch),
40}
41
42/// Service that aggregates all data that don't change in a given epoch.
43#[async_trait]
44pub trait EpochService: Sync + Send {
45    /// Inform the service a new epoch has been detected, telling it to update its
46    /// internal state for the new epoch.
47    async fn inform_epoch(&mut self, epoch: Epoch) -> StdResult<()>;
48
49    /// Update the next signers with stake for the next epoch.
50    async fn update_next_signers_with_stake(&mut self) -> StdResult<()>;
51
52    /// Inform the service that it can precompute data for its current epoch.
53    ///
54    /// Note: must be called after `inform_epoch`.
55    async fn precompute_epoch_data(&mut self) -> StdResult<()>;
56
57    /// Get the current Cardano era.
58    fn cardano_era(&self) -> StdResult<CardanoEra>;
59
60    /// Get the current Mithril era.
61    fn mithril_era(&self) -> StdResult<SupportedEra>;
62
63    /// Get the current epoch for which the data stored in this service are computed.
64    fn epoch_of_current_data(&self) -> StdResult<Epoch>;
65
66    /// Get the network configuration for the current epoch.
67    fn network_configuration(&self) -> StdResult<&MithrilNetworkConfiguration>;
68
69    /// Get protocol parameters used for signing in the current epoch.
70    fn current_protocol_parameters(&self) -> StdResult<&ProtocolParameters> {
71        Ok(&self
72            .network_configuration()?
73            .configuration_for_aggregation
74            .protocol_parameters)
75    }
76
77    /// Get protocol parameters used for signing in the next epoch.
78    fn next_protocol_parameters(&self) -> StdResult<&ProtocolParameters> {
79        Ok(&self
80            .network_configuration()?
81            .configuration_for_next_aggregation
82            .protocol_parameters)
83    }
84
85    /// Get protocol parameters for signer registration.
86    fn signer_registration_protocol_parameters(&self) -> StdResult<&ProtocolParameters> {
87        Ok(&self
88            .network_configuration()?
89            .configuration_for_registration
90            .protocol_parameters)
91    }
92
93    /// Get aggregate verification key for current epoch
94    fn current_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey>;
95
96    /// Get next aggregate verification key for next epoch
97    fn next_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey>;
98
99    /// Get signers with stake for the current epoch
100    fn current_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>>;
101
102    /// Get signers with stake for the next epoch
103    fn next_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>>;
104
105    /// Get signers for the current epoch
106    fn current_signers(&self) -> StdResult<&Vec<Signer>>;
107
108    /// Get signers for the next epoch
109    fn next_signers(&self) -> StdResult<&Vec<Signer>>;
110
111    /// Get the total stakes of signers for the current epoch
112    fn total_stakes_signers(&self) -> StdResult<Stake>;
113
114    /// Get the total stakes of signers for the next epoch
115    fn total_next_stakes_signers(&self) -> StdResult<Stake>;
116
117    /// Get the [protocol multi signer][ProtocolMultiSigner] for the current epoch
118    fn protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner>;
119
120    /// Get the [protocol multi signer][ProtocolMultiSigner] for the next epoch
121    fn next_protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner>;
122
123    /// Get the [SignedEntityConfig] for the current epoch.
124    fn signed_entity_config(&self) -> StdResult<&SignedEntityConfig>;
125
126    /// Get the total number of SPOs for the current epoch in the Cardano stake distribution.
127    ///
128    /// Optional as the stake distribution is not available at Aggregator first startup.
129    fn total_spo(&self) -> StdResult<Option<TotalSPOs>>;
130
131    /// Get the total stake for the current epoch in the Cardano stake distribution.
132    ///
133    /// Optional as the stake distribution is not available at Aggregator first startup.
134    fn total_stake(&self) -> StdResult<Option<Stake>>;
135}
136
137struct EpochData {
138    cardano_era: CardanoEra,
139    mithril_era: SupportedEra,
140    epoch: Epoch,
141    network_configuration: MithrilNetworkConfiguration,
142    current_signers_with_stake: Vec<SignerWithStake>,
143    next_signers_with_stake: Vec<SignerWithStake>,
144    current_signers: Vec<Signer>,
145    next_signers: Vec<Signer>,
146    total_stakes_signers: Stake,
147    total_next_stakes_signers: Stake,
148    signed_entity_config: SignedEntityConfig,
149    total_spo: Option<TotalSPOs>,
150    total_stake: Option<Stake>,
151}
152
153struct ComputedEpochData {
154    aggregate_verification_key: ProtocolAggregateVerificationKey,
155    next_aggregate_verification_key: ProtocolAggregateVerificationKey,
156    protocol_multi_signer: ProtocolMultiSigner,
157    next_protocol_multi_signer: ProtocolMultiSigner,
158}
159
160/// Dependencies required by the [MithrilEpochService].
161pub struct EpochServiceDependencies {
162    mithril_network_configuration_provider: Arc<dyn MithrilNetworkConfigurationProvider>,
163    epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
164    verification_key_store: Arc<dyn VerificationKeyStorer>,
165    chain_observer: Arc<dyn ChainObserver>,
166    era_checker: Arc<EraChecker>,
167    stake_store: Arc<dyn StakeStorer>,
168}
169
170impl EpochServiceDependencies {
171    /// Create a new instance of [EpochServiceDependencies].
172    pub fn new(
173        mithril_network_configuration_provider: Arc<dyn MithrilNetworkConfigurationProvider>,
174        epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
175        verification_key_store: Arc<dyn VerificationKeyStorer>,
176        chain_observer: Arc<dyn ChainObserver>,
177        era_checker: Arc<EraChecker>,
178        stake_store: Arc<dyn StakeStorer>,
179    ) -> Self {
180        Self {
181            mithril_network_configuration_provider,
182            epoch_settings_storer,
183            verification_key_store,
184            chain_observer,
185            era_checker,
186            stake_store,
187        }
188    }
189}
190
191/// Implementation of the [epoch service][EpochService].
192pub struct MithrilEpochService {
193    epoch_data: Option<EpochData>,
194    computed_epoch_data: Option<ComputedEpochData>,
195    mithril_network_configuration_provider: Arc<dyn MithrilNetworkConfigurationProvider>,
196    epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
197    verification_key_store: Arc<dyn VerificationKeyStorer>,
198    chain_observer: Arc<dyn ChainObserver>,
199    era_checker: Arc<EraChecker>,
200    stake_store: Arc<dyn StakeStorer>,
201    allowed_signed_entity_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
202    logger: Logger,
203}
204
205impl MithrilEpochService {
206    /// Create a new service instance
207    pub fn new(
208        dependencies: EpochServiceDependencies,
209        allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
210        logger: Logger,
211    ) -> Self {
212        Self {
213            epoch_data: None,
214            computed_epoch_data: None,
215            mithril_network_configuration_provider: dependencies
216                .mithril_network_configuration_provider,
217            epoch_settings_storer: dependencies.epoch_settings_storer,
218            verification_key_store: dependencies.verification_key_store,
219            chain_observer: dependencies.chain_observer,
220            era_checker: dependencies.era_checker,
221            stake_store: dependencies.stake_store,
222            allowed_signed_entity_discriminants: allowed_discriminants,
223            logger: logger.new_with_component_name::<Self>(),
224        }
225    }
226
227    async fn get_cardano_era(&self) -> StdResult<CardanoEra> {
228        let cardano_era = self
229            .chain_observer
230            .get_current_era()
231            .await?
232            .with_context(|| "No Cardano era returned by the chain observer")?;
233
234        Ok(cardano_era)
235    }
236
237    async fn get_total_spo_and_total_stake(
238        &self,
239        epoch: Epoch,
240    ) -> StdResult<(Option<TotalSPOs>, Option<Stake>)> {
241        match self.stake_store.get_stakes(epoch).await.with_context(|| {
242            format!("Epoch service failed to obtain the stake distribution for epoch: {epoch}")
243        })? {
244            None => Ok((None, None)),
245            Some(sd) => Ok((Some(sd.len() as TotalSPOs), Some(sd.values().sum()))),
246        }
247    }
248
249    async fn get_signers_with_stake_at_epoch(
250        &self,
251        signer_retrieval_epoch: Epoch,
252    ) -> StdResult<Vec<SignerWithStake>> {
253        let signers = self
254            .verification_key_store
255            .get_signers(signer_retrieval_epoch)
256            .await?
257            .unwrap_or_default();
258
259        Ok(signers)
260    }
261
262    async fn insert_epoch_settings(
263        &self,
264        recording_epoch: Epoch,
265        epoch_settings: &AggregatorEpochSettings,
266    ) -> StdResult<()> {
267        self.epoch_settings_storer
268            .save_epoch_settings(
269                recording_epoch,
270                epoch_settings.clone()
271            )
272            .await
273            .with_context(|| format!("Epoch service failed to insert future_epoch_settings to epoch {recording_epoch}"))
274            .map(|_| ())
275    }
276
277    fn unwrap_data(&self) -> Result<&EpochData, EpochServiceError> {
278        self.epoch_data.as_ref().ok_or(EpochServiceError::NotYetInitialized)
279    }
280
281    fn unwrap_computed_data(&self) -> Result<&ComputedEpochData, EpochServiceError> {
282        let epoch = self.unwrap_data()?.epoch;
283
284        self.computed_epoch_data
285            .as_ref()
286            .ok_or(EpochServiceError::NotYetComputed(epoch))
287    }
288}
289
290#[async_trait]
291impl EpochService for MithrilEpochService {
292    async fn inform_epoch(&mut self, epoch: Epoch) -> StdResult<()> {
293        debug!(self.logger, ">> inform_epoch(epoch: {epoch:?})");
294
295        let cardano_era = self.get_cardano_era().await?;
296
297        let mithril_era = self.era_checker.current_era();
298
299        let signer_retrieval_epoch =
300            epoch.offset_to_signer_retrieval_epoch().with_context(|| {
301                format!("EpochService could not compute signer retrieval epoch from epoch: {epoch}")
302            })?;
303        let next_signer_retrieval_epoch = epoch.offset_to_next_signer_retrieval_epoch();
304        let signer_registration_epoch = epoch.offset_to_recording_epoch();
305
306        let network_configuration = self
307            .mithril_network_configuration_provider
308            .get_network_configuration(epoch)
309            .await?;
310
311        let signer_registration_epoch_settings = AggregatorEpochSettings {
312            protocol_parameters: network_configuration
313                .configuration_for_registration
314                .protocol_parameters
315                .clone(),
316            cardano_transactions_signing_config: network_configuration
317                .configuration_for_registration
318                .signed_entity_types_config
319                .cardano_transactions
320                .clone(),
321        };
322        self.insert_epoch_settings(
323            signer_registration_epoch,
324            &signer_registration_epoch_settings,
325        )
326        .await?;
327
328        let current_signers_with_stake =
329            self.get_signers_with_stake_at_epoch(signer_retrieval_epoch).await?;
330        let next_signers_with_stake = self
331            .get_signers_with_stake_at_epoch(next_signer_retrieval_epoch)
332            .await?;
333        let current_signers = Signer::vec_from(current_signers_with_stake.clone());
334        let next_signers = Signer::vec_from(next_signers_with_stake.clone());
335        let total_stakes_signers = current_signers_with_stake.iter().map(|s| s.stake).sum();
336        let total_next_stakes_signers = next_signers_with_stake.iter().map(|s| s.stake).sum();
337
338        let signed_entity_config = SignedEntityConfig {
339            allowed_discriminants: self
340                .allowed_signed_entity_discriminants
341                .intersection(
342                    &network_configuration
343                        .configuration_for_aggregation
344                        .enabled_signed_entity_types,
345                )
346                .cloned()
347                .collect(),
348            cardano_transactions_signing_config: network_configuration
349                .configuration_for_aggregation
350                .signed_entity_types_config
351                .cardano_transactions
352                .clone(),
353        };
354
355        let (total_spo, total_stake) =
356            self.get_total_spo_and_total_stake(signer_retrieval_epoch).await?;
357
358        self.epoch_data = Some(EpochData {
359            cardano_era,
360            mithril_era,
361            epoch,
362            network_configuration,
363            current_signers_with_stake,
364            next_signers_with_stake,
365            current_signers,
366            next_signers,
367            total_stakes_signers,
368            total_next_stakes_signers,
369            signed_entity_config,
370            total_spo,
371            total_stake,
372        });
373        self.computed_epoch_data = None;
374
375        Ok(())
376    }
377
378    async fn update_next_signers_with_stake(&mut self) -> StdResult<()> {
379        debug!(self.logger, ">> update_next_signers_with_stake");
380
381        let data = self.unwrap_data().with_context(
382            || "can't update next signers with stake if inform_epoch has not been called first",
383        )?;
384
385        let next_signer_retrieval_epoch = data.epoch.offset_to_next_signer_retrieval_epoch();
386        let next_signers_with_stake = self
387            .get_signers_with_stake_at_epoch(next_signer_retrieval_epoch)
388            .await?;
389
390        self.epoch_data.as_mut().unwrap().next_signers_with_stake = next_signers_with_stake;
391
392        self.precompute_epoch_data()
393            .await
394            .with_context(|| "Epoch service failed to precompute epoch data")?;
395
396        Ok(())
397    }
398
399    async fn precompute_epoch_data(&mut self) -> StdResult<()> {
400        debug!(self.logger, ">> precompute_epoch_data");
401
402        let data = self.unwrap_data().with_context(
403            || "can't precompute epoch data if inform_epoch has not been called first",
404        )?;
405
406        let protocol_multi_signer = SignerBuilder::new(
407            &data.current_signers_with_stake,
408            &data
409                .network_configuration
410                .configuration_for_aggregation
411                .protocol_parameters,
412        )
413        .with_context(|| "Epoch service failed to build protocol multi signer")?
414        .build_multi_signer();
415
416        let next_protocol_multi_signer = SignerBuilder::new(
417            &data.next_signers_with_stake,
418            &data
419                .network_configuration
420                .configuration_for_next_aggregation
421                .protocol_parameters,
422        )
423        .with_context(|| "Epoch service failed to build next protocol multi signer")?
424        .build_multi_signer();
425
426        self.computed_epoch_data = Some(ComputedEpochData {
427            aggregate_verification_key: protocol_multi_signer.compute_aggregate_verification_key(),
428            next_aggregate_verification_key: next_protocol_multi_signer
429                .compute_aggregate_verification_key(),
430            protocol_multi_signer,
431            next_protocol_multi_signer,
432        });
433
434        Ok(())
435    }
436
437    fn cardano_era(&self) -> StdResult<CardanoEra> {
438        Ok(self.unwrap_data()?.cardano_era.clone())
439    }
440
441    fn mithril_era(&self) -> StdResult<SupportedEra> {
442        Ok(self.unwrap_data()?.mithril_era)
443    }
444
445    fn epoch_of_current_data(&self) -> StdResult<Epoch> {
446        Ok(self.unwrap_data()?.epoch)
447    }
448
449    fn network_configuration(&self) -> StdResult<&MithrilNetworkConfiguration> {
450        Ok(&self.unwrap_data()?.network_configuration)
451    }
452
453    fn current_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey> {
454        Ok(&self.unwrap_computed_data()?.aggregate_verification_key)
455    }
456
457    fn next_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey> {
458        Ok(&self.unwrap_computed_data()?.next_aggregate_verification_key)
459    }
460
461    fn current_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>> {
462        Ok(&self.unwrap_data()?.current_signers_with_stake)
463    }
464
465    fn next_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>> {
466        Ok(&self.unwrap_data()?.next_signers_with_stake)
467    }
468
469    fn current_signers(&self) -> StdResult<&Vec<Signer>> {
470        Ok(&self.unwrap_data()?.current_signers)
471    }
472
473    fn next_signers(&self) -> StdResult<&Vec<Signer>> {
474        Ok(&self.unwrap_data()?.next_signers)
475    }
476
477    fn total_stakes_signers(&self) -> StdResult<Stake> {
478        Ok(self.unwrap_data()?.total_stakes_signers)
479    }
480
481    fn total_next_stakes_signers(&self) -> StdResult<Stake> {
482        Ok(self.unwrap_data()?.total_next_stakes_signers)
483    }
484
485    fn protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner> {
486        Ok(&self.unwrap_computed_data()?.protocol_multi_signer)
487    }
488
489    fn next_protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner> {
490        Ok(&self.unwrap_computed_data()?.next_protocol_multi_signer)
491    }
492
493    fn signed_entity_config(&self) -> StdResult<&SignedEntityConfig> {
494        Ok(&self.unwrap_data()?.signed_entity_config)
495    }
496
497    fn total_spo(&self) -> StdResult<Option<TotalSPOs>> {
498        Ok(self.unwrap_data()?.total_spo)
499    }
500
501    fn total_stake(&self) -> StdResult<Option<Stake>> {
502        Ok(self.unwrap_data()?.total_stake)
503    }
504}
505
506#[cfg(test)]
507pub(crate) struct FakeEpochService {
508    epoch_data: Option<EpochData>,
509    computed_epoch_data: Option<ComputedEpochData>,
510    inform_epoch_error: bool,
511    precompute_epoch_data_error: bool,
512    update_next_signers_with_stake_error: bool,
513}
514
515#[cfg(test)]
516pub(crate) struct FakeEpochServiceBuilder {
517    pub cardano_era: CardanoEra,
518    pub mithril_era: SupportedEra,
519    pub epoch: Epoch,
520    pub current_epoch_settings: AggregatorEpochSettings,
521    pub next_epoch_settings: AggregatorEpochSettings,
522    pub signer_registration_epoch_settings: AggregatorEpochSettings,
523    pub current_signers_with_stake: Vec<SignerWithStake>,
524    pub next_signers_with_stake: Vec<SignerWithStake>,
525    pub signed_entity_config: SignedEntityConfig,
526    pub total_spo: Option<TotalSPOs>,
527    pub total_stake: Option<Stake>,
528}
529
530#[cfg(test)]
531impl FakeEpochServiceBuilder {
532    pub fn dummy(epoch: Epoch) -> Self {
533        use mithril_common::test::double::{Dummy, fake_data};
534        let signers = fake_data::signers_with_stakes(3);
535
536        Self {
537            cardano_era: "DummyEra".to_string(),
538            mithril_era: SupportedEra::dummy(),
539            epoch,
540            current_epoch_settings: AggregatorEpochSettings::dummy(),
541            next_epoch_settings: AggregatorEpochSettings::dummy(),
542            signer_registration_epoch_settings: AggregatorEpochSettings::dummy(),
543            current_signers_with_stake: signers.clone(),
544            next_signers_with_stake: signers,
545            signed_entity_config: SignedEntityConfig::dummy(),
546            total_spo: None,
547            total_stake: None,
548        }
549    }
550
551    pub fn build(self) -> FakeEpochService {
552        let current_signers = Signer::vec_from(self.current_signers_with_stake.clone());
553        let next_signers = Signer::vec_from(self.next_signers_with_stake.clone());
554        let total_stakes_signers = self.current_signers_with_stake.iter().map(|s| s.stake).sum();
555        let total_next_stakes_signers = self.next_signers_with_stake.iter().map(|s| s.stake).sum();
556
557        let protocol_multi_signer = SignerBuilder::new(
558            &self.current_signers_with_stake,
559            &self.current_epoch_settings.protocol_parameters,
560        )
561        .with_context(|| "Could not build protocol_multi_signer for epoch service")
562        .unwrap()
563        .build_multi_signer();
564        let next_protocol_multi_signer = SignerBuilder::new(
565            &self.next_signers_with_stake,
566            &self.next_epoch_settings.protocol_parameters,
567        )
568        .with_context(|| "Could not build protocol_multi_signer for epoch service")
569        .unwrap()
570        .build_multi_signer();
571
572        let network_configuration = MithrilNetworkConfiguration {
573            epoch: self.epoch,
574            configuration_for_aggregation: self
575                .current_epoch_settings
576                .into_network_configuration_for_epoch(
577                    self.signed_entity_config.allowed_discriminants.clone(),
578                ),
579            configuration_for_next_aggregation: self
580                .next_epoch_settings
581                .into_network_configuration_for_epoch(
582                    self.signed_entity_config.allowed_discriminants.clone(),
583                ),
584            configuration_for_registration: self
585                .signer_registration_epoch_settings
586                .into_network_configuration_for_epoch(
587                    self.signed_entity_config.allowed_discriminants.clone(),
588                ),
589        };
590
591        FakeEpochService {
592            epoch_data: Some(EpochData {
593                cardano_era: self.cardano_era,
594                mithril_era: self.mithril_era,
595                epoch: self.epoch,
596                network_configuration,
597                current_signers_with_stake: self.current_signers_with_stake,
598                next_signers_with_stake: self.next_signers_with_stake,
599                current_signers,
600                next_signers,
601                total_stakes_signers,
602                total_next_stakes_signers,
603                signed_entity_config: self.signed_entity_config,
604                total_spo: self.total_spo,
605                total_stake: self.total_stake,
606            }),
607            computed_epoch_data: Some(ComputedEpochData {
608                aggregate_verification_key: protocol_multi_signer
609                    .compute_aggregate_verification_key(),
610                next_aggregate_verification_key: next_protocol_multi_signer
611                    .compute_aggregate_verification_key(),
612                protocol_multi_signer,
613                next_protocol_multi_signer,
614            }),
615            inform_epoch_error: false,
616            precompute_epoch_data_error: false,
617            update_next_signers_with_stake_error: false,
618        }
619    }
620}
621
622#[cfg(test)]
623impl FakeEpochService {
624    pub fn from_fixture(
625        epoch: Epoch,
626        fixture: &mithril_common::test::builder::MithrilFixture,
627    ) -> Self {
628        use mithril_common::entities::CardanoTransactionsSigningConfig;
629        use mithril_common::test::double::Dummy;
630
631        let current_epoch_settings = AggregatorEpochSettings {
632            protocol_parameters: fixture.protocol_parameters(),
633            cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig::dummy()),
634        };
635        let next_epoch_settings = AggregatorEpochSettings {
636            protocol_parameters: fixture.protocol_parameters(),
637            cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig::dummy()),
638        };
639        let signer_registration_epoch_settings = AggregatorEpochSettings {
640            protocol_parameters: fixture.protocol_parameters(),
641            cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig::dummy()),
642        };
643
644        FakeEpochServiceBuilder {
645            current_epoch_settings,
646            next_epoch_settings,
647            signer_registration_epoch_settings,
648            current_signers_with_stake: fixture.signers_with_stake(),
649            next_signers_with_stake: fixture.signers_with_stake(),
650            ..FakeEpochServiceBuilder::dummy(epoch)
651        }
652        .build()
653    }
654
655    /// Note: using this will make all 'get' method from [EpochService] trait
656    /// return a [EpochServiceError::NotYetInitialized] error.
657    pub fn without_data() -> Self {
658        Self {
659            epoch_data: None,
660            computed_epoch_data: None,
661            inform_epoch_error: false,
662            precompute_epoch_data_error: false,
663            update_next_signers_with_stake_error: false,
664        }
665    }
666
667    pub fn toggle_errors(
668        &mut self,
669        inform_epoch: bool,
670        precompute_epoch: bool,
671        update_next_signers_with_stake: bool,
672    ) {
673        self.inform_epoch_error = inform_epoch;
674        self.precompute_epoch_data_error = precompute_epoch;
675        self.update_next_signers_with_stake_error = update_next_signers_with_stake;
676    }
677
678    fn unwrap_data(&self) -> Result<&EpochData, EpochServiceError> {
679        self.epoch_data.as_ref().ok_or(EpochServiceError::NotYetInitialized)
680    }
681
682    fn unwrap_computed_data(&self) -> Result<&ComputedEpochData, EpochServiceError> {
683        let epoch = self.unwrap_data()?.epoch;
684
685        self.computed_epoch_data
686            .as_ref()
687            .ok_or(EpochServiceError::NotYetComputed(epoch))
688    }
689}
690
691#[cfg(test)]
692#[async_trait]
693impl EpochService for FakeEpochService {
694    async fn inform_epoch(&mut self, epoch: Epoch) -> StdResult<()> {
695        if self.inform_epoch_error {
696            anyhow::bail!("inform_epoch fake error, given epoch: {epoch}");
697        }
698        Ok(())
699    }
700
701    async fn update_next_signers_with_stake(&mut self) -> StdResult<()> {
702        if self.update_next_signers_with_stake_error {
703            anyhow::bail!("update_next_signers_with_stake fake error");
704        }
705        Ok(())
706    }
707
708    async fn precompute_epoch_data(&mut self) -> StdResult<()> {
709        if self.precompute_epoch_data_error {
710            anyhow::bail!("precompute_epoch_data fake error");
711        }
712        Ok(())
713    }
714
715    fn cardano_era(&self) -> StdResult<CardanoEra> {
716        Ok(self.unwrap_data()?.cardano_era.clone())
717    }
718
719    fn mithril_era(&self) -> StdResult<SupportedEra> {
720        Ok(self.unwrap_data()?.mithril_era)
721    }
722
723    fn epoch_of_current_data(&self) -> StdResult<Epoch> {
724        Ok(self.unwrap_data()?.epoch)
725    }
726
727    fn network_configuration(&self) -> StdResult<&MithrilNetworkConfiguration> {
728        Ok(&self.unwrap_data()?.network_configuration)
729    }
730
731    fn current_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey> {
732        Ok(&self.unwrap_computed_data()?.aggregate_verification_key)
733    }
734
735    fn next_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey> {
736        Ok(&self.unwrap_computed_data()?.next_aggregate_verification_key)
737    }
738
739    fn current_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>> {
740        Ok(&self.unwrap_data()?.current_signers_with_stake)
741    }
742
743    fn next_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>> {
744        Ok(&self.unwrap_data()?.next_signers_with_stake)
745    }
746
747    fn current_signers(&self) -> StdResult<&Vec<Signer>> {
748        Ok(&self.unwrap_data()?.current_signers)
749    }
750
751    fn next_signers(&self) -> StdResult<&Vec<Signer>> {
752        Ok(&self.unwrap_data()?.next_signers)
753    }
754
755    fn total_stakes_signers(&self) -> StdResult<Stake> {
756        Ok(self.unwrap_data()?.total_stakes_signers)
757    }
758
759    fn total_next_stakes_signers(&self) -> StdResult<Stake> {
760        Ok(self.unwrap_data()?.total_next_stakes_signers)
761    }
762
763    fn protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner> {
764        Ok(&self.unwrap_computed_data()?.protocol_multi_signer)
765    }
766
767    fn next_protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner> {
768        Ok(&self.unwrap_computed_data()?.next_protocol_multi_signer)
769    }
770
771    fn signed_entity_config(&self) -> StdResult<&SignedEntityConfig> {
772        Ok(&self.unwrap_data()?.signed_entity_config)
773    }
774
775    fn total_spo(&self) -> StdResult<Option<u32>> {
776        Ok(self.unwrap_data()?.total_spo)
777    }
778
779    fn total_stake(&self) -> StdResult<Option<u64>> {
780        Ok(self.unwrap_data()?.total_stake)
781    }
782}
783
784#[cfg(test)]
785mod tests {
786    use mockall::predicate::eq;
787
788    use mithril_cardano_node_chain::test::double::FakeChainObserver;
789    use mithril_common::entities::{
790        BlockNumber, CardanoTransactionsSigningConfig, Stake, StakeDistribution, SupportedEra,
791    };
792    use mithril_common::test::{
793        builder::{MithrilFixture, MithrilFixtureBuilder, StakeDistributionGenerationMethod},
794        double::{Dummy, fake_data},
795    };
796    use mithril_protocol_config::{
797        model::{MithrilNetworkConfigurationForEpoch, SignedEntityTypeConfiguration},
798        test::double::configuration_provider::FakeMithrilNetworkConfigurationProvider,
799    };
800
801    use crate::store::{FakeEpochSettingsStorer, MockVerificationKeyStorer};
802    use crate::test::TestLogger;
803    use crate::test::double::mocks::MockStakeStore;
804
805    use super::*;
806
807    fn build_uniform_stake_distribution(
808        total_spo: TotalSPOs,
809        stake_by_spo: Stake,
810    ) -> StakeDistribution {
811        let fixture = MithrilFixtureBuilder::default()
812            .with_signers(total_spo as usize)
813            .with_stake_distribution(StakeDistributionGenerationMethod::Uniform(stake_by_spo))
814            .build();
815
816        fixture.stake_distribution()
817    }
818
819    #[derive(Debug, Clone, PartialEq)]
820    struct ExpectedEpochData {
821        cardano_era: CardanoEra,
822        mithril_era: SupportedEra,
823        epoch: Epoch,
824        protocol_parameters: ProtocolParameters,
825        next_protocol_parameters: ProtocolParameters,
826        signer_registration_protocol_parameters: ProtocolParameters,
827        current_signers_with_stake: BTreeSet<SignerWithStake>,
828        next_signers_with_stake: BTreeSet<SignerWithStake>,
829        current_signers: BTreeSet<Signer>,
830        next_signers: BTreeSet<Signer>,
831        signed_entity_config: SignedEntityConfig,
832        total_spo: Option<TotalSPOs>,
833        total_stake: Option<Stake>,
834    }
835
836    #[derive(Debug, Clone, PartialEq)]
837    struct ExpectedComputedEpochData {
838        aggregate_verification_key: ProtocolAggregateVerificationKey,
839        next_aggregate_verification_key: ProtocolAggregateVerificationKey,
840    }
841
842    impl ExpectedEpochData {
843        async fn from_service(service: &MithrilEpochService) -> StdResult<Self> {
844            Ok(Self {
845                cardano_era: service.cardano_era()?,
846                mithril_era: service.mithril_era()?,
847                epoch: service.epoch_of_current_data()?,
848                protocol_parameters: service.current_protocol_parameters()?.clone(),
849                next_protocol_parameters: service.next_protocol_parameters()?.clone(),
850                signer_registration_protocol_parameters: service
851                    .signer_registration_protocol_parameters()?
852                    .clone(),
853                current_signers_with_stake: service
854                    .current_signers_with_stake()?
855                    .clone()
856                    .into_iter()
857                    .collect(),
858                next_signers_with_stake: service
859                    .next_signers_with_stake()?
860                    .clone()
861                    .into_iter()
862                    .collect(),
863                current_signers: service.current_signers()?.clone().into_iter().collect(),
864                next_signers: service.next_signers()?.clone().into_iter().collect(),
865                signed_entity_config: service.signed_entity_config()?.clone(),
866                total_spo: service.total_spo()?,
867                total_stake: service.total_stake()?,
868            })
869        }
870    }
871
872    impl ExpectedComputedEpochData {
873        async fn from_service(service: &MithrilEpochService) -> StdResult<Self> {
874            Ok(Self {
875                aggregate_verification_key: service.current_aggregate_verification_key()?.clone(),
876                next_aggregate_verification_key: service.next_aggregate_verification_key()?.clone(),
877            })
878        }
879    }
880
881    struct EpochServiceBuilder {
882        allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
883        cardano_era: CardanoEra,
884        mithril_era: SupportedEra,
885        current_epoch: Epoch,
886        signers_with_stake: Vec<SignerWithStake>,
887        next_signers_with_stake: Vec<SignerWithStake>,
888        stored_current_epoch_settings: AggregatorEpochSettings,
889        stored_next_epoch_settings: AggregatorEpochSettings,
890        stored_signer_registration_epoch_settings: AggregatorEpochSettings,
891        total_spo: TotalSPOs,
892        total_stake: Stake,
893    }
894
895    impl EpochServiceBuilder {
896        fn new(epoch: Epoch, epoch_fixture: MithrilFixture) -> Self {
897            Self {
898                allowed_discriminants: BTreeSet::new(),
899                cardano_era: String::new(),
900                mithril_era: SupportedEra::dummy(),
901                current_epoch: epoch,
902                signers_with_stake: epoch_fixture.signers_with_stake(),
903                next_signers_with_stake: epoch_fixture.signers_with_stake(),
904                stored_current_epoch_settings: AggregatorEpochSettings {
905                    protocol_parameters: epoch_fixture.protocol_parameters(),
906                    cardano_transactions_signing_config: Some(
907                        CardanoTransactionsSigningConfig::dummy(),
908                    ),
909                },
910                stored_next_epoch_settings: AggregatorEpochSettings {
911                    protocol_parameters: epoch_fixture.protocol_parameters(),
912                    cardano_transactions_signing_config: Some(
913                        CardanoTransactionsSigningConfig::dummy(),
914                    ),
915                },
916                stored_signer_registration_epoch_settings: AggregatorEpochSettings {
917                    protocol_parameters: epoch_fixture.protocol_parameters(),
918                    cardano_transactions_signing_config: Some(
919                        CardanoTransactionsSigningConfig::dummy(),
920                    ),
921                },
922                total_spo: 1,
923                total_stake: 0,
924            }
925        }
926
927        async fn build(self) -> MithrilEpochService {
928            let signer_retrieval_epoch =
929                self.current_epoch.offset_to_signer_retrieval_epoch().unwrap();
930            let next_signer_retrieval_epoch =
931                self.current_epoch.offset_to_next_signer_retrieval_epoch();
932
933            let verification_key_store = {
934                let mut store = MockVerificationKeyStorer::new();
935                let signers_with_stake = self.signers_with_stake.clone();
936                store
937                    .expect_get_signers()
938                    .with(eq(signer_retrieval_epoch))
939                    .returning(move |_| Ok(Some(signers_with_stake.clone())));
940
941                let next_signers_with_stake = self.next_signers_with_stake.clone();
942                store
943                    .expect_get_signers()
944                    .with(eq(next_signer_retrieval_epoch))
945                    .returning(move |_| Ok(Some(next_signers_with_stake.clone())));
946                store
947            };
948
949            let chain_observer = FakeChainObserver::default();
950            chain_observer.set_current_era(self.cardano_era).await;
951            let era_checker = EraChecker::new(self.mithril_era, Epoch::default());
952
953            let stake_store = {
954                assert!(
955                    self.total_stake.is_multiple_of(self.total_spo as u64),
956                    "'total_stake' must be a multiple of 'total_spo' to create a uniform stake distribution"
957                );
958                let stake_per_spo = self.total_stake / self.total_spo as u64;
959
960                let stake_distribution =
961                    build_uniform_stake_distribution(self.total_spo, stake_per_spo);
962
963                let mut stake_store = MockStakeStore::new();
964                stake_store
965                    .expect_get_stakes()
966                    .with(eq(signer_retrieval_epoch))
967                    .returning(move |_| Ok(Some(stake_distribution.clone())));
968
969                stake_store
970            };
971
972            let configuration_for_aggregation = self
973                .stored_current_epoch_settings
974                .into_network_configuration_for_epoch(self.allowed_discriminants.clone());
975
976            let configuration_for_next_aggregation = self
977                .stored_next_epoch_settings
978                .into_network_configuration_for_epoch(self.allowed_discriminants.clone());
979
980            let configuration_for_registration = self
981                .stored_signer_registration_epoch_settings
982                .into_network_configuration_for_epoch(self.allowed_discriminants.clone());
983
984            let network_configuration_provider = FakeMithrilNetworkConfigurationProvider::new(
985                configuration_for_aggregation,
986                configuration_for_next_aggregation,
987                configuration_for_registration,
988            );
989
990            MithrilEpochService::new(
991                EpochServiceDependencies::new(
992                    Arc::new(network_configuration_provider),
993                    Arc::new(FakeEpochSettingsStorer::new(Vec::new())),
994                    Arc::new(verification_key_store),
995                    Arc::new(chain_observer),
996                    Arc::new(era_checker),
997                    Arc::new(stake_store),
998                ),
999                self.allowed_discriminants,
1000                TestLogger::stdout(),
1001            )
1002        }
1003    }
1004
1005    #[tokio::test]
1006    async fn inform_epoch_get_data_from_its_dependencies() {
1007        let current_epoch_fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1008        let next_epoch_fixture = MithrilFixtureBuilder::default()
1009            .with_protocol_parameters(ProtocolParameters::new(8, 80, 0.80))
1010            .with_signers(5)
1011            .build();
1012        let signer_registration_protocol_parameters = fake_data::protocol_parameters();
1013
1014        let epoch = Epoch(5);
1015        let builder = EpochServiceBuilder {
1016            next_signers_with_stake: next_epoch_fixture.signers_with_stake(),
1017            stored_next_epoch_settings: AggregatorEpochSettings {
1018                protocol_parameters: next_epoch_fixture.protocol_parameters(),
1019                ..AggregatorEpochSettings::dummy()
1020            },
1021            stored_signer_registration_epoch_settings: AggregatorEpochSettings {
1022                protocol_parameters: signer_registration_protocol_parameters.clone(),
1023                ..AggregatorEpochSettings::dummy()
1024            },
1025            allowed_discriminants: SignedEntityConfig::dummy().allowed_discriminants,
1026            cardano_era: "CardanoEra".to_string(),
1027            mithril_era: SupportedEra::eras()[0],
1028            total_spo: 10,
1029            total_stake: 20_000_000,
1030            ..EpochServiceBuilder::new(epoch, current_epoch_fixture.clone())
1031        };
1032
1033        let mut service = builder.build().await;
1034
1035        service
1036            .inform_epoch(epoch)
1037            .await
1038            .expect("inform_epoch should not fail");
1039
1040        let data = ExpectedEpochData::from_service(&service)
1041            .await
1042            .expect("extracting data from service should not fail");
1043
1044        assert_eq!(
1045            data.clone(),
1046            ExpectedEpochData {
1047                cardano_era: "CardanoEra".to_string(),
1048                mithril_era: SupportedEra::eras()[0],
1049                epoch,
1050                protocol_parameters: current_epoch_fixture.protocol_parameters(),
1051                next_protocol_parameters: next_epoch_fixture.protocol_parameters(),
1052                signer_registration_protocol_parameters,
1053                current_signers_with_stake: current_epoch_fixture
1054                    .signers_with_stake()
1055                    .into_iter()
1056                    .collect(),
1057                next_signers_with_stake: next_epoch_fixture
1058                    .signers_with_stake()
1059                    .into_iter()
1060                    .collect(),
1061                current_signers: current_epoch_fixture.signers().into_iter().collect(),
1062                next_signers: next_epoch_fixture.signers().into_iter().collect(),
1063                signed_entity_config: SignedEntityConfig::dummy(),
1064                total_spo: Some(10),
1065                total_stake: Some(20_000_000),
1066            }
1067        );
1068    }
1069
1070    #[tokio::test]
1071    async fn inform_epoch_get_signed_entity_config_from_its_dependencies_and_store() {
1072        let epoch = Epoch(5);
1073
1074        let cardano_transactions_signing_config = Some(CardanoTransactionsSigningConfig {
1075            security_parameter: BlockNumber(29),
1076            step: BlockNumber(986),
1077        });
1078        let allowed_discriminants = BTreeSet::from([
1079            SignedEntityTypeDiscriminants::CardanoTransactions,
1080            SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
1081        ]);
1082
1083        let mut service = EpochServiceBuilder {
1084            allowed_discriminants: allowed_discriminants.clone(),
1085            stored_current_epoch_settings: AggregatorEpochSettings {
1086                cardano_transactions_signing_config: cardano_transactions_signing_config.clone(),
1087                ..AggregatorEpochSettings::dummy()
1088            },
1089            ..EpochServiceBuilder::new(epoch, MithrilFixtureBuilder::default().build())
1090        }
1091        .build()
1092        .await;
1093
1094        service
1095            .inform_epoch(epoch)
1096            .await
1097            .expect("inform_epoch should not fail");
1098
1099        let signed_entity_config = service
1100            .signed_entity_config()
1101            .expect("extracting data from service should not fail");
1102
1103        assert_eq!(
1104            signed_entity_config.clone(),
1105            SignedEntityConfig {
1106                allowed_discriminants,
1107                cardano_transactions_signing_config,
1108            }
1109        );
1110    }
1111
1112    #[tokio::test]
1113    async fn inform_epoch_compute_allowed_discriminants_from_intersection_of_aggregation_network_config_and_configured_discriminants()
1114     {
1115        let epoch = Epoch(5);
1116        let allowed_discriminants = BTreeSet::from([
1117            SignedEntityTypeDiscriminants::CardanoStakeDistribution,
1118            SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
1119            SignedEntityTypeDiscriminants::CardanoTransactions,
1120        ]);
1121        let enabled_discriminants = BTreeSet::from([
1122            SignedEntityTypeDiscriminants::MithrilStakeDistribution,
1123            SignedEntityTypeDiscriminants::CardanoStakeDistribution,
1124            SignedEntityTypeDiscriminants::CardanoTransactions,
1125        ]);
1126
1127        let mut service = MithrilEpochService {
1128            mithril_network_configuration_provider: Arc::new(
1129                FakeMithrilNetworkConfigurationProvider::new(
1130                    MithrilNetworkConfigurationForEpoch {
1131                        enabled_signed_entity_types: enabled_discriminants,
1132                        ..Dummy::dummy()
1133                    },
1134                    MithrilNetworkConfigurationForEpoch::dummy(),
1135                    MithrilNetworkConfigurationForEpoch::dummy(),
1136                ),
1137            ),
1138            ..EpochServiceBuilder {
1139                allowed_discriminants: allowed_discriminants.clone(),
1140                ..EpochServiceBuilder::new(epoch, MithrilFixtureBuilder::default().build())
1141            }
1142            .build()
1143            .await
1144        };
1145
1146        service
1147            .inform_epoch(epoch)
1148            .await
1149            .expect("inform_epoch should not fail");
1150
1151        let signed_entity_config = service
1152            .signed_entity_config()
1153            .expect("extracting data from service should not fail");
1154
1155        assert_eq!(
1156            BTreeSet::from([
1157                SignedEntityTypeDiscriminants::CardanoTransactions,
1158                SignedEntityTypeDiscriminants::CardanoStakeDistribution,
1159            ]),
1160            signed_entity_config.allowed_discriminants
1161        );
1162    }
1163
1164    #[tokio::test]
1165    async fn compute_data_with_data_from_inform_epoch() {
1166        let current_epoch_fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1167        let next_epoch_fixture = MithrilFixtureBuilder::default()
1168            .with_protocol_parameters(ProtocolParameters::new(8, 80, 0.80))
1169            .with_signers(5)
1170            .build();
1171
1172        let epoch = Epoch(5);
1173        let mut service =
1174            EpochServiceBuilder {
1175                stored_next_epoch_settings: AggregatorEpochSettings {
1176                    protocol_parameters: next_epoch_fixture.protocol_parameters(),
1177                    cardano_transactions_signing_config: Some(
1178                        CardanoTransactionsSigningConfig::dummy(),
1179                    ),
1180                },
1181                next_signers_with_stake: next_epoch_fixture.signers_with_stake().clone(),
1182                ..EpochServiceBuilder::new(epoch, current_epoch_fixture.clone())
1183            }
1184            .build()
1185            .await;
1186
1187        service
1188            .inform_epoch(epoch)
1189            .await
1190            .expect("inform_epoch should not fail");
1191        service
1192            .precompute_epoch_data()
1193            .await
1194            .expect("precompute_epoch_data should not fail");
1195
1196        let data = ExpectedComputedEpochData::from_service(&service)
1197            .await
1198            .expect("extracting data from service should not fail");
1199
1200        assert_eq!(
1201            data,
1202            ExpectedComputedEpochData {
1203                aggregate_verification_key: current_epoch_fixture.compute_avk(),
1204                next_aggregate_verification_key: next_epoch_fixture.compute_avk(),
1205            }
1206        );
1207    }
1208
1209    #[tokio::test]
1210    async fn inform_epoch_reset_computed_data() {
1211        let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1212        let avk = fixture.compute_avk();
1213        let epoch = Epoch(4);
1214        let mut service = EpochServiceBuilder::new(epoch, fixture.clone()).build().await;
1215        let signer_builder = SignerBuilder::new(
1216            &fixture.signers_with_stake(),
1217            &fixture.protocol_parameters(),
1218        )
1219        .unwrap();
1220        service.computed_epoch_data = Some(ComputedEpochData {
1221            aggregate_verification_key: avk.clone(),
1222            next_aggregate_verification_key: avk.clone(),
1223            protocol_multi_signer: signer_builder.build_multi_signer(),
1224            next_protocol_multi_signer: signer_builder.build_multi_signer(),
1225        });
1226
1227        service
1228            .inform_epoch(epoch)
1229            .await
1230            .expect("inform_epoch should not fail");
1231
1232        assert!(service.computed_epoch_data.is_none());
1233    }
1234
1235    #[tokio::test]
1236    async fn update_next_signers_with_stake_succeeds() {
1237        let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1238        let next_fixture = MithrilFixtureBuilder::default().with_signers(5).build();
1239        let next_avk = next_fixture.compute_avk();
1240        let epoch = Epoch(4);
1241        let mut service = EpochServiceBuilder {
1242            next_signers_with_stake: next_fixture.signers_with_stake().clone(),
1243            ..EpochServiceBuilder::new(epoch, fixture.clone())
1244        }
1245        .build()
1246        .await;
1247        service
1248            .inform_epoch(epoch)
1249            .await
1250            .expect("inform_epoch should not fail");
1251        service.epoch_data = Some(EpochData {
1252            next_signers_with_stake: vec![],
1253            ..service.epoch_data.unwrap()
1254        });
1255        service.computed_epoch_data = None;
1256
1257        service
1258            .update_next_signers_with_stake()
1259            .await
1260            .expect("update_next_signers_with_stake should not fail");
1261
1262        let expected_next_signers_with_stake = next_fixture.signers_with_stake();
1263        assert_eq!(
1264            expected_next_signers_with_stake,
1265            service.epoch_data.unwrap().next_signers_with_stake
1266        );
1267
1268        assert_eq!(
1269            next_avk,
1270            service.computed_epoch_data.unwrap().next_aggregate_verification_key
1271        );
1272    }
1273
1274    #[tokio::test]
1275    async fn inform_epoch_insert_registration_epoch_settings_in_the_store() {
1276        let expected_epoch_settings = AggregatorEpochSettings {
1277            protocol_parameters: ProtocolParameters::new(6, 89, 0.124),
1278            cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig {
1279                security_parameter: BlockNumber(10),
1280                step: BlockNumber(15),
1281            }),
1282        };
1283
1284        let epoch = Epoch(4);
1285        let mut service = MithrilEpochService {
1286            mithril_network_configuration_provider: Arc::new(
1287                FakeMithrilNetworkConfigurationProvider::new(
1288                    MithrilNetworkConfigurationForEpoch::dummy(),
1289                    MithrilNetworkConfigurationForEpoch::dummy(),
1290                    MithrilNetworkConfigurationForEpoch {
1291                        protocol_parameters: expected_epoch_settings.protocol_parameters.clone(),
1292                        signed_entity_types_config: SignedEntityTypeConfiguration {
1293                            cardano_transactions: expected_epoch_settings
1294                                .cardano_transactions_signing_config
1295                                .clone(),
1296                        },
1297                        ..Dummy::dummy()
1298                    },
1299                ),
1300            ),
1301            ..EpochServiceBuilder::new(epoch, MithrilFixtureBuilder::default().build())
1302                .build()
1303                .await
1304        };
1305
1306        service
1307            .inform_epoch(epoch)
1308            .await
1309            .expect("inform_epoch should not fail");
1310
1311        let inserted_epoch_settings = service
1312            .epoch_settings_storer
1313            .get_epoch_settings(epoch.offset_to_recording_epoch())
1314            .await
1315            .unwrap_or_else(|_| {
1316                panic!(
1317                    "epoch settings should have been inserted for epoch {}",
1318                    epoch.offset_to_recording_epoch()
1319                )
1320            })
1321            .unwrap();
1322
1323        assert_eq!(inserted_epoch_settings, expected_epoch_settings);
1324    }
1325
1326    #[tokio::test]
1327    async fn cant_get_data_if_inform_epoch_has_not_been_called() {
1328        let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1329        let service = EpochServiceBuilder::new(Epoch(4), fixture.clone()).build().await;
1330
1331        for (name, res) in [
1332            ("cardano_era", service.cardano_era().err()),
1333            ("mithril_era", service.mithril_era().err()),
1334            (
1335                "epoch_of_current_data",
1336                service.epoch_of_current_data().err(),
1337            ),
1338            (
1339                "current_protocol_parameters",
1340                service.current_protocol_parameters().err(),
1341            ),
1342            (
1343                "next_protocol_parameters",
1344                service.next_protocol_parameters().err(),
1345            ),
1346            (
1347                "signer_registration_protocol_parameters",
1348                service.signer_registration_protocol_parameters().err(),
1349            ),
1350            (
1351                "current_signers_with_stake",
1352                service.current_signers_with_stake().err(),
1353            ),
1354            (
1355                "next_signers_with_stake",
1356                service.next_signers_with_stake().err(),
1357            ),
1358            ("current_signers", service.current_signers().err()),
1359            ("next_signers", service.next_signers().err()),
1360            (
1361                "current_aggregate_verification_key",
1362                service.current_aggregate_verification_key().err(),
1363            ),
1364            (
1365                "next_aggregate_verification_key",
1366                service.next_aggregate_verification_key().err(),
1367            ),
1368            (
1369                "protocol_multi_signer",
1370                service.protocol_multi_signer().err(),
1371            ),
1372            (
1373                "next_protocol_multi_signer",
1374                service.next_protocol_multi_signer().err(),
1375            ),
1376            ("signed_entity_config", service.signed_entity_config().err()),
1377            ("total_spo", service.total_spo().err()),
1378            ("total_stake", service.total_stake().err()),
1379        ] {
1380            let error =
1381                res.unwrap_or_else(|| panic!("getting {name} should have returned an error"));
1382
1383            match error.downcast_ref::<EpochServiceError>() {
1384                Some(EpochServiceError::NotYetInitialized) => (),
1385                _ => panic!("Expected an NotYetInitialized error, got: {error:?}"),
1386            }
1387        }
1388    }
1389
1390    #[tokio::test]
1391    async fn can_only_get_non_computed_data_if_inform_epoch_has_been_called_but_not_precompute_epoch_data()
1392     {
1393        let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1394        let mut service = EpochServiceBuilder::new(Epoch(4), fixture.clone()).build().await;
1395        service.inform_epoch(Epoch(4)).await.unwrap();
1396
1397        assert!(service.cardano_era().is_ok());
1398        assert!(service.mithril_era().is_ok());
1399        assert!(service.epoch_of_current_data().is_ok());
1400        assert!(service.current_protocol_parameters().is_ok());
1401        assert!(service.next_protocol_parameters().is_ok());
1402        assert!(service.signer_registration_protocol_parameters().is_ok());
1403        assert!(service.current_signers_with_stake().is_ok());
1404        assert!(service.next_signers_with_stake().is_ok());
1405        assert!(service.current_signers().is_ok());
1406        assert!(service.next_signers().is_ok());
1407        assert!(service.signed_entity_config().is_ok());
1408        assert!(service.total_spo().is_ok());
1409        assert!(service.total_stake().is_ok());
1410
1411        for (name, res) in [
1412            (
1413                "current_aggregate_verification_key",
1414                service.current_aggregate_verification_key().err(),
1415            ),
1416            (
1417                "next_aggregate_verification_key",
1418                service.next_aggregate_verification_key().err(),
1419            ),
1420            (
1421                "protocol_multi_signer",
1422                service.protocol_multi_signer().err(),
1423            ),
1424            (
1425                "next_protocol_multi_signer",
1426                service.next_protocol_multi_signer().err(),
1427            ),
1428        ] {
1429            let error =
1430                res.unwrap_or_else(|| panic!("getting {name} should have returned an error"));
1431
1432            match error.downcast_ref::<EpochServiceError>() {
1433                Some(EpochServiceError::NotYetComputed(Epoch(4))) => (),
1434                _ => panic!("Expected an NotYetComputed error for epoch 4, got: {error:?}"),
1435            }
1436        }
1437    }
1438}