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