mithril_aggregator/services/certifier/
certifier_service.rs

1use anyhow::Context;
2use async_trait::async_trait;
3use chrono::Utc;
4use slog::{debug, info, trace, warn, Logger};
5use std::sync::Arc;
6
7use mithril_common::certificate_chain::CertificateVerifier;
8use mithril_common::crypto_helper::{ProtocolGenesisVerifier, PROTOCOL_VERSION};
9use mithril_common::entities::{
10    Certificate, CertificateMetadata, CertificateSignature, Epoch, ProtocolMessage,
11    SignedEntityType, SingleSignature, StakeDistributionParty,
12};
13use mithril_common::logging::LoggerExtensions;
14use mithril_common::protocol::ToMessage;
15use mithril_common::{CardanoNetwork, StdResult};
16
17use crate::database::record::{OpenMessageRecord, OpenMessageWithSingleSignaturesRecord};
18use crate::database::repository::{
19    CertificateRepository, OpenMessageRepository, SingleSignatureRepository,
20};
21use crate::dependency_injection::EpochServiceWrapper;
22use crate::entities::OpenMessage;
23use crate::services::{CertifierService, CertifierServiceError, SignatureRegistrationStatus};
24use crate::MultiSigner;
25
26/// Mithril CertifierService implementation
27pub struct MithrilCertifierService {
28    network: CardanoNetwork,
29    open_message_repository: Arc<OpenMessageRepository>,
30    single_signature_repository: Arc<SingleSignatureRepository>,
31    certificate_repository: Arc<CertificateRepository>,
32    certificate_verifier: Arc<dyn CertificateVerifier>,
33    genesis_verifier: Arc<ProtocolGenesisVerifier>,
34    multi_signer: Arc<dyn MultiSigner>,
35    epoch_service: EpochServiceWrapper,
36    logger: Logger,
37}
38
39impl MithrilCertifierService {
40    /// instantiate the service
41    #[allow(clippy::too_many_arguments)]
42    pub fn new(
43        network: CardanoNetwork,
44        open_message_repository: Arc<OpenMessageRepository>,
45        single_signature_repository: Arc<SingleSignatureRepository>,
46        certificate_repository: Arc<CertificateRepository>,
47        certificate_verifier: Arc<dyn CertificateVerifier>,
48        genesis_verifier: Arc<ProtocolGenesisVerifier>,
49        multi_signer: Arc<dyn MultiSigner>,
50        epoch_service: EpochServiceWrapper,
51        logger: Logger,
52    ) -> Self {
53        Self {
54            network,
55            open_message_repository,
56            single_signature_repository,
57            certificate_repository,
58            multi_signer,
59            certificate_verifier,
60            genesis_verifier,
61            epoch_service,
62            logger: logger.new_with_component_name::<Self>(),
63        }
64    }
65
66    async fn get_open_message_record(
67        &self,
68        signed_entity_type: &SignedEntityType,
69    ) -> StdResult<Option<OpenMessageWithSingleSignaturesRecord>> {
70        debug!(
71            self.logger,
72            ">> get_open_message_record(signed_entity_type: {signed_entity_type:?})"
73        );
74
75        let open_message_with_single_signatures = self
76            .open_message_repository
77            .get_open_message_with_single_signatures(signed_entity_type)
78            .await
79            .with_context(|| format!("Certifier can not get open message with single signatures for signed entity type: '{signed_entity_type}'"))?;
80
81        Ok(open_message_with_single_signatures)
82    }
83}
84
85#[async_trait]
86impl CertifierService for MithrilCertifierService {
87    async fn inform_epoch(&self, epoch: Epoch) -> StdResult<()> {
88        debug!(self.logger, ">> inform_epoch(epoch: {epoch:?})");
89        let nb = self
90            .open_message_repository
91            .clean_epoch(epoch)
92            .await
93            .with_context(|| {
94                format!("Certifier can not clean open messages from epoch '{epoch}'")
95            })?;
96        info!(self.logger, "Informed of a new Epoch: {epoch:?}. Cleaned {nb} open messages along with their single signatures.");
97
98        Ok(())
99    }
100
101    async fn register_single_signature(
102        &self,
103        signed_entity_type: &SignedEntityType,
104        signature: &SingleSignature,
105    ) -> StdResult<SignatureRegistrationStatus> {
106        debug!(self.logger, ">> register_single_signature(signed_entity_type: {signed_entity_type:?}, single_signatures: {signature:?}");
107        trace!(self.logger, ">> register_single_signature"; "complete_single_signatures" => #?signature);
108
109        let open_message = self
110            .get_open_message_record(signed_entity_type)
111            .await.with_context(|| format!("CertifierService can not get open message record for signed_entity_type: '{signed_entity_type}'"))?
112            .ok_or_else(|| {
113                warn!(self.logger, "register_single_signature: OpenMessage not found for type {signed_entity_type:?}.");
114                CertifierServiceError::NotFound(signed_entity_type.clone())
115            })?;
116
117        if open_message.is_certified {
118            warn!(self.logger, "register_single_signature: open message {signed_entity_type:?} is already certified, cannot register single signature.");
119
120            return Err(CertifierServiceError::AlreadyCertified(signed_entity_type.clone()).into());
121        }
122
123        if open_message.is_expired {
124            warn!(self.logger, "register_single_signature: open message {signed_entity_type:?} has expired, cannot register single signature.");
125
126            return Err(CertifierServiceError::Expired(signed_entity_type.clone()).into());
127        }
128
129        self.multi_signer
130            .verify_single_signature(&open_message.protocol_message.to_message(), signature)
131            .await
132            .map_err(|err| {
133                CertifierServiceError::InvalidSingleSignature(signed_entity_type.clone(), err)
134            })?;
135
136        let single_signature = self
137            .single_signature_repository
138            .create_single_signature(signature, &open_message.clone().into())
139            .await.with_context(|| format!("Certifier can not create the single signature from single_signature: '{signature:?}', open_message: '{open_message:?}'"))?;
140        info!(self.logger, "register_single_signature: created pool '{}' single signature for {signed_entity_type:?}.", single_signature.signer_id);
141        debug!(
142            self.logger,
143            "register_single_signature: created single signature for open message ID='{}'.",
144            single_signature.open_message_id
145        );
146
147        Ok(SignatureRegistrationStatus::Registered)
148    }
149
150    async fn create_open_message(
151        &self,
152        signed_entity_type: &SignedEntityType,
153        protocol_message: &ProtocolMessage,
154    ) -> StdResult<OpenMessage> {
155        debug!(
156            self.logger, ">> create_open_message(signed_entity_type: {signed_entity_type:?})";
157            "protocol_message" => ?protocol_message
158        );
159        let open_message = self
160            .open_message_repository
161            .create_open_message(
162                signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
163                signed_entity_type,
164                protocol_message,
165            )
166            .await
167            .with_context(|| {
168                format!(
169                    "Certifier can not create open message from protocol_message: '{:?}, epoch: '{}''",
170                    protocol_message,
171                    signed_entity_type.get_epoch_when_signed_entity_type_is_signed()
172                )
173            })?;
174        info!(
175            self.logger,
176            "create_open_message: created open message for {signed_entity_type:?}"
177        );
178        debug!(
179            self.logger,
180            "create_open_message: created open message ID='{}'", open_message.open_message_id
181        );
182
183        Ok(open_message.into())
184    }
185
186    async fn get_open_message(
187        &self,
188        signed_entity_type: &SignedEntityType,
189    ) -> StdResult<Option<OpenMessage>> {
190        debug!(
191            self.logger,
192            ">> get_open_message(signed_entity_type: {signed_entity_type:?})"
193        );
194
195        let open_message = self
196            .open_message_repository
197            .get_open_message_with_single_signatures(signed_entity_type)
198            .await
199            .with_context(|| format!("Certifier can not get open message with single signatures for signed entity type: '{signed_entity_type}'"))?
200            .map(|record| record.into());
201
202        Ok(open_message)
203    }
204
205    async fn mark_open_message_if_expired(
206        &self,
207        signed_entity_type: &SignedEntityType,
208    ) -> StdResult<Option<OpenMessage>> {
209        debug!(self.logger, ">> mark_open_message_if_expired");
210
211        let mut open_message_record = self
212            .open_message_repository
213            .get_expired_open_message(signed_entity_type)
214            .await
215            .with_context(|| "Certifier can not get expired open messages")?;
216        if let Some(open_message_record) = open_message_record.as_mut() {
217            open_message_record.is_expired = true;
218            self.open_message_repository
219                .update_open_message(open_message_record)
220                .await
221                .with_context(|| "Certifier can not update open message to mark it as expired")?;
222        }
223
224        Ok(open_message_record.map(|record| record.into()))
225    }
226
227    async fn create_certificate(
228        &self,
229        signed_entity_type: &SignedEntityType,
230    ) -> StdResult<Option<Certificate>> {
231        debug!(
232            self.logger,
233            ">> create_certificate(signed_entity_type: {signed_entity_type:?})"
234        );
235        let open_message_record = self
236            .get_open_message_record(signed_entity_type)
237            .await?
238            .ok_or_else(|| {
239                warn!(
240                    self.logger,
241                    "create_certificate: OpenMessage not found for type {signed_entity_type:?}."
242                );
243                CertifierServiceError::NotFound(signed_entity_type.clone())
244            })?;
245        let open_message: OpenMessage = open_message_record.clone().into();
246
247        if open_message.is_certified {
248            warn!(self.logger, "create_certificate: open message {signed_entity_type:?} is already certified, cannot create certificate.");
249
250            return Err(CertifierServiceError::AlreadyCertified(signed_entity_type.clone()).into());
251        }
252
253        if open_message.is_expired {
254            warn!(self.logger, "create_certificate: open message {signed_entity_type:?} is expired, cannot create certificate.");
255
256            return Err(CertifierServiceError::Expired(signed_entity_type.clone()).into());
257        }
258
259        let multi_signature = match self
260            .multi_signer
261            .create_multi_signature(&open_message)
262            .await?
263        {
264            None => {
265                debug!(self.logger, "create_certificate: No multi-signature could be created for open message {signed_entity_type:?}");
266                return Ok(None);
267            }
268            Some(signature) => {
269                info!(self.logger, "create_certificate: multi-signature created for open message {signed_entity_type:?}");
270                signature
271            }
272        };
273
274        let epoch_service = self.epoch_service.read().await;
275        let signer_ids = open_message.get_signers_id();
276        let signers = epoch_service
277            .current_signers_with_stake()?
278            .clone()
279            .into_iter()
280            .filter(|signer| signer_ids.contains(&signer.party_id))
281            .collect::<Vec<_>>();
282
283        let protocol_version = PROTOCOL_VERSION.to_string();
284        let initiated_at = open_message.created_at;
285        let sealed_at = Utc::now();
286        let metadata = CertificateMetadata::new(
287            self.network,
288            protocol_version,
289            epoch_service.current_protocol_parameters()?.clone(),
290            initiated_at,
291            sealed_at,
292            StakeDistributionParty::from_signers(signers),
293        );
294        let parent_certificate_hash = self
295            .certificate_repository
296            .get_master_certificate_for_epoch::<Certificate>(open_message.epoch)
297            .await
298            .with_context(|| {
299                format!(
300                    "Certifier can not get leader certificate for epoch: '{}'",
301                    open_message.epoch
302                )
303            })?
304            .map(|cert| cert.hash)
305            .ok_or_else(|| Box::new(CertifierServiceError::NoParentCertificateFound))?;
306
307        let certificate = Certificate::new(
308            parent_certificate_hash,
309            open_message.epoch,
310            metadata,
311            open_message.protocol_message.clone(),
312            epoch_service.current_aggregate_verification_key()?.clone(),
313            CertificateSignature::MultiSignature(signed_entity_type.clone(), multi_signature),
314        );
315
316        self.certificate_verifier
317            .verify_certificate(&certificate, &self.genesis_verifier.to_verification_key())
318            .await
319            .with_context(|| {
320                format!(
321                    "CertificateVerifier can not verify certificate with hash: '{}'",
322                    certificate.hash
323                )
324            })?;
325
326        let certificate = self
327            .certificate_repository
328            .create_certificate(certificate)
329            .await
330            .with_context(|| {format!(
331                "Certifier can not create certificate for signed entity type: '{signed_entity_type}'")
332            })?;
333
334        let mut open_message_certified: OpenMessageRecord = open_message_record.into();
335        open_message_certified.is_certified = true;
336        self.open_message_repository
337            .update_open_message(&open_message_certified)
338            .await
339            .with_context(|| format!("Certifier can not update open message for signed entity type: '{signed_entity_type}'"))
340            ?;
341
342        Ok(Some(certificate))
343    }
344
345    async fn get_certificate_by_hash(&self, hash: &str) -> StdResult<Option<Certificate>> {
346        self.certificate_repository.get_certificate(hash).await
347    }
348
349    async fn get_latest_certificates(&self, last_n: usize) -> StdResult<Vec<Certificate>> {
350        self.certificate_repository
351            .get_latest_certificates(last_n)
352            .await
353            .with_context(|| format!("Certifier can not get last '{last_n}' certificates"))
354    }
355
356    async fn verify_certificate_chain(&self, epoch: Epoch) -> StdResult<()> {
357        if let Some(certificate) = self
358            .certificate_repository
359            .get_latest_certificates::<Certificate>(1)
360            .await?
361            .first()
362        {
363            if epoch.has_gap_with(&certificate.epoch) {
364                return Err(CertifierServiceError::CertificateEpochGap {
365                    certificate_epoch: certificate.epoch,
366                    current_epoch: epoch,
367                }
368                .into());
369            }
370
371            self.certificate_verifier
372                .verify_certificate_chain(
373                    certificate.to_owned(),
374                    &self.genesis_verifier.to_verification_key(),
375                )
376                .await
377                .with_context(|| "CertificateVerifier can not verify certificate chain")?;
378
379            Ok(())
380        } else {
381            Err(CertifierServiceError::CouldNotFindLastCertificate.into())
382        }
383    }
384}
385
386#[cfg(test)]
387mod tests {
388    use std::path::PathBuf;
389
390    use crate::{
391        dependency_injection::DependenciesBuilder, multi_signer::MockMultiSigner,
392        services::FakeEpochService, test_tools::TestLogger, ServeCommandConfiguration,
393    };
394    use chrono::{DateTime, Days};
395    use mithril_common::{
396        entities::{CardanoDbBeacon, ProtocolMessagePartKey},
397        temp_dir,
398        test_utils::{fake_data, MithrilFixture, MithrilFixtureBuilder},
399    };
400    use tokio::sync::RwLock;
401
402    use super::*;
403
404    impl MithrilCertifierService {
405        async fn from_deps(
406            network: CardanoNetwork,
407            mut dependency_builder: DependenciesBuilder,
408        ) -> Self {
409            let connection = dependency_builder.get_sqlite_connection().await.unwrap();
410            let open_message_repository = Arc::new(OpenMessageRepository::new(connection.clone()));
411            let single_signature_repository =
412                Arc::new(SingleSignatureRepository::new(connection.clone()));
413            let certificate_repository = Arc::new(CertificateRepository::new(connection));
414            let certificate_verifier = dependency_builder.get_certificate_verifier().await.unwrap();
415            let genesis_verifier = dependency_builder.get_genesis_verifier().await.unwrap();
416            let multi_signer = dependency_builder.get_multi_signer().await.unwrap();
417            let epoch_service = dependency_builder.get_epoch_service().await.unwrap();
418
419            Self::new(
420                network,
421                open_message_repository,
422                single_signature_repository,
423                certificate_repository,
424                certificate_verifier,
425                genesis_verifier,
426                multi_signer,
427                epoch_service,
428                TestLogger::stdout(),
429            )
430        }
431    }
432
433    /// Note: If current_epoch is provided the [EpochService] will be automatically initialized
434    async fn setup_certifier_service_with_network(
435        snapshot_directory: PathBuf,
436        network: CardanoNetwork,
437        fixture: &MithrilFixture,
438        epochs_with_signers: &[Epoch],
439        current_epoch: Option<Epoch>,
440    ) -> MithrilCertifierService {
441        let configuration = ServeCommandConfiguration::new_sample(snapshot_directory);
442        let cardano_transactions_signing_config =
443            configuration.cardano_transactions_signing_config.clone();
444        let mut dependency_builder =
445            DependenciesBuilder::new_with_stdout_logger(Arc::new(configuration));
446        if let Some(epoch) = current_epoch {
447            dependency_builder.epoch_service = Some(Arc::new(RwLock::new(
448                FakeEpochService::from_fixture(epoch, fixture),
449            )));
450        }
451
452        let dependency_manager = dependency_builder
453            .build_serve_dependencies_container()
454            .await
455            .unwrap();
456        dependency_manager
457            .init_state_from_fixture(
458                fixture,
459                &cardano_transactions_signing_config,
460                epochs_with_signers,
461            )
462            .await;
463
464        MithrilCertifierService::from_deps(network, dependency_builder).await
465    }
466
467    async fn setup_certifier_service(
468        snapshot_directory: PathBuf,
469        fixture: &MithrilFixture,
470        epochs_with_signers: &[Epoch],
471        current_epoch: Option<Epoch>,
472    ) -> MithrilCertifierService {
473        setup_certifier_service_with_network(
474            snapshot_directory,
475            fake_data::network(),
476            fixture,
477            epochs_with_signers,
478            current_epoch,
479        )
480        .await
481    }
482
483    #[tokio::test]
484    async fn should_clean_epoch_when_inform_epoch() {
485        let beacon = CardanoDbBeacon::new(1, 1);
486        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
487        let protocol_message = ProtocolMessage::new();
488        let epoch = beacon.epoch;
489        let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
490        let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
491        let certifier_service =
492            setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
493        certifier_service
494            .create_open_message(&signed_entity_type, &protocol_message)
495            .await
496            .unwrap();
497        certifier_service.inform_epoch(epoch + 1).await.unwrap();
498        let open_message = certifier_service
499            .get_open_message(&signed_entity_type)
500            .await
501            .unwrap();
502        assert!(open_message.is_none());
503    }
504
505    #[tokio::test]
506    async fn should_mark_open_message_expired_when_exists() {
507        let beacon = CardanoDbBeacon::new(3, 1);
508        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
509        let protocol_message = ProtocolMessage::new();
510        let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
511        let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
512        let certifier_service =
513            setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
514        let mut open_message = certifier_service
515            .open_message_repository
516            .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message)
517            .await
518            .unwrap();
519        open_message.expires_at = Some(
520            DateTime::parse_from_rfc3339("2000-01-19T13:43:05.618857482Z")
521                .unwrap()
522                .with_timezone(&Utc),
523        );
524        certifier_service
525            .open_message_repository
526            .update_open_message(&open_message)
527            .await
528            .unwrap();
529
530        let open_message = certifier_service
531            .mark_open_message_if_expired(&signed_entity_type)
532            .await
533            .expect("mark_open_message_if_expired should not fail");
534        assert!(open_message.is_some());
535        assert!(open_message.unwrap().is_expired);
536    }
537
538    #[tokio::test]
539    async fn should_not_mark_open_message_expired_when_does_not_expire() {
540        let beacon = CardanoDbBeacon::new(3, 1);
541        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
542        let protocol_message = ProtocolMessage::new();
543        let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
544        let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
545        let certifier_service =
546            setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
547        let mut open_message = certifier_service
548            .open_message_repository
549            .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message)
550            .await
551            .unwrap();
552        open_message.expires_at = None;
553        certifier_service
554            .open_message_repository
555            .update_open_message(&open_message)
556            .await
557            .unwrap();
558
559        let open_message = certifier_service
560            .mark_open_message_if_expired(&signed_entity_type)
561            .await
562            .expect("mark_open_message_if_expired should not fail");
563        assert!(open_message.is_none());
564    }
565
566    #[tokio::test]
567    async fn should_not_mark_open_message_expired_when_has_not_expired_yet() {
568        let beacon = CardanoDbBeacon::new(3, 1);
569        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
570        let protocol_message = ProtocolMessage::new();
571        let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
572        let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
573        let certifier_service =
574            setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
575        let mut open_message = certifier_service
576            .open_message_repository
577            .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message)
578            .await
579            .unwrap();
580        open_message.expires_at = Some(Utc::now().checked_add_days(Days::new(1)).unwrap());
581        certifier_service
582            .open_message_repository
583            .update_open_message(&open_message)
584            .await
585            .unwrap();
586
587        let open_message = certifier_service
588            .mark_open_message_if_expired(&signed_entity_type)
589            .await
590            .expect("mark_open_message_if_expired should not fail");
591        assert!(open_message.is_none());
592    }
593
594    #[tokio::test]
595    async fn should_register_valid_single_signature() {
596        let beacon = CardanoDbBeacon::new(3, 1);
597        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
598        let protocol_message = ProtocolMessage::new();
599        let epochs_with_signers = (1..=3).map(Epoch).collect::<Vec<_>>();
600        let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
601        let certifier_service = setup_certifier_service(
602            temp_dir!(),
603            &fixture,
604            &epochs_with_signers,
605            Some(beacon.epoch),
606        )
607        .await;
608
609        certifier_service
610            .create_open_message(&signed_entity_type, &protocol_message)
611            .await
612            .unwrap();
613
614        let mut signatures = Vec::new();
615        for signer_fixture in fixture.signers_fixture() {
616            if let Some(signature) = signer_fixture.sign(&protocol_message) {
617                signatures.push(signature);
618            }
619        }
620        certifier_service
621            .register_single_signature(&signed_entity_type, &signatures[0])
622            .await
623            .unwrap();
624        let open_message = certifier_service
625            .get_open_message(&signed_entity_type)
626            .await
627            .unwrap()
628            .unwrap();
629        assert!(!open_message.single_signatures.is_empty());
630    }
631
632    #[tokio::test]
633    async fn should_not_register_invalid_single_signature() {
634        let beacon = CardanoDbBeacon::new(3, 1);
635        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
636        let mut protocol_message = ProtocolMessage::new();
637        let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
638        let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
639        let certifier_service = setup_certifier_service(
640            temp_dir!(),
641            &fixture,
642            &epochs_with_signers,
643            Some(beacon.epoch),
644        )
645        .await;
646
647        certifier_service
648            .create_open_message(&signed_entity_type, &protocol_message)
649            .await
650            .unwrap();
651
652        protocol_message.set_message_part(
653            ProtocolMessagePartKey::SnapshotDigest,
654            "snapshot-digest-123".to_string(),
655        );
656
657        let mut signatures = Vec::new();
658        for signer_fixture in fixture.signers_fixture() {
659            if let Some(signature) = signer_fixture.sign(&protocol_message) {
660                signatures.push(signature);
661            }
662        }
663        let err = certifier_service
664            .register_single_signature(&signed_entity_type, &signatures[0])
665            .await
666            .expect_err("register_single_signature should fail");
667
668        assert!(
669            matches!(
670                err.downcast_ref::<CertifierServiceError>(),
671                Some(CertifierServiceError::InvalidSingleSignature(..))
672            ),
673            "Expected CertifierServiceError::InvalidSingleSignature, got: '{err:?}'"
674        );
675    }
676
677    #[tokio::test]
678    async fn should_not_register_single_signature_for_certified_open_message() {
679        let beacon = CardanoDbBeacon::new(3, 1);
680        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
681        let protocol_message = ProtocolMessage::new();
682        let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
683        let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
684        let certifier_service =
685            setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
686        let mut open_message = certifier_service
687            .open_message_repository
688            .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message)
689            .await
690            .unwrap();
691        open_message.is_certified = true;
692        certifier_service
693            .open_message_repository
694            .update_open_message(&open_message)
695            .await
696            .unwrap();
697
698        let mut signatures = Vec::new();
699        for signer_fixture in fixture.signers_fixture() {
700            if let Some(signature) = signer_fixture.sign(&protocol_message) {
701                signatures.push(signature);
702            }
703        }
704        certifier_service
705            .register_single_signature(&signed_entity_type, &signatures[0])
706            .await
707            .expect_err("register_single_signature should fail");
708    }
709
710    #[tokio::test]
711    async fn should_not_register_single_signature_for_expired_open_message() {
712        let beacon = CardanoDbBeacon::new(3, 1);
713        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
714        let protocol_message = ProtocolMessage::new();
715        let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
716        let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
717        let certifier_service =
718            setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
719        let mut open_message = certifier_service
720            .open_message_repository
721            .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message)
722            .await
723            .unwrap();
724        open_message.is_expired = true;
725        certifier_service
726            .open_message_repository
727            .update_open_message(&open_message)
728            .await
729            .unwrap();
730
731        let mut signatures = Vec::new();
732        for signer_fixture in fixture.signers_fixture() {
733            if let Some(signature) = signer_fixture.sign(&protocol_message) {
734                signatures.push(signature);
735            }
736        }
737        certifier_service
738            .register_single_signature(&signed_entity_type, &signatures[0])
739            .await
740            .expect_err("register_single_signature should fail");
741    }
742
743    #[tokio::test]
744    async fn should_create_certificate_when_multi_signature_produced() {
745        let network = fake_data::network();
746        let beacon = CardanoDbBeacon::new(3, 1);
747        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
748        let mut protocol_message = ProtocolMessage::new();
749        protocol_message.set_message_part(ProtocolMessagePartKey::CurrentEpoch, "3".to_string());
750        let epochs_with_signers = (1..=3).map(Epoch).collect::<Vec<_>>();
751        let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
752        let certifier_service = setup_certifier_service_with_network(
753            temp_dir!(),
754            network,
755            &fixture,
756            &epochs_with_signers,
757            Some(beacon.epoch),
758        )
759        .await;
760
761        certifier_service
762            .create_open_message(&signed_entity_type, &protocol_message)
763            .await
764            .unwrap();
765
766        let genesis_certificate = fixture.create_genesis_certificate(network, beacon.epoch - 1);
767        certifier_service
768            .certificate_repository
769            .create_certificate(genesis_certificate)
770            .await
771            .unwrap();
772
773        let mut signatures = Vec::new();
774        for signer_fixture in fixture.signers_fixture() {
775            if let Some(signature) = signer_fixture.sign(&protocol_message) {
776                signatures.push(signature);
777            }
778        }
779        for signature in signatures {
780            certifier_service
781                .register_single_signature(&signed_entity_type, &signature)
782                .await
783                .expect("register_single_signature should not fail");
784        }
785
786        let create_certificate_result = certifier_service
787            .create_certificate(&signed_entity_type)
788            .await
789            .unwrap();
790        assert!(create_certificate_result.is_some());
791
792        let certificate_created = create_certificate_result.unwrap();
793        certifier_service
794            .certificate_verifier
795            .verify_certificate(
796                &certificate_created,
797                &certifier_service.genesis_verifier.to_verification_key(),
798            )
799            .await
800            .unwrap();
801
802        let open_message = certifier_service
803            .get_open_message(&signed_entity_type)
804            .await
805            .unwrap()
806            .unwrap();
807        assert!(open_message.is_certified);
808
809        let certificate_retrieved = certifier_service
810            .get_certificate_by_hash(&certificate_created.hash)
811            .await
812            .unwrap()
813            .unwrap();
814        assert_eq!(certificate_created, certificate_retrieved);
815
816        let latest_certificates = certifier_service.get_latest_certificates(10).await.unwrap();
817        assert!(!latest_certificates.is_empty());
818    }
819
820    #[tokio::test]
821    async fn should_not_create_certificate_for_open_message_not_created() {
822        let beacon = CardanoDbBeacon::new(1, 1);
823        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
824        let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
825        let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
826        let certifier_service =
827            setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
828        certifier_service
829            .create_certificate(&signed_entity_type)
830            .await
831            .expect_err("create_certificate should fail");
832    }
833
834    #[tokio::test]
835    async fn should_not_create_certificate_for_open_message_already_certified() {
836        let beacon = CardanoDbBeacon::new(1, 1);
837        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
838        let protocol_message = ProtocolMessage::new();
839        let epoch = beacon.epoch;
840        let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
841        let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
842        let certifier_service =
843            setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
844        certifier_service
845            .open_message_repository
846            .create_open_message(epoch, &signed_entity_type, &protocol_message)
847            .await
848            .unwrap();
849        certifier_service
850            .create_certificate(&signed_entity_type)
851            .await
852            .expect_err("create_certificate should fail");
853    }
854
855    #[tokio::test]
856    async fn should_not_create_certificate_when_no_multi_signature_produced() {
857        let mut mock_multi_signer = MockMultiSigner::new();
858        mock_multi_signer
859            .expect_create_multi_signature()
860            .return_once(move |_| Ok(None));
861        let beacon = CardanoDbBeacon::new(1, 1);
862        let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
863        let protocol_message = ProtocolMessage::new();
864        let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
865        let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
866        let mut certifier_service =
867            setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
868        certifier_service.multi_signer = Arc::new(mock_multi_signer);
869        certifier_service
870            .create_open_message(&signed_entity_type, &protocol_message)
871            .await
872            .unwrap();
873        let create_certificate_result = certifier_service
874            .create_certificate(&signed_entity_type)
875            .await
876            .unwrap();
877        assert!(create_certificate_result.is_none());
878    }
879
880    #[tokio::test]
881    async fn test_epoch_gap_certificate_chain() {
882        let builder = MithrilFixtureBuilder::default();
883        let certifier_service =
884            setup_certifier_service(temp_dir!(), &builder.build(), &[], None).await;
885        let certificate = fake_data::genesis_certificate("whatever");
886        let epoch = certificate.epoch + 2;
887        certifier_service
888            .certificate_repository
889            .create_certificate(certificate)
890            .await
891            .unwrap();
892        let error = certifier_service
893            .verify_certificate_chain(epoch)
894            .await
895            .unwrap_err();
896
897        if let Some(err) = error.downcast_ref::<CertifierServiceError>() {
898            assert!(
899                matches!(err, CertifierServiceError::CertificateEpochGap {certificate_epoch: _, current_epoch} if *current_epoch == epoch)
900            );
901        } else {
902            panic!("Unexpected error {error:?}");
903        }
904    }
905
906    #[tokio::test]
907    async fn test_epoch_gap_certificate_chain_ok() {
908        let builder = MithrilFixtureBuilder::default();
909        let certifier_service =
910            setup_certifier_service(temp_dir!(), &builder.build(), &[], None).await;
911        let certificate = fake_data::genesis_certificate("whatever");
912        let epoch = certificate.epoch + 1;
913        certifier_service
914            .certificate_repository
915            .create_certificate(certificate)
916            .await
917            .unwrap();
918        let error = certifier_service
919            .verify_certificate_chain(epoch)
920            .await
921            .unwrap_err();
922
923        if let Some(err) = error.downcast_ref::<CertifierServiceError>() {
924            assert!(!matches!(
925                err,
926                CertifierServiceError::CertificateEpochGap {
927                    certificate_epoch: _,
928                    current_epoch: _
929                }
930            ));
931        }
932    }
933}