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