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