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