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#[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.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 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 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 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}