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