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