mithril_signer/services/
certifier.rs

1use async_trait::async_trait;
2use chrono::Utc;
3use slog::{debug, Logger};
4use std::sync::Arc;
5
6use mithril_common::entities::{ProtocolMessage, SignedEntityConfig, SignedEntityType, TimePoint};
7use mithril_common::logging::LoggerExtensions;
8use mithril_common::StdResult;
9use mithril_signed_entity_lock::SignedEntityTypeLock;
10
11use crate::entities::BeaconToSign;
12use crate::services::{SignaturePublisher, SingleSigner};
13
14/// Certifier Service
15///
16/// This service is responsible for providing the beacons that need to be signed by the signer.
17#[cfg_attr(test, mockall::automock)]
18#[async_trait]
19pub trait CertifierService: Sync + Send {
20    /// Get the beacon to sign.
21    ///
22    /// If all available signed entity have already been signed, `None` is returned.
23    async fn get_beacon_to_sign(&self, time_point: TimePoint) -> StdResult<Option<BeaconToSign>>;
24
25    /// Compute and publish a single signature for a given protocol message.
26    async fn compute_publish_single_signature(
27        &self,
28        beacon_to_sign: &BeaconToSign,
29        protocol_message: &ProtocolMessage,
30    ) -> StdResult<()>;
31}
32
33/// Trait to provide the current signed entity configuration that can change over time.
34#[cfg_attr(test, mockall::automock)]
35#[async_trait]
36pub trait SignedEntityConfigProvider: Sync + Send {
37    /// Get the current signed entity configuration.
38    async fn get(&self) -> StdResult<SignedEntityConfig>;
39}
40
41/// Trait to store beacons that have been signed in order to avoid signing them twice.
42#[cfg_attr(test, mockall::automock)]
43#[async_trait]
44pub trait SignedBeaconStore: Sync + Send {
45    /// Filter out already signed entities from a list of signed entities.
46    async fn filter_out_already_signed_entities(
47        &self,
48        entities: Vec<SignedEntityType>,
49    ) -> StdResult<Vec<SignedEntityType>>;
50
51    /// Mark a beacon as signed.
52    async fn mark_beacon_as_signed(&self, entity: &BeaconToSign) -> StdResult<()>;
53}
54
55/// Implementation of the [Certifier Service][CertifierService] for the Mithril Signer.
56pub struct SignerCertifierService {
57    signed_beacon_store: Arc<dyn SignedBeaconStore>,
58    signed_entity_config_provider: Arc<dyn SignedEntityConfigProvider>,
59    signed_entity_type_lock: Arc<SignedEntityTypeLock>,
60    single_signer: Arc<dyn SingleSigner>,
61    signature_publisher: Arc<dyn SignaturePublisher>,
62    logger: Logger,
63}
64
65impl SignerCertifierService {
66    /// Create a new `SignerCertifierService` instance.
67    pub fn new(
68        signed_beacon_store: Arc<dyn SignedBeaconStore>,
69        signed_entity_config_provider: Arc<dyn SignedEntityConfigProvider>,
70        signed_entity_type_lock: Arc<SignedEntityTypeLock>,
71        single_signer: Arc<dyn SingleSigner>,
72        signature_publisher: Arc<dyn SignaturePublisher>,
73        logger: Logger,
74    ) -> Self {
75        Self {
76            signed_beacon_store,
77            signed_entity_config_provider,
78            signed_entity_type_lock,
79            single_signer,
80            signature_publisher,
81            logger: logger.new_with_component_name::<Self>(),
82        }
83    }
84
85    async fn list_available_signed_entity_types(
86        &self,
87        time_point: &TimePoint,
88    ) -> StdResult<Vec<SignedEntityType>> {
89        let signed_entity_types = self
90            .signed_entity_config_provider
91            .get()
92            .await?
93            .list_allowed_signed_entity_types(time_point)?;
94        let unlocked_signed_entities = self
95            .signed_entity_type_lock
96            .filter_unlocked_entries(signed_entity_types)
97            .await;
98        let not_already_signed_entities = self
99            .signed_beacon_store
100            .filter_out_already_signed_entities(unlocked_signed_entities)
101            .await?;
102
103        Ok(not_already_signed_entities)
104    }
105}
106
107#[async_trait]
108impl CertifierService for SignerCertifierService {
109    async fn get_beacon_to_sign(&self, time_point: TimePoint) -> StdResult<Option<BeaconToSign>> {
110        let available_signed_entity_types =
111            self.list_available_signed_entity_types(&time_point).await?;
112
113        if available_signed_entity_types.is_empty() {
114            Ok(None)
115        } else {
116            let signed_entity_type = available_signed_entity_types[0].clone();
117            let beacon_to_sign =
118                BeaconToSign::new(time_point.epoch, signed_entity_type, Utc::now());
119
120            Ok(Some(beacon_to_sign))
121        }
122    }
123
124    async fn compute_publish_single_signature(
125        &self,
126        beacon_to_sign: &BeaconToSign,
127        protocol_message: &ProtocolMessage,
128    ) -> StdResult<()> {
129        if let Some(single_signature) = self
130            .single_signer
131            .compute_single_signature(protocol_message)
132            .await?
133        {
134            debug!(self.logger, " > There is a single signature to send");
135            self.signature_publisher
136                .publish(
137                    &beacon_to_sign.signed_entity_type,
138                    &single_signature,
139                    protocol_message,
140                )
141                .await?;
142        } else {
143            debug!(self.logger, " > NO single signature to send");
144        }
145
146        debug!(self.logger, " > Marking beacon as signed"; "beacon" => ?beacon_to_sign);
147        self.signed_beacon_store
148            .mark_beacon_as_signed(beacon_to_sign)
149            .await?;
150
151        Ok(())
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use mockall::predicate::eq;
158
159    use mithril_common::entities::{
160        CardanoTransactionsSigningConfig, ChainPoint, Epoch, ProtocolMessagePartKey,
161        SignedEntityTypeDiscriminants,
162    };
163    use mithril_common::test_utils::fake_data;
164
165    use crate::services::{MockSignaturePublisher, MockSingleSigner};
166
167    use super::{tests::tests_tooling::*, *};
168
169    #[tokio::test]
170    async fn no_beacon_can_be_signed_if_all_entities_are_locked() {
171        let locker = Arc::new(SignedEntityTypeLock::new());
172        for signed_entity_type in SignedEntityTypeDiscriminants::all() {
173            locker.lock(signed_entity_type).await;
174        }
175        let certifier_service = SignerCertifierService {
176            signed_entity_type_lock: locker.clone(),
177            signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
178                CardanoTransactionsSigningConfig::dummy(),
179                SignedEntityTypeDiscriminants::all(),
180            )),
181            ..SignerCertifierService::dumb_dependencies()
182        };
183
184        let beacon_to_sign = certifier_service
185            .get_beacon_to_sign(TimePoint::new(1, 14, ChainPoint::dummy()))
186            .await
187            .unwrap();
188        assert_eq!(beacon_to_sign, None);
189    }
190
191    #[tokio::test]
192    async fn only_one_unlocked_and_not_yet_signed_yield_a_beacon_to_sign() {
193        let locker = Arc::new(SignedEntityTypeLock::new());
194        for signed_entity_type in SignedEntityTypeDiscriminants::all().into_iter().skip(1) {
195            locker.lock(signed_entity_type).await;
196        }
197        let certifier_service = SignerCertifierService {
198            signed_entity_type_lock: locker.clone(),
199            signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
200                CardanoTransactionsSigningConfig::dummy(),
201                SignedEntityTypeDiscriminants::all(),
202            )),
203            ..SignerCertifierService::dumb_dependencies()
204        };
205
206        let beacon_to_sign = certifier_service
207            .get_beacon_to_sign(TimePoint::new(3, 14, ChainPoint::dummy()))
208            .await
209            .unwrap();
210        let signed_discriminant: Option<SignedEntityTypeDiscriminants> =
211            beacon_to_sign.map(|b| b.signed_entity_type.into());
212
213        assert_eq!(
214            SignedEntityTypeDiscriminants::all().first().cloned(),
215            signed_discriminant
216        );
217    }
218
219    #[tokio::test]
220    async fn if_already_signed_a_beacon_is_not_returned_anymore() {
221        let signed_beacon_store = Arc::new(DumbSignedBeaconStore::default());
222        let certifier_service = SignerCertifierService {
223            signed_beacon_store: signed_beacon_store.clone(),
224            signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
225                CardanoTransactionsSigningConfig::dummy(),
226                SignedEntityTypeDiscriminants::all(),
227            )),
228            ..SignerCertifierService::dumb_dependencies()
229        };
230
231        let time_point = TimePoint::new(1, 14, ChainPoint::dummy());
232        let first_beacon_to_sign = certifier_service
233            .get_beacon_to_sign(time_point.clone())
234            .await
235            .unwrap()
236            .unwrap();
237        signed_beacon_store
238            .mark_beacon_as_signed(&first_beacon_to_sign.clone())
239            .await
240            .unwrap();
241        let second_beacon_to_sign = certifier_service
242            .get_beacon_to_sign(time_point)
243            .await
244            .unwrap()
245            .unwrap();
246
247        assert_ne!(
248            first_beacon_to_sign.signed_entity_type,
249            second_beacon_to_sign.signed_entity_type
250        );
251    }
252
253    #[tokio::test]
254    async fn draining_out_all_beacons_to_sign_use_signed_entity_discriminant_order() {
255        let signed_beacon_store = Arc::new(DumbSignedBeaconStore::default());
256        let certifier_service = SignerCertifierService {
257            signed_beacon_store: signed_beacon_store.clone(),
258            signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
259                CardanoTransactionsSigningConfig::dummy(),
260                SignedEntityTypeDiscriminants::all(),
261            )),
262            ..SignerCertifierService::dumb_dependencies()
263        };
264
265        let time_point = TimePoint::new(1, 14, ChainPoint::dummy());
266        let mut previous_beacon_to_sign = certifier_service
267            .get_beacon_to_sign(time_point.clone())
268            .await
269            .unwrap()
270            .expect("There should be a beacon to sign since nothing is locked or signed");
271
272        loop {
273            signed_beacon_store
274                .mark_beacon_as_signed(&previous_beacon_to_sign)
275                .await
276                .unwrap();
277            let next_beacon_to_sign = certifier_service
278                .get_beacon_to_sign(time_point.clone())
279                .await
280                .unwrap();
281
282            if let Some(beacon) = next_beacon_to_sign {
283                assert!(
284                    SignedEntityTypeDiscriminants::from(
285                        &previous_beacon_to_sign.signed_entity_type
286                    ) < SignedEntityTypeDiscriminants::from(&beacon.signed_entity_type),
287                    "Beacon should follow SignedEntityTypeDiscriminants order"
288                );
289
290                previous_beacon_to_sign = beacon;
291            } else {
292                break;
293            }
294        }
295    }
296
297    #[tokio::test]
298    async fn draining_out_all_beacons_to_sign_doesnt_repeat_value() {
299        let signed_beacon_store = Arc::new(DumbSignedBeaconStore::default());
300        let certifier_service = SignerCertifierService {
301            signed_beacon_store: signed_beacon_store.clone(),
302            signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
303                CardanoTransactionsSigningConfig::dummy(),
304                SignedEntityTypeDiscriminants::all(),
305            )),
306            ..SignerCertifierService::dumb_dependencies()
307        };
308
309        let mut all_signed_beacons = vec![];
310        while let Some(beacon_to_sign) = certifier_service
311            .get_beacon_to_sign(TimePoint::new(1, 14, ChainPoint::dummy()))
312            .await
313            .unwrap()
314        {
315            signed_beacon_store
316                .mark_beacon_as_signed(&beacon_to_sign)
317                .await
318                .unwrap();
319            all_signed_beacons.push(beacon_to_sign);
320        }
321
322        let mut dedup_signed_beacons = all_signed_beacons.clone();
323        dedup_signed_beacons.dedup();
324        assert!(
325            !all_signed_beacons.is_empty(),
326            "There should be at least one beacon to sign"
327        );
328        assert_eq!(
329            all_signed_beacons, dedup_signed_beacons,
330            "Beacon should not repeat"
331        );
332    }
333
334    #[tokio::test]
335    async fn compute_publish_single_signature_success_if_a_signature_was_issued() {
336        let protocol_message = {
337            let mut message = ProtocolMessage::new();
338            message.set_message_part(ProtocolMessagePartKey::SnapshotDigest, "digest".to_string());
339            message
340        };
341        let beacon_to_sign = BeaconToSign::new(
342            Epoch(1),
343            SignedEntityType::MithrilStakeDistribution(Epoch(4)),
344            Utc::now(),
345        );
346
347        let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
348        let certifier_service = SignerCertifierService {
349            single_signer: {
350                let mut single_signer = MockSingleSigner::new();
351                single_signer
352                    .expect_compute_single_signature()
353                    .with(eq(protocol_message.clone()))
354                    .return_once(|_| Ok(Some(fake_data::single_signature(vec![1, 5, 12]))));
355                Arc::new(single_signer)
356            },
357            signature_publisher: {
358                let mut signature_publisher = MockSignaturePublisher::new();
359                signature_publisher
360                    .expect_publish()
361                    .with(
362                        eq(beacon_to_sign.signed_entity_type.clone()),
363                        eq(fake_data::single_signature(vec![1, 5, 12])),
364                        eq(protocol_message.clone()),
365                    )
366                    .returning(|_, _, _| Ok(()));
367                Arc::new(signature_publisher)
368            },
369            signed_beacon_store: signed_beacons_store.clone(),
370            ..SignerCertifierService::dumb_dependencies()
371        };
372
373        certifier_service
374            .compute_publish_single_signature(&beacon_to_sign, &protocol_message)
375            .await
376            .expect("Single signature should be computed and published");
377
378        let signed_beacons = signed_beacons_store.signed_beacons().await;
379        assert_eq!(
380            vec![beacon_to_sign.signed_entity_type.clone()],
381            signed_beacons
382        );
383    }
384
385    #[tokio::test]
386    async fn compute_publish_single_signature_success_but_dont_publish_if_no_signature_were_issued()
387    {
388        let protocol_message = {
389            let mut message = ProtocolMessage::new();
390            message.set_message_part(ProtocolMessagePartKey::SnapshotDigest, "digest".to_string());
391            message
392        };
393        let beacon_to_sign = BeaconToSign::new(
394            Epoch(1),
395            SignedEntityType::MithrilStakeDistribution(Epoch(4)),
396            Utc::now(),
397        );
398
399        let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
400        let certifier_service = SignerCertifierService {
401            single_signer: {
402                let mut single_signer = MockSingleSigner::new();
403                single_signer
404                    .expect_compute_single_signature()
405                    .with(eq(protocol_message.clone()))
406                    .return_once(|_| Ok(None));
407                Arc::new(single_signer)
408            },
409            signature_publisher: {
410                let mut signature_publisher = MockSignaturePublisher::new();
411                signature_publisher.expect_publish().never();
412                Arc::new(signature_publisher)
413            },
414            signed_beacon_store: signed_beacons_store.clone(),
415            ..SignerCertifierService::dumb_dependencies()
416        };
417
418        certifier_service
419            .compute_publish_single_signature(&beacon_to_sign, &protocol_message)
420            .await
421            .unwrap();
422
423        let signed_beacons = signed_beacons_store.signed_beacons().await;
424        assert_eq!(
425            vec![beacon_to_sign.signed_entity_type.clone()],
426            signed_beacons
427        );
428    }
429
430    #[tokio::test]
431    async fn beacon_isnt_mark_as_signed_if_computing_signature_fails() {
432        let beacon_to_sign = BeaconToSign::new(
433            Epoch(1),
434            SignedEntityType::MithrilStakeDistribution(Epoch(4)),
435            Utc::now(),
436        );
437
438        let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
439        let certifier_service = SignerCertifierService {
440            single_signer: {
441                let mut single_signer = MockSingleSigner::new();
442                single_signer
443                    .expect_compute_single_signature()
444                    .return_once(|_| Err(anyhow::anyhow!("error")));
445                Arc::new(single_signer)
446            },
447            signed_beacon_store: signed_beacons_store.clone(),
448            ..SignerCertifierService::dumb_dependencies()
449        };
450
451        certifier_service
452            .compute_publish_single_signature(&beacon_to_sign, &ProtocolMessage::new())
453            .await
454            .unwrap_err();
455
456        let signed_beacons = signed_beacons_store.signed_beacons().await;
457        assert_eq!(Vec::<SignedEntityType>::new(), signed_beacons);
458    }
459
460    #[tokio::test]
461    async fn beacon_isnt_mark_as_signed_if_publishing_signature_fails() {
462        let beacon_to_sign = BeaconToSign::new(
463            Epoch(1),
464            SignedEntityType::MithrilStakeDistribution(Epoch(4)),
465            Utc::now(),
466        );
467
468        let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
469        let certifier_service = SignerCertifierService {
470            single_signer: {
471                let mut single_signer = MockSingleSigner::new();
472                single_signer
473                    .expect_compute_single_signature()
474                    .return_once(|_| Ok(Some(fake_data::single_signature(vec![1, 5, 12]))));
475                Arc::new(single_signer)
476            },
477            signature_publisher: {
478                let mut signature_publisher = MockSignaturePublisher::new();
479                signature_publisher
480                    .expect_publish()
481                    .return_once(|_, _, _| Err(anyhow::anyhow!("error")));
482                Arc::new(signature_publisher)
483            },
484            signed_beacon_store: signed_beacons_store.clone(),
485            ..SignerCertifierService::dumb_dependencies()
486        };
487
488        certifier_service
489            .compute_publish_single_signature(&beacon_to_sign, &ProtocolMessage::new())
490            .await
491            .unwrap_err();
492
493        let signed_beacons = signed_beacons_store.signed_beacons().await;
494        assert_eq!(Vec::<SignedEntityType>::new(), signed_beacons);
495    }
496
497    pub mod tests_tooling {
498        use std::collections::BTreeSet;
499        use tokio::sync::RwLock;
500
501        use crate::test_tools::TestLogger;
502
503        use super::*;
504
505        impl SignerCertifierService {
506            pub(crate) fn dumb_dependencies() -> Self {
507                Self {
508                    signed_beacon_store: Arc::new(DumbSignedBeaconStore::default()),
509                    signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
510                        CardanoTransactionsSigningConfig::dummy(),
511                        SignedEntityTypeDiscriminants::all(),
512                    )),
513                    signed_entity_type_lock: Arc::new(SignedEntityTypeLock::new()),
514                    single_signer: Arc::new(MockSingleSigner::new()),
515                    signature_publisher: Arc::new(MockSignaturePublisher::new()),
516                    logger: TestLogger::stdout(),
517                }
518            }
519        }
520
521        pub struct DumbSignedEntityConfigProvider {
522            config: SignedEntityConfig,
523        }
524
525        impl DumbSignedEntityConfigProvider {
526            pub fn new(
527                config: CardanoTransactionsSigningConfig,
528                allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
529            ) -> Self {
530                Self {
531                    config: SignedEntityConfig {
532                        cardano_transactions_signing_config: config,
533                        allowed_discriminants,
534                    },
535                }
536            }
537        }
538
539        #[async_trait]
540        impl SignedEntityConfigProvider for DumbSignedEntityConfigProvider {
541            async fn get(&self) -> StdResult<SignedEntityConfig> {
542                Ok(self.config.clone())
543            }
544        }
545
546        pub struct DumbSignedBeaconStore {
547            signed_beacons: RwLock<Vec<SignedEntityType>>,
548        }
549
550        impl DumbSignedBeaconStore {
551            pub fn new(signed_beacons: Vec<SignedEntityType>) -> Self {
552                Self {
553                    signed_beacons: RwLock::new(signed_beacons),
554                }
555            }
556
557            pub async fn signed_beacons(&self) -> Vec<SignedEntityType> {
558                self.signed_beacons.read().await.clone()
559            }
560        }
561
562        impl Default for DumbSignedBeaconStore {
563            fn default() -> Self {
564                Self::new(vec![])
565            }
566        }
567
568        #[async_trait]
569        impl SignedBeaconStore for DumbSignedBeaconStore {
570            async fn filter_out_already_signed_entities(
571                &self,
572                entities: Vec<SignedEntityType>,
573            ) -> StdResult<Vec<SignedEntityType>> {
574                let already_signed_entities = self.signed_beacons.read().await.clone();
575                Ok(entities
576                    .into_iter()
577                    .filter(|entity| !already_signed_entities.contains(entity))
578                    .collect())
579            }
580
581            async fn mark_beacon_as_signed(&self, beacon: &BeaconToSign) -> StdResult<()> {
582                let mut already_signed_entities = self.signed_beacons.write().await;
583                already_signed_entities.push(beacon.signed_entity_type.clone());
584                Ok(())
585            }
586        }
587    }
588}