mithril_aggregator/services/
epoch_service.rs

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