1use async_trait::async_trait;
2use chrono::Utc;
3use slog::{debug, Logger};
4use std::sync::Arc;
5
6use mithril_common::entities::{
7 ProtocolMessage, SignedEntityConfig, SignedEntityType, SingleSignatures, TimePoint,
8};
9use mithril_common::logging::LoggerExtensions;
10use mithril_common::StdResult;
11use mithril_signed_entity_lock::SignedEntityTypeLock;
12
13use crate::entities::BeaconToSign;
14use crate::services::SingleSigner;
15
16#[cfg_attr(test, mockall::automock)]
20#[async_trait]
21pub trait CertifierService: Sync + Send {
22 async fn get_beacon_to_sign(&self, time_point: TimePoint) -> StdResult<Option<BeaconToSign>>;
26
27 async fn compute_publish_single_signature(
29 &self,
30 beacon_to_sign: &BeaconToSign,
31 protocol_message: &ProtocolMessage,
32 ) -> StdResult<()>;
33}
34
35#[cfg_attr(test, mockall::automock)]
37#[async_trait]
38pub trait SignedEntityConfigProvider: Sync + Send {
39 async fn get(&self) -> StdResult<SignedEntityConfig>;
41}
42
43#[cfg_attr(test, mockall::automock)]
45#[async_trait]
46pub trait SignedBeaconStore: Sync + Send {
47 async fn filter_out_already_signed_entities(
49 &self,
50 entities: Vec<SignedEntityType>,
51 ) -> StdResult<Vec<SignedEntityType>>;
52
53 async fn mark_beacon_as_signed(&self, entity: &BeaconToSign) -> StdResult<()>;
55}
56
57#[cfg_attr(test, mockall::automock)]
59#[async_trait]
60pub trait SignaturePublisher: Send + Sync {
61 async fn publish(
63 &self,
64 signed_entity_type: &SignedEntityType,
65 signatures: &SingleSignatures,
66 protocol_message: &ProtocolMessage,
67 ) -> StdResult<()>;
68}
69
70pub struct SignerCertifierService {
72 signed_beacon_store: Arc<dyn SignedBeaconStore>,
73 signed_entity_config_provider: Arc<dyn SignedEntityConfigProvider>,
74 signed_entity_type_lock: Arc<SignedEntityTypeLock>,
75 single_signer: Arc<dyn SingleSigner>,
76 signature_publisher: Arc<dyn SignaturePublisher>,
77 logger: Logger,
78}
79
80impl SignerCertifierService {
81 pub fn new(
83 signed_beacon_store: Arc<dyn SignedBeaconStore>,
84 signed_entity_config_provider: Arc<dyn SignedEntityConfigProvider>,
85 signed_entity_type_lock: Arc<SignedEntityTypeLock>,
86 single_signer: Arc<dyn SingleSigner>,
87 signature_publisher: Arc<dyn SignaturePublisher>,
88 logger: Logger,
89 ) -> Self {
90 Self {
91 signed_beacon_store,
92 signed_entity_config_provider,
93 signed_entity_type_lock,
94 single_signer,
95 signature_publisher,
96 logger: logger.new_with_component_name::<Self>(),
97 }
98 }
99
100 async fn list_available_signed_entity_types(
101 &self,
102 time_point: &TimePoint,
103 ) -> StdResult<Vec<SignedEntityType>> {
104 let signed_entity_types = self
105 .signed_entity_config_provider
106 .get()
107 .await?
108 .list_allowed_signed_entity_types(time_point)?;
109 let unlocked_signed_entities = self
110 .signed_entity_type_lock
111 .filter_unlocked_entries(signed_entity_types)
112 .await;
113 let not_already_signed_entities = self
114 .signed_beacon_store
115 .filter_out_already_signed_entities(unlocked_signed_entities)
116 .await?;
117
118 Ok(not_already_signed_entities)
119 }
120}
121
122#[async_trait]
123impl CertifierService for SignerCertifierService {
124 async fn get_beacon_to_sign(&self, time_point: TimePoint) -> StdResult<Option<BeaconToSign>> {
125 let available_signed_entity_types =
126 self.list_available_signed_entity_types(&time_point).await?;
127
128 if available_signed_entity_types.is_empty() {
129 Ok(None)
130 } else {
131 let signed_entity_type = available_signed_entity_types[0].clone();
132 let beacon_to_sign =
133 BeaconToSign::new(time_point.epoch, signed_entity_type, Utc::now());
134
135 Ok(Some(beacon_to_sign))
136 }
137 }
138
139 async fn compute_publish_single_signature(
140 &self,
141 beacon_to_sign: &BeaconToSign,
142 protocol_message: &ProtocolMessage,
143 ) -> StdResult<()> {
144 if let Some(single_signatures) = self
145 .single_signer
146 .compute_single_signatures(protocol_message)
147 .await?
148 {
149 debug!(self.logger, " > There is a single signature to send");
150 self.signature_publisher
151 .publish(
152 &beacon_to_sign.signed_entity_type,
153 &single_signatures,
154 protocol_message,
155 )
156 .await?;
157 } else {
158 debug!(self.logger, " > NO single signature to send");
159 }
160
161 debug!(self.logger, " > Marking beacon as signed"; "beacon" => ?beacon_to_sign);
162 self.signed_beacon_store
163 .mark_beacon_as_signed(beacon_to_sign)
164 .await?;
165
166 Ok(())
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use mockall::predicate::eq;
173
174 use mithril_common::entities::{
175 CardanoTransactionsSigningConfig, ChainPoint, Epoch, ProtocolMessagePartKey,
176 SignedEntityTypeDiscriminants,
177 };
178 use mithril_common::test_utils::fake_data;
179
180 use crate::services::MockSingleSigner;
181
182 use super::{tests::tests_tooling::*, *};
183
184 #[tokio::test]
185 async fn no_beacon_can_be_signed_if_all_entities_are_locked() {
186 let locker = Arc::new(SignedEntityTypeLock::new());
187 for signed_entity_type in SignedEntityTypeDiscriminants::all() {
188 locker.lock(signed_entity_type).await;
189 }
190 let certifier_service = SignerCertifierService {
191 signed_entity_type_lock: locker.clone(),
192 signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
193 CardanoTransactionsSigningConfig::dummy(),
194 SignedEntityTypeDiscriminants::all(),
195 )),
196 ..SignerCertifierService::dumb_dependencies()
197 };
198
199 let beacon_to_sign = certifier_service
200 .get_beacon_to_sign(TimePoint::new(1, 14, ChainPoint::dummy()))
201 .await
202 .unwrap();
203 assert_eq!(beacon_to_sign, None);
204 }
205
206 #[tokio::test]
207 async fn only_one_unlocked_and_not_yet_signed_yield_a_beacon_to_sign() {
208 let locker = Arc::new(SignedEntityTypeLock::new());
209 for signed_entity_type in SignedEntityTypeDiscriminants::all().into_iter().skip(1) {
210 locker.lock(signed_entity_type).await;
211 }
212 let certifier_service = SignerCertifierService {
213 signed_entity_type_lock: locker.clone(),
214 signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
215 CardanoTransactionsSigningConfig::dummy(),
216 SignedEntityTypeDiscriminants::all(),
217 )),
218 ..SignerCertifierService::dumb_dependencies()
219 };
220
221 let beacon_to_sign = certifier_service
222 .get_beacon_to_sign(TimePoint::new(3, 14, ChainPoint::dummy()))
223 .await
224 .unwrap();
225 let signed_discriminant: Option<SignedEntityTypeDiscriminants> =
226 beacon_to_sign.map(|b| b.signed_entity_type.into());
227
228 assert_eq!(
229 SignedEntityTypeDiscriminants::all().first().cloned(),
230 signed_discriminant
231 );
232 }
233
234 #[tokio::test]
235 async fn if_already_signed_a_beacon_is_not_returned_anymore() {
236 let signed_beacon_store = Arc::new(DumbSignedBeaconStore::default());
237 let certifier_service = SignerCertifierService {
238 signed_beacon_store: signed_beacon_store.clone(),
239 signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
240 CardanoTransactionsSigningConfig::dummy(),
241 SignedEntityTypeDiscriminants::all(),
242 )),
243 ..SignerCertifierService::dumb_dependencies()
244 };
245
246 let time_point = TimePoint::new(1, 14, ChainPoint::dummy());
247 let first_beacon_to_sign = certifier_service
248 .get_beacon_to_sign(time_point.clone())
249 .await
250 .unwrap()
251 .unwrap();
252 signed_beacon_store
253 .mark_beacon_as_signed(&first_beacon_to_sign.clone())
254 .await
255 .unwrap();
256 let second_beacon_to_sign = certifier_service
257 .get_beacon_to_sign(time_point)
258 .await
259 .unwrap()
260 .unwrap();
261
262 assert_ne!(
263 first_beacon_to_sign.signed_entity_type,
264 second_beacon_to_sign.signed_entity_type
265 );
266 }
267
268 #[tokio::test]
269 async fn draining_out_all_beacons_to_sign_use_signed_entity_discriminant_order() {
270 let signed_beacon_store = Arc::new(DumbSignedBeaconStore::default());
271 let certifier_service = SignerCertifierService {
272 signed_beacon_store: signed_beacon_store.clone(),
273 signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
274 CardanoTransactionsSigningConfig::dummy(),
275 SignedEntityTypeDiscriminants::all(),
276 )),
277 ..SignerCertifierService::dumb_dependencies()
278 };
279
280 let time_point = TimePoint::new(1, 14, ChainPoint::dummy());
281 let mut previous_beacon_to_sign = certifier_service
282 .get_beacon_to_sign(time_point.clone())
283 .await
284 .unwrap()
285 .expect("There should be a beacon to sign since nothing is locked or signed");
286
287 loop {
288 signed_beacon_store
289 .mark_beacon_as_signed(&previous_beacon_to_sign)
290 .await
291 .unwrap();
292 let next_beacon_to_sign = certifier_service
293 .get_beacon_to_sign(time_point.clone())
294 .await
295 .unwrap();
296
297 if let Some(beacon) = next_beacon_to_sign {
298 assert!(
299 SignedEntityTypeDiscriminants::from(
300 &previous_beacon_to_sign.signed_entity_type
301 ) < SignedEntityTypeDiscriminants::from(&beacon.signed_entity_type),
302 "Beacon should follow SignedEntityTypeDiscriminants order"
303 );
304
305 previous_beacon_to_sign = beacon;
306 } else {
307 break;
308 }
309 }
310 }
311
312 #[tokio::test]
313 async fn draining_out_all_beacons_to_sign_doesnt_repeat_value() {
314 let signed_beacon_store = Arc::new(DumbSignedBeaconStore::default());
315 let certifier_service = SignerCertifierService {
316 signed_beacon_store: signed_beacon_store.clone(),
317 signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
318 CardanoTransactionsSigningConfig::dummy(),
319 SignedEntityTypeDiscriminants::all(),
320 )),
321 ..SignerCertifierService::dumb_dependencies()
322 };
323
324 let mut all_signed_beacons = vec![];
325 while let Some(beacon_to_sign) = certifier_service
326 .get_beacon_to_sign(TimePoint::new(1, 14, ChainPoint::dummy()))
327 .await
328 .unwrap()
329 {
330 signed_beacon_store
331 .mark_beacon_as_signed(&beacon_to_sign)
332 .await
333 .unwrap();
334 all_signed_beacons.push(beacon_to_sign);
335 }
336
337 let mut dedup_signed_beacons = all_signed_beacons.clone();
338 dedup_signed_beacons.dedup();
339 assert!(
340 !all_signed_beacons.is_empty(),
341 "There should be at least one beacon to sign"
342 );
343 assert_eq!(
344 all_signed_beacons, dedup_signed_beacons,
345 "Beacon should not repeat"
346 );
347 }
348
349 #[tokio::test]
350 async fn compute_publish_single_signature_success_if_a_signature_was_issued() {
351 let protocol_message = {
352 let mut message = ProtocolMessage::new();
353 message.set_message_part(ProtocolMessagePartKey::SnapshotDigest, "digest".to_string());
354 message
355 };
356 let beacon_to_sign = BeaconToSign::new(
357 Epoch(1),
358 SignedEntityType::MithrilStakeDistribution(Epoch(4)),
359 Utc::now(),
360 );
361
362 let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
363 let certifier_service = SignerCertifierService {
364 single_signer: {
365 let mut single_signer = MockSingleSigner::new();
366 single_signer
367 .expect_compute_single_signatures()
368 .with(eq(protocol_message.clone()))
369 .return_once(|_| Ok(Some(fake_data::single_signatures(vec![1, 5, 12]))));
370 Arc::new(single_signer)
371 },
372 signature_publisher: {
373 let mut signature_publisher = MockSignaturePublisher::new();
374 signature_publisher
375 .expect_publish()
376 .with(
377 eq(beacon_to_sign.signed_entity_type.clone()),
378 eq(fake_data::single_signatures(vec![1, 5, 12])),
379 eq(protocol_message.clone()),
380 )
381 .returning(|_, _, _| Ok(()));
382 Arc::new(signature_publisher)
383 },
384 signed_beacon_store: signed_beacons_store.clone(),
385 ..SignerCertifierService::dumb_dependencies()
386 };
387
388 certifier_service
389 .compute_publish_single_signature(&beacon_to_sign, &protocol_message)
390 .await
391 .expect("Single signature should be computed and published");
392
393 let signed_beacons = signed_beacons_store.signed_beacons().await;
394 assert_eq!(
395 vec![beacon_to_sign.signed_entity_type.clone()],
396 signed_beacons
397 );
398 }
399
400 #[tokio::test]
401 async fn compute_publish_single_signature_success_but_dont_publish_if_no_signature_were_issued()
402 {
403 let protocol_message = {
404 let mut message = ProtocolMessage::new();
405 message.set_message_part(ProtocolMessagePartKey::SnapshotDigest, "digest".to_string());
406 message
407 };
408 let beacon_to_sign = BeaconToSign::new(
409 Epoch(1),
410 SignedEntityType::MithrilStakeDistribution(Epoch(4)),
411 Utc::now(),
412 );
413
414 let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
415 let certifier_service = SignerCertifierService {
416 single_signer: {
417 let mut single_signer = MockSingleSigner::new();
418 single_signer
419 .expect_compute_single_signatures()
420 .with(eq(protocol_message.clone()))
421 .return_once(|_| Ok(None));
422 Arc::new(single_signer)
423 },
424 signature_publisher: {
425 let mut signature_publisher = MockSignaturePublisher::new();
426 signature_publisher.expect_publish().never();
427 Arc::new(signature_publisher)
428 },
429 signed_beacon_store: signed_beacons_store.clone(),
430 ..SignerCertifierService::dumb_dependencies()
431 };
432
433 certifier_service
434 .compute_publish_single_signature(&beacon_to_sign, &protocol_message)
435 .await
436 .unwrap();
437
438 let signed_beacons = signed_beacons_store.signed_beacons().await;
439 assert_eq!(
440 vec![beacon_to_sign.signed_entity_type.clone()],
441 signed_beacons
442 );
443 }
444
445 #[tokio::test]
446 async fn beacon_isnt_mark_as_signed_if_computing_signature_fails() {
447 let beacon_to_sign = BeaconToSign::new(
448 Epoch(1),
449 SignedEntityType::MithrilStakeDistribution(Epoch(4)),
450 Utc::now(),
451 );
452
453 let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
454 let certifier_service = SignerCertifierService {
455 single_signer: {
456 let mut single_signer = MockSingleSigner::new();
457 single_signer
458 .expect_compute_single_signatures()
459 .return_once(|_| Err(anyhow::anyhow!("error")));
460 Arc::new(single_signer)
461 },
462 signed_beacon_store: signed_beacons_store.clone(),
463 ..SignerCertifierService::dumb_dependencies()
464 };
465
466 certifier_service
467 .compute_publish_single_signature(&beacon_to_sign, &ProtocolMessage::new())
468 .await
469 .unwrap_err();
470
471 let signed_beacons = signed_beacons_store.signed_beacons().await;
472 assert_eq!(Vec::<SignedEntityType>::new(), signed_beacons);
473 }
474
475 #[tokio::test]
476 async fn beacon_isnt_mark_as_signed_if_publishing_signature_fails() {
477 let beacon_to_sign = BeaconToSign::new(
478 Epoch(1),
479 SignedEntityType::MithrilStakeDistribution(Epoch(4)),
480 Utc::now(),
481 );
482
483 let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
484 let certifier_service = SignerCertifierService {
485 single_signer: {
486 let mut single_signer = MockSingleSigner::new();
487 single_signer
488 .expect_compute_single_signatures()
489 .return_once(|_| Ok(Some(fake_data::single_signatures(vec![1, 5, 12]))));
490 Arc::new(single_signer)
491 },
492 signature_publisher: {
493 let mut signature_publisher = MockSignaturePublisher::new();
494 signature_publisher
495 .expect_publish()
496 .return_once(|_, _, _| Err(anyhow::anyhow!("error")));
497 Arc::new(signature_publisher)
498 },
499 signed_beacon_store: signed_beacons_store.clone(),
500 ..SignerCertifierService::dumb_dependencies()
501 };
502
503 certifier_service
504 .compute_publish_single_signature(&beacon_to_sign, &ProtocolMessage::new())
505 .await
506 .unwrap_err();
507
508 let signed_beacons = signed_beacons_store.signed_beacons().await;
509 assert_eq!(Vec::<SignedEntityType>::new(), signed_beacons);
510 }
511
512 pub mod tests_tooling {
513 use std::collections::BTreeSet;
514 use tokio::sync::RwLock;
515
516 use crate::test_tools::TestLogger;
517
518 use super::*;
519
520 impl SignerCertifierService {
521 pub(crate) fn dumb_dependencies() -> Self {
522 Self {
523 signed_beacon_store: Arc::new(DumbSignedBeaconStore::default()),
524 signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
525 CardanoTransactionsSigningConfig::dummy(),
526 SignedEntityTypeDiscriminants::all(),
527 )),
528 signed_entity_type_lock: Arc::new(SignedEntityTypeLock::new()),
529 single_signer: Arc::new(MockSingleSigner::new()),
530 signature_publisher: Arc::new(MockSignaturePublisher::new()),
531 logger: TestLogger::stdout(),
532 }
533 }
534 }
535
536 pub struct DumbSignedEntityConfigProvider {
537 config: SignedEntityConfig,
538 }
539
540 impl DumbSignedEntityConfigProvider {
541 pub fn new(
542 config: CardanoTransactionsSigningConfig,
543 allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
544 ) -> Self {
545 Self {
546 config: SignedEntityConfig {
547 cardano_transactions_signing_config: config,
548 allowed_discriminants,
549 },
550 }
551 }
552 }
553
554 #[async_trait]
555 impl SignedEntityConfigProvider for DumbSignedEntityConfigProvider {
556 async fn get(&self) -> StdResult<SignedEntityConfig> {
557 Ok(self.config.clone())
558 }
559 }
560
561 pub struct DumbSignedBeaconStore {
562 signed_beacons: RwLock<Vec<SignedEntityType>>,
563 }
564
565 impl DumbSignedBeaconStore {
566 pub fn new(signed_beacons: Vec<SignedEntityType>) -> Self {
567 Self {
568 signed_beacons: RwLock::new(signed_beacons),
569 }
570 }
571
572 pub async fn signed_beacons(&self) -> Vec<SignedEntityType> {
573 self.signed_beacons.read().await.clone()
574 }
575 }
576
577 impl Default for DumbSignedBeaconStore {
578 fn default() -> Self {
579 Self::new(vec![])
580 }
581 }
582
583 #[async_trait]
584 impl SignedBeaconStore for DumbSignedBeaconStore {
585 async fn filter_out_already_signed_entities(
586 &self,
587 entities: Vec<SignedEntityType>,
588 ) -> StdResult<Vec<SignedEntityType>> {
589 let already_signed_entities = self.signed_beacons.read().await.clone();
590 Ok(entities
591 .into_iter()
592 .filter(|entity| !already_signed_entities.contains(entity))
593 .collect())
594 }
595
596 async fn mark_beacon_as_signed(&self, beacon: &BeaconToSign) -> StdResult<()> {
597 let mut already_signed_entities = self.signed_beacons.write().await;
598 already_signed_entities.push(beacon.signed_entity_type.clone());
599 Ok(())
600 }
601 }
602 }
603}