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 CardanoBlocksTransactionsSigningConfig, CardanoTransactionsSigningConfig, ChainPoint,
157 Epoch, ProtocolMessagePartKey, SignedEntityTypeDiscriminants,
158 };
159 use mithril_common::test::double::{Dummy, 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 CardanoBlocksTransactionsSigningConfig::dummy(),
176 SignedEntityTypeDiscriminants::all(),
177 )),
178 ..SignerCertifierService::dumb_dependencies()
179 };
180
181 let beacon_to_sign = certifier_service
182 .get_beacon_to_sign(TimePoint::new(1, 14, ChainPoint::dummy()))
183 .await
184 .unwrap();
185 assert_eq!(beacon_to_sign, None);
186 }
187
188 #[tokio::test]
189 async fn only_one_unlocked_and_not_yet_signed_yield_a_beacon_to_sign() {
190 let locker = Arc::new(SignedEntityTypeLock::new());
191 for signed_entity_type in SignedEntityTypeDiscriminants::all().into_iter().skip(1) {
192 locker.lock(signed_entity_type).await;
193 }
194 let certifier_service = SignerCertifierService {
195 signed_entity_type_lock: locker.clone(),
196 signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
197 CardanoTransactionsSigningConfig::dummy(),
198 CardanoBlocksTransactionsSigningConfig::dummy(),
199 SignedEntityTypeDiscriminants::all(),
200 )),
201 ..SignerCertifierService::dumb_dependencies()
202 };
203
204 let beacon_to_sign = certifier_service
205 .get_beacon_to_sign(TimePoint::new(3, 14, ChainPoint::dummy()))
206 .await
207 .unwrap();
208 let signed_discriminant: Option<SignedEntityTypeDiscriminants> =
209 beacon_to_sign.map(|b| b.signed_entity_type.into());
210
211 assert_eq!(
212 SignedEntityTypeDiscriminants::all().first().cloned(),
213 signed_discriminant
214 );
215 }
216
217 #[tokio::test]
218 async fn if_already_signed_a_beacon_is_not_returned_anymore() {
219 let signed_beacon_store = Arc::new(DumbSignedBeaconStore::default());
220 let certifier_service = SignerCertifierService {
221 signed_beacon_store: signed_beacon_store.clone(),
222 signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
223 CardanoTransactionsSigningConfig::dummy(),
224 CardanoBlocksTransactionsSigningConfig::dummy(),
225 SignedEntityTypeDiscriminants::all(),
226 )),
227 ..SignerCertifierService::dumb_dependencies()
228 };
229
230 let time_point = TimePoint::new(1, 14, ChainPoint::dummy());
231 let first_beacon_to_sign = certifier_service
232 .get_beacon_to_sign(time_point.clone())
233 .await
234 .unwrap()
235 .unwrap();
236 signed_beacon_store
237 .mark_beacon_as_signed(&first_beacon_to_sign.clone())
238 .await
239 .unwrap();
240 let second_beacon_to_sign = certifier_service
241 .get_beacon_to_sign(time_point)
242 .await
243 .unwrap()
244 .unwrap();
245
246 assert_ne!(
247 first_beacon_to_sign.signed_entity_type,
248 second_beacon_to_sign.signed_entity_type
249 );
250 }
251
252 #[tokio::test]
253 async fn draining_out_all_beacons_to_sign_use_signed_entity_discriminant_order() {
254 let signed_beacon_store = Arc::new(DumbSignedBeaconStore::default());
255 let certifier_service = SignerCertifierService {
256 signed_beacon_store: signed_beacon_store.clone(),
257 signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
258 CardanoTransactionsSigningConfig::dummy(),
259 CardanoBlocksTransactionsSigningConfig::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 CardanoBlocksTransactionsSigningConfig::dummy(),
305 SignedEntityTypeDiscriminants::all(),
306 )),
307 ..SignerCertifierService::dumb_dependencies()
308 };
309
310 let mut all_signed_beacons = vec![];
311 while let Some(beacon_to_sign) = certifier_service
312 .get_beacon_to_sign(TimePoint::new(1, 14, ChainPoint::dummy()))
313 .await
314 .unwrap()
315 {
316 signed_beacon_store
317 .mark_beacon_as_signed(&beacon_to_sign)
318 .await
319 .unwrap();
320 all_signed_beacons.push(beacon_to_sign);
321 }
322
323 let mut dedup_signed_beacons = all_signed_beacons.clone();
324 dedup_signed_beacons.dedup();
325 assert!(
326 !all_signed_beacons.is_empty(),
327 "There should be at least one beacon to sign"
328 );
329 assert_eq!(
330 all_signed_beacons, dedup_signed_beacons,
331 "Beacon should not repeat"
332 );
333 }
334
335 #[tokio::test]
336 async fn compute_publish_single_signature_success_if_a_signature_was_issued() {
337 let protocol_message = {
338 let mut message = ProtocolMessage::new();
339 message.set_message_part(ProtocolMessagePartKey::SnapshotDigest, "digest".to_string());
340 message
341 };
342 let beacon_to_sign = BeaconToSign::new(
343 Epoch(1),
344 SignedEntityType::MithrilStakeDistribution(Epoch(4)),
345 Utc::now(),
346 );
347
348 let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
349 let certifier_service = SignerCertifierService {
350 single_signer: {
351 let mut single_signer = MockSingleSigner::new();
352 single_signer
353 .expect_compute_single_signature()
354 .with(eq(protocol_message.clone()))
355 .return_once(|_| Ok(Some(fake_data::single_signature(vec![1, 5, 12]))));
356 Arc::new(single_signer)
357 },
358 signature_publisher: {
359 let mut signature_publisher = MockSignaturePublisher::new();
360 signature_publisher
361 .expect_publish()
362 .with(
363 eq(beacon_to_sign.signed_entity_type.clone()),
364 eq(fake_data::single_signature(vec![1, 5, 12])),
365 eq(protocol_message.clone()),
366 )
367 .returning(|_, _, _| Ok(()));
368 Arc::new(signature_publisher)
369 },
370 signed_beacon_store: signed_beacons_store.clone(),
371 ..SignerCertifierService::dumb_dependencies()
372 };
373
374 certifier_service
375 .compute_publish_single_signature(&beacon_to_sign, &protocol_message)
376 .await
377 .expect("Single signature should be computed and published");
378
379 let signed_beacons = signed_beacons_store.signed_beacons().await;
380 assert_eq!(
381 vec![beacon_to_sign.signed_entity_type.clone()],
382 signed_beacons
383 );
384 }
385
386 #[tokio::test]
387 async fn compute_publish_single_signature_success_but_dont_publish_if_no_signature_were_issued()
388 {
389 let protocol_message = {
390 let mut message = ProtocolMessage::new();
391 message.set_message_part(ProtocolMessagePartKey::SnapshotDigest, "digest".to_string());
392 message
393 };
394 let beacon_to_sign = BeaconToSign::new(
395 Epoch(1),
396 SignedEntityType::MithrilStakeDistribution(Epoch(4)),
397 Utc::now(),
398 );
399
400 let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
401 let certifier_service = SignerCertifierService {
402 single_signer: {
403 let mut single_signer = MockSingleSigner::new();
404 single_signer
405 .expect_compute_single_signature()
406 .with(eq(protocol_message.clone()))
407 .return_once(|_| Ok(None));
408 Arc::new(single_signer)
409 },
410 signature_publisher: {
411 let mut signature_publisher = MockSignaturePublisher::new();
412 signature_publisher.expect_publish().never();
413 Arc::new(signature_publisher)
414 },
415 signed_beacon_store: signed_beacons_store.clone(),
416 ..SignerCertifierService::dumb_dependencies()
417 };
418
419 certifier_service
420 .compute_publish_single_signature(&beacon_to_sign, &protocol_message)
421 .await
422 .unwrap();
423
424 let signed_beacons = signed_beacons_store.signed_beacons().await;
425 assert_eq!(
426 vec![beacon_to_sign.signed_entity_type.clone()],
427 signed_beacons
428 );
429 }
430
431 #[tokio::test]
432 async fn beacon_isnt_mark_as_signed_if_computing_signature_fails() {
433 let beacon_to_sign = BeaconToSign::new(
434 Epoch(1),
435 SignedEntityType::MithrilStakeDistribution(Epoch(4)),
436 Utc::now(),
437 );
438
439 let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
440 let certifier_service = SignerCertifierService {
441 single_signer: {
442 let mut single_signer = MockSingleSigner::new();
443 single_signer
444 .expect_compute_single_signature()
445 .return_once(|_| Err(anyhow::anyhow!("error")));
446 Arc::new(single_signer)
447 },
448 signed_beacon_store: signed_beacons_store.clone(),
449 ..SignerCertifierService::dumb_dependencies()
450 };
451
452 certifier_service
453 .compute_publish_single_signature(&beacon_to_sign, &ProtocolMessage::new())
454 .await
455 .unwrap_err();
456
457 let signed_beacons = signed_beacons_store.signed_beacons().await;
458 assert_eq!(Vec::<SignedEntityType>::new(), signed_beacons);
459 }
460
461 #[tokio::test]
462 async fn beacon_isnt_mark_as_signed_if_publishing_signature_fails() {
463 let beacon_to_sign = BeaconToSign::new(
464 Epoch(1),
465 SignedEntityType::MithrilStakeDistribution(Epoch(4)),
466 Utc::now(),
467 );
468
469 let signed_beacons_store = Arc::new(DumbSignedBeaconStore::default());
470 let certifier_service = SignerCertifierService {
471 single_signer: {
472 let mut single_signer = MockSingleSigner::new();
473 single_signer
474 .expect_compute_single_signature()
475 .return_once(|_| Ok(Some(fake_data::single_signature(vec![1, 5, 12]))));
476 Arc::new(single_signer)
477 },
478 signature_publisher: {
479 let mut signature_publisher = MockSignaturePublisher::new();
480 signature_publisher
481 .expect_publish()
482 .return_once(|_, _, _| Err(anyhow::anyhow!("error")));
483 Arc::new(signature_publisher)
484 },
485 signed_beacon_store: signed_beacons_store.clone(),
486 ..SignerCertifierService::dumb_dependencies()
487 };
488
489 certifier_service
490 .compute_publish_single_signature(&beacon_to_sign, &ProtocolMessage::new())
491 .await
492 .unwrap_err();
493
494 let signed_beacons = signed_beacons_store.signed_beacons().await;
495 assert_eq!(Vec::<SignedEntityType>::new(), signed_beacons);
496 }
497
498 pub mod tests_tooling {
499 use mithril_common::entities::CardanoBlocksTransactionsSigningConfig;
500 use std::collections::BTreeSet;
501 use tokio::sync::RwLock;
502
503 use crate::test::TestLogger;
504
505 use super::*;
506
507 impl SignerCertifierService {
508 pub(crate) fn dumb_dependencies() -> Self {
509 Self {
510 signed_beacon_store: Arc::new(DumbSignedBeaconStore::default()),
511 signed_entity_config_provider: Arc::new(DumbSignedEntityConfigProvider::new(
512 CardanoTransactionsSigningConfig::dummy(),
513 CardanoBlocksTransactionsSigningConfig::dummy(),
514 SignedEntityTypeDiscriminants::all(),
515 )),
516 signed_entity_type_lock: Arc::new(SignedEntityTypeLock::new()),
517 single_signer: Arc::new(MockSingleSigner::new()),
518 signature_publisher: Arc::new(MockSignaturePublisher::new()),
519 logger: TestLogger::stdout(),
520 }
521 }
522 }
523
524 pub struct DumbSignedEntityConfigProvider {
525 config: SignedEntityConfig,
526 }
527
528 impl DumbSignedEntityConfigProvider {
529 pub fn new(
530 cardano_transactions_config: CardanoTransactionsSigningConfig,
531 cardano_blocks_transactions_config: CardanoBlocksTransactionsSigningConfig,
532 allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
533 ) -> Self {
534 Self {
535 config: SignedEntityConfig {
536 cardano_transactions_signing_config: Some(cardano_transactions_config),
537 cardano_blocks_transactions_signing_config: Some(
538 cardano_blocks_transactions_config,
539 ),
540 allowed_discriminants,
541 },
542 }
543 }
544 }
545
546 #[async_trait]
547 impl SignedEntityConfigProvider for DumbSignedEntityConfigProvider {
548 async fn get(&self) -> StdResult<SignedEntityConfig> {
549 Ok(self.config.clone())
550 }
551 }
552
553 pub struct DumbSignedBeaconStore {
554 signed_beacons: RwLock<Vec<SignedEntityType>>,
555 }
556
557 impl DumbSignedBeaconStore {
558 pub fn new(signed_beacons: Vec<SignedEntityType>) -> Self {
559 Self {
560 signed_beacons: RwLock::new(signed_beacons),
561 }
562 }
563
564 pub async fn signed_beacons(&self) -> Vec<SignedEntityType> {
565 self.signed_beacons.read().await.clone()
566 }
567 }
568
569 impl Default for DumbSignedBeaconStore {
570 fn default() -> Self {
571 Self::new(vec![])
572 }
573 }
574
575 #[async_trait]
576 impl SignedBeaconStore for DumbSignedBeaconStore {
577 async fn filter_out_already_signed_entities(
578 &self,
579 entities: Vec<SignedEntityType>,
580 ) -> StdResult<Vec<SignedEntityType>> {
581 let already_signed_entities = self.signed_beacons.read().await.clone();
582 Ok(entities
583 .into_iter()
584 .filter(|entity| !already_signed_entities.contains(entity))
585 .collect())
586 }
587
588 async fn mark_beacon_as_signed(&self, beacon: &BeaconToSign) -> StdResult<()> {
589 let mut already_signed_entities = self.signed_beacons.write().await;
590 already_signed_entities.push(beacon.signed_entity_type.clone());
591 Ok(())
592 }
593 }
594 }
595}