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 {
224                party_id: self.services.single_signer.get_party_id(),
225                verification_key_for_concatenation: protocol_initializer
226                    .verification_key_for_concatenation()
227                    .into(),
228                verification_key_signature_for_concatenation: protocol_initializer
229                    .verification_key_signature_for_concatenation(),
230                operational_certificate: protocol_operational_certificate,
231                kes_evolutions,
232                #[cfg(feature = "future_snark")]
233                verification_key_for_snark: protocol_initializer
234                    .verification_key_for_snark()
235                    .map(Into::into),
236                #[cfg(feature = "future_snark")]
237                verification_key_signature_for_snark: protocol_initializer
238                    .verification_key_signature_for_snark(),
239            };
240            self.services
241                .signer_registration_publisher
242                .register_signer(epoch_offset_to_recording_epoch, &signer)
243                .await?;
244
245            self.services
246                .protocol_initializer_store
247                .save_protocol_initializer(epoch_offset_to_recording_epoch, protocol_initializer)
248                .await?;
249        }
250
251        Ok(())
252    }
253
254    async fn update_stake_distribution(&self, epoch: Epoch) -> StdResult<()> {
255        debug!(self.logger, ">> update_stake_distribution(epoch: {epoch})");
256
257        let exists_stake_distribution = !self
258            .services
259            .stake_store
260            .get_stakes(epoch.offset_to_recording_epoch())
261            .await?
262            .unwrap_or_default()
263            .is_empty();
264        if exists_stake_distribution {
265            return Ok(());
266        }
267
268        let stake_distribution = self
269            .services
270            .chain_observer
271            .get_current_stake_distribution()
272            .await?
273            .ok_or_else(|| RunnerError::NoValueError("current_stake_distribution".to_string()))?;
274        self.services
275            .stake_store
276            .save_stakes(epoch.offset_to_recording_epoch(), stake_distribution)
277            .await?;
278
279        Ok(())
280    }
281
282    async fn can_sign_current_epoch(&self) -> StdResult<bool> {
283        let epoch_service = self.epoch_service_read().await;
284        epoch_service.can_signer_sign_current_epoch(self.services.single_signer.get_party_id())
285    }
286
287    async fn inform_epoch_settings(
288        &self,
289        aggregator_signer_registration_epoch: Epoch,
290        mithril_network_configuration: MithrilNetworkConfiguration,
291        current_signer: Vec<Signer>,
292        next_signer: Vec<Signer>,
293    ) -> StdResult<()> {
294        debug!(
295            self.logger,
296            ">> inform_epoch_settings(epoch:{})", aggregator_signer_registration_epoch
297        );
298
299        self.services
300            .epoch_service
301            .write()
302            .await
303            .inform_epoch_settings(
304                aggregator_signer_registration_epoch,
305                mithril_network_configuration,
306                current_signer,
307                next_signer,
308            )
309            .await
310    }
311
312    async fn compute_message(
313        &self,
314        signed_entity_type: &SignedEntityType,
315    ) -> StdResult<ProtocolMessage> {
316        debug!(self.logger, ">> compute_message({signed_entity_type:?})");
317
318        let protocol_message = self
319            .services
320            .signable_builder_service
321            .compute_protocol_message(signed_entity_type.to_owned())
322            .await
323            .with_context(|| format!("Runner can not compute protocol message for signed entity type: '{signed_entity_type}'"))?;
324
325        Ok(protocol_message)
326    }
327
328    async fn compute_publish_single_signature(
329        &self,
330        beacon_to_sign: &BeaconToSign,
331        message: &ProtocolMessage,
332    ) -> StdResult<()> {
333        debug!(self.logger, ">> compute_publish_single_signature"; "beacon_to_sign" => ?beacon_to_sign);
334        self.services
335            .certifier
336            .compute_publish_single_signature(beacon_to_sign, message)
337            .await
338    }
339
340    async fn update_era_checker(&self, epoch: Epoch) -> StdResult<()> {
341        debug!(self.logger, ">> update_era_checker(epoch:{epoch})");
342
343        let era_token = self
344            .services
345            .era_reader
346            .read_era_epoch_token(epoch)
347            .await
348            .map_err(Box::new)?;
349        let current_era = era_token.get_current_supported_era()?;
350        self.services
351            .era_checker
352            .change_era(current_era, era_token.get_current_epoch());
353        debug!(
354            self.logger,
355            "Current Era is {} (Epoch {}).",
356            current_era,
357            era_token.get_current_epoch()
358        );
359
360        if era_token.get_next_supported_era().is_err() {
361            let era_name = &era_token.get_next_era_marker().unwrap().name;
362            warn!(
363                self.logger,
364                "Upcoming Era '{era_name}' is not supported by this version of the software. Please update!"
365            );
366        }
367
368        Ok(())
369    }
370
371    async fn upkeep(&self, current_epoch: Epoch) -> StdResult<()> {
372        debug!(self.logger, ">> upkeep(current_epoch:{current_epoch})");
373        self.services.upkeep_service.run(current_epoch).await?;
374        Ok(())
375    }
376}
377
378#[cfg(test)]
379mod tests {
380    use std::collections::BTreeSet;
381    use std::{path::Path, sync::Arc};
382
383    use mockall::mock;
384    use mockall::predicate::eq;
385    use tokio::sync::RwLock;
386
387    use mithril_cardano_node_chain::chain_importer::CardanoChainDataImporter;
388    use mithril_cardano_node_chain::test::double::{
389        DumbBlockScanner, FakeChainObserver, InMemoryChainDataStore,
390    };
391    use mithril_cardano_node_internal_database::{
392        signable_builder::{
393            CardanoDatabaseSignableBuilder, CardanoImmutableFilesFullSignableBuilder,
394        },
395        test::double::{DumbImmutableDigester, DumbImmutableFileObserver},
396    };
397    use mithril_common::{
398        api_version::APIVersionProvider,
399        crypto_helper::{MKMap, MKMapNode, MKTreeNode, MKTreeStoreInMemory, MKTreeStorer},
400        entities::{BlockNumber, BlockRange, Epoch, SignedEntityTypeDiscriminants},
401        signable_builder::{
402            BlockRangeRootRetriever, CardanoBlocksTransactionsSignableBuilder,
403            CardanoStakeDistributionSignableBuilder, CardanoTransactionsSignableBuilder,
404            LegacyBlockRangeRootRetriever, MithrilSignableBuilderService,
405            MithrilStakeDistributionSignableBuilder, SignableBuilderServiceDependencies,
406        },
407        test::{
408            builder::MithrilFixtureBuilder,
409            double::{Dummy, fake_data},
410        },
411    };
412    use mithril_era::{EraChecker, EraReader, adapters::EraReaderBootstrapAdapter};
413    use mithril_protocol_config::model::MithrilNetworkConfigurationForEpoch;
414    use mithril_protocol_config::test::double::configuration_provider::FakeMithrilNetworkConfigurationProvider;
415    use mithril_signed_entity_lock::SignedEntityTypeLock;
416    use mithril_signed_entity_preloader::{
417        CardanoTransactionsPreloader, CardanoTransactionsPreloaderActivation,
418    };
419    use mithril_ticker::{MithrilTickerService, TickerService};
420
421    use crate::database::repository::{
422        ProtocolInitializerRepository, SignedBeaconRepository, StakePoolStore,
423    };
424    use crate::database::test_helper::main_db_connection;
425    use crate::metrics::MetricsService;
426    use crate::services::{
427        MithrilEpochService, MithrilSingleSigner, MockUpkeepService, SignaturePublisherNoop,
428        SignerCertifierService, SignerChainDataImporter, SignerSignableSeedBuilder,
429        SignerSignedEntityConfigProvider,
430    };
431    use crate::test::TestLogger;
432    use crate::test::double::{DumbSignersRegistrationRetriever, SpySignerRegistrationPublisher};
433
434    use super::*;
435
436    const DIGESTER_RESULT: &str = "a digest";
437
438    mock! {
439        pub FakeTimePointProvider { }
440
441        #[async_trait]
442        impl TickerService for FakeTimePointProvider {
443            async fn get_current_time_point(&self) -> StdResult<TimePoint>;
444        }
445    }
446
447    mock! {
448        pub BlockRangeRootRetriever<S: MKTreeStorer> { }
449
450        #[async_trait]
451        impl<S: MKTreeStorer> BlockRangeRootRetriever<S> for BlockRangeRootRetriever<S> {
452            async fn retrieve_block_range_roots<'a>(
453                &'a self,
454                up_to_beacon: BlockNumber,
455            ) -> StdResult<Box<dyn Iterator<Item = (BlockRange, MKTreeNode)> + 'a>>;
456
457            async fn compute_merkle_map_from_block_range_roots(
458                &self,
459                up_to_beacon: BlockNumber,
460            ) -> StdResult<MKMap<BlockRange, MKMapNode<BlockRange,S>, S>>;
461        }
462    }
463
464    mock! {
465        pub LegacyBlockRangeRootRetriever<S: MKTreeStorer> { }
466
467        #[async_trait]
468        impl<S: MKTreeStorer> LegacyBlockRangeRootRetriever<S> for LegacyBlockRangeRootRetriever<S> {
469            async fn retrieve_block_range_roots<'a>(
470                &'a self,
471                up_to_beacon: BlockNumber,
472            ) -> StdResult<Box<dyn Iterator<Item = (BlockRange, MKTreeNode)> + 'a>>;
473
474            async fn compute_merkle_map_from_block_range_roots(
475                &self,
476                up_to_beacon: BlockNumber,
477            ) -> StdResult<MKMap<BlockRange, MKMapNode<BlockRange,S>, S>>;
478        }
479    }
480
481    async fn init_services() -> SignerDependencyContainer {
482        let logger = TestLogger::stdout();
483        let sqlite_connection = Arc::new(main_db_connection().unwrap());
484        let stake_distribution_signers = fake_data::signers_with_stakes(2);
485        let party_id = stake_distribution_signers[1].party_id.clone();
486        let fake_observer = FakeChainObserver::default();
487        fake_observer.set_signers(stake_distribution_signers).await;
488        let chain_observer = Arc::new(fake_observer);
489        let ticker_service = Arc::new(MithrilTickerService::new(
490            chain_observer.clone(),
491            Arc::new(DumbImmutableFileObserver::default()),
492        ));
493        let era_reader = Arc::new(EraReader::new(Arc::new(EraReaderBootstrapAdapter)));
494        let era_epoch_token = era_reader
495            .read_era_epoch_token(ticker_service.get_current_epoch().await.unwrap())
496            .await
497            .unwrap();
498        let era_checker = Arc::new(EraChecker::new(
499            era_epoch_token.get_current_supported_era().unwrap(),
500            era_epoch_token.get_current_epoch(),
501        ));
502
503        let api_version_provider = Arc::new(APIVersionProvider::new(era_checker.clone()));
504        let digester = Arc::new(DumbImmutableDigester::default().with_digest(DIGESTER_RESULT));
505        let cardano_immutable_signable_builder =
506            Arc::new(CardanoImmutableFilesFullSignableBuilder::new(
507                digester.clone(),
508                Path::new(""),
509                logger.clone(),
510            ));
511        let mithril_stake_distribution_signable_builder =
512            Arc::new(MithrilStakeDistributionSignableBuilder::default());
513        let transaction_parser = Arc::new(DumbBlockScanner::new());
514        let transaction_store = Arc::new(InMemoryChainDataStore::default());
515        let transactions_importer = Arc::new(SignerChainDataImporter::new(Arc::new(
516            CardanoChainDataImporter::new(
517                transaction_parser.clone(),
518                transaction_store.clone(),
519                logger.clone(),
520            ),
521        )));
522        let legacy_block_range_root_retriever =
523            Arc::new(MockLegacyBlockRangeRootRetriever::<MKTreeStoreInMemory>::new());
524        let cardano_transactions_builder = Arc::new(CardanoTransactionsSignableBuilder::new(
525            transactions_importer.clone(),
526            legacy_block_range_root_retriever,
527        ));
528        let block_range_root_retriever =
529            Arc::new(MockBlockRangeRootRetriever::<MKTreeStoreInMemory>::new());
530        let cardano_blocks_transactions_builder =
531            Arc::new(CardanoBlocksTransactionsSignableBuilder::new(
532                transactions_importer.clone(),
533                block_range_root_retriever,
534            ));
535        let stake_store = Arc::new(StakePoolStore::new(sqlite_connection.clone(), None));
536        let cardano_stake_distribution_builder = Arc::new(
537            CardanoStakeDistributionSignableBuilder::new(stake_store.clone()),
538        );
539        let cardano_database_signable_builder = Arc::new(CardanoDatabaseSignableBuilder::new(
540            digester.clone(),
541            Path::new(""),
542            logger.clone(),
543        ));
544        let protocol_initializer_store = Arc::new(ProtocolInitializerRepository::new(
545            sqlite_connection.clone(),
546            None,
547        ));
548        let epoch_service = Arc::new(RwLock::new(MithrilEpochService::new(
549            stake_store.clone(),
550            protocol_initializer_store.clone(),
551            logger.clone(),
552        )));
553        let single_signer = Arc::new(MithrilSingleSigner::new(
554            party_id,
555            epoch_service.clone(),
556            logger.clone(),
557        ));
558        let signable_seed_builder_service = Arc::new(SignerSignableSeedBuilder::new(
559            epoch_service.clone(),
560            protocol_initializer_store.clone(),
561        ));
562        let signable_builders_dependencies = SignableBuilderServiceDependencies::new(
563            mithril_stake_distribution_signable_builder,
564            cardano_immutable_signable_builder,
565            cardano_transactions_builder,
566            cardano_blocks_transactions_builder,
567            cardano_stake_distribution_builder,
568            cardano_database_signable_builder,
569        );
570        let signable_builder_service = Arc::new(MithrilSignableBuilderService::new(
571            signable_seed_builder_service,
572            signable_builders_dependencies,
573            logger.clone(),
574        ));
575        let metrics_service = Arc::new(MetricsService::new(logger.clone()).unwrap());
576        let signed_entity_type_lock = Arc::new(SignedEntityTypeLock::default());
577        let security_parameter = BlockNumber(0);
578        let cardano_transactions_preloader = Arc::new(CardanoTransactionsPreloader::new(
579            signed_entity_type_lock.clone(),
580            transactions_importer.clone(),
581            security_parameter,
582            chain_observer.clone(),
583            logger.clone(),
584            Arc::new(CardanoTransactionsPreloaderActivation::new(true)),
585        ));
586        let upkeep_service = Arc::new(MockUpkeepService::new());
587        let certifier = Arc::new(SignerCertifierService::new(
588            Arc::new(SignedBeaconRepository::new(sqlite_connection.clone(), None)),
589            Arc::new(SignerSignedEntityConfigProvider::new(epoch_service.clone())),
590            signed_entity_type_lock.clone(),
591            single_signer.clone(),
592            Arc::new(SignaturePublisherNoop),
593            logger.clone(),
594        ));
595        let kes_signer = None;
596
597        let configuration_for_aggregation = MithrilNetworkConfigurationForEpoch::dummy();
598        let configuration_for_next_aggregation = MithrilNetworkConfigurationForEpoch::dummy();
599        let configuration_for_registration = MithrilNetworkConfigurationForEpoch::dummy();
600
601        let network_configuration_service = Arc::new(FakeMithrilNetworkConfigurationProvider::new(
602            configuration_for_aggregation,
603            configuration_for_next_aggregation,
604            configuration_for_registration,
605        ));
606
607        SignerDependencyContainer {
608            stake_store,
609            chain_observer,
610            digester,
611            single_signer,
612            ticker_service,
613            protocol_initializer_store,
614            era_checker,
615            era_reader,
616            api_version_provider,
617            signable_builder_service,
618            metrics_service,
619            signed_entity_type_lock,
620            cardano_transactions_preloader,
621            upkeep_service,
622            epoch_service,
623            certifier,
624            signer_registration_publisher: Arc::new(SpySignerRegistrationPublisher::default()),
625            signers_registration_retriever: Arc::new(DumbSignersRegistrationRetriever::default()),
626            kes_signer,
627            network_configuration_service,
628        }
629    }
630
631    async fn init_runner(
632        maybe_services: Option<SignerDependencyContainer>,
633        maybe_config: Option<Configuration>,
634    ) -> SignerRunner {
635        SignerRunner::new(
636            maybe_config.unwrap_or(Configuration::new_sample("1")),
637            maybe_services.unwrap_or(init_services().await),
638            TestLogger::stdout(),
639        )
640    }
641
642    #[tokio::test]
643    async fn test_get_current_time_point() {
644        let mut services = init_services().await;
645        let expected = TimePoint::dummy();
646        let mut ticker_service = MockFakeTimePointProvider::new();
647        ticker_service
648            .expect_get_current_time_point()
649            .once()
650            .returning(move || Ok(TimePoint::dummy()));
651        services.ticker_service = Arc::new(ticker_service);
652        let runner = init_runner(Some(services), None).await;
653
654        assert_eq!(
655            expected,
656            runner
657                .get_current_time_point()
658                .await
659                .expect("Get current time point should not fail.")
660        );
661    }
662
663    #[tokio::test]
664    async fn test_update_stake_distribution() {
665        let services = init_services().await;
666        let stake_store = services.stake_store.clone();
667        let current_epoch = services
668            .chain_observer
669            .get_current_epoch()
670            .await
671            .expect("chain observer should not fail")
672            .expect("the observer should return an epoch");
673        let runner = init_runner(Some(services), None).await;
674        assert!(
675            stake_store
676                .get_stakes(current_epoch)
677                .await
678                .expect("getting stakes from store should not fail")
679                .is_none()
680        );
681
682        runner
683            .update_stake_distribution(current_epoch)
684            .await
685            .expect("update_stake_distribution should not fail.");
686
687        let stake_distribution = stake_store
688            .get_stakes(current_epoch.offset_to_recording_epoch())
689            .await
690            .expect("getting stakes from store should not fail")
691            .expect("there should be stakes for this epoch");
692
693        assert_eq!(2, stake_distribution.len());
694    }
695
696    #[tokio::test]
697    async fn test_register_signer_to_aggregator() {
698        let mut services = init_services().await;
699        let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
700        let registration_publisher_spy = Arc::new(SpySignerRegistrationPublisher::default());
701        services.signer_registration_publisher = registration_publisher_spy.clone();
702        let protocol_initializer_store = services.protocol_initializer_store.clone();
703        let current_epoch = services.ticker_service.get_current_epoch().await.unwrap();
704
705        let stakes = services
706            .chain_observer
707            .get_current_stake_distribution()
708            .await
709            .unwrap()
710            .unwrap();
711        services
712            .stake_store
713            .save_stakes(current_epoch.offset_to_recording_epoch(), stakes)
714            .await
715            .unwrap();
716
717        let runner = init_runner(Some(services), None).await;
718
719        let mithril_network_configuration = MithrilNetworkConfiguration {
720            epoch: current_epoch,
721            ..MithrilNetworkConfiguration::dummy()
722        };
723
724        runner
725            .inform_epoch_settings(
726                current_epoch,
727                mithril_network_configuration,
728                fixture.signers(),
729                fixture.signers(),
730            )
731            .await
732            .unwrap();
733
734        runner
735            .register_signer_to_aggregator()
736            .await
737            .expect("registering a signer to the aggregator should not fail");
738
739        let last_registered_signer_first_registration =
740            registration_publisher_spy.get_last_registered_signer().await.unwrap();
741        let maybe_protocol_initializer_first_registration = protocol_initializer_store
742            .get_protocol_initializer(current_epoch.offset_to_recording_epoch())
743            .await
744            .expect("get_protocol_initializer should not fail");
745        assert!(
746            maybe_protocol_initializer_first_registration.is_some(),
747            "A protocol initializer should have been registered at the 'Recording' epoch"
748        );
749
750        let total_registered_signers =
751            registration_publisher_spy.get_total_registered_signers().await;
752        assert_eq!(1, total_registered_signers);
753
754        runner
755            .register_signer_to_aggregator()
756            .await
757            .expect("registering a signer to the aggregator should not fail");
758
759        let last_registered_signer_second_registration =
760            registration_publisher_spy.get_last_registered_signer().await.unwrap();
761        let maybe_protocol_initializer_second_registration = protocol_initializer_store
762            .get_protocol_initializer(current_epoch.offset_to_recording_epoch())
763            .await
764            .expect("get_protocol_initializer should not fail");
765        assert!(
766            maybe_protocol_initializer_second_registration.is_some(),
767            "A protocol initializer should have been registered at the 'Recording' epoch"
768        );
769        assert_eq!(
770            serde_json::to_string(&last_registered_signer_first_registration).unwrap(),
771            serde_json::to_string(&last_registered_signer_second_registration).unwrap(),
772            "The signer registration should be the same and should have been registered twice"
773        );
774
775        let total_registered_signers =
776            registration_publisher_spy.get_total_registered_signers().await;
777        assert_eq!(1, total_registered_signers);
778    }
779
780    #[tokio::test]
781    async fn test_update_era_checker() {
782        let services = init_services().await;
783        let ticker_service = services.ticker_service.clone();
784        let era_checker = services.era_checker.clone();
785        let mut time_point = ticker_service.get_current_time_point().await.unwrap();
786
787        assert_eq!(time_point.epoch, era_checker.current_epoch());
788        let runner = init_runner(Some(services), None).await;
789        time_point.epoch += 1;
790        runner.update_era_checker(time_point.epoch).await.unwrap();
791
792        assert_eq!(time_point.epoch, era_checker.current_epoch());
793    }
794
795    #[tokio::test]
796    async fn test_upkeep() {
797        let mut services = init_services().await;
798        let mut upkeep_service_mock = MockUpkeepService::new();
799        upkeep_service_mock
800            .expect_run()
801            .with(eq(Epoch(17)))
802            .returning(|_| Ok(()))
803            .once();
804        services.upkeep_service = Arc::new(upkeep_service_mock);
805
806        let runner = init_runner(Some(services), None).await;
807        runner.upkeep(Epoch(17)).await.expect("upkeep should not fail");
808    }
809
810    #[tokio::test]
811    async fn test_inform_epoch_setting_pass_available_signed_entity_types_to_epoch_service() {
812        let services = init_services().await;
813        let runner = init_runner(Some(services), None).await;
814
815        let epoch = Epoch(1);
816        let signers = fake_data::signers(5);
817        let current_signers = signers[1..3].to_vec();
818        let next_signers = signers[2..5].to_vec();
819
820        let mithril_network_configuration = MithrilNetworkConfiguration {
821            epoch,
822            configuration_for_aggregation: MithrilNetworkConfigurationForEpoch {
823                enabled_signed_entity_types: BTreeSet::from([
824                    SignedEntityTypeDiscriminants::MithrilStakeDistribution,
825                    SignedEntityTypeDiscriminants::CardanoTransactions,
826                ]),
827                ..Dummy::dummy()
828            },
829            ..Dummy::dummy()
830        };
831
832        runner
833            .inform_epoch_settings(
834                epoch,
835                mithril_network_configuration,
836                current_signers,
837                next_signers,
838            )
839            .await
840            .unwrap();
841
842        let epoch_service = runner.services.epoch_service.read().await;
843        let recorded_allowed_discriminants = epoch_service.allowed_discriminants().unwrap();
844
845        assert_eq!(
846            &BTreeSet::from([
847                SignedEntityTypeDiscriminants::MithrilStakeDistribution,
848                SignedEntityTypeDiscriminants::CardanoTransactions,
849            ]),
850            recorded_allowed_discriminants
851        );
852    }
853}