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
26pub 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 #[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 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}