mithril_aggregator/services/certifier/
certifier_service.rs

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