mithril_signer/runtime/
runner.rs

1use anyhow::Context;
2use async_trait::async_trait;
3use slog::{Logger, debug, warn};
4use thiserror::Error;
5use tokio::sync::RwLockReadGuard;
6
7use mithril_common::StdResult;
8use mithril_common::crypto_helper::{OpCert, ProtocolOpCert, SerDeShelleyFileFormat};
9use mithril_common::entities::{
10    Epoch, PartyId, ProtocolMessage, SignedEntityType, Signer, TimePoint,
11};
12use mithril_common::logging::LoggerExtensions;
13use mithril_protocol_config::model::MithrilNetworkConfiguration;
14
15use crate::Configuration;
16use crate::dependency_injection::SignerDependencyContainer;
17use crate::entities::{BeaconToSign, RegisteredSigners};
18use crate::services::{EpochService, MithrilProtocolInitializerBuilder};
19
20/// This trait is mainly intended for mocking.
21#[async_trait]
22pub trait Runner: Send + Sync {
23    /// Fetch the configuration parameters of the Mithril network
24    async fn get_mithril_network_configuration(
25        &self,
26        epoch: Epoch,
27    ) -> StdResult<MithrilNetworkConfiguration>;
28
29    /// Fetch the current epoch settings if any.
30    async fn get_signer_registrations_from_aggregator(
31        &self,
32    ) -> StdResult<Option<RegisteredSigners>>;
33
34    /// Fetch the beacon to sign if any.
35    async fn get_beacon_to_sign(&self, time_point: TimePoint) -> StdResult<Option<BeaconToSign>>;
36
37    /// Fetch the current time point from the Cardano node.
38    async fn get_current_time_point(&self) -> StdResult<TimePoint>;
39
40    /// Register the signer verification key to the aggregator.
41    async fn register_signer_to_aggregator(&self) -> StdResult<()>;
42
43    /// Read the stake distribution and store it.
44    async fn update_stake_distribution(&self, epoch: Epoch) -> StdResult<()>;
45
46    /// Check if the signer can sign the current epoch.
47    async fn can_sign_current_epoch(&self) -> StdResult<bool>;
48
49    /// Register epoch information
50    async fn inform_epoch_settings(
51        &self,
52        aggregator_signer_registration_epoch: Epoch,
53        mithril_network_configuration: MithrilNetworkConfiguration,
54        current_signer: Vec<Signer>,
55        next_signer: Vec<Signer>,
56    ) -> StdResult<()>;
57
58    /// Create the message to be signed with the single signature.
59    async fn compute_message(
60        &self,
61        signed_entity_type: &SignedEntityType,
62    ) -> StdResult<ProtocolMessage>;
63
64    /// Create the single signature.
65    async fn compute_publish_single_signature(
66        &self,
67        beacon_to_sign: &BeaconToSign,
68        message: &ProtocolMessage,
69    ) -> StdResult<()>;
70
71    /// Read the current era and update the EraChecker.
72    async fn update_era_checker(&self, epoch: Epoch) -> StdResult<()>;
73
74    /// Perform the upkeep tasks.
75    async fn upkeep(&self, current_epoch: Epoch) -> StdResult<()>;
76}
77
78/// This type represents the errors thrown from the Runner.
79#[derive(Debug, Clone, PartialEq, Eq, Error)]
80pub enum RunnerError {
81    /// Value was expected from a subsystem but None was returned.
82    #[error("No value returned by the subsystem for `{0}`.")]
83    NoValueError(String),
84    /// Could not associate my node with a stake.
85    #[error("No stake associated with myself.")]
86    NoStakeForSelf(),
87    /// Could not find the stake for one of the signers.
88    #[error("No stake associated with this signer, party_id: {0}.")]
89    NoStakeForSigner(PartyId),
90    /// Parse file error
91    #[error("File parse failed: {0}.")]
92    FileParse(String),
93}
94
95/// Controller methods for the Signer's state machine.
96pub struct SignerRunner {
97    config: Configuration,
98    services: SignerDependencyContainer,
99    logger: Logger,
100}
101
102impl SignerRunner {
103    /// Create a new Runner instance.
104    pub fn new(config: Configuration, services: SignerDependencyContainer, logger: Logger) -> Self {
105        Self {
106            services,
107            config,
108            logger: logger.new_with_component_name::<Self>(),
109        }
110    }
111
112    async fn epoch_service_read(&self) -> RwLockReadGuard<'_, dyn EpochService> {
113        self.services.epoch_service.read().await
114    }
115}
116
117#[cfg_attr(test, mockall::automock)]
118#[async_trait]
119impl Runner for SignerRunner {
120    async fn get_mithril_network_configuration(
121        &self,
122        epoch: Epoch,
123    ) -> StdResult<MithrilNetworkConfiguration> {
124        debug!(self.logger, ">> get_mithril_network_configuration");
125
126        self.services
127            .network_configuration_service
128            .get_network_configuration(epoch)
129            .await
130    }
131
132    async fn get_signer_registrations_from_aggregator(
133        &self,
134    ) -> StdResult<Option<RegisteredSigners>> {
135        debug!(self.logger, ">> get_epoch_settings");
136
137        self.services
138            .signers_registration_retriever
139            .retrieve_all_signer_registrations()
140            .await
141    }
142
143    async fn get_beacon_to_sign(&self, time_point: TimePoint) -> StdResult<Option<BeaconToSign>> {
144        debug!(
145            self.logger,
146            ">> get_beacon_to_sign(time_point: {time_point})"
147        );
148
149        self.services.certifier.get_beacon_to_sign(time_point).await
150    }
151
152    async fn get_current_time_point(&self) -> StdResult<TimePoint> {
153        debug!(self.logger, ">> get_current_time_point");
154
155        self.services
156            .ticker_service
157            .get_current_time_point()
158            .await
159            .with_context(|| "Runner can not get current time point")
160    }
161
162    async fn register_signer_to_aggregator(&self) -> StdResult<()> {
163        debug!(self.logger, ">> register_signer_to_aggregator");
164
165        let (epoch, protocol_parameters) = {
166            let epoch_service = self.services.epoch_service.read().await;
167            let epoch = epoch_service.epoch_of_current_data()?;
168            let protocol_parameters = epoch_service.registration_protocol_parameters()?;
169
170            (epoch, protocol_parameters.clone())
171        };
172
173        let epoch_offset_to_recording_epoch = epoch.offset_to_recording_epoch();
174        let stake_distribution = self
175            .services
176            .stake_store
177            .get_stakes(epoch_offset_to_recording_epoch)
178            .await?
179            .ok_or_else(|| {
180                RunnerError::NoValueError(format!(
181                    "stakes at epoch {epoch_offset_to_recording_epoch}"
182                ))
183            })?;
184        let stake = stake_distribution
185            .get(&self.services.single_signer.get_party_id())
186            .ok_or_else(RunnerError::NoStakeForSelf)?;
187        let (operational_certificate, protocol_operational_certificate) = match &self
188            .config
189            .operational_certificate_path
190        {
191            Some(operational_certificate_path) => {
192                let opcert: OpCert = OpCert::from_file(operational_certificate_path)
193                    .map_err(|_| RunnerError::FileParse("operational_certificate_path".to_string()))
194                    .with_context(
195                        || "register_signer_to_aggregator can not decode OpCert from file",
196                    )?;
197                (Some(opcert.clone()), Some(ProtocolOpCert::new(opcert)))
198            }
199            _ => (None, None),
200        };
201        let current_kes_period = self.services.chain_observer.get_current_kes_period().await?;
202        let kes_evolutions = operational_certificate.map(|operational_certificate| {
203            current_kes_period.unwrap_or_default() - operational_certificate.get_start_kes_period()
204        });
205
206        let protocol_initializer = self
207            .services
208            .protocol_initializer_store
209            .get_protocol_initializer(epoch_offset_to_recording_epoch)
210            .await
211            .with_context(
212                || "register_signer_to_aggregator can not retrieve protocol initializer from store",
213            )?;
214
215        if protocol_initializer.is_none() {
216            let protocol_initializer = MithrilProtocolInitializerBuilder::build(
217                stake,
218                &protocol_parameters,
219                self.services.kes_signer.clone(),
220                current_kes_period,
221            )?;
222
223            let signer = Signer::new(
224                self.services.single_signer.get_party_id(),
225                protocol_initializer.verification_key().into(),
226                protocol_initializer.verification_key_signature(),
227                protocol_operational_certificate,
228                kes_evolutions,
229            );
230            self.services
231                .signer_registration_publisher
232                .register_signer(epoch_offset_to_recording_epoch, &signer)
233                .await?;
234
235            self.services
236                .protocol_initializer_store
237                .save_protocol_initializer(epoch_offset_to_recording_epoch, protocol_initializer)
238                .await?;
239        }
240
241        Ok(())
242    }
243
244    async fn update_stake_distribution(&self, epoch: Epoch) -> StdResult<()> {
245        debug!(self.logger, ">> update_stake_distribution(epoch: {epoch})");
246
247        let exists_stake_distribution = !self
248            .services
249            .stake_store
250            .get_stakes(epoch.offset_to_recording_epoch())
251            .await?
252            .unwrap_or_default()
253            .is_empty();
254        if exists_stake_distribution {
255            return Ok(());
256        }
257
258        let stake_distribution = self
259            .services
260            .chain_observer
261            .get_current_stake_distribution()
262            .await?
263            .ok_or_else(|| RunnerError::NoValueError("current_stake_distribution".to_string()))?;
264        self.services
265            .stake_store
266            .save_stakes(epoch.offset_to_recording_epoch(), stake_distribution)
267            .await?;
268
269        Ok(())
270    }
271
272    async fn can_sign_current_epoch(&self) -> StdResult<bool> {
273        let epoch_service = self.epoch_service_read().await;
274        epoch_service.can_signer_sign_current_epoch(self.services.single_signer.get_party_id())
275    }
276
277    async fn inform_epoch_settings(
278        &self,
279        aggregator_signer_registration_epoch: Epoch,
280        mithril_network_configuration: MithrilNetworkConfiguration,
281        current_signer: Vec<Signer>,
282        next_signer: Vec<Signer>,
283    ) -> StdResult<()> {
284        debug!(
285            self.logger,
286            ">> inform_epoch_settings(epoch:{})", aggregator_signer_registration_epoch
287        );
288
289        self.services
290            .epoch_service
291            .write()
292            .await
293            .inform_epoch_settings(
294                aggregator_signer_registration_epoch,
295                mithril_network_configuration,
296                current_signer,
297                next_signer,
298            )
299            .await
300    }
301
302    async fn compute_message(
303        &self,
304        signed_entity_type: &SignedEntityType,
305    ) -> StdResult<ProtocolMessage> {
306        debug!(self.logger, ">> compute_message({signed_entity_type:?})");
307
308        let protocol_message = self
309            .services
310            .signable_builder_service
311            .compute_protocol_message(signed_entity_type.to_owned())
312            .await
313            .with_context(|| format!("Runner can not compute protocol message for signed entity type: '{signed_entity_type}'"))?;
314
315        Ok(protocol_message)
316    }
317
318    async fn compute_publish_single_signature(
319        &self,
320        beacon_to_sign: &BeaconToSign,
321        message: &ProtocolMessage,
322    ) -> StdResult<()> {
323        debug!(self.logger, ">> compute_publish_single_signature"; "beacon_to_sign" => ?beacon_to_sign);
324        self.services
325            .certifier
326            .compute_publish_single_signature(beacon_to_sign, message)
327            .await
328    }
329
330    async fn update_era_checker(&self, epoch: Epoch) -> StdResult<()> {
331        debug!(self.logger, ">> update_era_checker(epoch:{epoch})");
332
333        let era_token = self
334            .services
335            .era_reader
336            .read_era_epoch_token(epoch)
337            .await
338            .map_err(Box::new)?;
339        let current_era = era_token.get_current_supported_era()?;
340        self.services
341            .era_checker
342            .change_era(current_era, era_token.get_current_epoch());
343        debug!(
344            self.logger,
345            "Current Era is {} (Epoch {}).",
346            current_era,
347            era_token.get_current_epoch()
348        );
349
350        if era_token.get_next_supported_era().is_err() {
351            let era_name = &era_token.get_next_era_marker().unwrap().name;
352            warn!(
353                self.logger,
354                "Upcoming Era '{era_name}' is not supported by this version of the software. Please update!"
355            );
356        }
357
358        Ok(())
359    }
360
361    async fn upkeep(&self, current_epoch: Epoch) -> StdResult<()> {
362        debug!(self.logger, ">> upkeep(current_epoch:{current_epoch})");
363        self.services.upkeep_service.run(current_epoch).await?;
364        Ok(())
365    }
366}
367
368#[cfg(test)]
369mod tests {
370    use std::collections::BTreeSet;
371    use std::{path::Path, sync::Arc};
372
373    use mockall::mock;
374    use mockall::predicate::eq;
375    use tokio::sync::RwLock;
376
377    use mithril_cardano_node_chain::chain_importer::CardanoChainDataImporter;
378    use mithril_cardano_node_chain::test::double::{
379        DumbBlockScanner, FakeChainObserver, InMemoryChainDataStore,
380    };
381    use mithril_cardano_node_internal_database::{
382        signable_builder::{
383            CardanoDatabaseSignableBuilder, CardanoImmutableFilesFullSignableBuilder,
384        },
385        test::double::{DumbImmutableDigester, DumbImmutableFileObserver},
386    };
387    use mithril_common::{
388        api_version::APIVersionProvider,
389        crypto_helper::{MKMap, MKMapNode, MKTreeNode, MKTreeStoreInMemory, MKTreeStorer},
390        entities::{BlockNumber, BlockRange, Epoch, SignedEntityTypeDiscriminants},
391        signable_builder::{
392            BlockRangeRootRetriever, CardanoBlocksTransactionsSignableBuilder,
393            CardanoStakeDistributionSignableBuilder, CardanoTransactionsSignableBuilder,
394            LegacyBlockRangeRootRetriever, MithrilSignableBuilderService,
395            MithrilStakeDistributionSignableBuilder, SignableBuilderServiceDependencies,
396        },
397        test::{
398            builder::MithrilFixtureBuilder,
399            double::{Dummy, fake_data},
400        },
401    };
402    use mithril_era::{EraChecker, EraReader, adapters::EraReaderBootstrapAdapter};
403    use mithril_protocol_config::model::MithrilNetworkConfigurationForEpoch;
404    use mithril_protocol_config::test::double::configuration_provider::FakeMithrilNetworkConfigurationProvider;
405    use mithril_signed_entity_lock::SignedEntityTypeLock;
406    use mithril_signed_entity_preloader::{
407        CardanoTransactionsPreloader, CardanoTransactionsPreloaderActivation,
408    };
409    use mithril_ticker::{MithrilTickerService, TickerService};
410
411    use crate::database::repository::{
412        ProtocolInitializerRepository, SignedBeaconRepository, StakePoolStore,
413    };
414    use crate::database::test_helper::main_db_connection;
415    use crate::metrics::MetricsService;
416    use crate::services::{
417        MithrilEpochService, MithrilSingleSigner, MockUpkeepService, SignaturePublisherNoop,
418        SignerCertifierService, SignerChainDataImporter, SignerSignableSeedBuilder,
419        SignerSignedEntityConfigProvider,
420    };
421    use crate::test::TestLogger;
422    use crate::test::double::{DumbSignersRegistrationRetriever, SpySignerRegistrationPublisher};
423
424    use super::*;
425
426    const DIGESTER_RESULT: &str = "a digest";
427
428    mock! {
429        pub FakeTimePointProvider { }
430
431        #[async_trait]
432        impl TickerService for FakeTimePointProvider {
433            async fn get_current_time_point(&self) -> StdResult<TimePoint>;
434        }
435    }
436
437    mock! {
438        pub BlockRangeRootRetriever<S: MKTreeStorer> { }
439
440        #[async_trait]
441        impl<S: MKTreeStorer> BlockRangeRootRetriever<S> for BlockRangeRootRetriever<S> {
442            async fn retrieve_block_range_roots<'a>(
443                &'a self,
444                up_to_beacon: BlockNumber,
445            ) -> StdResult<Box<dyn Iterator<Item = (BlockRange, MKTreeNode)> + 'a>>;
446
447            async fn compute_merkle_map_from_block_range_roots(
448                &self,
449                up_to_beacon: BlockNumber,
450            ) -> StdResult<MKMap<BlockRange, MKMapNode<BlockRange,S>, S>>;
451        }
452    }
453
454    mock! {
455        pub LegacyBlockRangeRootRetriever<S: MKTreeStorer> { }
456
457        #[async_trait]
458        impl<S: MKTreeStorer> LegacyBlockRangeRootRetriever<S> for LegacyBlockRangeRootRetriever<S> {
459            async fn retrieve_block_range_roots<'a>(
460                &'a self,
461                up_to_beacon: BlockNumber,
462            ) -> StdResult<Box<dyn Iterator<Item = (BlockRange, MKTreeNode)> + 'a>>;
463
464            async fn compute_merkle_map_from_block_range_roots(
465                &self,
466                up_to_beacon: BlockNumber,
467            ) -> StdResult<MKMap<BlockRange, MKMapNode<BlockRange,S>, S>>;
468        }
469    }
470
471    async fn init_services() -> SignerDependencyContainer {
472        let logger = TestLogger::stdout();
473        let sqlite_connection = Arc::new(main_db_connection().unwrap());
474        let stake_distribution_signers = fake_data::signers_with_stakes(2);
475        let party_id = stake_distribution_signers[1].party_id.clone();
476        let fake_observer = FakeChainObserver::default();
477        fake_observer.set_signers(stake_distribution_signers).await;
478        let chain_observer = Arc::new(fake_observer);
479        let ticker_service = Arc::new(MithrilTickerService::new(
480            chain_observer.clone(),
481            Arc::new(DumbImmutableFileObserver::default()),
482        ));
483        let era_reader = Arc::new(EraReader::new(Arc::new(EraReaderBootstrapAdapter)));
484        let era_epoch_token = era_reader
485            .read_era_epoch_token(ticker_service.get_current_epoch().await.unwrap())
486            .await
487            .unwrap();
488        let era_checker = Arc::new(EraChecker::new(
489            era_epoch_token.get_current_supported_era().unwrap(),
490            era_epoch_token.get_current_epoch(),
491        ));
492
493        let api_version_provider = Arc::new(APIVersionProvider::new(era_checker.clone()));
494        let digester = Arc::new(DumbImmutableDigester::default().with_digest(DIGESTER_RESULT));
495        let cardano_immutable_signable_builder =
496            Arc::new(CardanoImmutableFilesFullSignableBuilder::new(
497                digester.clone(),
498                Path::new(""),
499                logger.clone(),
500            ));
501        let mithril_stake_distribution_signable_builder =
502            Arc::new(MithrilStakeDistributionSignableBuilder::default());
503        let transaction_parser = Arc::new(DumbBlockScanner::new());
504        let transaction_store = Arc::new(InMemoryChainDataStore::default());
505        let transactions_importer = Arc::new(SignerChainDataImporter::new(Arc::new(
506            CardanoChainDataImporter::new(
507                transaction_parser.clone(),
508                transaction_store.clone(),
509                logger.clone(),
510            ),
511        )));
512        let legacy_block_range_root_retriever =
513            Arc::new(MockLegacyBlockRangeRootRetriever::<MKTreeStoreInMemory>::new());
514        let cardano_transactions_builder = Arc::new(CardanoTransactionsSignableBuilder::new(
515            transactions_importer.clone(),
516            legacy_block_range_root_retriever,
517        ));
518        let block_range_root_retriever =
519            Arc::new(MockBlockRangeRootRetriever::<MKTreeStoreInMemory>::new());
520        let cardano_blocks_transactions_builder =
521            Arc::new(CardanoBlocksTransactionsSignableBuilder::new(
522                transactions_importer.clone(),
523                block_range_root_retriever,
524            ));
525        let stake_store = Arc::new(StakePoolStore::new(sqlite_connection.clone(), None));
526        let cardano_stake_distribution_builder = Arc::new(
527            CardanoStakeDistributionSignableBuilder::new(stake_store.clone()),
528        );
529        let cardano_database_signable_builder = Arc::new(CardanoDatabaseSignableBuilder::new(
530            digester.clone(),
531            Path::new(""),
532            logger.clone(),
533        ));
534        let protocol_initializer_store = Arc::new(ProtocolInitializerRepository::new(
535            sqlite_connection.clone(),
536            None,
537        ));
538        let epoch_service = Arc::new(RwLock::new(MithrilEpochService::new(
539            stake_store.clone(),
540            protocol_initializer_store.clone(),
541            logger.clone(),
542        )));
543        let single_signer = Arc::new(MithrilSingleSigner::new(
544            party_id,
545            epoch_service.clone(),
546            logger.clone(),
547        ));
548        let signable_seed_builder_service = Arc::new(SignerSignableSeedBuilder::new(
549            epoch_service.clone(),
550            protocol_initializer_store.clone(),
551        ));
552        let signable_builders_dependencies = SignableBuilderServiceDependencies::new(
553            mithril_stake_distribution_signable_builder,
554            cardano_immutable_signable_builder,
555            cardano_transactions_builder,
556            cardano_blocks_transactions_builder,
557            cardano_stake_distribution_builder,
558            cardano_database_signable_builder,
559        );
560        let signable_builder_service = Arc::new(MithrilSignableBuilderService::new(
561            signable_seed_builder_service,
562            signable_builders_dependencies,
563            logger.clone(),
564        ));
565        let metrics_service = Arc::new(MetricsService::new(logger.clone()).unwrap());
566        let signed_entity_type_lock = Arc::new(SignedEntityTypeLock::default());
567        let security_parameter = BlockNumber(0);
568        let cardano_transactions_preloader = Arc::new(CardanoTransactionsPreloader::new(
569            signed_entity_type_lock.clone(),
570            transactions_importer.clone(),
571            security_parameter,
572            chain_observer.clone(),
573            logger.clone(),
574            Arc::new(CardanoTransactionsPreloaderActivation::new(true)),
575        ));
576        let upkeep_service = Arc::new(MockUpkeepService::new());
577        let certifier = Arc::new(SignerCertifierService::new(
578            Arc::new(SignedBeaconRepository::new(sqlite_connection.clone(), None)),
579            Arc::new(SignerSignedEntityConfigProvider::new(epoch_service.clone())),
580            signed_entity_type_lock.clone(),
581            single_signer.clone(),
582            Arc::new(SignaturePublisherNoop),
583            logger.clone(),
584        ));
585        let kes_signer = None;
586
587        let configuration_for_aggregation = MithrilNetworkConfigurationForEpoch::dummy();
588        let configuration_for_next_aggregation = MithrilNetworkConfigurationForEpoch::dummy();
589        let configuration_for_registration = MithrilNetworkConfigurationForEpoch::dummy();
590
591        let network_configuration_service = Arc::new(FakeMithrilNetworkConfigurationProvider::new(
592            configuration_for_aggregation,
593            configuration_for_next_aggregation,
594            configuration_for_registration,
595        ));
596
597        SignerDependencyContainer {
598            stake_store,
599            chain_observer,
600            digester,
601            single_signer,
602            ticker_service,
603            protocol_initializer_store,
604            era_checker,
605            era_reader,
606            api_version_provider,
607            signable_builder_service,
608            metrics_service,
609            signed_entity_type_lock,
610            cardano_transactions_preloader,
611            upkeep_service,
612            epoch_service,
613            certifier,
614            signer_registration_publisher: Arc::new(SpySignerRegistrationPublisher::default()),
615            signers_registration_retriever: Arc::new(DumbSignersRegistrationRetriever::default()),
616            kes_signer,
617            network_configuration_service,
618        }
619    }
620
621    async fn init_runner(
622        maybe_services: Option<SignerDependencyContainer>,
623        maybe_config: Option<Configuration>,
624    ) -> SignerRunner {
625        SignerRunner::new(
626            maybe_config.unwrap_or(Configuration::new_sample("1")),
627            maybe_services.unwrap_or(init_services().await),
628            TestLogger::stdout(),
629        )
630    }
631
632    #[tokio::test]
633    async fn test_get_current_time_point() {
634        let mut services = init_services().await;
635        let expected = TimePoint::dummy();
636        let mut ticker_service = MockFakeTimePointProvider::new();
637        ticker_service
638            .expect_get_current_time_point()
639            .once()
640            .returning(move || Ok(TimePoint::dummy()));
641        services.ticker_service = Arc::new(ticker_service);
642        let runner = init_runner(Some(services), None).await;
643
644        assert_eq!(
645            expected,
646            runner
647                .get_current_time_point()
648                .await
649                .expect("Get current time point should not fail.")
650        );
651    }
652
653    #[tokio::test]
654    async fn test_update_stake_distribution() {
655        let services = init_services().await;
656        let stake_store = services.stake_store.clone();
657        let current_epoch = services
658            .chain_observer
659            .get_current_epoch()
660            .await
661            .expect("chain observer should not fail")
662            .expect("the observer should return an epoch");
663        let runner = init_runner(Some(services), None).await;
664        assert!(
665            stake_store
666                .get_stakes(current_epoch)
667                .await
668                .expect("getting stakes from store should not fail")
669                .is_none()
670        );
671
672        runner
673            .update_stake_distribution(current_epoch)
674            .await
675            .expect("update_stake_distribution should not fail.");
676
677        let stake_distribution = stake_store
678            .get_stakes(current_epoch.offset_to_recording_epoch())
679            .await
680            .expect("getting stakes from store should not fail")
681            .expect("there should be stakes for this epoch");
682
683        assert_eq!(2, stake_distribution.len());
684    }
685
686    #[tokio::test]
687    async fn test_register_signer_to_aggregator() {
688        let mut services = init_services().await;
689        let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
690        let registration_publisher_spy = Arc::new(SpySignerRegistrationPublisher::default());
691        services.signer_registration_publisher = registration_publisher_spy.clone();
692        let protocol_initializer_store = services.protocol_initializer_store.clone();
693        let current_epoch = services.ticker_service.get_current_epoch().await.unwrap();
694
695        let stakes = services
696            .chain_observer
697            .get_current_stake_distribution()
698            .await
699            .unwrap()
700            .unwrap();
701        services
702            .stake_store
703            .save_stakes(current_epoch.offset_to_recording_epoch(), stakes)
704            .await
705            .unwrap();
706
707        let runner = init_runner(Some(services), None).await;
708
709        let mithril_network_configuration = MithrilNetworkConfiguration {
710            epoch: current_epoch,
711            ..MithrilNetworkConfiguration::dummy()
712        };
713
714        runner
715            .inform_epoch_settings(
716                current_epoch,
717                mithril_network_configuration,
718                fixture.signers(),
719                fixture.signers(),
720            )
721            .await
722            .unwrap();
723
724        runner
725            .register_signer_to_aggregator()
726            .await
727            .expect("registering a signer to the aggregator should not fail");
728
729        let last_registered_signer_first_registration =
730            registration_publisher_spy.get_last_registered_signer().await.unwrap();
731        let maybe_protocol_initializer_first_registration = protocol_initializer_store
732            .get_protocol_initializer(current_epoch.offset_to_recording_epoch())
733            .await
734            .expect("get_protocol_initializer should not fail");
735        assert!(
736            maybe_protocol_initializer_first_registration.is_some(),
737            "A protocol initializer should have been registered at the 'Recording' epoch"
738        );
739
740        let total_registered_signers =
741            registration_publisher_spy.get_total_registered_signers().await;
742        assert_eq!(1, total_registered_signers);
743
744        runner
745            .register_signer_to_aggregator()
746            .await
747            .expect("registering a signer to the aggregator should not fail");
748
749        let last_registered_signer_second_registration =
750            registration_publisher_spy.get_last_registered_signer().await.unwrap();
751        let maybe_protocol_initializer_second_registration = protocol_initializer_store
752            .get_protocol_initializer(current_epoch.offset_to_recording_epoch())
753            .await
754            .expect("get_protocol_initializer should not fail");
755        assert!(
756            maybe_protocol_initializer_second_registration.is_some(),
757            "A protocol initializer should have been registered at the 'Recording' epoch"
758        );
759        assert_eq!(
760            serde_json::to_string(&last_registered_signer_first_registration).unwrap(),
761            serde_json::to_string(&last_registered_signer_second_registration).unwrap(),
762            "The signer registration should be the same and should have been registered twice"
763        );
764
765        let total_registered_signers =
766            registration_publisher_spy.get_total_registered_signers().await;
767        assert_eq!(1, total_registered_signers);
768    }
769
770    #[tokio::test]
771    async fn test_update_era_checker() {
772        let services = init_services().await;
773        let ticker_service = services.ticker_service.clone();
774        let era_checker = services.era_checker.clone();
775        let mut time_point = ticker_service.get_current_time_point().await.unwrap();
776
777        assert_eq!(time_point.epoch, era_checker.current_epoch());
778        let runner = init_runner(Some(services), None).await;
779        time_point.epoch += 1;
780        runner.update_era_checker(time_point.epoch).await.unwrap();
781
782        assert_eq!(time_point.epoch, era_checker.current_epoch());
783    }
784
785    #[tokio::test]
786    async fn test_upkeep() {
787        let mut services = init_services().await;
788        let mut upkeep_service_mock = MockUpkeepService::new();
789        upkeep_service_mock
790            .expect_run()
791            .with(eq(Epoch(17)))
792            .returning(|_| Ok(()))
793            .once();
794        services.upkeep_service = Arc::new(upkeep_service_mock);
795
796        let runner = init_runner(Some(services), None).await;
797        runner.upkeep(Epoch(17)).await.expect("upkeep should not fail");
798    }
799
800    #[tokio::test]
801    async fn test_inform_epoch_setting_pass_available_signed_entity_types_to_epoch_service() {
802        let services = init_services().await;
803        let runner = init_runner(Some(services), None).await;
804
805        let epoch = Epoch(1);
806        let signers = fake_data::signers(5);
807        let current_signers = signers[1..3].to_vec();
808        let next_signers = signers[2..5].to_vec();
809
810        let mithril_network_configuration = MithrilNetworkConfiguration {
811            epoch,
812            configuration_for_aggregation: MithrilNetworkConfigurationForEpoch {
813                enabled_signed_entity_types: BTreeSet::from([
814                    SignedEntityTypeDiscriminants::MithrilStakeDistribution,
815                    SignedEntityTypeDiscriminants::CardanoTransactions,
816                ]),
817                ..Dummy::dummy()
818            },
819            ..Dummy::dummy()
820        };
821
822        runner
823            .inform_epoch_settings(
824                epoch,
825                mithril_network_configuration,
826                current_signers,
827                next_signers,
828            )
829            .await
830            .unwrap();
831
832        let epoch_service = runner.services.epoch_service.read().await;
833        let recorded_allowed_discriminants = epoch_service.allowed_discriminants().unwrap();
834
835        assert_eq!(
836            &BTreeSet::from([
837                SignedEntityTypeDiscriminants::MithrilStakeDistribution,
838                SignedEntityTypeDiscriminants::CardanoTransactions,
839            ]),
840            recorded_allowed_discriminants
841        );
842    }
843}