mithril_aggregator/services/
epoch_service.rs

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