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