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