1use anyhow::Context;
2use async_trait::async_trait;
3use slog::{Logger, debug};
4use std::collections::BTreeSet;
5use std::sync::Arc;
6use thiserror::Error;
7
8use mithril_cardano_node_chain::chain_observer::ChainObserver;
9use mithril_common::StdResult;
10use mithril_common::crypto_helper::ProtocolAggregateVerificationKey;
11use mithril_common::entities::{
12 CardanoEra, Epoch, ProtocolParameters, SignedEntityConfig, SignedEntityTypeDiscriminants,
13 Signer, SignerWithStake, Stake, SupportedEra, TotalSPOs,
14};
15use mithril_common::logging::LoggerExtensions;
16use mithril_common::protocol::{MultiSigner as ProtocolMultiSigner, SignerBuilder};
17use mithril_era::EraChecker;
18use mithril_persistence::store::StakeStorer;
19use mithril_protocol_config::interface::MithrilNetworkConfigurationProvider;
20use mithril_protocol_config::model::MithrilNetworkConfiguration;
21
22use crate::{EpochSettingsStorer, VerificationKeyStorer, entities::AggregatorEpochSettings};
23
24#[derive(Debug, Error)]
26pub enum EpochServiceError {
27 #[error("Epoch service could not obtain {1} for epoch {0}")]
29 UnavailableData(Epoch, String),
30
31 #[error("Epoch service was not initialized, the function `inform_epoch` must be called first")]
33 NotYetInitialized,
34
35 #[error(
37 "No data computed for epoch {0}, the function `precompute_epoch_data` must be called first"
38 )]
39 NotYetComputed(Epoch),
40}
41
42#[async_trait]
44pub trait EpochService: Sync + Send {
45 async fn inform_epoch(&mut self, epoch: Epoch) -> StdResult<()>;
48
49 async fn update_next_signers_with_stake(&mut self) -> StdResult<()>;
51
52 async fn precompute_epoch_data(&mut self) -> StdResult<()>;
56
57 fn cardano_era(&self) -> StdResult<CardanoEra>;
59
60 fn mithril_era(&self) -> StdResult<SupportedEra>;
62
63 fn epoch_of_current_data(&self) -> StdResult<Epoch>;
65
66 fn network_configuration(&self) -> StdResult<&MithrilNetworkConfiguration>;
68
69 fn current_protocol_parameters(&self) -> StdResult<&ProtocolParameters> {
71 Ok(&self
72 .network_configuration()?
73 .configuration_for_aggregation
74 .protocol_parameters)
75 }
76
77 fn next_protocol_parameters(&self) -> StdResult<&ProtocolParameters> {
79 Ok(&self
80 .network_configuration()?
81 .configuration_for_next_aggregation
82 .protocol_parameters)
83 }
84
85 fn signer_registration_protocol_parameters(&self) -> StdResult<&ProtocolParameters> {
87 Ok(&self
88 .network_configuration()?
89 .configuration_for_registration
90 .protocol_parameters)
91 }
92
93 fn current_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey>;
95
96 fn next_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey>;
98
99 fn current_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>>;
101
102 fn next_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>>;
104
105 fn current_signers(&self) -> StdResult<&Vec<Signer>>;
107
108 fn next_signers(&self) -> StdResult<&Vec<Signer>>;
110
111 fn total_stakes_signers(&self) -> StdResult<Stake>;
113
114 fn total_next_stakes_signers(&self) -> StdResult<Stake>;
116
117 fn protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner>;
119
120 fn next_protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner>;
122
123 fn signed_entity_config(&self) -> StdResult<&SignedEntityConfig>;
125
126 fn total_spo(&self) -> StdResult<Option<TotalSPOs>>;
130
131 fn total_stake(&self) -> StdResult<Option<Stake>>;
135}
136
137struct EpochData {
138 cardano_era: CardanoEra,
139 mithril_era: SupportedEra,
140 epoch: Epoch,
141 network_configuration: MithrilNetworkConfiguration,
142 current_signers_with_stake: Vec<SignerWithStake>,
143 next_signers_with_stake: Vec<SignerWithStake>,
144 current_signers: Vec<Signer>,
145 next_signers: Vec<Signer>,
146 total_stakes_signers: Stake,
147 total_next_stakes_signers: Stake,
148 signed_entity_config: SignedEntityConfig,
149 total_spo: Option<TotalSPOs>,
150 total_stake: Option<Stake>,
151}
152
153struct ComputedEpochData {
154 aggregate_verification_key: ProtocolAggregateVerificationKey,
155 next_aggregate_verification_key: ProtocolAggregateVerificationKey,
156 protocol_multi_signer: ProtocolMultiSigner,
157 next_protocol_multi_signer: ProtocolMultiSigner,
158}
159
160pub struct EpochServiceDependencies {
162 mithril_network_configuration_provider: Arc<dyn MithrilNetworkConfigurationProvider>,
163 epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
164 verification_key_store: Arc<dyn VerificationKeyStorer>,
165 chain_observer: Arc<dyn ChainObserver>,
166 era_checker: Arc<EraChecker>,
167 stake_store: Arc<dyn StakeStorer>,
168}
169
170impl EpochServiceDependencies {
171 pub fn new(
173 mithril_network_configuration_provider: Arc<dyn MithrilNetworkConfigurationProvider>,
174 epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
175 verification_key_store: Arc<dyn VerificationKeyStorer>,
176 chain_observer: Arc<dyn ChainObserver>,
177 era_checker: Arc<EraChecker>,
178 stake_store: Arc<dyn StakeStorer>,
179 ) -> Self {
180 Self {
181 mithril_network_configuration_provider,
182 epoch_settings_storer,
183 verification_key_store,
184 chain_observer,
185 era_checker,
186 stake_store,
187 }
188 }
189}
190
191pub struct MithrilEpochService {
193 epoch_data: Option<EpochData>,
194 computed_epoch_data: Option<ComputedEpochData>,
195 mithril_network_configuration_provider: Arc<dyn MithrilNetworkConfigurationProvider>,
196 epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
197 verification_key_store: Arc<dyn VerificationKeyStorer>,
198 chain_observer: Arc<dyn ChainObserver>,
199 era_checker: Arc<EraChecker>,
200 stake_store: Arc<dyn StakeStorer>,
201 allowed_signed_entity_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
202 logger: Logger,
203}
204
205impl MithrilEpochService {
206 pub fn new(
208 dependencies: EpochServiceDependencies,
209 allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
210 logger: Logger,
211 ) -> Self {
212 Self {
213 epoch_data: None,
214 computed_epoch_data: None,
215 mithril_network_configuration_provider: dependencies
216 .mithril_network_configuration_provider,
217 epoch_settings_storer: dependencies.epoch_settings_storer,
218 verification_key_store: dependencies.verification_key_store,
219 chain_observer: dependencies.chain_observer,
220 era_checker: dependencies.era_checker,
221 stake_store: dependencies.stake_store,
222 allowed_signed_entity_discriminants: allowed_discriminants,
223 logger: logger.new_with_component_name::<Self>(),
224 }
225 }
226
227 async fn get_cardano_era(&self) -> StdResult<CardanoEra> {
228 let cardano_era = self
229 .chain_observer
230 .get_current_era()
231 .await?
232 .with_context(|| "No Cardano era returned by the chain observer")?;
233
234 Ok(cardano_era)
235 }
236
237 async fn get_total_spo_and_total_stake(
238 &self,
239 epoch: Epoch,
240 ) -> StdResult<(Option<TotalSPOs>, Option<Stake>)> {
241 match self.stake_store.get_stakes(epoch).await.with_context(|| {
242 format!("Epoch service failed to obtain the stake distribution for epoch: {epoch}")
243 })? {
244 None => Ok((None, None)),
245 Some(sd) => Ok((Some(sd.len() as TotalSPOs), Some(sd.values().sum()))),
246 }
247 }
248
249 async fn get_signers_with_stake_at_epoch(
250 &self,
251 signer_retrieval_epoch: Epoch,
252 ) -> StdResult<Vec<SignerWithStake>> {
253 let signers = self
254 .verification_key_store
255 .get_signers(signer_retrieval_epoch)
256 .await?
257 .unwrap_or_default();
258
259 Ok(signers)
260 }
261
262 async fn insert_epoch_settings(
263 &self,
264 recording_epoch: Epoch,
265 epoch_settings: &AggregatorEpochSettings,
266 ) -> StdResult<()> {
267 self.epoch_settings_storer
268 .save_epoch_settings(
269 recording_epoch,
270 epoch_settings.clone()
271 )
272 .await
273 .with_context(|| format!("Epoch service failed to insert future_epoch_settings to epoch {recording_epoch}"))
274 .map(|_| ())
275 }
276
277 fn unwrap_data(&self) -> Result<&EpochData, EpochServiceError> {
278 self.epoch_data.as_ref().ok_or(EpochServiceError::NotYetInitialized)
279 }
280
281 fn unwrap_computed_data(&self) -> Result<&ComputedEpochData, EpochServiceError> {
282 let epoch = self.unwrap_data()?.epoch;
283
284 self.computed_epoch_data
285 .as_ref()
286 .ok_or(EpochServiceError::NotYetComputed(epoch))
287 }
288}
289
290#[async_trait]
291impl EpochService for MithrilEpochService {
292 async fn inform_epoch(&mut self, epoch: Epoch) -> StdResult<()> {
293 debug!(self.logger, ">> inform_epoch(epoch: {epoch:?})");
294
295 let cardano_era = self.get_cardano_era().await?;
296
297 let mithril_era = self.era_checker.current_era();
298
299 let signer_retrieval_epoch =
300 epoch.offset_to_signer_retrieval_epoch().with_context(|| {
301 format!("EpochService could not compute signer retrieval epoch from epoch: {epoch}")
302 })?;
303 let next_signer_retrieval_epoch = epoch.offset_to_next_signer_retrieval_epoch();
304 let signer_registration_epoch = epoch.offset_to_recording_epoch();
305
306 let network_configuration = self
307 .mithril_network_configuration_provider
308 .get_network_configuration(epoch)
309 .await?;
310
311 let signer_registration_epoch_settings = AggregatorEpochSettings {
312 protocol_parameters: network_configuration
313 .configuration_for_registration
314 .protocol_parameters
315 .clone(),
316 cardano_transactions_signing_config: network_configuration
317 .configuration_for_registration
318 .signed_entity_types_config
319 .cardano_transactions
320 .clone(),
321 cardano_blocks_transactions_signing_config: network_configuration
322 .configuration_for_registration
323 .signed_entity_types_config
324 .cardano_blocks_transactions
325 .clone(),
326 };
327 self.insert_epoch_settings(
328 signer_registration_epoch,
329 &signer_registration_epoch_settings,
330 )
331 .await?;
332
333 let current_signers_with_stake =
334 self.get_signers_with_stake_at_epoch(signer_retrieval_epoch).await?;
335 let next_signers_with_stake = self
336 .get_signers_with_stake_at_epoch(next_signer_retrieval_epoch)
337 .await?;
338 let current_signers = Signer::vec_from(current_signers_with_stake.clone());
339 let next_signers = Signer::vec_from(next_signers_with_stake.clone());
340 let total_stakes_signers = current_signers_with_stake.iter().map(|s| s.stake).sum();
341 let total_next_stakes_signers = next_signers_with_stake.iter().map(|s| s.stake).sum();
342
343 let signed_entity_config = SignedEntityConfig {
344 allowed_discriminants: self
345 .allowed_signed_entity_discriminants
346 .intersection(
347 &network_configuration
348 .configuration_for_aggregation
349 .enabled_signed_entity_types,
350 )
351 .cloned()
352 .collect(),
353 cardano_transactions_signing_config: network_configuration
354 .configuration_for_aggregation
355 .signed_entity_types_config
356 .cardano_transactions
357 .clone(),
358 cardano_blocks_transactions_signing_config: network_configuration
359 .configuration_for_aggregation
360 .signed_entity_types_config
361 .cardano_blocks_transactions
362 .clone(),
363 };
364
365 let (total_spo, total_stake) =
366 self.get_total_spo_and_total_stake(signer_retrieval_epoch).await?;
367
368 self.epoch_data = Some(EpochData {
369 cardano_era,
370 mithril_era,
371 epoch,
372 network_configuration,
373 current_signers_with_stake,
374 next_signers_with_stake,
375 current_signers,
376 next_signers,
377 total_stakes_signers,
378 total_next_stakes_signers,
379 signed_entity_config,
380 total_spo,
381 total_stake,
382 });
383 self.computed_epoch_data = None;
384
385 Ok(())
386 }
387
388 async fn update_next_signers_with_stake(&mut self) -> StdResult<()> {
389 debug!(self.logger, ">> update_next_signers_with_stake");
390
391 let data = self.unwrap_data().with_context(
392 || "can't update next signers with stake if inform_epoch has not been called first",
393 )?;
394
395 let next_signer_retrieval_epoch = data.epoch.offset_to_next_signer_retrieval_epoch();
396 let next_signers_with_stake = self
397 .get_signers_with_stake_at_epoch(next_signer_retrieval_epoch)
398 .await?;
399
400 self.epoch_data.as_mut().unwrap().next_signers_with_stake = next_signers_with_stake;
401
402 self.precompute_epoch_data()
403 .await
404 .with_context(|| "Epoch service failed to precompute epoch data")?;
405
406 Ok(())
407 }
408
409 async fn precompute_epoch_data(&mut self) -> StdResult<()> {
410 debug!(self.logger, ">> precompute_epoch_data");
411
412 let data = self.unwrap_data().with_context(
413 || "can't precompute epoch data if inform_epoch has not been called first",
414 )?;
415
416 let protocol_multi_signer = SignerBuilder::new(
417 &data.current_signers_with_stake,
418 &data
419 .network_configuration
420 .configuration_for_aggregation
421 .protocol_parameters,
422 )
423 .with_context(|| "Epoch service failed to build protocol multi signer")?
424 .build_multi_signer();
425
426 let next_protocol_multi_signer = SignerBuilder::new(
427 &data.next_signers_with_stake,
428 &data
429 .network_configuration
430 .configuration_for_next_aggregation
431 .protocol_parameters,
432 )
433 .with_context(|| "Epoch service failed to build next protocol multi signer")?
434 .build_multi_signer();
435
436 self.computed_epoch_data = Some(ComputedEpochData {
437 aggregate_verification_key: protocol_multi_signer.compute_aggregate_verification_key(),
438 next_aggregate_verification_key: next_protocol_multi_signer
439 .compute_aggregate_verification_key(),
440 protocol_multi_signer,
441 next_protocol_multi_signer,
442 });
443
444 Ok(())
445 }
446
447 fn cardano_era(&self) -> StdResult<CardanoEra> {
448 Ok(self.unwrap_data()?.cardano_era.clone())
449 }
450
451 fn mithril_era(&self) -> StdResult<SupportedEra> {
452 Ok(self.unwrap_data()?.mithril_era)
453 }
454
455 fn epoch_of_current_data(&self) -> StdResult<Epoch> {
456 Ok(self.unwrap_data()?.epoch)
457 }
458
459 fn network_configuration(&self) -> StdResult<&MithrilNetworkConfiguration> {
460 Ok(&self.unwrap_data()?.network_configuration)
461 }
462
463 fn current_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey> {
464 Ok(&self.unwrap_computed_data()?.aggregate_verification_key)
465 }
466
467 fn next_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey> {
468 Ok(&self.unwrap_computed_data()?.next_aggregate_verification_key)
469 }
470
471 fn current_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>> {
472 Ok(&self.unwrap_data()?.current_signers_with_stake)
473 }
474
475 fn next_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>> {
476 Ok(&self.unwrap_data()?.next_signers_with_stake)
477 }
478
479 fn current_signers(&self) -> StdResult<&Vec<Signer>> {
480 Ok(&self.unwrap_data()?.current_signers)
481 }
482
483 fn next_signers(&self) -> StdResult<&Vec<Signer>> {
484 Ok(&self.unwrap_data()?.next_signers)
485 }
486
487 fn total_stakes_signers(&self) -> StdResult<Stake> {
488 Ok(self.unwrap_data()?.total_stakes_signers)
489 }
490
491 fn total_next_stakes_signers(&self) -> StdResult<Stake> {
492 Ok(self.unwrap_data()?.total_next_stakes_signers)
493 }
494
495 fn protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner> {
496 Ok(&self.unwrap_computed_data()?.protocol_multi_signer)
497 }
498
499 fn next_protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner> {
500 Ok(&self.unwrap_computed_data()?.next_protocol_multi_signer)
501 }
502
503 fn signed_entity_config(&self) -> StdResult<&SignedEntityConfig> {
504 Ok(&self.unwrap_data()?.signed_entity_config)
505 }
506
507 fn total_spo(&self) -> StdResult<Option<TotalSPOs>> {
508 Ok(self.unwrap_data()?.total_spo)
509 }
510
511 fn total_stake(&self) -> StdResult<Option<Stake>> {
512 Ok(self.unwrap_data()?.total_stake)
513 }
514}
515
516#[cfg(test)]
517pub(crate) struct FakeEpochService {
518 epoch_data: Option<EpochData>,
519 computed_epoch_data: Option<ComputedEpochData>,
520 inform_epoch_error: bool,
521 precompute_epoch_data_error: bool,
522 update_next_signers_with_stake_error: bool,
523}
524
525#[cfg(test)]
526pub(crate) struct FakeEpochServiceBuilder {
527 pub cardano_era: CardanoEra,
528 pub mithril_era: SupportedEra,
529 pub epoch: Epoch,
530 pub current_epoch_settings: AggregatorEpochSettings,
531 pub next_epoch_settings: AggregatorEpochSettings,
532 pub signer_registration_epoch_settings: AggregatorEpochSettings,
533 pub current_signers_with_stake: Vec<SignerWithStake>,
534 pub next_signers_with_stake: Vec<SignerWithStake>,
535 pub signed_entity_config: SignedEntityConfig,
536 pub total_spo: Option<TotalSPOs>,
537 pub total_stake: Option<Stake>,
538}
539
540#[cfg(test)]
541impl FakeEpochServiceBuilder {
542 pub fn dummy(epoch: Epoch) -> Self {
543 use mithril_common::test::double::{Dummy, fake_data};
544 let signers = fake_data::signers_with_stakes(3);
545
546 Self {
547 cardano_era: "DummyEra".to_string(),
548 mithril_era: SupportedEra::dummy(),
549 epoch,
550 current_epoch_settings: AggregatorEpochSettings::dummy(),
551 next_epoch_settings: AggregatorEpochSettings::dummy(),
552 signer_registration_epoch_settings: AggregatorEpochSettings::dummy(),
553 current_signers_with_stake: signers.clone(),
554 next_signers_with_stake: signers,
555 signed_entity_config: SignedEntityConfig::dummy(),
556 total_spo: None,
557 total_stake: None,
558 }
559 }
560
561 pub fn build(self) -> FakeEpochService {
562 let current_signers = Signer::vec_from(self.current_signers_with_stake.clone());
563 let next_signers = Signer::vec_from(self.next_signers_with_stake.clone());
564 let total_stakes_signers = self.current_signers_with_stake.iter().map(|s| s.stake).sum();
565 let total_next_stakes_signers = self.next_signers_with_stake.iter().map(|s| s.stake).sum();
566
567 let protocol_multi_signer = SignerBuilder::new(
568 &self.current_signers_with_stake,
569 &self.current_epoch_settings.protocol_parameters,
570 )
571 .with_context(|| "Could not build protocol_multi_signer for epoch service")
572 .unwrap()
573 .build_multi_signer();
574 let next_protocol_multi_signer = SignerBuilder::new(
575 &self.next_signers_with_stake,
576 &self.next_epoch_settings.protocol_parameters,
577 )
578 .with_context(|| "Could not build protocol_multi_signer for epoch service")
579 .unwrap()
580 .build_multi_signer();
581
582 let network_configuration = MithrilNetworkConfiguration {
583 epoch: self.epoch,
584 configuration_for_aggregation: self
585 .current_epoch_settings
586 .into_network_configuration_for_epoch(
587 self.signed_entity_config.allowed_discriminants.clone(),
588 ),
589 configuration_for_next_aggregation: self
590 .next_epoch_settings
591 .into_network_configuration_for_epoch(
592 self.signed_entity_config.allowed_discriminants.clone(),
593 ),
594 configuration_for_registration: self
595 .signer_registration_epoch_settings
596 .into_network_configuration_for_epoch(
597 self.signed_entity_config.allowed_discriminants.clone(),
598 ),
599 };
600
601 FakeEpochService {
602 epoch_data: Some(EpochData {
603 cardano_era: self.cardano_era,
604 mithril_era: self.mithril_era,
605 epoch: self.epoch,
606 network_configuration,
607 current_signers_with_stake: self.current_signers_with_stake,
608 next_signers_with_stake: self.next_signers_with_stake,
609 current_signers,
610 next_signers,
611 total_stakes_signers,
612 total_next_stakes_signers,
613 signed_entity_config: self.signed_entity_config,
614 total_spo: self.total_spo,
615 total_stake: self.total_stake,
616 }),
617 computed_epoch_data: Some(ComputedEpochData {
618 aggregate_verification_key: protocol_multi_signer
619 .compute_aggregate_verification_key(),
620 next_aggregate_verification_key: next_protocol_multi_signer
621 .compute_aggregate_verification_key(),
622 protocol_multi_signer,
623 next_protocol_multi_signer,
624 }),
625 inform_epoch_error: false,
626 precompute_epoch_data_error: false,
627 update_next_signers_with_stake_error: false,
628 }
629 }
630}
631
632#[cfg(test)]
633impl FakeEpochService {
634 pub fn from_fixture(
635 epoch: Epoch,
636 fixture: &mithril_common::test::builder::MithrilFixture,
637 ) -> Self {
638 use mithril_common::entities::{
639 CardanoBlocksTransactionsSigningConfig, CardanoTransactionsSigningConfig,
640 };
641 use mithril_common::test::double::Dummy;
642
643 let current_epoch_settings = AggregatorEpochSettings {
644 protocol_parameters: fixture.protocol_parameters(),
645 cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig::dummy()),
646 cardano_blocks_transactions_signing_config: Some(
647 CardanoBlocksTransactionsSigningConfig::dummy(),
648 ),
649 };
650 let next_epoch_settings = AggregatorEpochSettings {
651 protocol_parameters: fixture.protocol_parameters(),
652 cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig::dummy()),
653 cardano_blocks_transactions_signing_config: Some(
654 CardanoBlocksTransactionsSigningConfig::dummy(),
655 ),
656 };
657 let signer_registration_epoch_settings = AggregatorEpochSettings {
658 protocol_parameters: fixture.protocol_parameters(),
659 cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig::dummy()),
660 cardano_blocks_transactions_signing_config: Some(
661 CardanoBlocksTransactionsSigningConfig::dummy(),
662 ),
663 };
664
665 FakeEpochServiceBuilder {
666 current_epoch_settings,
667 next_epoch_settings,
668 signer_registration_epoch_settings,
669 current_signers_with_stake: fixture.signers_with_stake(),
670 next_signers_with_stake: fixture.signers_with_stake(),
671 ..FakeEpochServiceBuilder::dummy(epoch)
672 }
673 .build()
674 }
675
676 pub fn without_data() -> Self {
679 Self {
680 epoch_data: None,
681 computed_epoch_data: None,
682 inform_epoch_error: false,
683 precompute_epoch_data_error: false,
684 update_next_signers_with_stake_error: false,
685 }
686 }
687
688 pub fn toggle_errors(
689 &mut self,
690 inform_epoch: bool,
691 precompute_epoch: bool,
692 update_next_signers_with_stake: bool,
693 ) {
694 self.inform_epoch_error = inform_epoch;
695 self.precompute_epoch_data_error = precompute_epoch;
696 self.update_next_signers_with_stake_error = update_next_signers_with_stake;
697 }
698
699 fn unwrap_data(&self) -> Result<&EpochData, EpochServiceError> {
700 self.epoch_data.as_ref().ok_or(EpochServiceError::NotYetInitialized)
701 }
702
703 fn unwrap_computed_data(&self) -> Result<&ComputedEpochData, EpochServiceError> {
704 let epoch = self.unwrap_data()?.epoch;
705
706 self.computed_epoch_data
707 .as_ref()
708 .ok_or(EpochServiceError::NotYetComputed(epoch))
709 }
710}
711
712#[cfg(test)]
713#[async_trait]
714impl EpochService for FakeEpochService {
715 async fn inform_epoch(&mut self, epoch: Epoch) -> StdResult<()> {
716 if self.inform_epoch_error {
717 anyhow::bail!("inform_epoch fake error, given epoch: {epoch}");
718 }
719 Ok(())
720 }
721
722 async fn update_next_signers_with_stake(&mut self) -> StdResult<()> {
723 if self.update_next_signers_with_stake_error {
724 anyhow::bail!("update_next_signers_with_stake fake error");
725 }
726 Ok(())
727 }
728
729 async fn precompute_epoch_data(&mut self) -> StdResult<()> {
730 if self.precompute_epoch_data_error {
731 anyhow::bail!("precompute_epoch_data fake error");
732 }
733 Ok(())
734 }
735
736 fn cardano_era(&self) -> StdResult<CardanoEra> {
737 Ok(self.unwrap_data()?.cardano_era.clone())
738 }
739
740 fn mithril_era(&self) -> StdResult<SupportedEra> {
741 Ok(self.unwrap_data()?.mithril_era)
742 }
743
744 fn epoch_of_current_data(&self) -> StdResult<Epoch> {
745 Ok(self.unwrap_data()?.epoch)
746 }
747
748 fn network_configuration(&self) -> StdResult<&MithrilNetworkConfiguration> {
749 Ok(&self.unwrap_data()?.network_configuration)
750 }
751
752 fn current_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey> {
753 Ok(&self.unwrap_computed_data()?.aggregate_verification_key)
754 }
755
756 fn next_aggregate_verification_key(&self) -> StdResult<&ProtocolAggregateVerificationKey> {
757 Ok(&self.unwrap_computed_data()?.next_aggregate_verification_key)
758 }
759
760 fn current_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>> {
761 Ok(&self.unwrap_data()?.current_signers_with_stake)
762 }
763
764 fn next_signers_with_stake(&self) -> StdResult<&Vec<SignerWithStake>> {
765 Ok(&self.unwrap_data()?.next_signers_with_stake)
766 }
767
768 fn current_signers(&self) -> StdResult<&Vec<Signer>> {
769 Ok(&self.unwrap_data()?.current_signers)
770 }
771
772 fn next_signers(&self) -> StdResult<&Vec<Signer>> {
773 Ok(&self.unwrap_data()?.next_signers)
774 }
775
776 fn total_stakes_signers(&self) -> StdResult<Stake> {
777 Ok(self.unwrap_data()?.total_stakes_signers)
778 }
779
780 fn total_next_stakes_signers(&self) -> StdResult<Stake> {
781 Ok(self.unwrap_data()?.total_next_stakes_signers)
782 }
783
784 fn protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner> {
785 Ok(&self.unwrap_computed_data()?.protocol_multi_signer)
786 }
787
788 fn next_protocol_multi_signer(&self) -> StdResult<&ProtocolMultiSigner> {
789 Ok(&self.unwrap_computed_data()?.next_protocol_multi_signer)
790 }
791
792 fn signed_entity_config(&self) -> StdResult<&SignedEntityConfig> {
793 Ok(&self.unwrap_data()?.signed_entity_config)
794 }
795
796 fn total_spo(&self) -> StdResult<Option<u32>> {
797 Ok(self.unwrap_data()?.total_spo)
798 }
799
800 fn total_stake(&self) -> StdResult<Option<u64>> {
801 Ok(self.unwrap_data()?.total_stake)
802 }
803}
804
805#[cfg(test)]
806mod tests {
807 use mockall::predicate::eq;
808
809 use mithril_cardano_node_chain::test::double::FakeChainObserver;
810 use mithril_common::entities::{
811 BlockNumber, CardanoBlocksTransactionsSigningConfig, CardanoTransactionsSigningConfig,
812 Stake, StakeDistribution, SupportedEra,
813 };
814 use mithril_common::test::{
815 builder::{MithrilFixture, MithrilFixtureBuilder, StakeDistributionGenerationMethod},
816 double::{Dummy, fake_data},
817 };
818 use mithril_protocol_config::{
819 model::{MithrilNetworkConfigurationForEpoch, SignedEntityTypeConfiguration},
820 test::double::configuration_provider::FakeMithrilNetworkConfigurationProvider,
821 };
822
823 use crate::store::{FakeEpochSettingsStorer, MockVerificationKeyStorer};
824 use crate::test::TestLogger;
825 use crate::test::double::mocks::MockStakeStore;
826
827 use super::*;
828
829 fn build_uniform_stake_distribution(
830 total_spo: TotalSPOs,
831 stake_by_spo: Stake,
832 ) -> StakeDistribution {
833 let fixture = MithrilFixtureBuilder::default()
834 .with_signers(total_spo as usize)
835 .with_stake_distribution(StakeDistributionGenerationMethod::Uniform(stake_by_spo))
836 .build();
837
838 fixture.stake_distribution()
839 }
840
841 #[derive(Debug, Clone, PartialEq)]
842 struct ExpectedEpochData {
843 cardano_era: CardanoEra,
844 mithril_era: SupportedEra,
845 epoch: Epoch,
846 protocol_parameters: ProtocolParameters,
847 next_protocol_parameters: ProtocolParameters,
848 signer_registration_protocol_parameters: ProtocolParameters,
849 current_signers_with_stake: BTreeSet<SignerWithStake>,
850 next_signers_with_stake: BTreeSet<SignerWithStake>,
851 current_signers: BTreeSet<Signer>,
852 next_signers: BTreeSet<Signer>,
853 signed_entity_config: SignedEntityConfig,
854 total_spo: Option<TotalSPOs>,
855 total_stake: Option<Stake>,
856 }
857
858 #[derive(Debug, Clone, PartialEq)]
859 struct ExpectedComputedEpochData {
860 aggregate_verification_key: ProtocolAggregateVerificationKey,
861 next_aggregate_verification_key: ProtocolAggregateVerificationKey,
862 }
863
864 impl ExpectedEpochData {
865 async fn from_service(service: &MithrilEpochService) -> StdResult<Self> {
866 Ok(Self {
867 cardano_era: service.cardano_era()?,
868 mithril_era: service.mithril_era()?,
869 epoch: service.epoch_of_current_data()?,
870 protocol_parameters: service.current_protocol_parameters()?.clone(),
871 next_protocol_parameters: service.next_protocol_parameters()?.clone(),
872 signer_registration_protocol_parameters: service
873 .signer_registration_protocol_parameters()?
874 .clone(),
875 current_signers_with_stake: service
876 .current_signers_with_stake()?
877 .clone()
878 .into_iter()
879 .collect(),
880 next_signers_with_stake: service
881 .next_signers_with_stake()?
882 .clone()
883 .into_iter()
884 .collect(),
885 current_signers: service.current_signers()?.clone().into_iter().collect(),
886 next_signers: service.next_signers()?.clone().into_iter().collect(),
887 signed_entity_config: service.signed_entity_config()?.clone(),
888 total_spo: service.total_spo()?,
889 total_stake: service.total_stake()?,
890 })
891 }
892 }
893
894 impl ExpectedComputedEpochData {
895 async fn from_service(service: &MithrilEpochService) -> StdResult<Self> {
896 Ok(Self {
897 aggregate_verification_key: service.current_aggregate_verification_key()?.clone(),
898 next_aggregate_verification_key: service.next_aggregate_verification_key()?.clone(),
899 })
900 }
901 }
902
903 struct EpochServiceBuilder {
904 allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
905 cardano_era: CardanoEra,
906 mithril_era: SupportedEra,
907 current_epoch: Epoch,
908 signers_with_stake: Vec<SignerWithStake>,
909 next_signers_with_stake: Vec<SignerWithStake>,
910 stored_current_epoch_settings: AggregatorEpochSettings,
911 stored_next_epoch_settings: AggregatorEpochSettings,
912 stored_signer_registration_epoch_settings: AggregatorEpochSettings,
913 total_spo: TotalSPOs,
914 total_stake: Stake,
915 }
916
917 impl EpochServiceBuilder {
918 fn new(epoch: Epoch, epoch_fixture: MithrilFixture) -> Self {
919 Self {
920 allowed_discriminants: BTreeSet::new(),
921 cardano_era: String::new(),
922 mithril_era: SupportedEra::dummy(),
923 current_epoch: epoch,
924 signers_with_stake: epoch_fixture.signers_with_stake(),
925 next_signers_with_stake: epoch_fixture.signers_with_stake(),
926 stored_current_epoch_settings: AggregatorEpochSettings {
927 protocol_parameters: epoch_fixture.protocol_parameters(),
928 cardano_transactions_signing_config: Some(
929 CardanoTransactionsSigningConfig::dummy(),
930 ),
931 cardano_blocks_transactions_signing_config: Some(
932 CardanoBlocksTransactionsSigningConfig::dummy(),
933 ),
934 },
935 stored_next_epoch_settings: AggregatorEpochSettings {
936 protocol_parameters: epoch_fixture.protocol_parameters(),
937 cardano_transactions_signing_config: Some(
938 CardanoTransactionsSigningConfig::dummy(),
939 ),
940 cardano_blocks_transactions_signing_config: Some(
941 CardanoBlocksTransactionsSigningConfig::dummy(),
942 ),
943 },
944 stored_signer_registration_epoch_settings: AggregatorEpochSettings {
945 protocol_parameters: epoch_fixture.protocol_parameters(),
946 cardano_transactions_signing_config: Some(
947 CardanoTransactionsSigningConfig::dummy(),
948 ),
949 cardano_blocks_transactions_signing_config: Some(
950 CardanoBlocksTransactionsSigningConfig::dummy(),
951 ),
952 },
953 total_spo: 1,
954 total_stake: 0,
955 }
956 }
957
958 async fn build(self) -> MithrilEpochService {
959 let signer_retrieval_epoch =
960 self.current_epoch.offset_to_signer_retrieval_epoch().unwrap();
961 let next_signer_retrieval_epoch =
962 self.current_epoch.offset_to_next_signer_retrieval_epoch();
963
964 let verification_key_store = {
965 let mut store = MockVerificationKeyStorer::new();
966 let signers_with_stake = self.signers_with_stake.clone();
967 store
968 .expect_get_signers()
969 .with(eq(signer_retrieval_epoch))
970 .returning(move |_| Ok(Some(signers_with_stake.clone())));
971
972 let next_signers_with_stake = self.next_signers_with_stake.clone();
973 store
974 .expect_get_signers()
975 .with(eq(next_signer_retrieval_epoch))
976 .returning(move |_| Ok(Some(next_signers_with_stake.clone())));
977 store
978 };
979
980 let chain_observer = FakeChainObserver::default();
981 chain_observer.set_current_era(self.cardano_era).await;
982 let era_checker = EraChecker::new(self.mithril_era, Epoch::default());
983
984 let stake_store = {
985 assert!(
986 self.total_stake.is_multiple_of(self.total_spo as u64),
987 "'total_stake' must be a multiple of 'total_spo' to create a uniform stake distribution"
988 );
989 let stake_per_spo = self.total_stake / self.total_spo as u64;
990
991 let stake_distribution =
992 build_uniform_stake_distribution(self.total_spo, stake_per_spo);
993
994 let mut stake_store = MockStakeStore::new();
995 stake_store
996 .expect_get_stakes()
997 .with(eq(signer_retrieval_epoch))
998 .returning(move |_| Ok(Some(stake_distribution.clone())));
999
1000 stake_store
1001 };
1002
1003 let configuration_for_aggregation = self
1004 .stored_current_epoch_settings
1005 .into_network_configuration_for_epoch(self.allowed_discriminants.clone());
1006
1007 let configuration_for_next_aggregation = self
1008 .stored_next_epoch_settings
1009 .into_network_configuration_for_epoch(self.allowed_discriminants.clone());
1010
1011 let configuration_for_registration = self
1012 .stored_signer_registration_epoch_settings
1013 .into_network_configuration_for_epoch(self.allowed_discriminants.clone());
1014
1015 let network_configuration_provider = FakeMithrilNetworkConfigurationProvider::new(
1016 configuration_for_aggregation,
1017 configuration_for_next_aggregation,
1018 configuration_for_registration,
1019 );
1020
1021 MithrilEpochService::new(
1022 EpochServiceDependencies::new(
1023 Arc::new(network_configuration_provider),
1024 Arc::new(FakeEpochSettingsStorer::new(Vec::new())),
1025 Arc::new(verification_key_store),
1026 Arc::new(chain_observer),
1027 Arc::new(era_checker),
1028 Arc::new(stake_store),
1029 ),
1030 self.allowed_discriminants,
1031 TestLogger::stdout(),
1032 )
1033 }
1034 }
1035
1036 #[tokio::test]
1037 async fn inform_epoch_get_data_from_its_dependencies() {
1038 let current_epoch_fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1039 let next_epoch_fixture = MithrilFixtureBuilder::default()
1040 .with_protocol_parameters(ProtocolParameters::new(8, 80, 0.80))
1041 .with_signers(5)
1042 .build();
1043 let signer_registration_protocol_parameters = fake_data::protocol_parameters();
1044
1045 let epoch = Epoch(5);
1046 let builder = EpochServiceBuilder {
1047 next_signers_with_stake: next_epoch_fixture.signers_with_stake(),
1048 stored_next_epoch_settings: AggregatorEpochSettings {
1049 protocol_parameters: next_epoch_fixture.protocol_parameters(),
1050 ..AggregatorEpochSettings::dummy()
1051 },
1052 stored_signer_registration_epoch_settings: AggregatorEpochSettings {
1053 protocol_parameters: signer_registration_protocol_parameters.clone(),
1054 ..AggregatorEpochSettings::dummy()
1055 },
1056 allowed_discriminants: SignedEntityConfig::dummy().allowed_discriminants,
1057 cardano_era: "CardanoEra".to_string(),
1058 mithril_era: SupportedEra::eras()[0],
1059 total_spo: 10,
1060 total_stake: 20_000_000,
1061 ..EpochServiceBuilder::new(epoch, current_epoch_fixture.clone())
1062 };
1063
1064 let mut service = builder.build().await;
1065
1066 service
1067 .inform_epoch(epoch)
1068 .await
1069 .expect("inform_epoch should not fail");
1070
1071 let data = ExpectedEpochData::from_service(&service)
1072 .await
1073 .expect("extracting data from service should not fail");
1074
1075 assert_eq!(
1076 data.clone(),
1077 ExpectedEpochData {
1078 cardano_era: "CardanoEra".to_string(),
1079 mithril_era: SupportedEra::eras()[0],
1080 epoch,
1081 protocol_parameters: current_epoch_fixture.protocol_parameters(),
1082 next_protocol_parameters: next_epoch_fixture.protocol_parameters(),
1083 signer_registration_protocol_parameters,
1084 current_signers_with_stake: current_epoch_fixture
1085 .signers_with_stake()
1086 .into_iter()
1087 .collect(),
1088 next_signers_with_stake: next_epoch_fixture
1089 .signers_with_stake()
1090 .into_iter()
1091 .collect(),
1092 current_signers: current_epoch_fixture.signers().into_iter().collect(),
1093 next_signers: next_epoch_fixture.signers().into_iter().collect(),
1094 signed_entity_config: SignedEntityConfig::dummy(),
1095 total_spo: Some(10),
1096 total_stake: Some(20_000_000),
1097 }
1098 );
1099 }
1100
1101 #[tokio::test]
1102 async fn inform_epoch_get_signed_entity_config_from_its_dependencies_and_store() {
1103 let epoch = Epoch(5);
1104
1105 let cardano_transactions_signing_config = Some(CardanoTransactionsSigningConfig {
1106 security_parameter: BlockNumber(29),
1107 step: BlockNumber(986),
1108 });
1109 let cardano_blocks_transactions_signing_config =
1110 Some(CardanoBlocksTransactionsSigningConfig {
1111 security_parameter: BlockNumber(45),
1112 step: BlockNumber(999),
1113 });
1114
1115 let allowed_discriminants = BTreeSet::from([
1116 SignedEntityTypeDiscriminants::CardanoTransactions,
1117 SignedEntityTypeDiscriminants::CardanoBlocksTransactions,
1118 SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
1119 ]);
1120
1121 let mut service = EpochServiceBuilder {
1122 allowed_discriminants: allowed_discriminants.clone(),
1123 stored_current_epoch_settings: AggregatorEpochSettings {
1124 cardano_transactions_signing_config: cardano_transactions_signing_config.clone(),
1125 cardano_blocks_transactions_signing_config:
1126 cardano_blocks_transactions_signing_config.clone(),
1127 ..AggregatorEpochSettings::dummy()
1128 },
1129 ..EpochServiceBuilder::new(epoch, MithrilFixtureBuilder::default().build())
1130 }
1131 .build()
1132 .await;
1133
1134 service
1135 .inform_epoch(epoch)
1136 .await
1137 .expect("inform_epoch should not fail");
1138
1139 let signed_entity_config = service
1140 .signed_entity_config()
1141 .expect("extracting data from service should not fail");
1142
1143 assert_eq!(
1144 signed_entity_config.clone(),
1145 SignedEntityConfig {
1146 allowed_discriminants,
1147 cardano_transactions_signing_config,
1148 cardano_blocks_transactions_signing_config,
1149 }
1150 );
1151 }
1152
1153 #[tokio::test]
1154 async fn inform_epoch_compute_allowed_discriminants_from_intersection_of_aggregation_network_config_and_configured_discriminants()
1155 {
1156 let epoch = Epoch(5);
1157 let allowed_discriminants = BTreeSet::from([
1158 SignedEntityTypeDiscriminants::CardanoStakeDistribution,
1159 SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
1160 SignedEntityTypeDiscriminants::CardanoTransactions,
1161 ]);
1162 let enabled_discriminants = BTreeSet::from([
1163 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
1164 SignedEntityTypeDiscriminants::CardanoStakeDistribution,
1165 SignedEntityTypeDiscriminants::CardanoTransactions,
1166 ]);
1167
1168 let mut service = MithrilEpochService {
1169 mithril_network_configuration_provider: Arc::new(
1170 FakeMithrilNetworkConfigurationProvider::new(
1171 MithrilNetworkConfigurationForEpoch {
1172 enabled_signed_entity_types: enabled_discriminants,
1173 ..Dummy::dummy()
1174 },
1175 MithrilNetworkConfigurationForEpoch::dummy(),
1176 MithrilNetworkConfigurationForEpoch::dummy(),
1177 ),
1178 ),
1179 ..EpochServiceBuilder {
1180 allowed_discriminants: allowed_discriminants.clone(),
1181 ..EpochServiceBuilder::new(epoch, MithrilFixtureBuilder::default().build())
1182 }
1183 .build()
1184 .await
1185 };
1186
1187 service
1188 .inform_epoch(epoch)
1189 .await
1190 .expect("inform_epoch should not fail");
1191
1192 let signed_entity_config = service
1193 .signed_entity_config()
1194 .expect("extracting data from service should not fail");
1195
1196 assert_eq!(
1197 BTreeSet::from([
1198 SignedEntityTypeDiscriminants::CardanoTransactions,
1199 SignedEntityTypeDiscriminants::CardanoStakeDistribution,
1200 ]),
1201 signed_entity_config.allowed_discriminants
1202 );
1203 }
1204
1205 #[tokio::test]
1206 async fn compute_data_with_data_from_inform_epoch() {
1207 let current_epoch_fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1208 let next_epoch_fixture = MithrilFixtureBuilder::default()
1209 .with_protocol_parameters(ProtocolParameters::new(8, 80, 0.80))
1210 .with_signers(5)
1211 .build();
1212
1213 let epoch = Epoch(5);
1214 let mut service =
1215 EpochServiceBuilder {
1216 stored_next_epoch_settings: AggregatorEpochSettings {
1217 protocol_parameters: next_epoch_fixture.protocol_parameters(),
1218 cardano_transactions_signing_config: Some(
1219 CardanoTransactionsSigningConfig::dummy(),
1220 ),
1221 cardano_blocks_transactions_signing_config: Some(
1222 CardanoBlocksTransactionsSigningConfig::dummy(),
1223 ),
1224 },
1225 next_signers_with_stake: next_epoch_fixture.signers_with_stake().clone(),
1226 ..EpochServiceBuilder::new(epoch, current_epoch_fixture.clone())
1227 }
1228 .build()
1229 .await;
1230
1231 service
1232 .inform_epoch(epoch)
1233 .await
1234 .expect("inform_epoch should not fail");
1235 service
1236 .precompute_epoch_data()
1237 .await
1238 .expect("precompute_epoch_data should not fail");
1239
1240 let data = ExpectedComputedEpochData::from_service(&service)
1241 .await
1242 .expect("extracting data from service should not fail");
1243
1244 assert_eq!(
1245 data,
1246 ExpectedComputedEpochData {
1247 aggregate_verification_key: current_epoch_fixture
1248 .compute_aggregate_verification_key(),
1249 next_aggregate_verification_key: next_epoch_fixture
1250 .compute_aggregate_verification_key(),
1251 }
1252 );
1253 }
1254
1255 #[tokio::test]
1256 async fn inform_epoch_reset_computed_data() {
1257 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1258 let avk = fixture.compute_aggregate_verification_key();
1259 let epoch = Epoch(4);
1260 let mut service = EpochServiceBuilder::new(epoch, fixture.clone()).build().await;
1261 let signer_builder = SignerBuilder::new(
1262 &fixture.signers_with_stake(),
1263 &fixture.protocol_parameters(),
1264 )
1265 .unwrap();
1266 service.computed_epoch_data = Some(ComputedEpochData {
1267 aggregate_verification_key: avk.clone(),
1268 next_aggregate_verification_key: avk.clone(),
1269 protocol_multi_signer: signer_builder.build_multi_signer(),
1270 next_protocol_multi_signer: signer_builder.build_multi_signer(),
1271 });
1272
1273 service
1274 .inform_epoch(epoch)
1275 .await
1276 .expect("inform_epoch should not fail");
1277
1278 assert!(service.computed_epoch_data.is_none());
1279 }
1280
1281 #[tokio::test]
1282 async fn update_next_signers_with_stake_succeeds() {
1283 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1284 let next_fixture = MithrilFixtureBuilder::default().with_signers(5).build();
1285 let next_avk = next_fixture.compute_aggregate_verification_key();
1286 let epoch = Epoch(4);
1287 let mut service = EpochServiceBuilder {
1288 next_signers_with_stake: next_fixture.signers_with_stake().clone(),
1289 ..EpochServiceBuilder::new(epoch, fixture.clone())
1290 }
1291 .build()
1292 .await;
1293 service
1294 .inform_epoch(epoch)
1295 .await
1296 .expect("inform_epoch should not fail");
1297 service.epoch_data = Some(EpochData {
1298 next_signers_with_stake: vec![],
1299 ..service.epoch_data.unwrap()
1300 });
1301 service.computed_epoch_data = None;
1302
1303 service
1304 .update_next_signers_with_stake()
1305 .await
1306 .expect("update_next_signers_with_stake should not fail");
1307
1308 let expected_next_signers_with_stake = next_fixture.signers_with_stake();
1309 assert_eq!(
1310 expected_next_signers_with_stake,
1311 service.epoch_data.unwrap().next_signers_with_stake
1312 );
1313
1314 assert_eq!(
1315 next_avk,
1316 service.computed_epoch_data.unwrap().next_aggregate_verification_key
1317 );
1318 }
1319
1320 #[tokio::test]
1321 async fn inform_epoch_insert_registration_epoch_settings_in_the_store() {
1322 let expected_epoch_settings = AggregatorEpochSettings {
1323 protocol_parameters: ProtocolParameters::new(6, 89, 0.124),
1324 cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig {
1325 security_parameter: BlockNumber(1),
1326 step: BlockNumber(11),
1327 }),
1328 cardano_blocks_transactions_signing_config: Some(
1329 CardanoBlocksTransactionsSigningConfig {
1330 security_parameter: BlockNumber(111),
1331 step: BlockNumber(1111),
1332 },
1333 ),
1334 };
1335
1336 let mut aggregation_configuration = MithrilNetworkConfigurationForEpoch::dummy();
1337 aggregation_configuration
1338 .signed_entity_types_config
1339 .cardano_transactions = Some(CardanoTransactionsSigningConfig {
1340 security_parameter: BlockNumber(2),
1341 step: BlockNumber(22),
1342 });
1343
1344 let mut next_aggregation_configuration = MithrilNetworkConfigurationForEpoch::dummy();
1345 next_aggregation_configuration
1346 .signed_entity_types_config
1347 .cardano_transactions = Some(CardanoTransactionsSigningConfig {
1348 security_parameter: BlockNumber(3),
1349 step: BlockNumber(33),
1350 });
1351
1352 let epoch = Epoch(4);
1353 let mut service = MithrilEpochService {
1354 mithril_network_configuration_provider: Arc::new(
1355 FakeMithrilNetworkConfigurationProvider::new(
1356 aggregation_configuration,
1357 next_aggregation_configuration,
1358 MithrilNetworkConfigurationForEpoch {
1359 protocol_parameters: expected_epoch_settings.protocol_parameters.clone(),
1360 signed_entity_types_config: SignedEntityTypeConfiguration {
1361 cardano_transactions: expected_epoch_settings
1362 .cardano_transactions_signing_config
1363 .clone(),
1364 cardano_blocks_transactions: expected_epoch_settings
1365 .cardano_blocks_transactions_signing_config
1366 .clone(),
1367 },
1368 ..Dummy::dummy()
1369 },
1370 ),
1371 ),
1372 ..EpochServiceBuilder::new(epoch, MithrilFixtureBuilder::default().build())
1373 .build()
1374 .await
1375 };
1376
1377 service
1378 .inform_epoch(epoch)
1379 .await
1380 .expect("inform_epoch should not fail");
1381
1382 let inserted_epoch_settings = service
1383 .epoch_settings_storer
1384 .get_epoch_settings(epoch.offset_to_recording_epoch())
1385 .await
1386 .unwrap_or_else(|_| {
1387 panic!(
1388 "epoch settings should have been inserted for epoch {}",
1389 epoch.offset_to_recording_epoch()
1390 )
1391 })
1392 .unwrap();
1393
1394 assert_eq!(inserted_epoch_settings, expected_epoch_settings);
1395 }
1396
1397 #[tokio::test]
1398 async fn cant_get_data_if_inform_epoch_has_not_been_called() {
1399 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1400 let service = EpochServiceBuilder::new(Epoch(4), fixture.clone()).build().await;
1401
1402 for (name, res) in [
1403 ("cardano_era", service.cardano_era().err()),
1404 ("mithril_era", service.mithril_era().err()),
1405 (
1406 "epoch_of_current_data",
1407 service.epoch_of_current_data().err(),
1408 ),
1409 (
1410 "current_protocol_parameters",
1411 service.current_protocol_parameters().err(),
1412 ),
1413 (
1414 "next_protocol_parameters",
1415 service.next_protocol_parameters().err(),
1416 ),
1417 (
1418 "signer_registration_protocol_parameters",
1419 service.signer_registration_protocol_parameters().err(),
1420 ),
1421 (
1422 "current_signers_with_stake",
1423 service.current_signers_with_stake().err(),
1424 ),
1425 (
1426 "next_signers_with_stake",
1427 service.next_signers_with_stake().err(),
1428 ),
1429 ("current_signers", service.current_signers().err()),
1430 ("next_signers", service.next_signers().err()),
1431 (
1432 "current_aggregate_verification_key",
1433 service.current_aggregate_verification_key().err(),
1434 ),
1435 (
1436 "next_aggregate_verification_key",
1437 service.next_aggregate_verification_key().err(),
1438 ),
1439 (
1440 "protocol_multi_signer",
1441 service.protocol_multi_signer().err(),
1442 ),
1443 (
1444 "next_protocol_multi_signer",
1445 service.next_protocol_multi_signer().err(),
1446 ),
1447 ("signed_entity_config", service.signed_entity_config().err()),
1448 ("total_spo", service.total_spo().err()),
1449 ("total_stake", service.total_stake().err()),
1450 ] {
1451 let error =
1452 res.unwrap_or_else(|| panic!("getting {name} should have returned an error"));
1453
1454 match error.downcast_ref::<EpochServiceError>() {
1455 Some(EpochServiceError::NotYetInitialized) => (),
1456 _ => panic!("Expected an NotYetInitialized error, got: {error:?}"),
1457 }
1458 }
1459 }
1460
1461 #[tokio::test]
1462 async fn can_only_get_non_computed_data_if_inform_epoch_has_been_called_but_not_precompute_epoch_data()
1463 {
1464 let fixture = MithrilFixtureBuilder::default().with_signers(3).build();
1465 let mut service = EpochServiceBuilder::new(Epoch(4), fixture.clone()).build().await;
1466 service.inform_epoch(Epoch(4)).await.unwrap();
1467
1468 assert!(service.cardano_era().is_ok());
1469 assert!(service.mithril_era().is_ok());
1470 assert!(service.epoch_of_current_data().is_ok());
1471 assert!(service.current_protocol_parameters().is_ok());
1472 assert!(service.next_protocol_parameters().is_ok());
1473 assert!(service.signer_registration_protocol_parameters().is_ok());
1474 assert!(service.current_signers_with_stake().is_ok());
1475 assert!(service.next_signers_with_stake().is_ok());
1476 assert!(service.current_signers().is_ok());
1477 assert!(service.next_signers().is_ok());
1478 assert!(service.signed_entity_config().is_ok());
1479 assert!(service.total_spo().is_ok());
1480 assert!(service.total_stake().is_ok());
1481
1482 for (name, res) in [
1483 (
1484 "current_aggregate_verification_key",
1485 service.current_aggregate_verification_key().err(),
1486 ),
1487 (
1488 "next_aggregate_verification_key",
1489 service.next_aggregate_verification_key().err(),
1490 ),
1491 (
1492 "protocol_multi_signer",
1493 service.protocol_multi_signer().err(),
1494 ),
1495 (
1496 "next_protocol_multi_signer",
1497 service.next_protocol_multi_signer().err(),
1498 ),
1499 ] {
1500 let error =
1501 res.unwrap_or_else(|| panic!("getting {name} should have returned an error"));
1502
1503 match error.downcast_ref::<EpochServiceError>() {
1504 Some(EpochServiceError::NotYetComputed(Epoch(4))) => (),
1505 _ => panic!("Expected an NotYetComputed error for epoch 4, got: {error:?}"),
1506 }
1507 }
1508 }
1509}