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, SingleSignatures, 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: &SingleSignatures,
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, Configuration,
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 = Configuration::new_sample(snapshot_directory);
442 let cardano_transactions_signing_config =
443 configuration.cardano_transactions_signing_config.clone();
444 let mut dependency_builder = DependenciesBuilder::new_with_stdout_logger(configuration);
445 if let Some(epoch) = current_epoch {
446 dependency_builder.epoch_service = Some(Arc::new(RwLock::new(
447 FakeEpochService::from_fixture(epoch, fixture),
448 )));
449 }
450
451 let dependency_manager = dependency_builder
452 .build_dependency_container()
453 .await
454 .unwrap();
455 dependency_manager
456 .init_state_from_fixture(
457 fixture,
458 &cardano_transactions_signing_config,
459 epochs_with_signers,
460 )
461 .await;
462
463 MithrilCertifierService::from_deps(network, dependency_builder).await
464 }
465
466 async fn setup_certifier_service(
467 snapshot_directory: PathBuf,
468 fixture: &MithrilFixture,
469 epochs_with_signers: &[Epoch],
470 current_epoch: Option<Epoch>,
471 ) -> MithrilCertifierService {
472 setup_certifier_service_with_network(
473 snapshot_directory,
474 fake_data::network(),
475 fixture,
476 epochs_with_signers,
477 current_epoch,
478 )
479 .await
480 }
481
482 #[tokio::test]
483 async fn should_clean_epoch_when_inform_epoch() {
484 let beacon = CardanoDbBeacon::new(1, 1);
485 let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
486 let protocol_message = ProtocolMessage::new();
487 let epoch = beacon.epoch;
488 let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
489 let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
490 let certifier_service =
491 setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
492 certifier_service
493 .create_open_message(&signed_entity_type, &protocol_message)
494 .await
495 .unwrap();
496 certifier_service.inform_epoch(epoch + 1).await.unwrap();
497 let open_message = certifier_service
498 .get_open_message(&signed_entity_type)
499 .await
500 .unwrap();
501 assert!(open_message.is_none());
502 }
503
504 #[tokio::test]
505 async fn should_mark_open_message_expired_when_exists() {
506 let beacon = CardanoDbBeacon::new(3, 1);
507 let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
508 let protocol_message = ProtocolMessage::new();
509 let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
510 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
511 let certifier_service =
512 setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).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 epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
543 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
544 let certifier_service =
545 setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
546 let mut open_message = certifier_service
547 .open_message_repository
548 .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message)
549 .await
550 .unwrap();
551 open_message.expires_at = None;
552 certifier_service
553 .open_message_repository
554 .update_open_message(&open_message)
555 .await
556 .unwrap();
557
558 let open_message = certifier_service
559 .mark_open_message_if_expired(&signed_entity_type)
560 .await
561 .expect("mark_open_message_if_expired should not fail");
562 assert!(open_message.is_none());
563 }
564
565 #[tokio::test]
566 async fn should_not_mark_open_message_expired_when_has_not_expired_yet() {
567 let beacon = CardanoDbBeacon::new(3, 1);
568 let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
569 let protocol_message = ProtocolMessage::new();
570 let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
571 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
572 let certifier_service =
573 setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
574 let mut open_message = certifier_service
575 .open_message_repository
576 .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message)
577 .await
578 .unwrap();
579 open_message.expires_at = Some(Utc::now().checked_add_days(Days::new(1)).unwrap());
580 certifier_service
581 .open_message_repository
582 .update_open_message(&open_message)
583 .await
584 .unwrap();
585
586 let open_message = certifier_service
587 .mark_open_message_if_expired(&signed_entity_type)
588 .await
589 .expect("mark_open_message_if_expired should not fail");
590 assert!(open_message.is_none());
591 }
592
593 #[tokio::test]
594 async fn should_register_valid_single_signature() {
595 let beacon = CardanoDbBeacon::new(3, 1);
596 let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
597 let protocol_message = ProtocolMessage::new();
598 let epochs_with_signers = (1..=3).map(Epoch).collect::<Vec<_>>();
599 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
600 let certifier_service = setup_certifier_service(
601 temp_dir!(),
602 &fixture,
603 &epochs_with_signers,
604 Some(beacon.epoch),
605 )
606 .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 epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
637 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
638 let certifier_service = setup_certifier_service(
639 temp_dir!(),
640 &fixture,
641 &epochs_with_signers,
642 Some(beacon.epoch),
643 )
644 .await;
645
646 certifier_service
647 .create_open_message(&signed_entity_type, &protocol_message)
648 .await
649 .unwrap();
650
651 protocol_message.set_message_part(
652 ProtocolMessagePartKey::SnapshotDigest,
653 "snapshot-digest-123".to_string(),
654 );
655
656 let mut signatures = Vec::new();
657 for signer_fixture in fixture.signers_fixture() {
658 if let Some(signature) = signer_fixture.sign(&protocol_message) {
659 signatures.push(signature);
660 }
661 }
662 let err = certifier_service
663 .register_single_signature(&signed_entity_type, &signatures[0])
664 .await
665 .expect_err("register_single_signature should fail");
666
667 assert!(
668 matches!(
669 err.downcast_ref::<CertifierServiceError>(),
670 Some(CertifierServiceError::InvalidSingleSignature(..))
671 ),
672 "Expected CertifierServiceError::InvalidSingleSignature, got: '{err:?}'"
673 );
674 }
675
676 #[tokio::test]
677 async fn should_not_register_single_signature_for_certified_open_message() {
678 let beacon = CardanoDbBeacon::new(3, 1);
679 let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
680 let protocol_message = ProtocolMessage::new();
681 let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
682 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
683 let certifier_service =
684 setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
685 let mut open_message = certifier_service
686 .open_message_repository
687 .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message)
688 .await
689 .unwrap();
690 open_message.is_certified = true;
691 certifier_service
692 .open_message_repository
693 .update_open_message(&open_message)
694 .await
695 .unwrap();
696
697 let mut signatures = Vec::new();
698 for signer_fixture in fixture.signers_fixture() {
699 if let Some(signature) = signer_fixture.sign(&protocol_message) {
700 signatures.push(signature);
701 }
702 }
703 certifier_service
704 .register_single_signature(&signed_entity_type, &signatures[0])
705 .await
706 .expect_err("register_single_signature should fail");
707 }
708
709 #[tokio::test]
710 async fn should_not_register_single_signature_for_expired_open_message() {
711 let beacon = CardanoDbBeacon::new(3, 1);
712 let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
713 let protocol_message = ProtocolMessage::new();
714 let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
715 let fixture = MithrilFixtureBuilder::default().with_signers(1).build();
716 let certifier_service =
717 setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
718 let mut open_message = certifier_service
719 .open_message_repository
720 .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message)
721 .await
722 .unwrap();
723 open_message.is_expired = true;
724 certifier_service
725 .open_message_repository
726 .update_open_message(&open_message)
727 .await
728 .unwrap();
729
730 let mut signatures = Vec::new();
731 for signer_fixture in fixture.signers_fixture() {
732 if let Some(signature) = signer_fixture.sign(&protocol_message) {
733 signatures.push(signature);
734 }
735 }
736 certifier_service
737 .register_single_signature(&signed_entity_type, &signatures[0])
738 .await
739 .expect_err("register_single_signature should fail");
740 }
741
742 #[tokio::test]
743 async fn should_create_certificate_when_multi_signature_produced() {
744 let network = fake_data::network();
745 let beacon = CardanoDbBeacon::new(3, 1);
746 let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
747 let mut protocol_message = ProtocolMessage::new();
748 protocol_message.set_message_part(ProtocolMessagePartKey::CurrentEpoch, "3".to_string());
749 let epochs_with_signers = (1..=3).map(Epoch).collect::<Vec<_>>();
750 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
751 let certifier_service = setup_certifier_service_with_network(
752 temp_dir!(),
753 network,
754 &fixture,
755 &epochs_with_signers,
756 Some(beacon.epoch),
757 )
758 .await;
759
760 certifier_service
761 .create_open_message(&signed_entity_type, &protocol_message)
762 .await
763 .unwrap();
764
765 let genesis_certificate = fixture.create_genesis_certificate(network, beacon.epoch - 1);
766 certifier_service
767 .certificate_repository
768 .create_certificate(genesis_certificate)
769 .await
770 .unwrap();
771
772 let mut signatures = Vec::new();
773 for signer_fixture in fixture.signers_fixture() {
774 if let Some(signature) = signer_fixture.sign(&protocol_message) {
775 signatures.push(signature);
776 }
777 }
778 for signature in signatures {
779 certifier_service
780 .register_single_signature(&signed_entity_type, &signature)
781 .await
782 .expect("register_single_signature should not fail");
783 }
784
785 let create_certificate_result = certifier_service
786 .create_certificate(&signed_entity_type)
787 .await
788 .unwrap();
789 assert!(create_certificate_result.is_some());
790
791 let certificate_created = create_certificate_result.unwrap();
792 certifier_service
793 .certificate_verifier
794 .verify_certificate(
795 &certificate_created,
796 &certifier_service.genesis_verifier.to_verification_key(),
797 )
798 .await
799 .unwrap();
800
801 let open_message = certifier_service
802 .get_open_message(&signed_entity_type)
803 .await
804 .unwrap()
805 .unwrap();
806 assert!(open_message.is_certified);
807
808 let certificate_retrieved = certifier_service
809 .get_certificate_by_hash(&certificate_created.hash)
810 .await
811 .unwrap()
812 .unwrap();
813 assert_eq!(certificate_created, certificate_retrieved);
814
815 let latest_certificates = certifier_service.get_latest_certificates(10).await.unwrap();
816 assert!(!latest_certificates.is_empty());
817 }
818
819 #[tokio::test]
820 async fn should_not_create_certificate_for_open_message_not_created() {
821 let beacon = CardanoDbBeacon::new(1, 1);
822 let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
823 let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
824 let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
825 let certifier_service =
826 setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
827 certifier_service
828 .create_certificate(&signed_entity_type)
829 .await
830 .expect_err("create_certificate should fail");
831 }
832
833 #[tokio::test]
834 async fn should_not_create_certificate_for_open_message_already_certified() {
835 let beacon = CardanoDbBeacon::new(1, 1);
836 let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
837 let protocol_message = ProtocolMessage::new();
838 let epoch = beacon.epoch;
839 let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
840 let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
841 let certifier_service =
842 setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
843 certifier_service
844 .open_message_repository
845 .create_open_message(epoch, &signed_entity_type, &protocol_message)
846 .await
847 .unwrap();
848 certifier_service
849 .create_certificate(&signed_entity_type)
850 .await
851 .expect_err("create_certificate should fail");
852 }
853
854 #[tokio::test]
855 async fn should_not_create_certificate_when_no_multi_signature_produced() {
856 let mut mock_multi_signer = MockMultiSigner::new();
857 mock_multi_signer
858 .expect_create_multi_signature()
859 .return_once(move |_| Ok(None));
860 let beacon = CardanoDbBeacon::new(1, 1);
861 let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone());
862 let protocol_message = ProtocolMessage::new();
863 let epochs_with_signers = (1..=5).map(Epoch).collect::<Vec<_>>();
864 let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
865 let mut certifier_service =
866 setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await;
867 certifier_service.multi_signer = Arc::new(mock_multi_signer);
868 certifier_service
869 .create_open_message(&signed_entity_type, &protocol_message)
870 .await
871 .unwrap();
872 let create_certificate_result = certifier_service
873 .create_certificate(&signed_entity_type)
874 .await
875 .unwrap();
876 assert!(create_certificate_result.is_none());
877 }
878
879 #[tokio::test]
880 async fn test_epoch_gap_certificate_chain() {
881 let builder = MithrilFixtureBuilder::default();
882 let certifier_service =
883 setup_certifier_service(temp_dir!(), &builder.build(), &[], None).await;
884 let certificate = fake_data::genesis_certificate("whatever");
885 let epoch = certificate.epoch + 2;
886 certifier_service
887 .certificate_repository
888 .create_certificate(certificate)
889 .await
890 .unwrap();
891 let error = certifier_service
892 .verify_certificate_chain(epoch)
893 .await
894 .unwrap_err();
895
896 if let Some(err) = error.downcast_ref::<CertifierServiceError>() {
897 assert!(
898 matches!(err, CertifierServiceError::CertificateEpochGap {certificate_epoch: _, current_epoch} if *current_epoch == epoch)
899 );
900 } else {
901 panic!("Unexpected error {error:?}");
902 }
903 }
904
905 #[tokio::test]
906 async fn test_epoch_gap_certificate_chain_ok() {
907 let builder = MithrilFixtureBuilder::default();
908 let certifier_service =
909 setup_certifier_service(temp_dir!(), &builder.build(), &[], None).await;
910 let certificate = fake_data::genesis_certificate("whatever");
911 let epoch = certificate.epoch + 1;
912 certifier_service
913 .certificate_repository
914 .create_certificate(certificate)
915 .await
916 .unwrap();
917 let error = certifier_service
918 .verify_certificate_chain(epoch)
919 .await
920 .unwrap_err();
921
922 if let Some(err) = error.downcast_ref::<CertifierServiceError>() {
923 assert!(!matches!(
924 err,
925 CertifierServiceError::CertificateEpochGap {
926 certificate_epoch: _,
927 current_epoch: _
928 }
929 ));
930 }
931 }
932}