mithril_signer/runtime/
state_machine.rs

1use anyhow::Error;
2use chrono::Local;
3use slog::{Logger, debug, info};
4use std::{fmt::Display, ops::Deref, sync::Arc, time::Duration};
5use tokio::sync::Mutex;
6
7use mithril_common::{
8    crypto_helper::ProtocolInitializerError,
9    entities::{Epoch, TimePoint},
10    logging::LoggerExtensions,
11};
12
13use crate::{
14    MetricsService,
15    entities::{BeaconToSign, SignerEpochSettings},
16    services::AggregatorClientError,
17};
18
19use super::{Runner, RuntimeError};
20
21/// Different possible states of the state machine.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum SignerState {
24    /// Starting state
25    Init,
26    /// Hold the latest known epoch in order to help synchronisation
27    /// with the aggregator
28    Unregistered {
29        /// Current Epoch
30        epoch: Epoch,
31    },
32
33    /// `ReadyToSign` state. The signer is registered and ready to sign new messages.
34    ReadyToSign {
35        /// Epoch when signer transitioned to the state.
36        epoch: Epoch,
37    },
38
39    /// `RegisteredNotAbleToSign` state. The signer is registered but not able to sign for the duration of the epoch.
40    RegisteredNotAbleToSign {
41        /// Epoch when signer transitioned to the state.
42        epoch: Epoch,
43    },
44}
45
46impl SignerState {
47    /// Returns `true` if the state in `Init`
48    pub fn is_init(&self) -> bool {
49        matches!(*self, SignerState::Init)
50    }
51
52    /// Returns `true` if the state in `Unregistered`
53    pub fn is_unregistered(&self) -> bool {
54        matches!(*self, SignerState::Unregistered { .. })
55    }
56
57    /// Returns `true` if the state in `ReadyToSign`
58    pub fn is_ready_to_sign(&self) -> bool {
59        matches!(*self, SignerState::ReadyToSign { .. })
60    }
61
62    /// Returns `true` if the state in `RegisteredNotAbleToSign`
63    pub fn is_registered_not_able_to_sign(&self) -> bool {
64        matches!(*self, SignerState::RegisteredNotAbleToSign { .. })
65    }
66}
67
68impl Display for SignerState {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        match self {
71            Self::Init => write!(f, "Init"),
72            Self::Unregistered { epoch } => write!(f, "Unregistered - {epoch:?}"),
73            Self::RegisteredNotAbleToSign { epoch } => {
74                write!(f, "RegisteredNotAbleToSign - {epoch}")
75            }
76            Self::ReadyToSign { epoch } => {
77                write!(f, "ReadyToSign - {epoch}")
78            }
79        }
80    }
81}
82
83enum EpochStatus {
84    NewEpoch(Epoch),
85    Unchanged(TimePoint),
86}
87
88/// The state machine is responsible of the execution of the signer automate.
89pub struct StateMachine {
90    state: Mutex<SignerState>,
91    runner: Box<dyn Runner>,
92    interval: Duration,
93    metrics_service: Arc<MetricsService>,
94    logger: Logger,
95}
96
97impl StateMachine {
98    /// Create a new StateMachine instance.
99    pub fn new(
100        starting_state: SignerState,
101        runner: Box<dyn Runner>,
102        interval: Duration,
103        metrics_service: Arc<MetricsService>,
104        logger: Logger,
105    ) -> Self {
106        Self {
107            state: Mutex::new(starting_state),
108            runner,
109            interval,
110            metrics_service,
111            logger: logger.new_with_component_name::<Self>(),
112        }
113    }
114
115    /// Return the current state of the state machine.
116    pub async fn get_state(&self) -> SignerState {
117        self.state.lock().await.to_owned()
118    }
119
120    /// Launch the state machine until an error occurs or it is interrupted.
121    pub async fn run(&self) -> Result<(), RuntimeError> {
122        info!(self.logger, "Launching State Machine");
123        let mut interval = tokio::time::interval(self.interval);
124
125        loop {
126            interval.tick().await;
127            // Note: the "time" property in logs produced by our formatter (slog_bunyan) uses local
128            // time, so we must use it as well to avoid confusion.
129            let approximate_next_cycle_time = Local::now() + self.interval;
130
131            if let Err(e) = self.cycle().await {
132                e.write_to_log(&self.logger);
133                if e.is_critical() {
134                    return Err(e);
135                }
136            }
137
138            info!(
139                self.logger, "… Cycle finished";
140                "approximate_next_cycle_time" => %approximate_next_cycle_time.time().format("%H:%M:%S%.3f"),
141                "run_interval_in_ms" => self.interval.as_millis(),
142            );
143        }
144    }
145
146    /// Perform a cycle of the state machine.
147    pub async fn cycle(&self) -> Result<(), RuntimeError> {
148        let mut state = self.state.lock().await;
149        info!(
150            self.logger,
151            "================================================================================"
152        );
153        info!(self.logger, "New cycle: {}", *state);
154
155        self.metrics_service
156            .get_runtime_cycle_total_since_startup_counter()
157            .increment();
158
159        match state.deref() {
160            SignerState::Init => {
161                *state = self.transition_from_init_to_unregistered().await?;
162            }
163            SignerState::Unregistered { epoch } => {
164                if let EpochStatus::NewEpoch(new_epoch) = self.has_epoch_changed(*epoch).await? {
165                    info!(
166                        self.logger,
167                        "→ Epoch has changed, transiting to Unregistered"
168                    );
169                    *state = self.transition_from_unregistered_to_unregistered(new_epoch).await?;
170                } else if let Some(epoch_settings) = self
171                    .runner
172                    .get_epoch_settings()
173                    .await
174                    .map_err(|e| RuntimeError::KeepState {
175                        message: format!("could not retrieve epoch settings at epoch {epoch:?}"),
176                        nested_error: Some(e),
177                    })?
178                {
179                    info!(self.logger, "→ Epoch settings found");
180                    if epoch_settings.epoch >= *epoch {
181                        info!(self.logger, "New Epoch found");
182                        info!(self.logger, " ⋅ Transiting to Registered");
183                        *state = self
184                            .transition_from_unregistered_to_one_of_registered_states(
185                                epoch_settings,
186                            )
187                            .await?;
188                    } else {
189                        info!(
190                            self.logger, " ⋅ Epoch settings found, but its epoch is behind the known epoch, waiting…";
191                            "epoch_settings" => ?epoch_settings,
192                            "known_epoch" => ?epoch,
193                        );
194                    }
195                } else {
196                    info!(self.logger, "→ No epoch settings found yet, waiting…");
197                }
198            }
199            SignerState::RegisteredNotAbleToSign { epoch } => {
200                if let EpochStatus::NewEpoch(new_epoch) = self.has_epoch_changed(*epoch).await? {
201                    info!(
202                        self.logger,
203                        " → New Epoch detected, transiting to Unregistered"
204                    );
205                    *state = self
206                        .transition_from_registered_not_able_to_sign_to_unregistered(new_epoch)
207                        .await?;
208                } else {
209                    info!(self.logger, " ⋅ Epoch has NOT changed, waiting…");
210                }
211            }
212
213            SignerState::ReadyToSign { epoch } => match self.has_epoch_changed(*epoch).await? {
214                EpochStatus::NewEpoch(new_epoch) => {
215                    info!(
216                        self.logger,
217                        "→ Epoch has changed, transiting to Unregistered"
218                    );
219                    *state = self.transition_from_ready_to_sign_to_unregistered(new_epoch).await?;
220                }
221                EpochStatus::Unchanged(timepoint) => {
222                    let beacon_to_sign =
223                        self.runner.get_beacon_to_sign(timepoint).await.map_err(|e| {
224                            RuntimeError::KeepState {
225                                message: "could not fetch the beacon to sign".to_string(),
226                                nested_error: Some(e),
227                            }
228                        })?;
229
230                    match beacon_to_sign {
231                        Some(beacon) => {
232                            info!(
233                                self.logger, "→ Epoch has NOT changed we can sign this beacon, transiting to ReadyToSign";
234                                "beacon_to_sign" => ?beacon,
235                            );
236                            *state = self
237                                .transition_from_ready_to_sign_to_ready_to_sign(*epoch, beacon)
238                                .await?;
239                        }
240                        None => {
241                            info!(self.logger, " ⋅ No beacon to sign, waiting…");
242                        }
243                    }
244                }
245            },
246        };
247
248        self.metrics_service
249            .get_runtime_cycle_success_since_startup_counter()
250            .increment();
251
252        Ok(())
253    }
254
255    /// Return the new epoch if the epoch is different than the given one, otherwise return the current time point.
256    async fn has_epoch_changed(&self, epoch: Epoch) -> Result<EpochStatus, RuntimeError> {
257        let current_time_point =
258            self.get_current_time_point("checking if epoch has changed").await?;
259
260        if current_time_point.epoch > epoch {
261            Ok(EpochStatus::NewEpoch(current_time_point.epoch))
262        } else {
263            Ok(EpochStatus::Unchanged(current_time_point))
264        }
265    }
266
267    async fn transition_from_unregistered_to_unregistered(
268        &self,
269        new_epoch: Epoch,
270    ) -> Result<SignerState, RuntimeError> {
271        self.update_era_checker(new_epoch, "unregistered → unregistered")
272            .await?;
273
274        Ok(SignerState::Unregistered { epoch: new_epoch })
275    }
276
277    async fn transition_from_init_to_unregistered(&self) -> Result<SignerState, RuntimeError> {
278        let current_epoch = self.get_current_time_point("init → unregistered").await?.epoch;
279        self.update_era_checker(current_epoch, "init → unregistered").await?;
280
281        Ok(SignerState::Unregistered {
282            epoch: current_epoch,
283        })
284    }
285
286    /// Launch the transition process from the `Unregistered` to `ReadyToSign` or `RegisteredNotAbleToSign` state.
287    async fn transition_from_unregistered_to_one_of_registered_states(
288        &self,
289        epoch_settings: SignerEpochSettings,
290    ) -> Result<SignerState, RuntimeError> {
291        self.metrics_service
292            .get_signer_registration_total_since_startup_counter()
293            .increment();
294
295        let time_point = self.get_current_time_point("unregistered → registered").await?;
296        let epoch = time_point.epoch;
297        self.runner.update_stake_distribution(epoch)
298            .await
299            .map_err(|e| RuntimeError::KeepState {
300                message: format!("Could not update stake distribution in 'unregistered → registered' phase for epoch {epoch:?}."),
301                nested_error: Some(e),
302            })?;
303
304        self.runner
305            .inform_epoch_settings(epoch_settings)
306            .await
307            .map_err(|e| RuntimeError::KeepState {
308                message: format!(
309                    "Could not register epoch information in 'unregistered → registered' phase for epoch {epoch:?}."
310                ),
311                nested_error: Some(e),
312            })?;
313
314        fn handle_registration_result(
315            register_result: Result<(), Error>,
316            epoch: Epoch,
317        ) -> Result<Option<SignerState>, RuntimeError> {
318            if let Err(e) = register_result {
319                if let Some(AggregatorClientError::RegistrationRoundNotYetOpened(_)) =
320                    e.downcast_ref::<AggregatorClientError>()
321                {
322                    Ok(Some(SignerState::Unregistered { epoch }))
323                } else if e.downcast_ref::<ProtocolInitializerError>().is_some() {
324                    Err(RuntimeError::Critical {
325                        message: format!(
326                            "Could not register to aggregator in 'unregistered → registered' phase for epoch {epoch:?}."
327                        ),
328                        nested_error: Some(e),
329                    })
330                } else {
331                    Err(RuntimeError::KeepState {
332                        message: format!(
333                            "Could not register to aggregator in 'unregistered → registered' phase for epoch {epoch:?}."
334                        ),
335                        nested_error: Some(e),
336                    })
337                }
338            } else {
339                Ok(None)
340            }
341        }
342
343        let register_result = self.runner.register_signer_to_aggregator().await;
344        let next_state_found = handle_registration_result(register_result, epoch)?;
345
346        self.metrics_service
347            .get_signer_registration_success_since_startup_counter()
348            .increment();
349
350        if let Some(state) = next_state_found {
351            return Ok(state);
352        }
353
354        self.metrics_service
355            .get_signer_registration_success_last_epoch_gauge()
356            .record(epoch);
357
358        self.runner.upkeep(epoch).await.map_err(|e| RuntimeError::KeepState {
359            message: "Failed to upkeep signer in 'unregistered → registered' phase".to_string(),
360            nested_error: Some(e),
361        })?;
362
363        match self
364            .runner
365            .can_sign_current_epoch()
366            .await
367            .map_err(|e| RuntimeError::KeepState {
368                message: "Failed to check if signer can sign in the current epoch in 'unregistered → ?' phase".to_string(),
369                nested_error: Some(e),
370            })? {
371            true => Ok(SignerState::ReadyToSign { epoch }),
372            false => Ok(SignerState::RegisteredNotAbleToSign { epoch }),
373        }
374    }
375
376    async fn transition_from_registered_not_able_to_sign_to_unregistered(
377        &self,
378        epoch: Epoch,
379    ) -> Result<SignerState, RuntimeError> {
380        self.update_era_checker(epoch, "registered not able to sign → unregistered")
381            .await?;
382
383        Ok(SignerState::Unregistered { epoch })
384    }
385
386    async fn transition_from_ready_to_sign_to_unregistered(
387        &self,
388        epoch: Epoch,
389    ) -> Result<SignerState, RuntimeError> {
390        self.update_era_checker(epoch, "ready to sign → unregistered").await?;
391
392        Ok(SignerState::Unregistered { epoch })
393    }
394
395    /// Launch the transition process from the `ReadyToSign` to the `ReadyToSign` state.
396    async fn transition_from_ready_to_sign_to_ready_to_sign(
397        &self,
398        current_epoch: Epoch,
399        beacon_to_sign: BeaconToSign,
400    ) -> Result<SignerState, RuntimeError> {
401        let (retrieval_epoch, next_retrieval_epoch) = (
402            current_epoch.offset_to_signer_retrieval_epoch()?,
403            current_epoch.offset_to_next_signer_retrieval_epoch(),
404        );
405
406        debug!(
407            self.logger, ">> transition_from_ready_to_sign_to_ready_to_sign";
408            "current_epoch" => ?current_epoch,
409            "retrieval_epoch" => ?retrieval_epoch,
410            "next_retrieval_epoch" => ?next_retrieval_epoch,
411        );
412
413        self.metrics_service
414            .get_signature_registration_total_since_startup_counter()
415            .increment();
416
417        let message = self
418            .runner
419            .compute_message(&beacon_to_sign.signed_entity_type)
420            .await
421            .map_err(|e| RuntimeError::KeepState {
422                message: format!("Could not compute message during 'ready to sign → ready to sign' phase (current epoch {current_epoch:?})"),
423                nested_error: Some(e)
424            })?;
425
426        self.runner.compute_publish_single_signature(&beacon_to_sign, &message)
427            .await
428            .map_err(|e| RuntimeError::KeepState {
429                message: format!("Could not compute and publish single signature during 'ready to sign → ready to sign' phase (current epoch {current_epoch:?})"),
430                nested_error: Some(e)
431            })?;
432
433        self.metrics_service
434            .get_signature_registration_success_since_startup_counter()
435            .increment();
436        self.metrics_service
437            .get_signature_registration_success_last_epoch_gauge()
438            .record(current_epoch);
439
440        Ok(SignerState::ReadyToSign {
441            epoch: current_epoch,
442        })
443    }
444
445    async fn get_current_time_point(&self, context: &str) -> Result<TimePoint, RuntimeError> {
446        let current_time_point =
447            self.runner
448                .get_current_time_point()
449                .await
450                .map_err(|e| RuntimeError::KeepState {
451                    message: format!(
452                        "Could not retrieve current time point in context '{context}'."
453                    ),
454                    nested_error: Some(e),
455                })?;
456
457        Ok(current_time_point)
458    }
459
460    async fn update_era_checker(&self, epoch: Epoch, context: &str) -> Result<(), RuntimeError> {
461        self.runner
462            .update_era_checker(epoch)
463            .await
464            .map_err(|e| RuntimeError::Critical {
465                message: format!(
466                    "Could not update Era checker with context '{context}' for epoch {epoch:?}"
467                ),
468                nested_error: Some(e),
469            })
470    }
471}
472
473#[cfg(test)]
474mod tests {
475    use anyhow::anyhow;
476    use chrono::DateTime;
477    use mockall::predicate;
478
479    use mithril_common::entities::{ChainPoint, Epoch, ProtocolMessage, SignedEntityType};
480    use mithril_common::test::double::{Dummy, fake_data};
481
482    use crate::runtime::runner::MockSignerRunner;
483    use crate::services::AggregatorClientError;
484    use crate::test_tools::TestLogger;
485
486    use super::*;
487
488    fn init_state_machine(init_state: SignerState, runner: MockSignerRunner) -> StateMachine {
489        let logger = TestLogger::stdout();
490        let metrics_service = Arc::new(MetricsService::new(logger.clone()).unwrap());
491        StateMachine {
492            state: init_state.into(),
493            runner: Box::new(runner),
494            interval: Duration::from_millis(100),
495            metrics_service,
496            logger,
497        }
498    }
499
500    #[tokio::test]
501    async fn unregistered_epoch_settings_not_found() {
502        let mut runner = MockSignerRunner::new();
503        runner.expect_get_epoch_settings().once().returning(|| Ok(None));
504        runner
505            .expect_get_current_time_point()
506            .once()
507            .returning(|| Ok(TimePoint::dummy()));
508        let state_machine = init_state_machine(
509            SignerState::Unregistered {
510                epoch: TimePoint::dummy().epoch,
511            },
512            runner,
513        );
514        state_machine
515            .cycle()
516            .await
517            .expect("Cycling the state machine should not fail");
518
519        assert_eq!(
520            SignerState::Unregistered {
521                epoch: TimePoint::dummy().epoch
522            },
523            state_machine.get_state().await
524        );
525    }
526
527    #[tokio::test]
528    async fn unregistered_epoch_settings_behind_known_epoch() {
529        let mut runner = MockSignerRunner::new();
530        let epoch_settings = SignerEpochSettings {
531            epoch: Epoch(3),
532            registration_protocol_parameters: fake_data::protocol_parameters(),
533            current_signers: vec![],
534            next_signers: vec![],
535            cardano_transactions_signing_config: None,
536        };
537        let known_epoch = Epoch(4);
538        runner
539            .expect_get_epoch_settings()
540            .once()
541            .returning(move || Ok(Some(epoch_settings.to_owned())));
542        runner.expect_get_current_time_point().once().returning(|| {
543            Ok(TimePoint {
544                epoch: Epoch(4),
545                ..TimePoint::dummy()
546            })
547        });
548        let state_machine =
549            init_state_machine(SignerState::Unregistered { epoch: known_epoch }, runner);
550        state_machine
551            .cycle()
552            .await
553            .expect("Cycling the state machine should not fail");
554
555        assert_eq!(
556            SignerState::Unregistered { epoch: known_epoch },
557            state_machine.get_state().await
558        );
559    }
560
561    #[tokio::test]
562    async fn unregistered_to_registered_not_able_to_sign() {
563        let mut runner = MockSignerRunner::new();
564        runner.expect_upkeep().returning(|_| Ok(())).once();
565        runner
566            .expect_get_epoch_settings()
567            .once()
568            .returning(|| Ok(Some(SignerEpochSettings::dummy())));
569
570        runner
571            .expect_inform_epoch_settings()
572            .with(predicate::eq(SignerEpochSettings::dummy()))
573            .once()
574            .returning(|_| Ok(()));
575
576        runner
577            .expect_get_current_time_point()
578            .times(2)
579            .returning(|| Ok(TimePoint::dummy()));
580        runner.expect_update_stake_distribution().once().returning(|_| Ok(()));
581        runner
582            .expect_register_signer_to_aggregator()
583            .once()
584            .returning(|| Ok(()));
585
586        runner.expect_can_sign_current_epoch().once().returning(|| Ok(false));
587
588        let state_machine = init_state_machine(
589            SignerState::Unregistered {
590                epoch: TimePoint::dummy().epoch,
591            },
592            runner,
593        );
594
595        state_machine
596            .cycle()
597            .await
598            .expect("Cycling the state machine should not fail");
599
600        if let SignerState::RegisteredNotAbleToSign { epoch: _ } = state_machine.get_state().await {
601        } else {
602            panic!(
603                "state machine did not return a RegisteredNotAbleToSign state but {:?}",
604                state_machine.get_state().await
605            );
606        }
607    }
608
609    #[tokio::test]
610    async fn unregistered_to_ready_to_sign() {
611        let mut runner = MockSignerRunner::new();
612        runner.expect_upkeep().returning(|_| Ok(())).once();
613        runner
614            .expect_get_epoch_settings()
615            .once()
616            .returning(|| Ok(Some(SignerEpochSettings::dummy())));
617
618        runner
619            .expect_inform_epoch_settings()
620            .with(predicate::eq(SignerEpochSettings::dummy()))
621            .once()
622            .returning(|_| Ok(()));
623
624        runner
625            .expect_get_current_time_point()
626            .times(2)
627            .returning(|| Ok(TimePoint::dummy()));
628        runner.expect_update_stake_distribution().once().returning(|_| Ok(()));
629        runner
630            .expect_register_signer_to_aggregator()
631            .once()
632            .returning(|| Ok(()));
633
634        runner.expect_can_sign_current_epoch().once().returning(|| Ok(true));
635
636        let state_machine = init_state_machine(
637            SignerState::Unregistered {
638                epoch: TimePoint::dummy().epoch,
639            },
640            runner,
641        );
642
643        state_machine
644            .cycle()
645            .await
646            .expect("Cycling the state machine should not fail");
647
648        assert_eq!(
649            SignerState::ReadyToSign {
650                epoch: TimePoint::dummy().epoch,
651            },
652            state_machine.get_state().await
653        );
654
655        let metrics_service = state_machine.metrics_service;
656        let success_since_startup =
657            metrics_service.get_runtime_cycle_success_since_startup_counter();
658        assert_eq!(1, success_since_startup.get());
659    }
660
661    #[tokio::test]
662    async fn unregistered_to_ready_to_sign_counter() {
663        let mut runner = MockSignerRunner::new();
664
665        runner
666            .expect_get_epoch_settings()
667            .once()
668            .returning(|| Ok(Some(SignerEpochSettings::dummy())));
669
670        runner
671            .expect_inform_epoch_settings()
672            .with(predicate::eq(SignerEpochSettings::dummy()))
673            .once()
674            .returning(|_| Ok(()));
675
676        runner
677            .expect_get_current_time_point()
678            .times(2)
679            .returning(|| Ok(TimePoint::dummy()));
680        runner.expect_update_stake_distribution().once().returning(|_| Ok(()));
681        runner.expect_register_signer_to_aggregator().once().returning(|| {
682            Err(AggregatorClientError::RegistrationRoundNotYetOpened(
683                anyhow!("Not yet opened"),
684            ))?
685        });
686
687        runner.expect_upkeep().never();
688        runner.expect_can_sign_current_epoch().never();
689
690        let state_machine = init_state_machine(
691            SignerState::Unregistered {
692                epoch: TimePoint::dummy().epoch,
693            },
694            runner,
695        );
696
697        state_machine
698            .cycle()
699            .await
700            .expect("Cycling the state machine should not fail");
701
702        assert_eq!(
703            SignerState::Unregistered {
704                epoch: TimePoint::dummy().epoch,
705            },
706            state_machine.get_state().await
707        );
708
709        let metrics_service = state_machine.metrics_service;
710        assert_eq!(
711            1,
712            metrics_service
713                .get_signer_registration_success_since_startup_counter()
714                .get()
715        );
716
717        assert_eq!(
718            0 as f64,
719            metrics_service
720                .get_signer_registration_success_last_epoch_gauge()
721                .get()
722        );
723
724        assert_eq!(
725            1,
726            metrics_service.get_runtime_cycle_total_since_startup_counter().get()
727        );
728    }
729
730    #[tokio::test]
731    async fn registered_not_able_to_sign_to_unregistered() {
732        let mut runner = MockSignerRunner::new();
733        runner
734            .expect_get_current_time_point()
735            .once()
736            .returning(|| Ok(TimePoint::new(10, 100, ChainPoint::dummy())));
737        runner
738            .expect_update_era_checker()
739            .once()
740            .returning(|_e: Epoch| Ok(()));
741
742        let state_machine = init_state_machine(
743            SignerState::RegisteredNotAbleToSign { epoch: Epoch(0) },
744            runner,
745        );
746
747        state_machine
748            .cycle()
749            .await
750            .expect("Cycling the state machine should not fail");
751        assert_eq!(
752            SignerState::Unregistered { epoch: Epoch(10) },
753            state_machine.get_state().await
754        );
755    }
756
757    #[tokio::test]
758    async fn registered_not_able_to_sign_to_registered_not_able_to_sign() {
759        let mut runner = MockSignerRunner::new();
760        runner
761            .expect_get_current_time_point()
762            .once()
763            .returning(|| Ok(TimePoint::new(10, 100, ChainPoint::dummy())));
764
765        let state_machine = init_state_machine(
766            SignerState::RegisteredNotAbleToSign { epoch: Epoch(10) },
767            runner,
768        );
769
770        state_machine
771            .cycle()
772            .await
773            .expect("Cycling the state machine should not fail");
774        assert_eq!(
775            SignerState::RegisteredNotAbleToSign { epoch: Epoch(10) },
776            state_machine.get_state().await
777        );
778    }
779
780    #[tokio::test]
781    async fn ready_to_sign_to_unregistered() {
782        let mut runner = MockSignerRunner::new();
783        runner
784            .expect_get_current_time_point()
785            .once()
786            .returning(|| Ok(TimePoint::new(10, 100, ChainPoint::dummy())));
787        runner
788            .expect_update_era_checker()
789            .once()
790            .returning(|_e: Epoch| Ok(()));
791
792        let state_machine =
793            init_state_machine(SignerState::ReadyToSign { epoch: Epoch(0) }, runner);
794
795        state_machine
796            .cycle()
797            .await
798            .expect("Cycling the state machine should not fail");
799        assert_eq!(
800            SignerState::Unregistered { epoch: Epoch(10) },
801            state_machine.get_state().await
802        );
803    }
804
805    #[tokio::test]
806    async fn ready_to_sign_to_ready_to_sign_when_there_is_a_beacon_to_sign() {
807        let time_point = TimePoint::dummy();
808        let beacon_to_sign = BeaconToSign {
809            epoch: time_point.epoch,
810            signed_entity_type: SignedEntityType::MithrilStakeDistribution(time_point.epoch),
811            initiated_at: DateTime::default(),
812        };
813        let beacon_to_sign_clone = beacon_to_sign.clone();
814        let current_epoch = time_point.epoch;
815
816        let mut runner = MockSignerRunner::new();
817        runner
818            .expect_get_current_time_point()
819            .once()
820            .returning(move || Ok(time_point.to_owned()));
821        runner
822            .expect_get_beacon_to_sign()
823            .once()
824            .returning(move |_| Ok(Some(beacon_to_sign_clone.clone())));
825        runner
826            .expect_compute_message()
827            .once()
828            .returning(|_| Ok(ProtocolMessage::new()));
829        runner
830            .expect_compute_publish_single_signature()
831            .once()
832            .returning(|_, _| Ok(()));
833
834        let state_machine = init_state_machine(
835            SignerState::ReadyToSign {
836                epoch: current_epoch,
837            },
838            runner,
839        );
840        state_machine
841            .cycle()
842            .await
843            .expect("Cycling the state machine should not fail");
844
845        assert_eq!(
846            SignerState::ReadyToSign {
847                epoch: current_epoch
848            },
849            state_machine.get_state().await,
850            "state machine did not return a ReadyToSign state but {:?}",
851            state_machine.get_state().await
852        );
853    }
854
855    #[tokio::test]
856    async fn ready_to_sign_to_ready_to_sign_when_there_no_beacon_to_sign() {
857        let time_point = TimePoint::dummy();
858        let current_epoch = time_point.epoch;
859
860        let mut runner = MockSignerRunner::new();
861        runner
862            .expect_get_current_time_point()
863            .once()
864            .returning(move || Ok(time_point.to_owned()));
865        runner.expect_get_beacon_to_sign().once().returning(move |_| Ok(None));
866
867        let state_machine = init_state_machine(
868            SignerState::ReadyToSign {
869                epoch: current_epoch,
870            },
871            runner,
872        );
873        state_machine
874            .cycle()
875            .await
876            .expect("Cycling the state machine should not fail");
877
878        assert_eq!(
879            SignerState::ReadyToSign {
880                epoch: current_epoch
881            },
882            state_machine.get_state().await,
883            "state machine did not return a ReadyToSign state but {:?}",
884            state_machine.get_state().await
885        );
886    }
887}