mithril_aggregator/services/
epoch_service.rs

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