mithril_aggregator/services/
epoch_service.rs

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