mithril_signer/runtime/
runner.rs

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