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