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