From 567b1dfeb02976172ddb530c14b0c95df80214a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Turmel?= Date: Tue, 7 Oct 2025 18:06:15 +0200 Subject: [PATCH 01/15] feature(aggregator): WIP: add protocol-config dependency and start a local configuration implementation --- Cargo.lock | 1 + mithril-aggregator/Cargo.toml | 1 + .../enablers/mithril_network_configuration.rs | 32 +++++++++++++++++++ .../builder/enablers/mod.rs | 1 + 4 files changed, 35 insertions(+) create mode 100644 mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs diff --git a/Cargo.lock b/Cargo.lock index 1976d4de071..ec8948b76e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3621,6 +3621,7 @@ dependencies = [ "mithril-era", "mithril-metric", "mithril-persistence", + "mithril-protocol-config", "mithril-resource-pool", "mithril-signed-entity-lock", "mithril-signed-entity-preloader", diff --git a/mithril-aggregator/Cargo.toml b/mithril-aggregator/Cargo.toml index 8390d0136fb..89a470ad16b 100644 --- a/mithril-aggregator/Cargo.toml +++ b/mithril-aggregator/Cargo.toml @@ -36,6 +36,7 @@ mithril-doc = { path = "../internal/mithril-doc" } mithril-era = { path = "../internal/mithril-era" } mithril-metric = { path = "../internal/mithril-metric" } mithril-persistence = { path = "../internal/mithril-persistence" } +mithril-protocol-config = { path = "../internal/mithril-protocol-config" } mithril-resource-pool = { path = "../internal/mithril-resource-pool" } mithril-signed-entity-lock = { path = "../internal/signed-entity/mithril-signed-entity-lock" } mithril-signed-entity-preloader = { path = "../internal/signed-entity/mithril-signed-entity-preloader" } diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs new file mode 100644 index 00000000000..09506f27098 --- /dev/null +++ b/mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs @@ -0,0 +1,32 @@ +use std::collections::BTreeSet; + +use crate::entities::AggregatorEpochSettings; +use async_trait::async_trait; +use mithril_common::{StdResult, entities::SignedEntityTypeDiscriminants, test::double::Dummy}; +use mithril_protocol_config::{ + interface::MithrilNetworkConfigurationProvider, model::MithrilNetworkConfiguration, +}; + +pub struct LocalMithrilNetworkConfigurationProvider { + epoch_settings: AggregatorEpochSettings, + allowed_discriminants: BTreeSet, +} + +impl LocalMithrilNetworkConfigurationProvider { + pub fn new( + epoch_settings: AggregatorEpochSettings, + allowed_discriminants: BTreeSet, + ) -> Self { + Self { + epoch_settings, + allowed_discriminants, + } + } +} + +#[async_trait] +impl MithrilNetworkConfigurationProvider for LocalMithrilNetworkConfigurationProvider { + async fn get(&self) -> StdResult { + Ok(MithrilNetworkConfiguration::dummy()) + } +} diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/mod.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/mod.rs index ce6ea560874..542bd3a817b 100644 --- a/mithril-aggregator/src/dependency_injection/builder/enablers/mod.rs +++ b/mithril-aggregator/src/dependency_injection/builder/enablers/mod.rs @@ -3,4 +3,5 @@ mod cardano_node; mod epoch; mod misc; +mod mithril_network_configuration; mod ticker; From d40ef61431713b66067cd272831388b6e98e44f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Turmel?= Date: Fri, 10 Oct 2025 17:03:18 +0200 Subject: [PATCH 02/15] WIP: implement local configuration for mithril network configuration and adapt dependencies --- .../enablers/mithril_network_configuration.rs | 66 ++++++++++++++++--- .../src/dependency_injection/builder/mod.rs | 9 +++ .../dependency_injection/containers/serve.rs | 4 ++ 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs index 09506f27098..38ca150fb67 100644 --- a/mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs +++ b/mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs @@ -1,10 +1,19 @@ -use std::collections::BTreeSet; +use std::{collections::BTreeSet, sync::Arc}; -use crate::entities::AggregatorEpochSettings; +use crate::{ + ConfigurationSource, + dependency_injection::{DependenciesBuilder, Result}, + entities::AggregatorEpochSettings, + get_dependency, +}; use async_trait::async_trait; -use mithril_common::{StdResult, entities::SignedEntityTypeDiscriminants, test::double::Dummy}; +use mithril_common::{ + StdResult, + entities::{Epoch, SignedEntityTypeDiscriminants}, +}; use mithril_protocol_config::{ - interface::MithrilNetworkConfigurationProvider, model::MithrilNetworkConfiguration, + interface::MithrilNetworkConfigurationProvider, + model::{MithrilNetworkConfiguration, SignedEntityTypeConfiguration}, }; pub struct LocalMithrilNetworkConfigurationProvider { @@ -13,10 +22,12 @@ pub struct LocalMithrilNetworkConfigurationProvider { } impl LocalMithrilNetworkConfigurationProvider { - pub fn new( - epoch_settings: AggregatorEpochSettings, - allowed_discriminants: BTreeSet, - ) -> Self { + pub fn new(configuration: Arc) -> Self { + let epoch_settings = configuration.get_epoch_settings_configuration(); + let allowed_discriminants = configuration + .compute_allowed_signed_entity_types_discriminants() + .expect("Failed to compute allowed signed entity types discriminants"); + Self { epoch_settings, allowed_discriminants, @@ -26,7 +37,42 @@ impl LocalMithrilNetworkConfigurationProvider { #[async_trait] impl MithrilNetworkConfigurationProvider for LocalMithrilNetworkConfigurationProvider { - async fn get(&self) -> StdResult { - Ok(MithrilNetworkConfiguration::dummy()) + async fn get_network_configuration(&self) -> StdResult { + let epoch = Epoch(42); // TODO implement proper epoch retrieval + let signer_registration_protocol_parameters = + self.epoch_settings.protocol_parameters.clone(); + let available_signed_entity_types = self.allowed_discriminants.clone(); + let signed_entity_types_config = SignedEntityTypeConfiguration { + cardano_transactions: Some( + self.epoch_settings.cardano_transactions_signing_config.clone(), + ), + }; + + let config = MithrilNetworkConfiguration { + epoch, //TODO implement + signer_registration_protocol_parameters, + available_signed_entity_types, + signed_entity_types_config, + }; + Ok(config) + } +} + +impl DependenciesBuilder { + async fn build_mithril_network_configuration_provider( + &mut self, + ) -> Result> { + let network_configuration_provider = Arc::new( + LocalMithrilNetworkConfigurationProvider::new(self.configuration.clone()), + ); + + Ok(network_configuration_provider) + } + + /// [MithrilNetworkConfigurationProvider][mithril_protocol_config::interface::MithrilNetworkConfigurationProvider] service + pub async fn get_mithril_network_configuration_provider( + &mut self, + ) -> Result> { + get_dependency!(self.mithril_network_configuration_provider) } } diff --git a/mithril-aggregator/src/dependency_injection/builder/mod.rs b/mithril-aggregator/src/dependency_injection/builder/mod.rs index 5e84c8a541a..d27a350a9a6 100644 --- a/mithril-aggregator/src/dependency_injection/builder/mod.rs +++ b/mithril-aggregator/src/dependency_injection/builder/mod.rs @@ -36,6 +36,7 @@ use mithril_persistence::{ database::repository::CardanoTransactionRepository, sqlite::{SqliteConnection, SqliteConnectionPool}, }; +use mithril_protocol_config::interface::MithrilNetworkConfigurationProvider; use mithril_signed_entity_lock::SignedEntityTypeLock; use mithril_ticker::TickerService; @@ -254,6 +255,10 @@ pub struct DependenciesBuilder { /// Epoch service. pub epoch_service: Option, + /// Mithril network configuration provider + pub mithril_network_configuration_provider: + Option>, + /// Signed Entity storer pub signed_entity_storer: Option>, @@ -339,6 +344,7 @@ impl DependenciesBuilder { signed_entity_service: None, certifier_service: None, epoch_service: None, + mithril_network_configuration_provider: None, signed_entity_storer: None, message_service: None, prover_service: None, @@ -395,6 +401,9 @@ impl DependenciesBuilder { signed_entity_service: self.get_signed_entity_service().await?, certifier_service: self.get_certifier_service().await?, epoch_service: self.get_epoch_service().await?, + mithril_network_configuration_provider: self + .get_mithril_network_configuration_provider() + .await?, ticker_service: self.get_ticker_service().await?, signed_entity_storer: self.get_signed_entity_storer().await?, signer_getter: self.get_signer_store().await?, diff --git a/mithril-aggregator/src/dependency_injection/containers/serve.rs b/mithril-aggregator/src/dependency_injection/containers/serve.rs index b37d783aad5..56a00376380 100644 --- a/mithril-aggregator/src/dependency_injection/containers/serve.rs +++ b/mithril-aggregator/src/dependency_injection/containers/serve.rs @@ -1,3 +1,4 @@ +use mithril_protocol_config::interface::MithrilNetworkConfigurationProvider; use slog::Logger; use std::sync::Arc; use tokio::sync::RwLock; @@ -100,6 +101,9 @@ pub struct ServeCommandDependenciesContainer { /// Epoch service pub(crate) epoch_service: EpochServiceWrapper, + /// Mithril network configuration provider + pub(crate) mithril_network_configuration_provider: Arc, + /// Ticker Service pub(crate) ticker_service: Arc, From bf12ea9f81d63f47a023a4448efb2c7c1d90a221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Turmel?= Date: Wed, 15 Oct 2025 11:37:35 +0200 Subject: [PATCH 03/15] WIP: using mithril network configuration provider in EPoch Service --- .../builder/enablers/epoch.rs | 4 ++- .../src/services/epoch_service.rs | 32 +++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs index edc0cf5eed8..fe0fe56dd4a 100644 --- a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs +++ b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs @@ -11,13 +11,15 @@ impl DependenciesBuilder { let chain_observer = self.get_chain_observer().await?; let era_checker = self.get_era_checker().await?; let stake_store = self.get_stake_store().await?; + //remove conf usage for epoch_settings and allowed_discriminants and send mithril_network_configuration instread let epoch_settings = self.configuration.get_epoch_settings_configuration(); let allowed_discriminants = self .configuration .compute_allowed_signed_entity_types_discriminants()?; + let network_configuration_provider = self.get_mithril_network_configuration_provider(); let epoch_service = Arc::new(RwLock::new(MithrilEpochService::new( - epoch_settings, + network_configuration_provider, EpochServiceDependencies::new( epoch_settings_storer, verification_key_store, diff --git a/mithril-aggregator/src/services/epoch_service.rs b/mithril-aggregator/src/services/epoch_service.rs index 9aa85a5d863..55f2f1038ea 100644 --- a/mithril-aggregator/src/services/epoch_service.rs +++ b/mithril-aggregator/src/services/epoch_service.rs @@ -1,5 +1,6 @@ use anyhow::{Context, anyhow}; use async_trait::async_trait; +use mithril_protocol_config::interface::MithrilNetworkConfigurationProvider; use slog::{Logger, debug}; use std::collections::BTreeSet; use std::sync::Arc; @@ -180,7 +181,8 @@ impl EpochServiceDependencies { /// Implementation of the [epoch service][EpochService]. pub struct MithrilEpochService { /// Epoch settings that will be inserted when inform_epoch is called - future_epoch_settings: AggregatorEpochSettings, + //future_epoch_settings: AggregatorEpochSettings, + mithril_network_configuration_provider: Arc, epoch_data: Option, computed_epoch_data: Option, epoch_settings_storer: Arc, @@ -195,13 +197,15 @@ pub struct MithrilEpochService { impl MithrilEpochService { /// Create a new service instance pub fn new( - future_epoch_settings: AggregatorEpochSettings, + //future_epoch_settings: AggregatorEpochSettings, + mithril_network_configuration_provider: Arc, dependencies: EpochServiceDependencies, allowed_discriminants: BTreeSet, logger: Logger, ) -> Self { Self { - future_epoch_settings, + //future_epoch_settings, + mithril_network_configuration_provider, epoch_data: None, computed_epoch_data: None, epoch_settings_storer: dependencies.epoch_settings_storer, @@ -266,15 +270,31 @@ impl MithrilEpochService { async fn insert_future_epoch_settings(&self, actual_epoch: Epoch) -> StdResult<()> { let recording_epoch = actual_epoch.offset_to_epoch_settings_recording_epoch(); - debug!( + /* debug!( self.logger, "Inserting epoch settings in epoch {recording_epoch}"; "epoch_settings" => ?self.future_epoch_settings - ); + ); */ + + let network_configuration = self + .mithril_network_configuration_provider + .get_network_configuration() + .await?; + let aggregator_epoch_settings = AggregatorEpochSettings { + protocol_parameters: network_configuration + .signer_registration_protocol_parameters + .clone(), + cardano_transactions_signing_config: network_configuration + .signed_entity_types_config + .cardano_transactions + .clone() + .unwrap(), + }; self.epoch_settings_storer .save_epoch_settings( recording_epoch, - self.future_epoch_settings.clone(), + /* self.future_epoch_settings.clone(), */ + aggregator_epoch_settings ) .await .with_context(|| format!("Epoch service failed to insert future_epoch_settings to epoch {recording_epoch}")) From cfc99d79eeb8063fcbc58efe51c3dc73d7c21533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Turmel?= Date: Fri, 31 Oct 2025 19:10:58 +0100 Subject: [PATCH 04/15] feature(aggregator): implement local network configuration provider --- .../builder/enablers/epoch.rs | 54 +++- .../enablers/mithril_network_configuration.rs | 78 ------ .../builder/enablers/mod.rs | 1 - mithril-aggregator/src/services/mod.rs | 2 + .../network_configuration_provider.rs | 248 ++++++++++++++++++ 5 files changed, 299 insertions(+), 84 deletions(-) delete mode 100644 mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs create mode 100644 mithril-aggregator/src/services/network_configuration_provider.rs diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs index fe0fe56dd4a..46ede6e3cb1 100644 --- a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs +++ b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs @@ -1,9 +1,15 @@ +use mithril_protocol_config::http_client::http_impl::HttpMithrilNetworkConfigurationProvider; use std::sync::Arc; use tokio::sync::RwLock; +use mithril_protocol_config::interface::MithrilNetworkConfigurationProvider; + use crate::dependency_injection::{DependenciesBuilder, EpochServiceWrapper, Result}; use crate::get_dependency; -use crate::services::{EpochServiceDependencies, MithrilEpochService}; +use crate::services::{ + EpochServiceDependencies, LocalMithrilNetworkConfigurationProvider, MithrilEpochService, +}; + impl DependenciesBuilder { async fn build_epoch_service(&mut self) -> Result { let verification_key_store = self.get_verification_key_store().await?; @@ -11,15 +17,12 @@ impl DependenciesBuilder { let chain_observer = self.get_chain_observer().await?; let era_checker = self.get_era_checker().await?; let stake_store = self.get_stake_store().await?; - //remove conf usage for epoch_settings and allowed_discriminants and send mithril_network_configuration instread - let epoch_settings = self.configuration.get_epoch_settings_configuration(); let allowed_discriminants = self .configuration .compute_allowed_signed_entity_types_discriminants()?; - let network_configuration_provider = self.get_mithril_network_configuration_provider(); let epoch_service = Arc::new(RwLock::new(MithrilEpochService::new( - network_configuration_provider, + self.get_mithril_network_configuration_provider().await?, EpochServiceDependencies::new( epoch_settings_storer, verification_key_store, @@ -38,4 +41,45 @@ impl DependenciesBuilder { pub async fn get_epoch_service(&mut self) -> Result { get_dependency!(self.epoch_service) } + + async fn build_mithril_network_configuration_provider( + &mut self, + ) -> Result> { + let network_configuration_provider: Arc = + if self.configuration.is_follower_aggregator() { + Arc::new(HttpMithrilNetworkConfigurationProvider::new( + self.get_leader_aggregator_client().await?, + )) + } else { + Arc::new(LocalMithrilNetworkConfigurationProvider::new( + self.configuration.get_epoch_settings_configuration(), + self.configuration + .compute_allowed_signed_entity_types_discriminants()?, + self.get_epoch_settings_store().await?, + )) + }; + + Ok(network_configuration_provider) + } + + /// [MithrilNetworkConfigurationProvider][mithril_protocol_config::interface::MithrilNetworkConfigurationProvider] service + pub async fn get_mithril_network_configuration_provider( + &mut self, + ) -> Result> { + get_dependency!(self.mithril_network_configuration_provider) + } +} + +// Todo remove after rebase with shared aggregator client: +#[async_trait::async_trait] +impl mithril_protocol_config::http_client::http_impl::ProtocolConfigurationRetrieverFromAggregator + for crate::services::AggregatorHTTPClient +{ + /// Retrieves protocol configuration for a given epoch from the aggregator + async fn retrieve_protocol_configuration( + &self, + _epoch: mithril_common::entities::Epoch, + ) -> mithril_common::StdResult { + unimplemented!() + } } diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs deleted file mode 100644 index 38ca150fb67..00000000000 --- a/mithril-aggregator/src/dependency_injection/builder/enablers/mithril_network_configuration.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::{collections::BTreeSet, sync::Arc}; - -use crate::{ - ConfigurationSource, - dependency_injection::{DependenciesBuilder, Result}, - entities::AggregatorEpochSettings, - get_dependency, -}; -use async_trait::async_trait; -use mithril_common::{ - StdResult, - entities::{Epoch, SignedEntityTypeDiscriminants}, -}; -use mithril_protocol_config::{ - interface::MithrilNetworkConfigurationProvider, - model::{MithrilNetworkConfiguration, SignedEntityTypeConfiguration}, -}; - -pub struct LocalMithrilNetworkConfigurationProvider { - epoch_settings: AggregatorEpochSettings, - allowed_discriminants: BTreeSet, -} - -impl LocalMithrilNetworkConfigurationProvider { - pub fn new(configuration: Arc) -> Self { - let epoch_settings = configuration.get_epoch_settings_configuration(); - let allowed_discriminants = configuration - .compute_allowed_signed_entity_types_discriminants() - .expect("Failed to compute allowed signed entity types discriminants"); - - Self { - epoch_settings, - allowed_discriminants, - } - } -} - -#[async_trait] -impl MithrilNetworkConfigurationProvider for LocalMithrilNetworkConfigurationProvider { - async fn get_network_configuration(&self) -> StdResult { - let epoch = Epoch(42); // TODO implement proper epoch retrieval - let signer_registration_protocol_parameters = - self.epoch_settings.protocol_parameters.clone(); - let available_signed_entity_types = self.allowed_discriminants.clone(); - let signed_entity_types_config = SignedEntityTypeConfiguration { - cardano_transactions: Some( - self.epoch_settings.cardano_transactions_signing_config.clone(), - ), - }; - - let config = MithrilNetworkConfiguration { - epoch, //TODO implement - signer_registration_protocol_parameters, - available_signed_entity_types, - signed_entity_types_config, - }; - Ok(config) - } -} - -impl DependenciesBuilder { - async fn build_mithril_network_configuration_provider( - &mut self, - ) -> Result> { - let network_configuration_provider = Arc::new( - LocalMithrilNetworkConfigurationProvider::new(self.configuration.clone()), - ); - - Ok(network_configuration_provider) - } - - /// [MithrilNetworkConfigurationProvider][mithril_protocol_config::interface::MithrilNetworkConfigurationProvider] service - pub async fn get_mithril_network_configuration_provider( - &mut self, - ) -> Result> { - get_dependency!(self.mithril_network_configuration_provider) - } -} diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/mod.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/mod.rs index 542bd3a817b..ce6ea560874 100644 --- a/mithril-aggregator/src/dependency_injection/builder/enablers/mod.rs +++ b/mithril-aggregator/src/dependency_injection/builder/enablers/mod.rs @@ -3,5 +3,4 @@ mod cardano_node; mod epoch; mod misc; -mod mithril_network_configuration; mod ticker; diff --git a/mithril-aggregator/src/services/mod.rs b/mithril-aggregator/src/services/mod.rs index 26c053b63e4..9ae55f26bc7 100644 --- a/mithril-aggregator/src/services/mod.rs +++ b/mithril-aggregator/src/services/mod.rs @@ -15,6 +15,7 @@ mod certificate_chain_synchronizer; mod certifier; mod epoch_service; mod message; +mod network_configuration_provider; mod prover; mod signable_builder; mod signature_consumer; @@ -31,6 +32,7 @@ pub use certificate_chain_synchronizer::*; pub use certifier::*; pub use epoch_service::*; pub use message::*; +pub use network_configuration_provider::*; pub use prover::*; pub use signable_builder::*; pub use signature_consumer::*; diff --git a/mithril-aggregator/src/services/network_configuration_provider.rs b/mithril-aggregator/src/services/network_configuration_provider.rs new file mode 100644 index 00000000000..c837647095e --- /dev/null +++ b/mithril-aggregator/src/services/network_configuration_provider.rs @@ -0,0 +1,248 @@ +use anyhow::Context; +use async_trait::async_trait; +use std::collections::BTreeSet; +use std::sync::Arc; + +use mithril_common::StdResult; +use mithril_common::entities::{Epoch, SignedEntityTypeDiscriminants}; +use mithril_protocol_config::interface::MithrilNetworkConfigurationProvider; +use mithril_protocol_config::model::{ + MithrilNetworkConfiguration, MithrilNetworkConfigurationForEpoch, SignedEntityTypeConfiguration, +}; + +use crate::EpochSettingsStorer; +use crate::entities::AggregatorEpochSettings; + +/// Read network configuration from the database epoch_settings or, if no records is available for +/// an epoch, fallback to the provided configuration value +pub struct LocalMithrilNetworkConfigurationProvider { + local_configuration_epoch_settings: AggregatorEpochSettings, + allowed_discriminants: BTreeSet, + epoch_settings_store: Arc, +} + +impl LocalMithrilNetworkConfigurationProvider { + /// Instantiate a new `LocalMithrilNetworkConfigurationProvider` + pub fn new( + local_configuration_epoch_settings: AggregatorEpochSettings, + allowed_discriminants: BTreeSet, + epoch_settings_store: Arc, + ) -> Self { + Self { + local_configuration_epoch_settings, + allowed_discriminants, + epoch_settings_store, + } + } + + /// Get epoch configuration from store or fallback to local configuration if it does not exists + async fn get_stored_configuration_or_fallback( + &self, + epoch: Epoch, + ) -> StdResult { + let epoch_settings = self.epoch_settings_store.get_epoch_settings(epoch).await?.unwrap_or( + AggregatorEpochSettings { + protocol_parameters: self + .local_configuration_epoch_settings + .protocol_parameters + .clone(), + cardano_transactions_signing_config: self + .local_configuration_epoch_settings + .cardano_transactions_signing_config + .clone(), + }, + ); + + Ok(MithrilNetworkConfigurationForEpoch { + enabled_signed_entity_types: self.allowed_discriminants.clone(), + protocol_parameters: epoch_settings.protocol_parameters, + signed_entity_types_config: SignedEntityTypeConfiguration { + cardano_transactions: Some(epoch_settings.cardano_transactions_signing_config), + }, + }) + } +} + +#[async_trait] +impl MithrilNetworkConfigurationProvider for LocalMithrilNetworkConfigurationProvider { + async fn get_network_configuration( + &self, + epoch: Epoch, + ) -> StdResult { + let aggregation_epoch = + epoch.offset_to_signer_retrieval_epoch().with_context(|| { + format!("MithrilNetworkConfigurationProvider could not compute aggregation epoch from epoch: {epoch}") + })?; + let next_aggregation_epoch = epoch.offset_to_next_signer_retrieval_epoch(); + let registration_epoch = epoch.offset_to_next_signer_retrieval_epoch().next(); + + let configuration_for_aggregation = + self.get_stored_configuration_or_fallback(aggregation_epoch).await?; + let configuration_for_next_aggregation = self + .get_stored_configuration_or_fallback(next_aggregation_epoch) + .await?; + let configuration_for_registration = + self.get_stored_configuration_or_fallback(registration_epoch).await?; + + let config = MithrilNetworkConfiguration { + epoch, + configuration_for_aggregation, + configuration_for_next_aggregation, + configuration_for_registration, + }; + Ok(config) + } +} + +#[cfg(test)] +mod tests { + use mithril_common::{ + entities::{BlockNumber, CardanoTransactionsSigningConfig, ProtocolParameters}, + test::double::Dummy, + }; + + use crate::store::FakeEpochSettingsStorer; + + use super::*; + + #[tokio::test] + async fn get_stored_configuration_with_stored_value_returns_them() { + let local_configuration_epoch_settings = AggregatorEpochSettings { + protocol_parameters: ProtocolParameters::new(2000, 200, 0.2), + ..Dummy::dummy() + }; + let stored_epoch_settings = AggregatorEpochSettings { + protocol_parameters: ProtocolParameters::new(1000, 100, 0.1), + ..Dummy::dummy() + }; + + let local_provider = LocalMithrilNetworkConfigurationProvider::new( + local_configuration_epoch_settings, + SignedEntityTypeDiscriminants::all(), + Arc::new(FakeEpochSettingsStorer::new(vec![( + Epoch(42), + stored_epoch_settings.clone(), + )])), + ); + + let network_configuration = local_provider + .get_stored_configuration_or_fallback(Epoch(42)) + .await + .unwrap(); + + assert_eq!( + stored_epoch_settings.protocol_parameters, + network_configuration.protocol_parameters + ) + } + + #[tokio::test] + async fn get_stored_configuration_without_stored_value_fallback_to_configuration_value() { + let local_configuration_epoch_settings = AggregatorEpochSettings { + protocol_parameters: ProtocolParameters::new(2000, 200, 0.2), + ..Dummy::dummy() + }; + + let local_provider = LocalMithrilNetworkConfigurationProvider::new( + local_configuration_epoch_settings.clone(), + SignedEntityTypeDiscriminants::all(), + Arc::new(FakeEpochSettingsStorer::new(vec![])), + ); + + let network_configuration = local_provider + .get_stored_configuration_or_fallback(Epoch(42)) + .await + .unwrap(); + + assert_eq!( + local_configuration_epoch_settings.protocol_parameters, + network_configuration.protocol_parameters + ) + } + + #[tokio::test] + async fn test_get_network_configuration_retrieve_configurations_for_aggregation_next_aggregation_and_registration() + { + let local_configuration_epoch_settings = AggregatorEpochSettings { + protocol_parameters: ProtocolParameters::new(3000, 300, 0.3), + cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + security_parameter: BlockNumber(3), + step: BlockNumber(30), + }, + }; + + // Nothing stored at 44, should fallback to configuration + let local_provider = LocalMithrilNetworkConfigurationProvider::new( + local_configuration_epoch_settings, + SignedEntityTypeDiscriminants::all(), + Arc::new(FakeEpochSettingsStorer::new(vec![ + ( + Epoch(42), + AggregatorEpochSettings { + protocol_parameters: ProtocolParameters::new(1000, 100, 0.1), + cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + security_parameter: BlockNumber(1), + step: BlockNumber(10), + }, + }, + ), + ( + Epoch(43), + AggregatorEpochSettings { + protocol_parameters: ProtocolParameters::new(2000, 200, 0.2), + cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + security_parameter: BlockNumber(2), + step: BlockNumber(20), + }, + }, + ), + ])), + ); + + let configuration = local_provider.get_network_configuration(Epoch(43)).await.unwrap(); + + assert_eq!(Epoch(43), configuration.epoch); + + assert_eq!( + MithrilNetworkConfigurationForEpoch { + protocol_parameters: ProtocolParameters::new(1000, 100, 0.1), + enabled_signed_entity_types: SignedEntityTypeDiscriminants::all(), + signed_entity_types_config: SignedEntityTypeConfiguration { + cardano_transactions: Some(CardanoTransactionsSigningConfig { + security_parameter: BlockNumber(1), + step: BlockNumber(10), + }), + }, + }, + configuration.configuration_for_aggregation + ); + + assert_eq!( + MithrilNetworkConfigurationForEpoch { + protocol_parameters: ProtocolParameters::new(2000, 200, 0.2), + enabled_signed_entity_types: SignedEntityTypeDiscriminants::all(), + signed_entity_types_config: SignedEntityTypeConfiguration { + cardano_transactions: Some(CardanoTransactionsSigningConfig { + security_parameter: BlockNumber(2), + step: BlockNumber(20), + }), + }, + }, + configuration.configuration_for_next_aggregation + ); + + assert_eq!( + MithrilNetworkConfigurationForEpoch { + protocol_parameters: ProtocolParameters::new(3000, 300, 0.3), + enabled_signed_entity_types: SignedEntityTypeDiscriminants::all(), + signed_entity_types_config: SignedEntityTypeConfiguration { + cardano_transactions: Some(CardanoTransactionsSigningConfig { + security_parameter: BlockNumber(3), + step: BlockNumber(30), + }), + }, + }, + configuration.configuration_for_registration + ); + } +} From 48612b67a893ab86d5f0c42080d456c7769fb8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Turmel?= Date: Mon, 3 Nov 2025 17:02:00 +0100 Subject: [PATCH 05/15] feature(aggregator): inform_epoch is now reading data from network_configuration_provider, and persist signer resigration epoch configuration --- .../builder/enablers/epoch.rs | 23 +- .../src/entities/aggregator_epoch_settings.rs | 28 +++ .../src/services/epoch_service.rs | 219 ++++++++++-------- 3 files changed, 152 insertions(+), 118 deletions(-) diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs index 46ede6e3cb1..1ec8b30c9e0 100644 --- a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs +++ b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs @@ -1,8 +1,9 @@ -use mithril_protocol_config::http_client::http_impl::HttpMithrilNetworkConfigurationProvider; use std::sync::Arc; use tokio::sync::RwLock; -use mithril_protocol_config::interface::MithrilNetworkConfigurationProvider; +use mithril_protocol_config::{ + http::HttpMithrilNetworkConfigurationProvider, interface::MithrilNetworkConfigurationProvider, +}; use crate::dependency_injection::{DependenciesBuilder, EpochServiceWrapper, Result}; use crate::get_dependency; @@ -22,8 +23,8 @@ impl DependenciesBuilder { .compute_allowed_signed_entity_types_discriminants()?; let epoch_service = Arc::new(RwLock::new(MithrilEpochService::new( - self.get_mithril_network_configuration_provider().await?, EpochServiceDependencies::new( + self.get_mithril_network_configuration_provider().await?, epoch_settings_storer, verification_key_store, chain_observer, @@ -59,6 +60,8 @@ impl DependenciesBuilder { )) }; + //TODO handle discrepency here + Ok(network_configuration_provider) } @@ -69,17 +72,3 @@ impl DependenciesBuilder { get_dependency!(self.mithril_network_configuration_provider) } } - -// Todo remove after rebase with shared aggregator client: -#[async_trait::async_trait] -impl mithril_protocol_config::http_client::http_impl::ProtocolConfigurationRetrieverFromAggregator - for crate::services::AggregatorHTTPClient -{ - /// Retrieves protocol configuration for a given epoch from the aggregator - async fn retrieve_protocol_configuration( - &self, - _epoch: mithril_common::entities::Epoch, - ) -> mithril_common::StdResult { - unimplemented!() - } -} diff --git a/mithril-aggregator/src/entities/aggregator_epoch_settings.rs b/mithril-aggregator/src/entities/aggregator_epoch_settings.rs index 014392f689c..80b8188d88c 100644 --- a/mithril-aggregator/src/entities/aggregator_epoch_settings.rs +++ b/mithril-aggregator/src/entities/aggregator_epoch_settings.rs @@ -9,3 +9,31 @@ pub struct AggregatorEpochSettings { /// Cardano transactions signing configuration pub cardano_transactions_signing_config: CardanoTransactionsSigningConfig, } + +#[cfg(test)] +mod test_utils { + use std::collections::BTreeSet; + + use mithril_common::entities::SignedEntityTypeDiscriminants; + use mithril_protocol_config::model::{ + MithrilNetworkConfigurationForEpoch, SignedEntityTypeConfiguration, + }; + + use super::*; + + impl AggregatorEpochSettings { + /// Convert this epoch setting into a [MithrilNetworkConfigurationForEpoch] + pub fn into_network_configuration_for_epoch( + self, + enabled_signed_entity_types: BTreeSet, + ) -> MithrilNetworkConfigurationForEpoch { + MithrilNetworkConfigurationForEpoch { + protocol_parameters: self.protocol_parameters, + enabled_signed_entity_types, + signed_entity_types_config: SignedEntityTypeConfiguration { + cardano_transactions: Some(self.cardano_transactions_signing_config), + }, + } + } + } +} diff --git a/mithril-aggregator/src/services/epoch_service.rs b/mithril-aggregator/src/services/epoch_service.rs index 55f2f1038ea..afb4c3d931a 100644 --- a/mithril-aggregator/src/services/epoch_service.rs +++ b/mithril-aggregator/src/services/epoch_service.rs @@ -1,6 +1,7 @@ use anyhow::{Context, anyhow}; use async_trait::async_trait; use mithril_protocol_config::interface::MithrilNetworkConfigurationProvider; +use mithril_protocol_config::model::MithrilNetworkConfiguration; use slog::{Logger, debug}; use std::collections::BTreeSet; use std::sync::Arc; @@ -152,6 +153,7 @@ struct ComputedEpochData { /// Dependencies required by the [MithrilEpochService]. pub struct EpochServiceDependencies { + mithril_network_configuration_provider: Arc, epoch_settings_storer: Arc, verification_key_store: Arc, chain_observer: Arc, @@ -162,6 +164,7 @@ pub struct EpochServiceDependencies { impl EpochServiceDependencies { /// Create a new instance of [EpochServiceDependencies]. pub fn new( + mithril_network_configuration_provider: Arc, epoch_settings_storer: Arc, verification_key_store: Arc, chain_observer: Arc, @@ -169,6 +172,7 @@ impl EpochServiceDependencies { stake_store: Arc, ) -> Self { Self { + mithril_network_configuration_provider, epoch_settings_storer, verification_key_store, chain_observer, @@ -180,11 +184,9 @@ impl EpochServiceDependencies { /// Implementation of the [epoch service][EpochService]. pub struct MithrilEpochService { - /// Epoch settings that will be inserted when inform_epoch is called - //future_epoch_settings: AggregatorEpochSettings, - mithril_network_configuration_provider: Arc, epoch_data: Option, computed_epoch_data: Option, + mithril_network_configuration_provider: Arc, epoch_settings_storer: Arc, verification_key_store: Arc, chain_observer: Arc, @@ -197,17 +199,15 @@ pub struct MithrilEpochService { impl MithrilEpochService { /// Create a new service instance pub fn new( - //future_epoch_settings: AggregatorEpochSettings, - mithril_network_configuration_provider: Arc, dependencies: EpochServiceDependencies, allowed_discriminants: BTreeSet, logger: Logger, ) -> Self { Self { - //future_epoch_settings, - mithril_network_configuration_provider, epoch_data: None, computed_epoch_data: None, + mithril_network_configuration_provider: dependencies + .mithril_network_configuration_provider, epoch_settings_storer: dependencies.epoch_settings_storer, verification_key_store: dependencies.verification_key_store, chain_observer: dependencies.chain_observer, @@ -252,49 +252,15 @@ impl MithrilEpochService { Ok(signers) } - async fn get_epoch_settings( + async fn insert_epoch_settings( &self, - epoch: Epoch, - name: &str, - ) -> StdResult { - let epoch_settings = self - .epoch_settings_storer - .get_epoch_settings(epoch) - .await - .with_context(|| format!("Epoch service failed to obtain {name}"))? - .ok_or(EpochServiceError::UnavailableData(epoch, name.to_string()))?; - - Ok(epoch_settings) - } - - async fn insert_future_epoch_settings(&self, actual_epoch: Epoch) -> StdResult<()> { - let recording_epoch = actual_epoch.offset_to_epoch_settings_recording_epoch(); - - /* debug!( - self.logger, "Inserting epoch settings in epoch {recording_epoch}"; - "epoch_settings" => ?self.future_epoch_settings - ); */ - - let network_configuration = self - .mithril_network_configuration_provider - .get_network_configuration() - .await?; - - let aggregator_epoch_settings = AggregatorEpochSettings { - protocol_parameters: network_configuration - .signer_registration_protocol_parameters - .clone(), - cardano_transactions_signing_config: network_configuration - .signed_entity_types_config - .cardano_transactions - .clone() - .unwrap(), - }; + recording_epoch: Epoch, + epoch_settings: &AggregatorEpochSettings, + ) -> StdResult<()> { self.epoch_settings_storer .save_epoch_settings( recording_epoch, - /* self.future_epoch_settings.clone(), */ - aggregator_epoch_settings + epoch_settings.clone() ) .await .with_context(|| format!("Epoch service failed to insert future_epoch_settings to epoch {recording_epoch}")) @@ -328,21 +294,59 @@ impl EpochService for MithrilEpochService { format!("EpochService could not compute signer retrieval epoch from epoch: {epoch}") })?; let next_signer_retrieval_epoch = epoch.offset_to_next_signer_retrieval_epoch(); + let signer_registration_epoch = epoch.offset_to_recording_epoch(); - let current_epoch_settings = self - .get_epoch_settings(signer_retrieval_epoch, "current epoch settings") + let network_configuration = self + .mithril_network_configuration_provider + .get_network_configuration(epoch) .await?; - let next_epoch_settings = self - .get_epoch_settings(next_signer_retrieval_epoch, "next epoch settings") - .await?; + let current_epoch_settings = AggregatorEpochSettings { + protocol_parameters: network_configuration + .configuration_for_aggregation + .protocol_parameters, + cardano_transactions_signing_config: network_configuration + .configuration_for_aggregation + .signed_entity_types_config + .cardano_transactions + .ok_or(anyhow!( + "Missing cardano transactions signing config for aggregation epoch {:?}", + epoch + ))?, + }; - let signer_registration_epoch_settings = self - .get_epoch_settings( - next_signer_retrieval_epoch.next(), - "signer registration epoch settings", - ) - .await?; + let next_epoch_settings = AggregatorEpochSettings { + protocol_parameters: network_configuration + .configuration_for_next_aggregation + .protocol_parameters, + cardano_transactions_signing_config: network_configuration + .configuration_for_next_aggregation + .signed_entity_types_config + .cardano_transactions + .ok_or(anyhow!( + "Missing cardano transactions signing config for next aggregation epoch {:?}", + epoch + ))?, + }; + + let signer_registration_epoch_settings = AggregatorEpochSettings { + protocol_parameters: network_configuration + .configuration_for_registration + .protocol_parameters, + cardano_transactions_signing_config: network_configuration + .configuration_for_registration + .signed_entity_types_config + .cardano_transactions + .ok_or(anyhow!( + "Missing cardano transactions signing config for registration epoch {:?}", + epoch + ))?, + }; + self.insert_epoch_settings( + signer_registration_epoch, + &signer_registration_epoch_settings, + ) + .await?; let current_signers_with_stake = self.get_signers_with_stake_at_epoch(signer_retrieval_epoch).await?; @@ -354,6 +358,7 @@ impl EpochService for MithrilEpochService { let total_stakes_signers = current_signers_with_stake.iter().map(|s| s.stake).sum(); let total_next_stakes_signers = next_signers_with_stake.iter().map(|s| s.stake).sum(); + //TODO: Can be removed if we replace type 'AggregatorEpochSettings' by 'MithrilNetworkConfigurationForEpoch' everywhere ? let signed_entity_config = SignedEntityConfig { allowed_discriminants: self.allowed_signed_entity_discriminants.clone(), cardano_transactions_signing_config: current_epoch_settings @@ -388,12 +393,7 @@ impl EpochService for MithrilEpochService { async fn update_epoch_settings(&mut self) -> StdResult<()> { debug!(self.logger, ">> update_epoch_settings"); - - let data = self.unwrap_data().with_context( - || "can't update epoch settings if inform_epoch has not been called first", - )?; - - self.insert_future_epoch_settings(data.epoch).await + todo!("remove") } async fn update_next_signers_with_stake(&mut self) -> StdResult<()> { @@ -843,6 +843,10 @@ mod tests { builder::{MithrilFixture, MithrilFixtureBuilder, StakeDistributionGenerationMethod}, double::{Dummy, fake_data}, }; + use mithril_protocol_config::{ + model::{MithrilNetworkConfigurationForEpoch, SignedEntityTypeConfiguration}, + test::double::configuration_provider::FakeMithrilNetworkConfigurationProvider, + }; use crate::store::{FakeEpochSettingsStorer, MockVerificationKeyStorer}; use crate::test::TestLogger; @@ -929,8 +933,6 @@ mod tests { } struct EpochServiceBuilder { - cardano_transactions_signing_config: CardanoTransactionsSigningConfig, - future_protocol_parameters: ProtocolParameters, allowed_discriminants: BTreeSet, cardano_era: CardanoEra, mithril_era: SupportedEra, @@ -947,8 +949,6 @@ mod tests { impl EpochServiceBuilder { fn new(epoch: Epoch, epoch_fixture: MithrilFixture) -> Self { Self { - cardano_transactions_signing_config: CardanoTransactionsSigningConfig::dummy(), - future_protocol_parameters: epoch_fixture.protocol_parameters(), allowed_discriminants: BTreeSet::new(), cardano_era: String::new(), mithril_era: SupportedEra::dummy(), @@ -978,18 +978,6 @@ mod tests { let next_signer_retrieval_epoch = self.current_epoch.offset_to_next_signer_retrieval_epoch(); - let epoch_settings_storer = FakeEpochSettingsStorer::new(vec![ - (signer_retrieval_epoch, self.stored_current_epoch_settings), - ( - next_signer_retrieval_epoch, - self.stored_next_epoch_settings.clone(), - ), - ( - next_signer_retrieval_epoch.next(), - self.stored_signer_registration_epoch_settings.clone(), - ), - ]); - let verification_key_store = { let mut store = MockVerificationKeyStorer::new(); let signers_with_stake = self.signers_with_stake.clone(); @@ -1029,13 +1017,28 @@ mod tests { stake_store }; + let configuration_for_aggregation = self + .stored_current_epoch_settings + .into_network_configuration_for_epoch(self.allowed_discriminants.clone()); + + let configuration_for_next_aggregation = self + .stored_next_epoch_settings + .into_network_configuration_for_epoch(self.allowed_discriminants.clone()); + + let configuration_for_registration = self + .stored_signer_registration_epoch_settings + .into_network_configuration_for_epoch(self.allowed_discriminants.clone()); + + let network_configuration_provider = FakeMithrilNetworkConfigurationProvider::new( + configuration_for_aggregation, + configuration_for_next_aggregation, + configuration_for_registration, + ); + MithrilEpochService::new( - AggregatorEpochSettings { - protocol_parameters: self.future_protocol_parameters, - cardano_transactions_signing_config: self.cardano_transactions_signing_config, - }, EpochServiceDependencies::new( - Arc::new(epoch_settings_storer), + Arc::new(network_configuration_provider), + Arc::new(FakeEpochSettingsStorer::new(Vec::new())), Arc::new(verification_key_store), Arc::new(chain_observer), Arc::new(era_checker), @@ -1263,41 +1266,55 @@ mod tests { } #[tokio::test] - async fn update_epoch_settings_insert_future_epoch_settings_in_the_store() { - let future_protocol_parameters = ProtocolParameters::new(6, 89, 0.124); + async fn inform_epoch_insert_registration_epoch_settings_in_the_store() { + let expected_epoch_settings = AggregatorEpochSettings { + protocol_parameters: ProtocolParameters::new(6, 89, 0.124), + cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + security_parameter: BlockNumber(10), + step: BlockNumber(15), + }, + }; + let epoch = Epoch(4); - let mut service = EpochServiceBuilder { - future_protocol_parameters: future_protocol_parameters.clone(), + let mut service = MithrilEpochService { + mithril_network_configuration_provider: Arc::new( + FakeMithrilNetworkConfigurationProvider::new( + MithrilNetworkConfigurationForEpoch::dummy(), + MithrilNetworkConfigurationForEpoch::dummy(), + MithrilNetworkConfigurationForEpoch { + protocol_parameters: expected_epoch_settings.protocol_parameters.clone(), + signed_entity_types_config: SignedEntityTypeConfiguration { + cardano_transactions: Some( + expected_epoch_settings.cardano_transactions_signing_config.clone(), + ), + }, + ..Dummy::dummy() + }, + ), + ), ..EpochServiceBuilder::new(epoch, MithrilFixtureBuilder::default().build()) - } - .build() - .await; + .build() + .await + }; service .inform_epoch(epoch) .await .expect("inform_epoch should not fail"); - service - .update_epoch_settings() - .await - .expect("update_epoch_settings should not fail"); let inserted_epoch_settings = service .epoch_settings_storer - .get_epoch_settings(epoch.offset_to_epoch_settings_recording_epoch()) + .get_epoch_settings(epoch.offset_to_recording_epoch()) .await .unwrap_or_else(|_| { panic!( "epoch settings should have been inserted for epoch {}", - epoch.offset_to_epoch_settings_recording_epoch() + epoch.offset_to_recording_epoch() ) }) .unwrap(); - assert_eq!( - inserted_epoch_settings.protocol_parameters, - future_protocol_parameters - ); + assert_eq!(inserted_epoch_settings, expected_epoch_settings); } #[tokio::test] From ad07386b989521aedf53782a7a9b50d47f4f233c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Turmel?= Date: Mon, 3 Nov 2025 18:23:19 +0100 Subject: [PATCH 06/15] chore(aggregator): remove update_epoch_settings from epoch_service, cleanup dependency injection --- .../src/dependency_injection/builder/mod.rs | 3 - .../dependency_injection/containers/serve.rs | 4 -- mithril-aggregator/src/runtime/runner.rs | 55 ------------------- .../src/runtime/state_machine.rs | 4 -- .../src/services/epoch_service.rs | 23 -------- .../services/signer_registration/follower.rs | 2 +- .../leader_aggregator_http_server.rs | 22 +++++++- 7 files changed, 22 insertions(+), 91 deletions(-) diff --git a/mithril-aggregator/src/dependency_injection/builder/mod.rs b/mithril-aggregator/src/dependency_injection/builder/mod.rs index d27a350a9a6..ac1649c4c89 100644 --- a/mithril-aggregator/src/dependency_injection/builder/mod.rs +++ b/mithril-aggregator/src/dependency_injection/builder/mod.rs @@ -401,9 +401,6 @@ impl DependenciesBuilder { signed_entity_service: self.get_signed_entity_service().await?, certifier_service: self.get_certifier_service().await?, epoch_service: self.get_epoch_service().await?, - mithril_network_configuration_provider: self - .get_mithril_network_configuration_provider() - .await?, ticker_service: self.get_ticker_service().await?, signed_entity_storer: self.get_signed_entity_storer().await?, signer_getter: self.get_signer_store().await?, diff --git a/mithril-aggregator/src/dependency_injection/containers/serve.rs b/mithril-aggregator/src/dependency_injection/containers/serve.rs index 56a00376380..b37d783aad5 100644 --- a/mithril-aggregator/src/dependency_injection/containers/serve.rs +++ b/mithril-aggregator/src/dependency_injection/containers/serve.rs @@ -1,4 +1,3 @@ -use mithril_protocol_config::interface::MithrilNetworkConfigurationProvider; use slog::Logger; use std::sync::Arc; use tokio::sync::RwLock; @@ -101,9 +100,6 @@ pub struct ServeCommandDependenciesContainer { /// Epoch service pub(crate) epoch_service: EpochServiceWrapper, - /// Mithril network configuration provider - pub(crate) mithril_network_configuration_provider: Arc, - /// Ticker Service pub(crate) ticker_service: Arc, diff --git a/mithril-aggregator/src/runtime/runner.rs b/mithril-aggregator/src/runtime/runner.rs index e9de8698a9a..758f9ffaf9e 100644 --- a/mithril-aggregator/src/runtime/runner.rs +++ b/mithril-aggregator/src/runtime/runner.rs @@ -78,9 +78,6 @@ pub trait AggregatorRunnerTrait: Sync + Send { force_sync: bool, ) -> StdResult<()>; - /// Ask the EpochService to update the epoch settings. - async fn update_epoch_settings(&self) -> StdResult<()>; - /// Compute the protocol message async fn compute_protocol_message( &self, @@ -298,16 +295,6 @@ impl AggregatorRunnerTrait for AggregatorRunner { .map_err(|e| e.into()) } - async fn update_epoch_settings(&self) -> StdResult<()> { - debug!(self.logger, ">> update_epoch_settings"); - self.dependencies - .epoch_service - .write() - .await - .update_epoch_settings() - .await - } - async fn compute_protocol_message( &self, signed_entity_type: &SignedEntityType, @@ -908,48 +895,6 @@ pub mod tests { runner.upkeep(Epoch(5)).await.unwrap(); } - #[tokio::test] - async fn test_update_epoch_settings() { - let mut mock_certifier_service = MockCertifierService::new(); - mock_certifier_service - .expect_inform_epoch() - .returning(|_| Ok(())) - .times(1); - - let config = ServeCommandConfiguration::new_sample(temp_dir!()); - let mut deps = DependenciesBuilder::new_with_stdout_logger(Arc::new(config.clone())) - .build_serve_dependencies_container() - .await - .unwrap(); - deps.certifier_service = Arc::new(mock_certifier_service); - let epoch_settings_storer = deps.epoch_settings_storer.clone(); - let current_epoch = deps.ticker_service.get_current_epoch().await.unwrap(); - let insert_epoch = current_epoch.offset_to_epoch_settings_recording_epoch(); - - let runner = build_runner_with_fixture_data(deps).await; - runner.inform_new_epoch(current_epoch).await.unwrap(); - runner - .update_epoch_settings() - .await - .expect("update_epoch_settings should not fail"); - - let saved_epoch_settings = epoch_settings_storer - .get_epoch_settings(insert_epoch) - .await - .unwrap() - .unwrap_or_else(|| panic!("should have epoch settings for epoch {insert_epoch}",)); - - assert_eq!( - AggregatorEpochSettings { - protocol_parameters: config.protocol_parameters.clone(), - cardano_transactions_signing_config: config - .cardano_transactions_signing_config - .clone(), - }, - saved_epoch_settings - ); - } - #[tokio::test] async fn test_precompute_epoch_data() { let mut deps = initialize_dependencies!().await; diff --git a/mithril-aggregator/src/runtime/state_machine.rs b/mithril-aggregator/src/runtime/state_machine.rs index fc613f3f5c8..c9e286fdc0c 100644 --- a/mithril-aggregator/src/runtime/state_machine.rs +++ b/mithril-aggregator/src/runtime/state_machine.rs @@ -268,7 +268,6 @@ impl AggregatorRuntime { self.runner.inform_new_epoch(new_time_point.epoch).await?; self.runner.upkeep(new_time_point.epoch).await?; self.runner.open_signer_registration_round(&new_time_point).await?; - self.runner.update_epoch_settings().await?; if self.config.is_follower { self.runner .synchronize_follower_aggregator_signer_registration() @@ -447,7 +446,6 @@ mod tests { .with(predicate::eq(TimePoint::dummy().epoch)) .once() .returning(|_| Ok(())); - runner.expect_update_epoch_settings().once().returning(|| Ok(())); runner.expect_precompute_epoch_data().once().returning(|| Ok(())); runner .expect_upkeep() @@ -510,7 +508,6 @@ mod tests { .with(predicate::eq(TimePoint::dummy().epoch)) .once() .returning(|_| Ok(())); - runner.expect_update_epoch_settings().once().returning(|| Ok(())); runner.expect_precompute_epoch_data().once().returning(|| Ok(())); runner .expect_upkeep() @@ -936,7 +933,6 @@ mod tests { .with(predicate::eq(new_time_point_clone.clone().epoch)) .once() .returning(|_| Ok(())); - runner.expect_update_epoch_settings().once().returning(|| Ok(())); runner.expect_precompute_epoch_data().once().returning(|| Ok(())); runner .expect_upkeep() diff --git a/mithril-aggregator/src/services/epoch_service.rs b/mithril-aggregator/src/services/epoch_service.rs index afb4c3d931a..b53006302a0 100644 --- a/mithril-aggregator/src/services/epoch_service.rs +++ b/mithril-aggregator/src/services/epoch_service.rs @@ -1,7 +1,6 @@ use anyhow::{Context, anyhow}; use async_trait::async_trait; use mithril_protocol_config::interface::MithrilNetworkConfigurationProvider; -use mithril_protocol_config::model::MithrilNetworkConfiguration; use slog::{Logger, debug}; use std::collections::BTreeSet; use std::sync::Arc; @@ -46,11 +45,6 @@ pub trait EpochService: Sync + Send { /// internal state for the new epoch. async fn inform_epoch(&mut self, epoch: Epoch) -> StdResult<()>; - /// Insert future epoch settings in the store based on this service current epoch (epoch offset +2). - /// - /// Note: must be called after `inform_epoch`. - async fn update_epoch_settings(&mut self) -> StdResult<()>; - /// Update the next signers with stake for the next epoch. async fn update_next_signers_with_stake(&mut self) -> StdResult<()>; @@ -391,11 +385,6 @@ impl EpochService for MithrilEpochService { Ok(()) } - async fn update_epoch_settings(&mut self) -> StdResult<()> { - debug!(self.logger, ">> update_epoch_settings"); - todo!("remove") - } - async fn update_next_signers_with_stake(&mut self) -> StdResult<()> { debug!(self.logger, ">> update_next_signers_with_stake"); @@ -543,7 +532,6 @@ pub(crate) struct FakeEpochService { epoch_data: Option, computed_epoch_data: Option, inform_epoch_error: bool, - update_epoch_settings_error: bool, precompute_epoch_data_error: bool, update_next_signers_with_stake_error: bool, } @@ -632,7 +620,6 @@ impl FakeEpochServiceBuilder { next_protocol_multi_signer, }), inform_epoch_error: false, - update_epoch_settings_error: false, precompute_epoch_data_error: false, update_next_signers_with_stake_error: false, } @@ -679,7 +666,6 @@ impl FakeEpochService { epoch_data: None, computed_epoch_data: None, inform_epoch_error: false, - update_epoch_settings_error: false, precompute_epoch_data_error: false, update_next_signers_with_stake_error: false, } @@ -688,12 +674,10 @@ impl FakeEpochService { pub fn toggle_errors( &mut self, inform_epoch: bool, - update_protocol_parameters: bool, precompute_epoch: bool, update_next_signers_with_stake: bool, ) { self.inform_epoch_error = inform_epoch; - self.update_epoch_settings_error = update_protocol_parameters; self.precompute_epoch_data_error = precompute_epoch; self.update_next_signers_with_stake_error = update_next_signers_with_stake; } @@ -721,13 +705,6 @@ impl EpochService for FakeEpochService { Ok(()) } - async fn update_epoch_settings(&mut self) -> StdResult<()> { - if self.update_epoch_settings_error { - anyhow::bail!("update_epoch_settings fake error"); - } - Ok(()) - } - async fn precompute_epoch_data(&mut self) -> StdResult<()> { if self.precompute_epoch_data_error { anyhow::bail!("precompute_epoch_data fake error"); diff --git a/mithril-aggregator/src/services/signer_registration/follower.rs b/mithril-aggregator/src/services/signer_registration/follower.rs index 68465ff4de3..5c472ea6ffa 100644 --- a/mithril-aggregator/src/services/signer_registration/follower.rs +++ b/mithril-aggregator/src/services/signer_registration/follower.rs @@ -456,7 +456,7 @@ mod tests { let signer_registration_follower = MithrilSignerRegistrationFollowerBuilder::default() .with_epoch_service({ let mut epoch_service = FakeEpochService::without_data(); - epoch_service.toggle_errors(false, false, false, true); + epoch_service.toggle_errors(false, false, true); epoch_service }) diff --git a/mithril-aggregator/tests/test_extensions/leader_aggregator_http_server.rs b/mithril-aggregator/tests/test_extensions/leader_aggregator_http_server.rs index 72a387aa284..cedcbac27e2 100644 --- a/mithril-aggregator/tests/test_extensions/leader_aggregator_http_server.rs +++ b/mithril-aggregator/tests/test_extensions/leader_aggregator_http_server.rs @@ -11,7 +11,7 @@ use axum_test::TestServer; use reqwest::Url; use mithril_aggregator::services::MessageService; -use mithril_common::entities::SignedEntityTypeDiscriminants; +use mithril_common::entities::{Epoch, SignedEntityTypeDiscriminants}; use mithril_common::logging::LoggerExtensions; use mithril_common::{StdError, StdResult}; @@ -33,6 +33,10 @@ impl LeaderAggregatorHttpServer { .route("/certificates", get(certificates_list)) .route("/certificate/genesis", get(certificate_last_genesis)) .route("/certificate/{hash}", get(certificate_by_hash)) + .route( + "/protocol-configuration/{epoch}", + get(protocol_configuration_by_epoch), + ) .with_state(state); let server = TestServer::builder().http_transport().build(router)?; @@ -98,3 +102,19 @@ async fn certificate_by_hash( Err(err) => internal_server_error(err).into_response(), } } + +async fn protocol_configuration_by_epoch( + Path(epoch): Path, + state: State, +) -> Response { + slog::debug!(state.logger, "/protocol-configuration/{epoch}"); + match state + .message_service + .get_protocol_configuration_message(Epoch(epoch), SignedEntityTypeDiscriminants::all()) + .await + { + Ok(Some(message)) => (StatusCode::OK, Json(message)).into_response(), + Ok(None) => StatusCode::NOT_FOUND.into_response(), + Err(err) => internal_server_error(err).into_response(), + } +} From e90aa2f2b7b7df82ee77b68fa0a8cdaa857fcc70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Turmel?= Date: Mon, 3 Nov 2025 19:12:53 +0100 Subject: [PATCH 07/15] refactor(aggregator): do not override existing epoch settings on save --- .../insert_or_ignore_epoch_settings.rs | 116 ++++++++++++++++++ .../src/database/query/epoch_settings/mod.rs | 4 +- .../epoch_settings/update_epoch_settings.rs | 103 ---------------- .../src/database/record/epoch_settings.rs | 2 +- .../repository/epoch_settings_store.rs | 45 ++++++- .../src/database/test_helper.rs | 25 ++-- 6 files changed, 170 insertions(+), 125 deletions(-) create mode 100644 mithril-aggregator/src/database/query/epoch_settings/insert_or_ignore_epoch_settings.rs delete mode 100644 mithril-aggregator/src/database/query/epoch_settings/update_epoch_settings.rs diff --git a/mithril-aggregator/src/database/query/epoch_settings/insert_or_ignore_epoch_settings.rs b/mithril-aggregator/src/database/query/epoch_settings/insert_or_ignore_epoch_settings.rs new file mode 100644 index 00000000000..8bac1b12804 --- /dev/null +++ b/mithril-aggregator/src/database/query/epoch_settings/insert_or_ignore_epoch_settings.rs @@ -0,0 +1,116 @@ +use sqlite::Value; + +use mithril_persistence::sqlite::{Query, SourceAlias, SqLiteEntity, WhereCondition}; + +use crate::database::record::EpochSettingsRecord; + +/// Query to update [EpochSettingsRecord] in the sqlite database +pub struct InsertOrIgnoreEpochSettingsQuery { + condition: WhereCondition, +} + +impl InsertOrIgnoreEpochSettingsQuery { + pub fn one(epoch_settings: EpochSettingsRecord) -> Self { + Self { + condition: WhereCondition::new( + "(epoch_setting_id, protocol_parameters, cardano_transactions_signing_config) values (?1, ?2, ?3)", + vec![ + Value::Integer(*epoch_settings.epoch_settings_id as i64), + Value::String( + serde_json::to_string(&epoch_settings.protocol_parameters).unwrap(), + ), + Value::String( + serde_json::to_string(&epoch_settings.cardano_transactions_signing_config) + .unwrap(), + ), + ], + ), + } + } +} + +impl Query for InsertOrIgnoreEpochSettingsQuery { + type Entity = EpochSettingsRecord; + + fn filters(&self) -> WhereCondition { + self.condition.clone() + } + + fn get_definition(&self, condition: &str) -> String { + // it is important to alias the fields with the same name as the table + // since the table cannot be aliased in a RETURNING statement in SQLite. + let projection = Self::Entity::get_projection() + .expand(SourceAlias::new(&[("{:epoch_setting:}", "epoch_setting")])); + + format!("insert or ignore into epoch_setting {condition} returning {projection}") + } +} + +#[cfg(test)] +mod tests { + use mithril_common::entities::{BlockNumber, CardanoTransactionsSigningConfig, Epoch}; + use mithril_common::test::double::fake_data; + use mithril_persistence::sqlite::ConnectionExtensions; + + use crate::database::query::GetEpochSettingsQuery; + use crate::database::test_helper::main_db_connection; + + use super::*; + + #[test] + fn test_insert_epoch_setting_in_empty_db() { + let connection = main_db_connection().unwrap(); + + let expected_epoch_settings = EpochSettingsRecord { + epoch_settings_id: Epoch(3), + protocol_parameters: fake_data::protocol_parameters(), + cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + security_parameter: BlockNumber(24), + step: BlockNumber(62), + }, + }; + let record = connection + .fetch_first(InsertOrIgnoreEpochSettingsQuery::one( + expected_epoch_settings.clone(), + )) + .unwrap(); + + assert_eq!(Some(expected_epoch_settings), record); + } + + #[test] + fn test_cant_replace_existing_value() { + let connection = main_db_connection().unwrap(); + + let expected_epoch_settings = EpochSettingsRecord { + epoch_settings_id: Epoch(3), + protocol_parameters: fake_data::protocol_parameters(), + cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + security_parameter: BlockNumber(24), + step: BlockNumber(62), + }, + }; + let record = connection + .fetch_first(InsertOrIgnoreEpochSettingsQuery::one( + expected_epoch_settings.clone(), + )) + .unwrap(); + assert!(record.is_some()); + + let record = connection + .fetch_first(InsertOrIgnoreEpochSettingsQuery::one(EpochSettingsRecord { + cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + security_parameter: BlockNumber(134), + step: BlockNumber(872), + }, + ..expected_epoch_settings.clone() + })) + .unwrap(); + assert!(record.is_none()); + + let record_in_db = connection + .fetch_first(GetEpochSettingsQuery::by_epoch(Epoch(3)).unwrap()) + .unwrap(); + assert_eq!(Some(expected_epoch_settings), record_in_db); + } +} diff --git a/mithril-aggregator/src/database/query/epoch_settings/mod.rs b/mithril-aggregator/src/database/query/epoch_settings/mod.rs index 8ff7fb12e7d..e45403ee09d 100644 --- a/mithril-aggregator/src/database/query/epoch_settings/mod.rs +++ b/mithril-aggregator/src/database/query/epoch_settings/mod.rs @@ -1,7 +1,7 @@ mod delete_epoch_settings; mod get_epoch_settings; -mod update_epoch_settings; +mod insert_or_ignore_epoch_settings; pub use delete_epoch_settings::*; pub use get_epoch_settings::*; -pub use update_epoch_settings::*; +pub use insert_or_ignore_epoch_settings::*; diff --git a/mithril-aggregator/src/database/query/epoch_settings/update_epoch_settings.rs b/mithril-aggregator/src/database/query/epoch_settings/update_epoch_settings.rs deleted file mode 100644 index d21c1312059..00000000000 --- a/mithril-aggregator/src/database/query/epoch_settings/update_epoch_settings.rs +++ /dev/null @@ -1,103 +0,0 @@ -use sqlite::Value; - -use mithril_common::entities::Epoch; -use mithril_persistence::sqlite::{Query, SourceAlias, SqLiteEntity, WhereCondition}; - -use crate::database::record::EpochSettingsRecord; -use crate::entities::AggregatorEpochSettings; - -/// Query to update [EpochSettingsRecord] in the sqlite database -pub struct UpdateEpochSettingsQuery { - condition: WhereCondition, -} - -impl UpdateEpochSettingsQuery { - pub fn one(epoch: Epoch, epoch_settings: AggregatorEpochSettings) -> Self { - let epoch_settings_id: i64 = epoch.try_into().unwrap(); - - Self { - condition: WhereCondition::new( - "(epoch_setting_id, protocol_parameters, cardano_transactions_signing_config) values (?1, ?2, ?3)", - vec![ - Value::Integer(epoch_settings_id), - Value::String( - serde_json::to_string(&epoch_settings.protocol_parameters).unwrap(), - ), - Value::String( - serde_json::to_string(&epoch_settings.cardano_transactions_signing_config) - .unwrap(), - ), - ], - ), - } - } -} - -impl Query for UpdateEpochSettingsQuery { - type Entity = EpochSettingsRecord; - - fn filters(&self) -> WhereCondition { - self.condition.clone() - } - - fn get_definition(&self, condition: &str) -> String { - // it is important to alias the fields with the same name as the table - // since the table cannot be aliased in a RETURNING statement in SQLite. - let projection = Self::Entity::get_projection() - .expand(SourceAlias::new(&[("{:epoch_setting:}", "epoch_setting")])); - - format!("insert or replace into epoch_setting {condition} returning {projection}") - } -} - -#[cfg(test)] -mod tests { - use mithril_common::entities::{BlockNumber, CardanoTransactionsSigningConfig}; - use mithril_common::test::double::fake_data; - use mithril_persistence::sqlite::ConnectionExtensions; - - use crate::database::query::GetEpochSettingsQuery; - use crate::database::test_helper::{insert_epoch_settings, main_db_connection}; - - use super::*; - - #[test] - fn test_update_epoch_settings() { - let connection = main_db_connection().unwrap(); - insert_epoch_settings(&connection, &[*Epoch(3)]).unwrap(); - - let epoch_settings_send_to_update = AggregatorEpochSettings { - protocol_parameters: fake_data::protocol_parameters(), - cardano_transactions_signing_config: CardanoTransactionsSigningConfig { - security_parameter: BlockNumber(24), - step: BlockNumber(62), - }, - }; - let record_returned_by_update_query = connection - .fetch_first(UpdateEpochSettingsQuery::one( - Epoch(3), - epoch_settings_send_to_update.clone(), - )) - .unwrap() - .unwrap(); - - assert_eq!(Epoch(3), record_returned_by_update_query.epoch_settings_id); - assert_eq!( - epoch_settings_send_to_update.protocol_parameters, - record_returned_by_update_query.protocol_parameters - ); - assert_eq!( - epoch_settings_send_to_update.cardano_transactions_signing_config, - record_returned_by_update_query.cardano_transactions_signing_config - ); - - let mut cursor = connection - .fetch(GetEpochSettingsQuery::by_epoch(Epoch(3)).unwrap()) - .unwrap(); - let epoch_settings_record = - cursor.next().expect("Should have an epoch settings for epoch 3."); - - assert_eq!(record_returned_by_update_query, epoch_settings_record); - assert_eq!(0, cursor.count()); - } -} diff --git a/mithril-aggregator/src/database/record/epoch_settings.rs b/mithril-aggregator/src/database/record/epoch_settings.rs index 21cc605742a..bf9b271e2ea 100644 --- a/mithril-aggregator/src/database/record/epoch_settings.rs +++ b/mithril-aggregator/src/database/record/epoch_settings.rs @@ -4,7 +4,7 @@ use mithril_persistence::sqlite::{HydrationError, Projection, SqLiteEntity}; use crate::entities::AggregatorEpochSettings; /// Settings for an epoch, including the protocol parameters. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct EpochSettingsRecord { /// Epoch settings id, i.e. the epoch number. pub epoch_settings_id: Epoch, diff --git a/mithril-aggregator/src/database/repository/epoch_settings_store.rs b/mithril-aggregator/src/database/repository/epoch_settings_store.rs index 8949657149a..28defe049e9 100644 --- a/mithril-aggregator/src/database/repository/epoch_settings_store.rs +++ b/mithril-aggregator/src/database/repository/epoch_settings_store.rs @@ -8,8 +8,9 @@ use mithril_common::entities::{Epoch, ProtocolParameters}; use mithril_persistence::sqlite::{ConnectionExtensions, SqliteConnection}; use crate::database::query::{ - DeleteEpochSettingsQuery, GetEpochSettingsQuery, UpdateEpochSettingsQuery, + DeleteEpochSettingsQuery, GetEpochSettingsQuery, InsertOrIgnoreEpochSettingsQuery, }; +use crate::database::record::EpochSettingsRecord; use crate::entities::AggregatorEpochSettings; use crate::services::EpochPruningTask; use crate::{EpochSettingsStorer, ProtocolParametersRetriever}; @@ -50,13 +51,17 @@ impl EpochSettingsStorer for EpochSettingsStore { epoch: Epoch, epoch_settings: AggregatorEpochSettings, ) -> StdResult> { + let record_to_insert = EpochSettingsRecord { + epoch_settings_id: epoch, + cardano_transactions_signing_config: epoch_settings.cardano_transactions_signing_config, + protocol_parameters: epoch_settings.protocol_parameters, + }; let epoch_settings_record = self .connection - .fetch_first(UpdateEpochSettingsQuery::one(epoch, epoch_settings)) - .with_context(|| format!("persist epoch settings failure for epoch {epoch:?}"))? - .unwrap_or_else(|| panic!("No entity returned by the persister, epoch = {epoch:?}")); + .fetch_first(InsertOrIgnoreEpochSettingsQuery::one(record_to_insert)) + .with_context(|| format!("persist epoch settings failure for epoch {epoch:?}"))?; - Ok(Some(epoch_settings_record.into())) + Ok(epoch_settings_record.map(Into::into)) } async fn get_epoch_settings(&self, epoch: Epoch) -> StdResult> { @@ -171,4 +176,34 @@ mod tests { assert_eq!(None, epoch_settings); } } + + #[tokio::test] + async fn save_epoch_settings_does_not_replace_existing_value_in_database() { + let connection = main_db_connection().unwrap(); + + let store = EpochSettingsStore::new(Arc::new(connection), None); + let expected_epoch_settings = AggregatorEpochSettings { + protocol_parameters: ProtocolParameters::new(1, 1, 0.5), + ..Dummy::dummy() + }; + + store + .save_epoch_settings(Epoch(2), expected_epoch_settings.clone()) + .await + .expect("saving epoch settings should not fails"); + + store + .save_epoch_settings( + Epoch(2), + AggregatorEpochSettings { + protocol_parameters: ProtocolParameters::new(2, 2, 1.5), + ..Dummy::dummy() + }, + ) + .await + .expect("saving epoch settings should not fails"); + + let epoch_settings = store.get_epoch_settings(Epoch(2)).await.unwrap().unwrap(); + assert_eq!(expected_epoch_settings, epoch_settings); + } } diff --git a/mithril-aggregator/src/database/test_helper.rs b/mithril-aggregator/src/database/test_helper.rs index 73992afc682..cdb19aa0de7 100644 --- a/mithril-aggregator/src/database/test_helper.rs +++ b/mithril-aggregator/src/database/test_helper.rs @@ -12,16 +12,15 @@ use mithril_persistence::sqlite::{ }; use crate::database::query::{ - ImportSignerRecordQuery, InsertCertificateRecordQuery, + ImportSignerRecordQuery, InsertCertificateRecordQuery, InsertOrIgnoreEpochSettingsQuery, InsertOrReplaceBufferedSingleSignatureRecordQuery, InsertOrReplaceSignerRegistrationRecordQuery, InsertOrReplaceStakePoolQuery, - InsertSignedEntityRecordQuery, UpdateEpochSettingsQuery, UpdateSingleSignatureRecordQuery, + InsertSignedEntityRecordQuery, UpdateSingleSignatureRecordQuery, }; use crate::database::record::{ - BufferedSingleSignatureRecord, CertificateRecord, SignedEntityRecord, SignerRecord, - SignerRegistrationRecord, SingleSignatureRecord, + BufferedSingleSignatureRecord, CertificateRecord, EpochSettingsRecord, SignedEntityRecord, + SignerRecord, SignerRegistrationRecord, SingleSignatureRecord, }; -use crate::entities::AggregatorEpochSettings; /// In-memory sqlite database without foreign key support with migrations applied pub fn main_db_connection() -> StdResult { @@ -206,16 +205,14 @@ pub fn insert_epoch_settings( let query = { // leverage the expanded parameter from this query which is unit // tested on its own above. - let (sql_values, _) = UpdateEpochSettingsQuery::one( - Epoch(1), - AggregatorEpochSettings { - protocol_parameters: ProtocolParameters::new(1, 2, 1.0), - cardano_transactions_signing_config: CardanoTransactionsSigningConfig { - security_parameter: BlockNumber(0), - step: BlockNumber(0), - }, + let (sql_values, _) = InsertOrIgnoreEpochSettingsQuery::one(EpochSettingsRecord { + epoch_settings_id: Epoch(1), + protocol_parameters: ProtocolParameters::new(1, 2, 1.0), + cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + security_parameter: BlockNumber(0), + step: BlockNumber(0), }, - ) + }) .filters() .expand(); From 30abb33ec2756f052bd84157b78d680f7b4e6202 Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Wed, 5 Nov 2025 17:45:37 +0100 Subject: [PATCH 08/15] feat(aggregator): add `preload_security_parameter` config parameter --- mithril-aggregator/src/configuration.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/mithril-aggregator/src/configuration.rs b/mithril-aggregator/src/configuration.rs index fc3d2400899..e29eb430838 100644 --- a/mithril-aggregator/src/configuration.rs +++ b/mithril-aggregator/src/configuration.rs @@ -255,6 +255,11 @@ pub trait ConfigurationSource { panic!("cardano_transactions_signing_config is not implemented."); } + /// Blocks offset, from the tip of the chain, to exclude during the cardano transactions preload + fn preload_security_parameter(&self) -> BlockNumber { + panic!("preload_security_parameter is not implemented."); + } + /// Maximum number of transactions hashes allowed by request to the prover of the Cardano transactions fn cardano_transactions_prover_max_hashes_allowed_by_request(&self) -> usize { panic!("cardano_transactions_prover_max_hashes_allowed_by_request is not implemented."); @@ -558,6 +563,10 @@ pub struct ServeCommandConfiguration { #[example = "`{ security_parameter: 3000, step: 120 }`"] pub cardano_transactions_signing_config: CardanoTransactionsSigningConfig, + /// Blocks offset, from the tip of the chain, to exclude during the cardano transactions preload + /// `[default: 2160]`. + pub preload_security_parameter: BlockNumber, + /// Maximum number of transactions hashes allowed by request to the prover of the Cardano transactions pub cardano_transactions_prover_max_hashes_allowed_by_request: usize, @@ -714,6 +723,7 @@ impl ServeCommandConfiguration { security_parameter: BlockNumber(120), step: BlockNumber(15), }, + preload_security_parameter: BlockNumber(30), cardano_transactions_prover_max_hashes_allowed_by_request: 100, cardano_transactions_block_streamer_max_roll_forwards_per_poll: 1000, enable_metrics_server: true, @@ -881,6 +891,10 @@ impl ConfigurationSource for ServeCommandConfiguration { self.cardano_transactions_signing_config.clone() } + fn preload_security_parameter(&self) -> BlockNumber { + self.preload_security_parameter + } + fn cardano_transactions_prover_max_hashes_allowed_by_request(&self) -> usize { self.cardano_transactions_prover_max_hashes_allowed_by_request } @@ -981,6 +995,9 @@ pub struct DefaultConfiguration { /// Cardano transactions signing configuration pub cardano_transactions_signing_config: CardanoTransactionsSigningConfig, + /// Blocks offset, from the tip of the chain, to exclude during the cardano transactions preload + pub preload_security_parameter: u64, + /// Maximum number of transactions hashes allowed by request to the prover of the Cardano transactions pub cardano_transactions_prover_max_hashes_allowed_by_request: u32, @@ -1026,6 +1043,7 @@ impl Default for DefaultConfiguration { security_parameter: BlockNumber(3000), step: BlockNumber(120), }, + preload_security_parameter: 2160, cardano_transactions_prover_max_hashes_allowed_by_request: 100, cardano_transactions_block_streamer_max_roll_forwards_per_poll: 10000, enable_metrics_server: "false".to_string(), @@ -1104,6 +1122,7 @@ impl Source for DefaultConfiguration { &namespace, myself.persist_usage_report_interval_in_seconds ); + register_config_value!(result, &namespace, myself.preload_security_parameter); register_config_value!( result, &namespace, @@ -1111,7 +1130,7 @@ impl Source for DefaultConfiguration { |v: CardanoTransactionsSigningConfig| HashMap::from([ ( "security_parameter".to_string(), - ValueKind::from(*v.security_parameter,), + ValueKind::from(*v.security_parameter), ), ("step".to_string(), ValueKind::from(*v.step),) ]) From 6d14cce49455065891037cd379232f57d79007b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Turmel?= Date: Tue, 4 Nov 2025 18:46:43 +0100 Subject: [PATCH 09/15] feat(aggregator): make protocol_parameters and cardano_transaction_signing_config optionnal (mandatory for leader) --- mithril-aggregator/src/configuration.rs | 40 +++++++++++-------- .../builder/enablers/cardano_node.rs | 4 +- .../builder/enablers/epoch.rs | 3 +- .../builder/support/stores.rs | 4 +- .../services/certifier/certifier_service.rs | 2 +- ...ardano_stake_distribution_verify_stakes.rs | 2 +- mithril-aggregator/tests/certificate_chain.rs | 2 +- .../tests/create_certificate.rs | 6 +-- .../tests/create_certificate_follower.rs | 2 +- ...te_certificate_with_buffered_signatures.rs | 6 +-- mithril-aggregator/tests/era_checker.rs | 2 +- .../tests/genesis_to_signing.rs | 2 +- .../tests/open_message_expiration.rs | 2 +- .../tests/open_message_newer_exists.rs | 2 +- .../tests/prove_transactions.rs | 6 +-- .../tests/test_extensions/runtime_tester.rs | 2 +- 16 files changed, 48 insertions(+), 39 deletions(-) diff --git a/mithril-aggregator/src/configuration.rs b/mithril-aggregator/src/configuration.rs index e29eb430838..ff72b93810b 100644 --- a/mithril-aggregator/src/configuration.rs +++ b/mithril-aggregator/src/configuration.rs @@ -109,7 +109,7 @@ pub trait ConfigurationSource { } /// Protocol parameters - fn protocol_parameters(&self) -> ProtocolParameters { + fn protocol_parameters(&self) -> Option { panic!("protocol_parameters is not implemented."); } @@ -251,7 +251,7 @@ pub trait ConfigurationSource { } /// Cardano transactions signing configuration - fn cardano_transactions_signing_config(&self) -> CardanoTransactionsSigningConfig { + fn cardano_transactions_signing_config(&self) -> Option { panic!("cardano_transactions_signing_config is not implemented."); } @@ -378,12 +378,20 @@ pub trait ConfigurationSource { } } - /// Infer the [AggregatorEpochSettings] from the configuration. - fn get_epoch_settings_configuration(&self) -> AggregatorEpochSettings { - AggregatorEpochSettings { - protocol_parameters: self.protocol_parameters(), - cardano_transactions_signing_config: self.cardano_transactions_signing_config(), - } + /// `leader aggregator only` Infer the [AggregatorEpochSettings] from the configuration. + fn get_leader_aggregator_epoch_settings_configuration( + &self, + ) -> StdResult { + Ok(AggregatorEpochSettings { + protocol_parameters: self.protocol_parameters().with_context( + || "Configuration `protocol_parameter` is mandatory for a Leader Aggregator", + )?, + cardano_transactions_signing_config: self + .cardano_transactions_signing_config() + .with_context( + || "Configuration `cardano_transactions_signing_config` is mandatory for a Leader Aggregator", + )?, + }) } /// Check if the aggregator is running in follower mode. @@ -458,7 +466,7 @@ pub struct ServeCommandConfiguration { /// Protocol parameters #[example = "`{ k: 5, m: 100, phi_f: 0.65 }`"] - pub protocol_parameters: ProtocolParameters, + pub protocol_parameters: Option, /// Type of snapshot uploader to use #[example = "`gcp` or `local`"] @@ -561,7 +569,7 @@ pub struct ServeCommandConfiguration { /// Cardano transactions signing configuration #[example = "`{ security_parameter: 3000, step: 120 }`"] - pub cardano_transactions_signing_config: CardanoTransactionsSigningConfig, + pub cardano_transactions_signing_config: Option, /// Blocks offset, from the tip of the chain, to exclude during the cardano transactions preload /// `[default: 2160]`. @@ -681,11 +689,11 @@ impl ServeCommandConfiguration { network_magic: Some(42), dmq_network_magic: Some(3141592), chain_observer_type: ChainObserverType::Fake, - protocol_parameters: ProtocolParameters { + protocol_parameters: Some(ProtocolParameters { k: 5, m: 100, phi_f: 0.95, - }, + }), snapshot_uploader_type: SnapshotUploaderType::Local, snapshot_bucket_name: None, snapshot_use_cdn_domain: false, @@ -719,10 +727,10 @@ impl ServeCommandConfiguration { allow_unparsable_block: false, cardano_transactions_prover_cache_pool_size: 3, cardano_transactions_database_connection_pool_size: 5, - cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig { security_parameter: BlockNumber(120), step: BlockNumber(15), - }, + }), preload_security_parameter: BlockNumber(30), cardano_transactions_prover_max_hashes_allowed_by_request: 100, cardano_transactions_block_streamer_max_roll_forwards_per_poll: 1000, @@ -783,7 +791,7 @@ impl ConfigurationSource for ServeCommandConfiguration { self.chain_observer_type.clone() } - fn protocol_parameters(&self) -> ProtocolParameters { + fn protocol_parameters(&self) -> Option { self.protocol_parameters.clone() } @@ -887,7 +895,7 @@ impl ConfigurationSource for ServeCommandConfiguration { self.cardano_transactions_database_connection_pool_size } - fn cardano_transactions_signing_config(&self) -> CardanoTransactionsSigningConfig { + fn cardano_transactions_signing_config(&self) -> Option { self.cardano_transactions_signing_config.clone() } diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/cardano_node.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/cardano_node.rs index 90eeab09ab6..074c70c5909 100644 --- a/mithril-aggregator/src/dependency_injection/builder/enablers/cardano_node.rs +++ b/mithril-aggregator/src/dependency_injection/builder/enablers/cardano_node.rs @@ -134,9 +134,7 @@ impl DependenciesBuilder { let cardano_transactions_preloader = CardanoTransactionsPreloader::new( self.get_signed_entity_type_lock().await?, self.get_transactions_importer().await?, - self.configuration - .cardano_transactions_signing_config() - .security_parameter, + self.configuration.preload_security_parameter(), self.get_chain_observer().await?, self.root_logger(), Arc::new(CardanoTransactionsPreloaderActivation::new(activation)), diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs index 1ec8b30c9e0..e16a109c6fa 100644 --- a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs +++ b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs @@ -53,7 +53,8 @@ impl DependenciesBuilder { )) } else { Arc::new(LocalMithrilNetworkConfigurationProvider::new( - self.configuration.get_epoch_settings_configuration(), + self.configuration + .get_leader_aggregator_epoch_settings_configuration()?, self.configuration .compute_allowed_signed_entity_types_discriminants()?, self.get_epoch_settings_store().await?, diff --git a/mithril-aggregator/src/dependency_injection/builder/support/stores.rs b/mithril-aggregator/src/dependency_injection/builder/support/stores.rs index c178b6435c7..d2113380cad 100644 --- a/mithril-aggregator/src/dependency_injection/builder/support/stores.rs +++ b/mithril-aggregator/src/dependency_injection/builder/support/stores.rs @@ -115,7 +115,9 @@ impl DependenciesBuilder { error: Some(e.into()), })?; - let epoch_settings_configuration = self.configuration.get_epoch_settings_configuration(); + let epoch_settings_configuration = self + .configuration + .get_leader_aggregator_epoch_settings_configuration()?; debug!( logger, "Handle discrepancies at startup of epoch settings store, will record epoch settings from the configuration for epoch {retrieval_epoch}"; diff --git a/mithril-aggregator/src/services/certifier/certifier_service.rs b/mithril-aggregator/src/services/certifier/certifier_service.rs index 574867822d5..a4a2777d83f 100644 --- a/mithril-aggregator/src/services/certifier/certifier_service.rs +++ b/mithril-aggregator/src/services/certifier/certifier_service.rs @@ -481,7 +481,7 @@ mod tests { dependency_manager .init_state_from_fixture( fixture, - &cardano_transactions_signing_config, + &cardano_transactions_signing_config.unwrap(), epochs_with_signers, ) .await; diff --git a/mithril-aggregator/tests/cardano_stake_distribution_verify_stakes.rs b/mithril-aggregator/tests/cardano_stake_distribution_verify_stakes.rs index 2dbe303d143..c7fb7eab01b 100644 --- a/mithril-aggregator/tests/cardano_stake_distribution_verify_stakes.rs +++ b/mithril-aggregator/tests/cardano_stake_distribution_verify_stakes.rs @@ -22,7 +22,7 @@ async fn cardano_stake_distribution_verify_stakes() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: protocol_parameters.clone(), + protocol_parameters: Some(protocol_parameters.clone()), signed_entity_types: Some( SignedEntityTypeDiscriminants::CardanoStakeDistribution.to_string(), ), diff --git a/mithril-aggregator/tests/certificate_chain.rs b/mithril-aggregator/tests/certificate_chain.rs index dcf7ab2a62f..6e0ff498be1 100644 --- a/mithril-aggregator/tests/certificate_chain.rs +++ b/mithril-aggregator/tests/certificate_chain.rs @@ -22,7 +22,7 @@ async fn certificate_chain() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: protocol_parameters.clone(), + protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("certificate_chain"), signed_entity_types: Some(SignedEntityTypeDiscriminants::CardanoDatabase.to_string()), ..ServeCommandConfiguration::new_sample(temp_dir!()) diff --git a/mithril-aggregator/tests/create_certificate.rs b/mithril-aggregator/tests/create_certificate.rs index b6a34970664..b983feb8e73 100644 --- a/mithril-aggregator/tests/create_certificate.rs +++ b/mithril-aggregator/tests/create_certificate.rs @@ -22,7 +22,7 @@ async fn create_certificate() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: protocol_parameters.clone(), + protocol_parameters: Some(protocol_parameters.clone()), signed_entity_types: Some( [ SignedEntityTypeDiscriminants::CardanoTransactions.to_string(), @@ -31,10 +31,10 @@ async fn create_certificate() { .join(","), ), data_stores_directory: get_test_dir("create_certificate"), - cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig { security_parameter: BlockNumber(0), step: BlockNumber(30), - }, + }), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; let mut tester = RuntimeTester::build( diff --git a/mithril-aggregator/tests/create_certificate_follower.rs b/mithril-aggregator/tests/create_certificate_follower.rs index 371fe067b06..9f75c791a51 100644 --- a/mithril-aggregator/tests/create_certificate_follower.rs +++ b/mithril-aggregator/tests/create_certificate_follower.rs @@ -98,7 +98,7 @@ async fn create_certificate_follower() { }, }; let leader_configuration = ServeCommandConfiguration { - protocol_parameters: protocol_parameters.clone(), + protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("create_certificate_leader"), signed_entity_types: Some( SignedEntityTypeDiscriminants::CardanoStakeDistribution.to_string(), diff --git a/mithril-aggregator/tests/create_certificate_with_buffered_signatures.rs b/mithril-aggregator/tests/create_certificate_with_buffered_signatures.rs index 073b1e5b587..f46d497920f 100644 --- a/mithril-aggregator/tests/create_certificate_with_buffered_signatures.rs +++ b/mithril-aggregator/tests/create_certificate_with_buffered_signatures.rs @@ -22,13 +22,13 @@ async fn create_certificate_with_buffered_signatures() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: protocol_parameters.clone(), + protocol_parameters: Some(protocol_parameters.clone()), signed_entity_types: Some(SignedEntityTypeDiscriminants::CardanoTransactions.to_string()), data_stores_directory: get_test_dir("create_certificate_with_buffered_signatures"), - cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig { security_parameter: BlockNumber(0), step: BlockNumber(30), - }, + }), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; let mut tester = RuntimeTester::build( diff --git a/mithril-aggregator/tests/era_checker.rs b/mithril-aggregator/tests/era_checker.rs index 36202f14da9..04fbfb2173f 100644 --- a/mithril-aggregator/tests/era_checker.rs +++ b/mithril-aggregator/tests/era_checker.rs @@ -22,7 +22,7 @@ async fn testing_eras() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: protocol_parameters.clone(), + protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("testing_eras"), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; diff --git a/mithril-aggregator/tests/genesis_to_signing.rs b/mithril-aggregator/tests/genesis_to_signing.rs index e47dba18f5a..6ee5ed3d2e5 100644 --- a/mithril-aggregator/tests/genesis_to_signing.rs +++ b/mithril-aggregator/tests/genesis_to_signing.rs @@ -16,7 +16,7 @@ async fn genesis_to_signing() { phi_f: 0.65, }; let configuration = ServeCommandConfiguration { - protocol_parameters: protocol_parameters.clone(), + protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("genesis_to_signing"), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; diff --git a/mithril-aggregator/tests/open_message_expiration.rs b/mithril-aggregator/tests/open_message_expiration.rs index 4e321f2b158..7442aedc556 100644 --- a/mithril-aggregator/tests/open_message_expiration.rs +++ b/mithril-aggregator/tests/open_message_expiration.rs @@ -23,7 +23,7 @@ async fn open_message_expiration() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: protocol_parameters.clone(), + protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("open_message_expiration"), signed_entity_types: Some(SignedEntityTypeDiscriminants::CardanoDatabase.to_string()), ..ServeCommandConfiguration::new_sample(temp_dir!()) diff --git a/mithril-aggregator/tests/open_message_newer_exists.rs b/mithril-aggregator/tests/open_message_newer_exists.rs index f3850e3201a..4f1253576cd 100644 --- a/mithril-aggregator/tests/open_message_newer_exists.rs +++ b/mithril-aggregator/tests/open_message_newer_exists.rs @@ -21,7 +21,7 @@ async fn open_message_newer_exists() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: protocol_parameters.clone(), + protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("open_message_newer_exists"), signed_entity_types: Some(SignedEntityTypeDiscriminants::CardanoDatabase.to_string()), ..ServeCommandConfiguration::new_sample(temp_dir!()) diff --git a/mithril-aggregator/tests/prove_transactions.rs b/mithril-aggregator/tests/prove_transactions.rs index 43868d0fd92..3e6fa861a2c 100644 --- a/mithril-aggregator/tests/prove_transactions.rs +++ b/mithril-aggregator/tests/prove_transactions.rs @@ -23,13 +23,13 @@ async fn prove_transactions() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: protocol_parameters.clone(), + protocol_parameters: Some(protocol_parameters.clone()), signed_entity_types: Some(SignedEntityTypeDiscriminants::CardanoTransactions.to_string()), data_stores_directory: get_test_dir("prove_transactions"), - cardano_transactions_signing_config: CardanoTransactionsSigningConfig { + cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig { security_parameter: BlockNumber(0), step: BlockNumber(30), - }, + }), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; let mut tester = RuntimeTester::build( diff --git a/mithril-aggregator/tests/test_extensions/runtime_tester.rs b/mithril-aggregator/tests/test_extensions/runtime_tester.rs index 2adb9d8d70b..6c7e91cab13 100644 --- a/mithril-aggregator/tests/test_extensions/runtime_tester.rs +++ b/mithril-aggregator/tests/test_extensions/runtime_tester.rs @@ -160,7 +160,7 @@ impl RuntimeTester { let global_logger = slog_scope::set_global_logger(logger.clone()); let network = configuration.network.clone(); let cardano_transactions_signing_config = - configuration.cardano_transactions_signing_config.clone(); + configuration.cardano_transactions_signing_config.clone().unwrap(); let snapshot_uploader = Arc::new(DumbUploader::default()); let immutable_file_observer = Arc::new(DumbImmutableFileObserver::new()); immutable_file_observer From b80c37296698d51769c9049cff43486f1aa3a3d8 Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Tue, 4 Nov 2025 23:13:04 +0100 Subject: [PATCH 10/15] test(aggregator): simplify & rework serve deps container test tooling Rework `init_state_from_fixture` to not save epoch_settings and works with the fixed window of three epoch (aggregate/next aggregate/signer registration), epoch settings should already exists, most of the time they will be inserted by the handle discrepancies system --- .../src/dependency_injection/builder/mod.rs | 1 - .../dependency_injection/containers/serve.rs | 136 ++++++---------- mithril-aggregator/src/runtime/runner.rs | 39 ++--- .../services/certifier/certifier_service.rs | 154 +++++++----------- .../tests/test_extensions/runtime_tester.rs | 19 +-- 5 files changed, 121 insertions(+), 228 deletions(-) diff --git a/mithril-aggregator/src/dependency_injection/builder/mod.rs b/mithril-aggregator/src/dependency_injection/builder/mod.rs index ac1649c4c89..ba6836347a3 100644 --- a/mithril-aggregator/src/dependency_injection/builder/mod.rs +++ b/mithril-aggregator/src/dependency_injection/builder/mod.rs @@ -386,7 +386,6 @@ impl DependenciesBuilder { certificate_repository: self.get_certificate_repository().await?, verification_key_store: self.get_verification_key_store().await?, epoch_settings_storer: self.get_epoch_settings_store().await?, - chain_observer: self.get_chain_observer().await?, certificate_chain_synchronizer: self.get_certificate_chain_synchronizer().await?, signer_registerer: self.get_signer_registerer().await?, signer_synchronizer: self.get_signer_synchronizer().await?, diff --git a/mithril-aggregator/src/dependency_injection/containers/serve.rs b/mithril-aggregator/src/dependency_injection/containers/serve.rs index b37d783aad5..3ea00fa6868 100644 --- a/mithril-aggregator/src/dependency_injection/containers/serve.rs +++ b/mithril-aggregator/src/dependency_injection/containers/serve.rs @@ -2,13 +2,9 @@ use slog::Logger; use std::sync::Arc; use tokio::sync::RwLock; -use mithril_cardano_node_chain::chain_observer::ChainObserver; use mithril_common::{ api_version::APIVersionProvider, - entities::{ - CardanoTransactionsSigningConfig, Epoch, ProtocolParameters, SignerWithStake, - StakeDistribution, - }, + entities::{Epoch, SignerWithStake, StakeDistribution}, signable_builder::SignableBuilderService, test::builder::MithrilFixture, }; @@ -24,7 +20,6 @@ use crate::{ database::repository::{ CertificateRepository, SignedEntityStorer, SignerGetter, StakePoolStore, }, - entities::AggregatorEpochSettings, event_store::{EventMessage, TransmitterService}, services::{ CertificateChainSynchronizer, CertifierService, EpochService, MessageService, @@ -55,9 +50,6 @@ pub struct ServeCommandDependenciesContainer { /// Epoch settings storer. pub epoch_settings_storer: Arc, - /// Chain observer service. - pub(crate) chain_observer: Arc, - /// Certificate chain synchronizer service pub(crate) certificate_chain_synchronizer: Arc, @@ -132,94 +124,64 @@ pub struct ServeCommandDependenciesContainer { impl ServeCommandDependenciesContainer { /// `TEST METHOD ONLY` /// - /// Get the first two epochs that will be used by a newly started aggregator - pub async fn get_genesis_epochs(&self) -> (Epoch, Epoch) { - let current_epoch = self - .chain_observer - .get_current_epoch() - .await - .expect("get_current_epoch should not fail") - .expect("an epoch should've been set to the chain observer"); - let work_epoch = current_epoch - .offset_to_signer_retrieval_epoch() - .expect("epoch.offset_by SIGNER_EPOCH_RETRIEVAL_OFFSET should not fail"); - let epoch_to_sign = current_epoch.offset_to_next_signer_retrieval_epoch(); - - (work_epoch, epoch_to_sign) - } - - /// `TEST METHOD ONLY` - /// - /// Fill the stores of a [DependencyManager] in a way to simulate an aggregator state + /// Fill the stores of this container in a way to simulate an aggregator state /// using the data from a precomputed fixture. + /// + /// Data will be inserted in the given `next_aggregation_epoch`, the current aggregation epoch + /// (`next_aggregation_epoch - 1`), and the signer registration epoch (`next_aggregation_epoch + 1`). + /// + /// Note: `epoch_settings` store must have data for the inserted epochs, this should be done + /// automatically when building the [ServeCommandDependenciesContainer] by `handle_discrepancies_at_startup` pub async fn init_state_from_fixture( &self, fixture: &MithrilFixture, - cardano_transactions_signing_config: &CardanoTransactionsSigningConfig, - target_epochs: &[Epoch], + next_aggregation_epoch: Epoch, ) { - for epoch in target_epochs { - self.epoch_settings_storer - .save_epoch_settings( - *epoch, - AggregatorEpochSettings { - protocol_parameters: fixture.protocol_parameters(), - cardano_transactions_signing_config: cardano_transactions_signing_config - .clone(), - }, - ) - .await - .expect("save_epoch_settings should not fail"); - self.fill_verification_key_store(*epoch, &fixture.signers_with_stake()) - .await; - self.fill_stakes_store(*epoch, fixture.signers_with_stake()).await; - } + self.init_state_from_fixture_internal( + fixture, + [ + next_aggregation_epoch.offset_to_signer_retrieval_epoch().unwrap(), + next_aggregation_epoch, + next_aggregation_epoch.offset_to_recording_epoch(), + ], + ) + .await } /// `TEST METHOD ONLY` /// - /// Fill the stores of a [DependencyManager] in a way to simulate an aggregator genesis state. + /// Fill the stores of this container in a way to simulate an aggregator state ready to sign a + /// genesis certificate using the data from a precomputed fixture. + /// + /// Data will be inserted in the given `next_aggregation_epoch`, the current aggregation epoch + /// (`next_aggregation_epoch - 1`). /// - /// For the current and the next epoch: - /// * Fill the [VerificationKeyStorer] with the given signers keys. - /// * Fill the [StakeStore] with the given signers stakes. - /// * Fill the [ProtocolParametersStore] with the given parameters. - pub async fn prepare_for_genesis( + /// Note: `epoch_settings` store must have data for the inserted epochs, this should be done + /// automatically when building the [ServeCommandDependenciesContainer] by `handle_discrepancies_at_startup` + pub async fn init_state_from_fixture_for_genesis( &self, - genesis_signers: Vec, - second_epoch_signers: Vec, - genesis_protocol_parameters: &ProtocolParameters, - cardano_transactions_signing_config: &CardanoTransactionsSigningConfig, + fixture: &MithrilFixture, + next_aggregation_epoch: Epoch, ) { - self.init_epoch_settings_storer(&AggregatorEpochSettings { - protocol_parameters: genesis_protocol_parameters.clone(), - cardano_transactions_signing_config: cardano_transactions_signing_config.clone(), - }) - .await; - - let (work_epoch, epoch_to_sign) = self.get_genesis_epochs().await; - for (epoch, signers) in - [(work_epoch, genesis_signers), (epoch_to_sign, second_epoch_signers)] - { - self.fill_verification_key_store(epoch, &signers).await; - self.fill_stakes_store(epoch, signers).await; - } + self.init_state_from_fixture_internal( + fixture, + [ + next_aggregation_epoch.offset_to_signer_retrieval_epoch().unwrap(), + next_aggregation_epoch, + ], + ) + .await } - /// `TEST METHOD ONLY` - /// - /// Fill up to the first three epochs of the [EpochSettingsStorer] with the given value. - pub async fn init_epoch_settings_storer(&self, epoch_settings: &AggregatorEpochSettings) { - let (work_epoch, epoch_to_sign) = self.get_genesis_epochs().await; - let mut epochs_to_save = Vec::new(); - epochs_to_save.push(work_epoch); - epochs_to_save.push(epoch_to_sign); - epochs_to_save.push(epoch_to_sign.next()); - for epoch in epochs_to_save { - self.epoch_settings_storer - .save_epoch_settings(epoch, epoch_settings.clone()) - .await - .expect("save_epoch_settings should not fail"); + async fn init_state_from_fixture_internal( + &self, + fixture: &MithrilFixture, + epochs_to_fill: [Epoch; N], + ) { + for epoch in epochs_to_fill { + self.fill_verification_key_store(epoch, &fixture.signers_with_stake()) + .await; + self.fill_stakes_store(epoch, fixture.signers_with_stake()).await; } } @@ -250,13 +212,11 @@ impl ServeCommandDependenciesContainer { #[cfg(test)] pub(crate) mod tests { + use std::path::PathBuf; - use std::{path::PathBuf, sync::Arc}; + use crate::{ServeCommandConfiguration, dependency_injection::DependenciesBuilder}; - use crate::{ - ServeCommandConfiguration, ServeCommandDependenciesContainer, - dependency_injection::DependenciesBuilder, - }; + use super::*; /// Initialize dependency container with a unique temporary snapshot directory build from test path. /// This macro should used directly in a function test to be able to retrieve the function name. diff --git a/mithril-aggregator/src/runtime/runner.rs b/mithril-aggregator/src/runtime/runner.rs index 758f9ffaf9e..3d412abc124 100644 --- a/mithril-aggregator/src/runtime/runner.rs +++ b/mithril-aggregator/src/runtime/runner.rs @@ -530,9 +530,8 @@ pub mod tests { use mithril_common::{ StdResult, entities::{ - CardanoTransactionsSigningConfig, ChainPoint, Epoch, ProtocolMessage, - SignedEntityConfig, SignedEntityType, SignedEntityTypeDiscriminants, StakeDistribution, - TimePoint, + ChainPoint, Epoch, ProtocolMessage, SignedEntityConfig, SignedEntityType, + SignedEntityTypeDiscriminants, StakeDistribution, TimePoint, }, signable_builder::SignableBuilderService, temp_dir, @@ -549,12 +548,11 @@ pub mod tests { MithrilSignerRegistrationLeader, ServeCommandConfiguration, ServeCommandDependenciesContainer, SignerRegistrationRound, dependency_injection::DependenciesBuilder, - entities::{AggregatorEpochSettings, OpenMessage}, + entities::OpenMessage, initialize_dependencies, runtime::{AggregatorRunner, AggregatorRunnerTrait}, services::{ - FakeEpochService, FakeEpochServiceBuilder, MithrilStakeDistributionService, - MockCertifierService, MockUpkeepService, + FakeEpochService, FakeEpochServiceBuilder, MockCertifierService, MockUpkeepService, }, }; @@ -576,17 +574,8 @@ pub mod tests { deps: ServeCommandDependenciesContainer, ) -> AggregatorRunner { let fixture = MithrilFixtureBuilder::default().with_signers(5).build(); - let current_epoch = deps.chain_observer.get_current_epoch().await.unwrap().unwrap(); - deps.init_state_from_fixture( - &fixture, - &CardanoTransactionsSigningConfig::dummy(), - &[ - current_epoch.offset_to_signer_retrieval_epoch().unwrap(), - current_epoch, - current_epoch.next(), - ], - ) - .await; + let current_epoch = deps.ticker_service.get_current_epoch().await.unwrap(); + deps.init_state_from_fixture(&fixture, current_epoch).await; AggregatorRunner::new(Arc::new(deps)) } @@ -704,13 +693,11 @@ pub mod tests { async fn test_update_stake_distribution() { let chain_observer = Arc::new(FakeChainObserver::default()); let deps = { - let mut deps = initialize_dependencies!().await; - deps.chain_observer = chain_observer.clone(); - deps.stake_distribution_service = Arc::new(MithrilStakeDistributionService::new( - deps.stake_store.clone(), - chain_observer.clone(), - )); - Arc::new(deps) + let config = ServeCommandConfiguration::new_sample(temp_dir!()); + let mut builder = DependenciesBuilder::new_with_stdout_logger(Arc::new(config)); + builder.chain_observer = Some(chain_observer.clone()); + + Arc::new(builder.build_serve_dependencies_container().await.unwrap()) }; let runner = AggregatorRunner::new(deps.clone()); let time_point = runner.get_time_point_from_chain().await.unwrap(); @@ -865,7 +852,7 @@ pub mod tests { .returning(|_| Ok(())) .times(1); let mut deps = initialize_dependencies!().await; - let current_epoch = deps.chain_observer.get_current_epoch().await.unwrap().unwrap(); + let current_epoch = deps.ticker_service.get_current_epoch().await.unwrap(); deps.certifier_service = Arc::new(mock_certifier_service); deps.epoch_service = Arc::new(RwLock::new(FakeEpochService::from_fixture( @@ -898,7 +885,7 @@ pub mod tests { #[tokio::test] async fn test_precompute_epoch_data() { let mut deps = initialize_dependencies!().await; - let current_epoch = deps.chain_observer.get_current_epoch().await.unwrap().unwrap(); + let current_epoch = deps.ticker_service.get_current_epoch().await.unwrap(); deps.epoch_service = Arc::new(RwLock::new(FakeEpochService::from_fixture( current_epoch, diff --git a/mithril-aggregator/src/services/certifier/certifier_service.rs b/mithril-aggregator/src/services/certifier/certifier_service.rs index a4a2777d83f..725cee1abfb 100644 --- a/mithril-aggregator/src/services/certifier/certifier_service.rs +++ b/mithril-aggregator/src/services/certifier/certifier_service.rs @@ -409,22 +409,24 @@ impl CertifierService for MithrilCertifierService { #[cfg(test)] mod tests { + use chrono::{DateTime, Days}; use std::path::PathBuf; + use tokio::sync::RwLock; - use crate::{ - ServeCommandConfiguration, dependency_injection::DependenciesBuilder, - multi_signer::MockMultiSigner, services::FakeEpochService, test::TestLogger, - }; - use chrono::{DateTime, Days}; + use mithril_cardano_node_chain::test::double::FakeChainObserver; use mithril_common::{ - entities::{CardanoDbBeacon, ProtocolMessagePartKey}, + entities::{CardanoDbBeacon, ProtocolMessagePartKey, TimePoint}, temp_dir, test::{ builder::{MithrilFixture, MithrilFixtureBuilder}, - double::fake_data, + double::{Dummy, fake_data}, }, }; - use tokio::sync::RwLock; + + use crate::{ + ServeCommandConfiguration, dependency_injection::DependenciesBuilder, + multi_signer::MockMultiSigner, services::FakeEpochService, test::TestLogger, + }; use super::*; @@ -458,51 +460,30 @@ mod tests { } /// Note: If current_epoch is provided the [EpochService] will be automatically initialized - async fn setup_certifier_service_with_network( + async fn setup_certifier_service( snapshot_directory: PathBuf, - network: CardanoNetwork, fixture: &MithrilFixture, - epochs_with_signers: &[Epoch], - current_epoch: Option, + current_epoch: Epoch, ) -> MithrilCertifierService { let configuration = ServeCommandConfiguration::new_sample(snapshot_directory); - let cardano_transactions_signing_config = - configuration.cardano_transactions_signing_config.clone(); let mut dependency_builder = DependenciesBuilder::new_with_stdout_logger(Arc::new(configuration)); - if let Some(epoch) = current_epoch { - dependency_builder.epoch_service = Some(Arc::new(RwLock::new( - FakeEpochService::from_fixture(epoch, fixture), - ))); - } + dependency_builder.epoch_service = Some(Arc::new(RwLock::new( + FakeEpochService::from_fixture(current_epoch, fixture), + ))); + dependency_builder.chain_observer = + Some(Arc::new(FakeChainObserver::new(Some(TimePoint { + epoch: current_epoch, + ..Dummy::dummy() + })))); let dependency_manager = dependency_builder.build_serve_dependencies_container().await.unwrap(); dependency_manager - .init_state_from_fixture( - fixture, - &cardano_transactions_signing_config.unwrap(), - epochs_with_signers, - ) + .init_state_from_fixture(fixture, current_epoch) .await; - MithrilCertifierService::from_deps(network, dependency_builder).await - } - - async fn setup_certifier_service( - snapshot_directory: PathBuf, - fixture: &MithrilFixture, - epochs_with_signers: &[Epoch], - current_epoch: Option, - ) -> MithrilCertifierService { - setup_certifier_service_with_network( - snapshot_directory, - fake_data::network(), - fixture, - epochs_with_signers, - current_epoch, - ) - .await + MithrilCertifierService::from_deps(fake_data::network(), dependency_builder).await } #[tokio::test] @@ -511,10 +492,8 @@ mod tests { let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); let protocol_message = ProtocolMessage::new(); let epoch = beacon.epoch; - let epochs_with_signers = (1..=5).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(5).build(); - let certifier_service = - setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await; + let certifier_service = setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; certifier_service .create_open_message(&signed_entity_type, &protocol_message) .await @@ -529,10 +508,8 @@ mod tests { let beacon = CardanoDbBeacon::new(3, 1); let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); let protocol_message = ProtocolMessage::new(); - let epochs_with_signers = (1..=5).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(1).build(); - let certifier_service = - setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await; + let certifier_service = setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; let mut open_message = certifier_service .open_message_repository .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message) @@ -562,10 +539,8 @@ mod tests { let beacon = CardanoDbBeacon::new(3, 1); let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); let protocol_message = ProtocolMessage::new(); - let epochs_with_signers = (1..=5).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(1).build(); - let certifier_service = - setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await; + let certifier_service = setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; let mut open_message = certifier_service .open_message_repository .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message) @@ -590,10 +565,8 @@ mod tests { let beacon = CardanoDbBeacon::new(3, 1); let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); let protocol_message = ProtocolMessage::new(); - let epochs_with_signers = (1..=5).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(1).build(); - let certifier_service = - setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await; + let certifier_service = setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; let mut open_message = certifier_service .open_message_repository .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message) @@ -618,15 +591,8 @@ mod tests { let beacon = CardanoDbBeacon::new(3, 1); let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); let protocol_message = ProtocolMessage::new(); - let epochs_with_signers = (1..=3).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(1).build(); - let certifier_service = setup_certifier_service( - temp_dir!(), - &fixture, - &epochs_with_signers, - Some(beacon.epoch), - ) - .await; + let certifier_service = setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; certifier_service .create_open_message(&signed_entity_type, &protocol_message) @@ -656,15 +622,8 @@ mod tests { let beacon = CardanoDbBeacon::new(3, 1); let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); let mut protocol_message = ProtocolMessage::new(); - let epochs_with_signers = (1..=5).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(1).build(); - let certifier_service = setup_certifier_service( - temp_dir!(), - &fixture, - &epochs_with_signers, - Some(beacon.epoch), - ) - .await; + let certifier_service = setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; certifier_service .create_open_message(&signed_entity_type, &protocol_message) @@ -701,10 +660,8 @@ mod tests { let beacon = CardanoDbBeacon::new(3, 1); let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); let protocol_message = ProtocolMessage::new(); - let epochs_with_signers = (1..=5).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(1).build(); - let certifier_service = - setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await; + let certifier_service = setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; let mut open_message = certifier_service .open_message_repository .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message) @@ -734,10 +691,8 @@ mod tests { let beacon = CardanoDbBeacon::new(3, 1); let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); let protocol_message = ProtocolMessage::new(); - let epochs_with_signers = (1..=5).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(1).build(); - let certifier_service = - setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await; + let certifier_service = setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; let mut open_message = certifier_service .open_message_repository .create_open_message(beacon.epoch, &signed_entity_type, &protocol_message) @@ -764,28 +719,20 @@ mod tests { #[tokio::test] async fn should_create_certificate_when_multi_signature_produced() { - let network = fake_data::network(); let beacon = CardanoDbBeacon::new(3, 1); let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); let mut protocol_message = ProtocolMessage::new(); protocol_message.set_message_part(ProtocolMessagePartKey::CurrentEpoch, "3".to_string()); - let epochs_with_signers = (1..=3).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(3).build(); - let certifier_service = setup_certifier_service_with_network( - temp_dir!(), - network, - &fixture, - &epochs_with_signers, - Some(beacon.epoch), - ) - .await; + let certifier_service = setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; certifier_service .create_open_message(&signed_entity_type, &protocol_message) .await .unwrap(); - let genesis_certificate = fixture.create_genesis_certificate(network, beacon.epoch - 1); + let genesis_certificate = + fixture.create_genesis_certificate(certifier_service.network, beacon.epoch - 1); certifier_service .certificate_repository .create_certificate(genesis_certificate) @@ -843,10 +790,8 @@ mod tests { async fn should_not_create_certificate_for_open_message_not_created() { let beacon = CardanoDbBeacon::new(1, 1); let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); - let epochs_with_signers = (1..=5).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(5).build(); - let certifier_service = - setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await; + let certifier_service = setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; certifier_service .create_certificate(&signed_entity_type) .await @@ -859,19 +804,33 @@ mod tests { let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); let protocol_message = ProtocolMessage::new(); let epoch = beacon.epoch; - let epochs_with_signers = (1..=5).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(5).build(); - let certifier_service = - setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await; - certifier_service + let certifier_service = setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; + let mut record = certifier_service .open_message_repository .create_open_message(epoch, &signed_entity_type, &protocol_message) .await .unwrap(); + record.is_certified = true; certifier_service + .open_message_repository + .update_open_message(&record) + .await + .unwrap(); + + let error = certifier_service .create_certificate(&signed_entity_type) .await .expect_err("create_certificate should fail"); + + if let Some(err) = error.downcast_ref::() { + assert!(matches!( + err, + CertifierServiceError::AlreadyCertified(signed_entity) if signed_entity == &signed_entity_type + ),); + } else { + panic!("Unexpected error {error:?}"); + } } #[tokio::test] @@ -883,10 +842,9 @@ mod tests { let beacon = CardanoDbBeacon::new(1, 1); let signed_entity_type = SignedEntityType::CardanoImmutableFilesFull(beacon.clone()); let protocol_message = ProtocolMessage::new(); - let epochs_with_signers = (1..=5).map(Epoch).collect::>(); let fixture = MithrilFixtureBuilder::default().with_signers(5).build(); let mut certifier_service = - setup_certifier_service(temp_dir!(), &fixture, &epochs_with_signers, None).await; + setup_certifier_service(temp_dir!(), &fixture, beacon.epoch).await; certifier_service.multi_signer = Arc::new(mock_multi_signer); certifier_service .create_open_message(&signed_entity_type, &protocol_message) @@ -902,10 +860,9 @@ mod tests { #[tokio::test] async fn test_epoch_gap_certificate_chain() { let builder = MithrilFixtureBuilder::default(); - let certifier_service = - setup_certifier_service(temp_dir!(), &builder.build(), &[], None).await; let certificate = fake_data::genesis_certificate("whatever"); let epoch = certificate.epoch + 2; + let certifier_service = setup_certifier_service(temp_dir!(), &builder.build(), epoch).await; certifier_service .certificate_repository .create_certificate(certificate) @@ -925,10 +882,9 @@ mod tests { #[tokio::test] async fn test_epoch_gap_certificate_chain_ok() { let builder = MithrilFixtureBuilder::default(); - let certifier_service = - setup_certifier_service(temp_dir!(), &builder.build(), &[], None).await; let certificate = fake_data::genesis_certificate("whatever"); let epoch = certificate.epoch + 1; + let certifier_service = setup_certifier_service(temp_dir!(), &builder.build(), epoch).await; certifier_service .certificate_repository .create_certificate(certificate) diff --git a/mithril-aggregator/tests/test_extensions/runtime_tester.rs b/mithril-aggregator/tests/test_extensions/runtime_tester.rs index 6c7e91cab13..49aa5ba52e4 100644 --- a/mithril-aggregator/tests/test_extensions/runtime_tester.rs +++ b/mithril-aggregator/tests/test_extensions/runtime_tester.rs @@ -24,10 +24,9 @@ use mithril_common::{ StdResult, crypto_helper::ProtocolGenesisSigner, entities::{ - BlockNumber, CardanoTransactionsSigningConfig, Certificate, CertificateSignature, - ChainPoint, Epoch, ImmutableFileNumber, SignedEntityType, SignedEntityTypeDiscriminants, - SingleSignatureAuthenticationStatus, SlotNumber, StakeDistribution, SupportedEra, - TimePoint, + BlockNumber, Certificate, CertificateSignature, ChainPoint, Epoch, ImmutableFileNumber, + SignedEntityType, SignedEntityTypeDiscriminants, SingleSignatureAuthenticationStatus, + SlotNumber, StakeDistribution, SupportedEra, TimePoint, }, test::{ builder::{ @@ -127,7 +126,6 @@ macro_rules! assert_metrics_eq { pub struct RuntimeTester { pub network: String, - pub cardano_transactions_signing_config: CardanoTransactionsSigningConfig, pub snapshot_uploader: Arc, pub chain_observer: Arc, pub immutable_file_observer: Arc, @@ -159,8 +157,6 @@ impl RuntimeTester { let logger = build_logger(); let global_logger = slog_scope::set_global_logger(logger.clone()); let network = configuration.network.clone(); - let cardano_transactions_signing_config = - configuration.cardano_transactions_signing_config.clone().unwrap(); let snapshot_uploader = Arc::new(DumbUploader::default()); let immutable_file_observer = Arc::new(DumbImmutableFileObserver::new()); immutable_file_observer @@ -196,7 +192,6 @@ impl RuntimeTester { Self { network, - cardano_transactions_signing_config, snapshot_uploader, chain_observer, immutable_file_observer, @@ -228,13 +223,9 @@ impl RuntimeTester { self.chain_observer.set_signers(fixture.signers_with_stake()).await; // Init the stores needed for a genesis certificate - let genesis_epochs = self.dependencies.get_genesis_epochs().await; + let time_point = self.observer.current_time_point().await; self.dependencies - .init_state_from_fixture( - fixture, - &self.cardano_transactions_signing_config, - &[genesis_epochs.0, genesis_epochs.1], - ) + .init_state_from_fixture_for_genesis(fixture, time_point.epoch) .await; Ok(()) } From 0504939a08d058fe59f5d18619a0ff07303d6a07 Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Wed, 5 Nov 2025 17:10:50 +0100 Subject: [PATCH 11/15] feat(aggregator): move & rework handle discrepancies - run it at the end of the serve dependency container build - retrieve and save data from the network configuration provider instead of the local node configuration - update follower integration test to check that local protocol parameter configuration is not read, instead the configuration is read through the network configuration provider from the leader --- .../builder/enablers/epoch.rs | 2 - .../src/dependency_injection/builder/mod.rs | 60 +++++++++++- .../builder/support/stores.rs | 46 +-------- .../src/store/epoch_settings_storer.rs | 96 ++++++++++++------- .../tests/create_certificate_follower.rs | 3 + 5 files changed, 124 insertions(+), 83 deletions(-) diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs index e16a109c6fa..1f45c91158e 100644 --- a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs +++ b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs @@ -61,8 +61,6 @@ impl DependenciesBuilder { )) }; - //TODO handle discrepency here - Ok(network_configuration_provider) } diff --git a/mithril-aggregator/src/dependency_injection/builder/mod.rs b/mithril-aggregator/src/dependency_injection/builder/mod.rs index ba6836347a3..ef320d9130d 100644 --- a/mithril-aggregator/src/dependency_injection/builder/mod.rs +++ b/mithril-aggregator/src/dependency_injection/builder/mod.rs @@ -3,7 +3,7 @@ mod protocol; mod support; use anyhow::Context; -use slog::Logger; +use slog::{Logger, debug}; use std::{path::PathBuf, sync::Arc}; use tokio::{ sync::{ @@ -45,10 +45,11 @@ use super::{ GenesisCommandDependenciesContainer, Result, ToolsCommandDependenciesContainer, }; use crate::{ - AggregatorConfig, AggregatorRunner, AggregatorRuntime, ImmutableFileDigestMapper, - MetricsService, MithrilSignerRegistrationLeader, MultiSigner, ProtocolParametersRetriever, - ServeCommandDependenciesContainer, SignerRegisterer, SignerRegistrationRoundOpener, - SignerRegistrationVerifier, SingleSignatureAuthenticator, VerificationKeyStorer, + AggregatorConfig, AggregatorRunner, AggregatorRuntime, EpochSettingsStorer, + ImmutableFileDigestMapper, MetricsService, MithrilSignerRegistrationLeader, MultiSigner, + ProtocolParametersRetriever, ServeCommandDependenciesContainer, SignerRegisterer, + SignerRegistrationRoundOpener, SignerRegistrationVerifier, SingleSignatureAuthenticator, + VerificationKeyStorer, configuration::ConfigurationSource, database::repository::{ CertificateRepository, EpochSettingsStore, OpenMessageRepository, SignedEntityStorer, @@ -411,6 +412,8 @@ impl DependenciesBuilder { metrics_service: self.get_metrics_service().await?, }; + self.handle_discrepancies_at_startup().await?; + Ok(dependencies_manager) } @@ -527,6 +530,53 @@ impl DependenciesBuilder { Ok(dependencies) } + /// Look for discrepancies in stored data and fix them. + /// + /// Fix discrepancies for: + /// - epoch settings: ensure that the network configuration parameters for the three working epochs + /// window are stored in the database (see [mithril_protocol_config::model::MithrilNetworkConfiguration]) + pub async fn handle_discrepancies_at_startup(&mut self) -> Result<()> { + let logger = self.root_logger(); + let current_epoch = self + .get_chain_observer() + .await? + .get_current_epoch() + .await + .map_err(|e| DependenciesBuilderError::Initialization { + message: "cannot handle startup discrepancies: failed to retrieve current epoch." + .to_string(), + error: Some(e.into()), + })? + .ok_or(DependenciesBuilderError::Initialization { + message: "cannot handle startup discrepancies: no epoch returned.".to_string(), + error: None, + })?; + let network_configuration = self + .get_mithril_network_configuration_provider() + .await? + .get_network_configuration(current_epoch) + .await + .map_err(|e| DependenciesBuilderError::Initialization { + message: format!("cannot handle startup discrepancies: failed to retrieve network configuration for epoch {current_epoch}"), + error: Some(e), + })?; + let epoch_settings_store = self.get_epoch_settings_store().await?; + + debug!( + logger, + "Handle discrepancies at startup of epoch settings store, will record epoch settings from the configuration for epoch {current_epoch}"; + "network_configuration" => ?network_configuration, + ); + epoch_settings_store + .handle_discrepancies_at_startup(&network_configuration) + .await + .map_err(|e| DependenciesBuilderError::Initialization { + message: "can not create aggregator runner".to_string(), + error: Some(e), + })?; + Ok(()) + } + /// Remove the dependencies builder from memory to release Arc instances. pub async fn vanish(self) { self.drop_sqlite_connections().await; diff --git a/mithril-aggregator/src/dependency_injection/builder/support/stores.rs b/mithril-aggregator/src/dependency_injection/builder/support/stores.rs index d2113380cad..32e3ac6b3c7 100644 --- a/mithril-aggregator/src/dependency_injection/builder/support/stores.rs +++ b/mithril-aggregator/src/dependency_injection/builder/support/stores.rs @@ -1,5 +1,4 @@ use anyhow::Context; -use slog::debug; use std::sync::Arc; use std::time::Duration; @@ -11,11 +10,11 @@ use crate::database::repository::{ OpenMessageRepository, SignedEntityStore, SignedEntityStorer, SignerRegistrationStore, SignerStore, StakePoolStore, }; -use crate::dependency_injection::{DependenciesBuilder, DependenciesBuilderError, Result}; +use crate::dependency_injection::{DependenciesBuilder, Result}; use crate::get_dependency; use crate::{ - CExplorerSignerRetriever, EpochSettingsStorer, ImmutableFileDigestMapper, - ProtocolParametersRetriever, SignersImporter, VerificationKeyStorer, + CExplorerSignerRetriever, ImmutableFileDigestMapper, ProtocolParametersRetriever, + SignersImporter, VerificationKeyStorer, }; impl DependenciesBuilder { @@ -89,52 +88,15 @@ impl DependenciesBuilder { } async fn build_epoch_settings_store(&mut self) -> Result> { - let logger = self.root_logger(); let epoch_settings_store = EpochSettingsStore::new( self.get_sqlite_connection().await?, self.configuration.safe_epoch_retention_limit(), ); - let current_epoch = self - .get_chain_observer() - .await? - .get_current_epoch() - .await - .map_err(|e| DependenciesBuilderError::Initialization { - message: "cannot create aggregator runner: failed to retrieve current epoch." - .to_string(), - error: Some(e.into()), - })? - .ok_or(DependenciesBuilderError::Initialization { - message: "cannot build aggregator runner: no epoch returned.".to_string(), - error: None, - })?; - let retrieval_epoch = current_epoch - .offset_to_signer_retrieval_epoch() - .map_err(|e| DependenciesBuilderError::Initialization { - message: format!("cannot create aggregator runner: failed to offset current epoch '{current_epoch}' to signer retrieval epoch."), - error: Some(e.into()), - })?; - - let epoch_settings_configuration = self - .configuration - .get_leader_aggregator_epoch_settings_configuration()?; - debug!( - logger, - "Handle discrepancies at startup of epoch settings store, will record epoch settings from the configuration for epoch {retrieval_epoch}"; - "epoch_settings_configuration" => ?epoch_settings_configuration, - ); - epoch_settings_store - .handle_discrepancies_at_startup(retrieval_epoch, &epoch_settings_configuration) - .await - .map_err(|e| DependenciesBuilderError::Initialization { - message: "can not create aggregator runner".to_string(), - error: Some(e), - })?; Ok(Arc::new(epoch_settings_store)) } - /// Get a configured [EpochSettingsStorer]. + /// Get a configured [EpochSettingsStore]. pub async fn get_epoch_settings_store(&mut self) -> Result> { get_dependency!(self.epoch_settings_store) } diff --git a/mithril-aggregator/src/store/epoch_settings_storer.rs b/mithril-aggregator/src/store/epoch_settings_storer.rs index c8d93362bd2..f971d77c851 100644 --- a/mithril-aggregator/src/store/epoch_settings_storer.rs +++ b/mithril-aggregator/src/store/epoch_settings_storer.rs @@ -1,12 +1,13 @@ +use anyhow::anyhow; +use async_trait::async_trait; #[cfg(test)] use std::collections::HashMap; - -use async_trait::async_trait; -use mithril_common::StdResult; #[cfg(test)] use tokio::sync::RwLock; +use mithril_common::StdResult; use mithril_common::entities::{Epoch, ProtocolParameters}; +use mithril_protocol_config::model::MithrilNetworkConfiguration; use crate::{entities::AggregatorEpochSettings, services::EpochPruningTask}; @@ -43,14 +44,37 @@ pub trait EpochSettingsStorer: /// call and the epoch service call. async fn handle_discrepancies_at_startup( &self, - current_epoch: Epoch, - epoch_settings_configuration: &AggregatorEpochSettings, + network_configuration: &MithrilNetworkConfiguration, ) -> StdResult<()> { - for epoch_offset in 0..=3 { - let epoch = current_epoch + epoch_offset; + for (epoch, epoch_configuration) in [ + ( + network_configuration.epoch.offset_to_signer_retrieval_epoch()?, + &network_configuration.configuration_for_aggregation, + ), + ( + network_configuration.epoch, + &network_configuration.configuration_for_next_aggregation, + ), + ( + network_configuration.epoch.offset_to_recording_epoch(), + &network_configuration.configuration_for_registration, + ), + ] { if self.get_epoch_settings(epoch).await?.is_none() { - self.save_epoch_settings(epoch, epoch_settings_configuration.clone()) - .await?; + self.save_epoch_settings( + epoch, + AggregatorEpochSettings { + protocol_parameters: epoch_configuration.protocol_parameters.clone(), + cardano_transactions_signing_config: epoch_configuration + .signed_entity_types_config + .cardano_transactions + .clone() + .ok_or(anyhow!( + "missing cardano transactions signing config for epoch {epoch}" + ))?, + }, + ) + .await?; } } @@ -116,7 +140,8 @@ impl EpochPruningTask for FakeEpochSettingsStorer { #[cfg(test)] mod tests { - use mithril_common::entities::CardanoTransactionsSigningConfig; + use std::collections::BTreeSet; + use mithril_common::test::double::Dummy; use super::*; @@ -178,40 +203,43 @@ mod tests { #[tokio::test] async fn test_handle_discrepancies_at_startup_should_complete_at_least_four_epochs() { let epoch_settings = AggregatorEpochSettings::dummy(); - let epoch_settings_new = AggregatorEpochSettings { - protocol_parameters: ProtocolParameters { - k: epoch_settings.protocol_parameters.k + 1, - ..epoch_settings.protocol_parameters - }, - cardano_transactions_signing_config: CardanoTransactionsSigningConfig { - step: epoch_settings.cardano_transactions_signing_config.step + 1, - ..epoch_settings.cardano_transactions_signing_config - }, - }; - let epoch = Epoch(1); - let store = FakeEpochSettingsStorer::new(vec![ - (epoch, epoch_settings.clone()), - (epoch + 1, epoch_settings.clone()), - ]); + let mut aggregation_epoch_settings = epoch_settings.clone(); + aggregation_epoch_settings.protocol_parameters.k += 15; + let mut next_aggregation_epoch_settings = epoch_settings.clone(); + next_aggregation_epoch_settings.protocol_parameters.k += 26; + + let mut registration_epoch_settings = epoch_settings.clone(); + registration_epoch_settings.protocol_parameters.k += 37; + + let epoch = Epoch(5); + let store = FakeEpochSettingsStorer::new(vec![]); store - .handle_discrepancies_at_startup(epoch, &epoch_settings_new) + .handle_discrepancies_at_startup(&MithrilNetworkConfiguration { + epoch, + configuration_for_aggregation: aggregation_epoch_settings + .clone() + .into_network_configuration_for_epoch(BTreeSet::new()), + configuration_for_next_aggregation: next_aggregation_epoch_settings + .clone() + .into_network_configuration_for_epoch(BTreeSet::new()), + configuration_for_registration: registration_epoch_settings + .clone() + .into_network_configuration_for_epoch(BTreeSet::new()), + }) .await .unwrap(); + let epoch_settings_stored = store.get_epoch_settings(epoch - 1).await.unwrap(); + assert_eq!(Some(aggregation_epoch_settings), epoch_settings_stored); + let epoch_settings_stored = store.get_epoch_settings(epoch).await.unwrap(); - assert_eq!(Some(epoch_settings.clone()), epoch_settings_stored); + assert_eq!(Some(next_aggregation_epoch_settings), epoch_settings_stored); let epoch_settings_stored = store.get_epoch_settings(epoch + 1).await.unwrap(); - assert_eq!(Some(epoch_settings.clone()), epoch_settings_stored); + assert_eq!(Some(registration_epoch_settings), epoch_settings_stored); let epoch_settings_stored = store.get_epoch_settings(epoch + 2).await.unwrap(); - assert_eq!(Some(epoch_settings_new.clone()), epoch_settings_stored); - - let epoch_settings_stored = store.get_epoch_settings(epoch + 3).await.unwrap(); - assert_eq!(Some(epoch_settings_new.clone()), epoch_settings_stored); - - let epoch_settings_stored = store.get_epoch_settings(epoch + 4).await.unwrap(); assert!(epoch_settings_stored.is_none()); } } diff --git a/mithril-aggregator/tests/create_certificate_follower.rs b/mithril-aggregator/tests/create_certificate_follower.rs index 9f75c791a51..2fea0d9b18e 100644 --- a/mithril-aggregator/tests/create_certificate_follower.rs +++ b/mithril-aggregator/tests/create_certificate_follower.rs @@ -117,6 +117,9 @@ async fn create_certificate_follower() { "create_certificate_follower", ), leader_aggregator_endpoint: Some(leader_aggregator_http_server.url().to_string()), + // Follower must retrieve parameters from the network configuration (today through the leader) + // so this parameters should not be read + protocol_parameters: None, ..leader_configuration }; let mut follower_tester = RuntimeTester::build(start_time_point, follower_configuration).await; From e348709c5fdb9b6379de738512900060114e7264 Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Wed, 5 Nov 2025 17:18:24 +0100 Subject: [PATCH 12/15] chore(aggregator): log when a dependency is built by the dep builder --- mithril-aggregator/src/dependency_injection/builder/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/mithril-aggregator/src/dependency_injection/builder/mod.rs b/mithril-aggregator/src/dependency_injection/builder/mod.rs index ef320d9130d..17f54dc39dd 100644 --- a/mithril-aggregator/src/dependency_injection/builder/mod.rs +++ b/mithril-aggregator/src/dependency_injection/builder/mod.rs @@ -82,6 +82,7 @@ macro_rules! get_dependency { ( $self:ident.$attribute:ident = $builder:expr ) => {{ paste::paste! { if $self.$attribute.is_none() { + slog::debug!($self.root_logger(), "Building dependency {}", stringify!($attribute)); $self.$attribute = Some($builder); } From 81b9bf892705a76a1f77b0ce4080f459f9c224ea Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:09:44 +0100 Subject: [PATCH 13/15] fix(e2e): only update protocol parameters on leader aggregator Since now the follower read the network config from the leader, this means that the update of the protocol parameters is now a responsability of the leader only. This lead to flakiness because this step was restarting all aggregators, and sometimes the follower started before the leader and had a error when it executed its handle discrepencies because the leader http server was still down. --- mithril-test-lab/mithril-end-to-end/src/end_to_end_spec.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mithril-test-lab/mithril-end-to-end/src/end_to_end_spec.rs b/mithril-test-lab/mithril-end-to-end/src/end_to_end_spec.rs index 1a3aa4078a0..04baacad1d0 100644 --- a/mithril-test-lab/mithril-end-to-end/src/end_to_end_spec.rs +++ b/mithril-test-lab/mithril-end-to-end/src/end_to_end_spec.rs @@ -136,7 +136,9 @@ impl Spec { "epoch after which the protocol parameters will change".to_string(), ) .await?; - assertions::update_protocol_parameters(aggregator).await?; + if aggregator.is_first() { + assertions::update_protocol_parameters(aggregator).await?; + } // Wait 6 epochs after protocol parameters update, so that we make sure that we use new protocol parameters as well as new stake distribution a few times target_epoch += 6; From f1186e4166721b82b46d7d7a3a058ff0f9502952 Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:26:49 +0100 Subject: [PATCH 14/15] feat(e2e): output logs in expandable group when running in github actions --- .../src/utils/formatting.rs | 37 +++++++++++++++++++ .../src/utils/mithril_command.rs | 26 +++++-------- .../mithril-end-to-end/src/utils/mod.rs | 6 +++ 3 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 mithril-test-lab/mithril-end-to-end/src/utils/formatting.rs diff --git a/mithril-test-lab/mithril-end-to-end/src/utils/formatting.rs b/mithril-test-lab/mithril-end-to-end/src/utils/formatting.rs new file mode 100644 index 00000000000..9c069a98c4e --- /dev/null +++ b/mithril-test-lab/mithril-end-to-end/src/utils/formatting.rs @@ -0,0 +1,37 @@ +use crate::utils::is_running_in_github_actions; + +/// Handle printing a log group title and a separator. +/// +/// If running in GitHub actions, the logs produced between the creation of the group and its +/// drop will be wrapped within a `::group::` and `::endgroup::` github action command. +pub struct LogGroup; + +impl LogGroup { + pub fn new(name: &str, title: &str) -> Self { + let group_title = Self::group_title(name, title); + if is_running_in_github_actions() { + println!("::group::{group_title}"); + } + Self::print_header(&group_title); + + Self + } + + fn group_title(name: &str, title: &str) -> String { + format!("{} LOGS - {}:", name.to_uppercase(), title) + } + + fn print_header(title: &str) { + println!("{:-^100}", ""); + println!("{title:^30}",); + println!("{:-^100}", ""); + } +} + +impl Drop for LogGroup { + fn drop(&mut self) { + if is_running_in_github_actions() { + println!("::endgroup::"); + } + } +} diff --git a/mithril-test-lab/mithril-end-to-end/src/utils/mithril_command.rs b/mithril-test-lab/mithril-end-to-end/src/utils/mithril_command.rs index 1823a7d8b5c..bbae3b8df57 100644 --- a/mithril-test-lab/mithril-end-to-end/src/utils/mithril_command.rs +++ b/mithril-test-lab/mithril-end-to-end/src/utils/mithril_command.rs @@ -1,4 +1,4 @@ -use crate::utils::file_utils; +use crate::utils::{LogGroup, file_utils}; use anyhow::{Context, anyhow}; use mithril_common::StdResult; use slog_scope::info; @@ -146,7 +146,10 @@ impl MithrilCommand { )); } - self.print_header(name, &format!("LAST {number_of_line} LINES")); + let _log_group_guard = LogGroup::new( + name.unwrap_or(&self.name), + &format!("LAST {number_of_line} LINES"), + ); println!( "{}", @@ -172,7 +175,10 @@ impl MithrilCommand { )); } - self.print_header(name, &format!("LAST {number_of_error} ERROR(S)")); + let _log_group_guard = LogGroup::new( + name.unwrap_or(&self.name), + &format!("LAST {number_of_error} ERROR(S)"), + ); println!( "{}", @@ -183,18 +189,4 @@ impl MithrilCommand { Ok(()) } - - fn print_header(&self, name: Option<&str>, title: &str) { - let name = match name { - Some(n) => n, - None => &self.name, - }; - - println!("{:-^100}", ""); - println!( - "{:^30}", - format!("{} LOGS - {}:", name.to_uppercase(), title) - ); - println!("{:-^100}", ""); - } } diff --git a/mithril-test-lab/mithril-end-to-end/src/utils/mod.rs b/mithril-test-lab/mithril-end-to-end/src/utils/mod.rs index 90fabb0f39b..acdd6db8084 100644 --- a/mithril-test-lab/mithril-end-to-end/src/utils/mod.rs +++ b/mithril-test-lab/mithril-end-to-end/src/utils/mod.rs @@ -2,6 +2,12 @@ mod mithril_command; #[macro_use] mod spec_utils; mod file_utils; +mod formatting; +pub use formatting::*; pub use mithril_command::MithrilCommand; pub use spec_utils::AttemptResult; + +pub fn is_running_in_github_actions() -> bool { + std::env::var("GITHUB_ACTIONS").is_ok() +} From 80abe55e226a5d35390859fa73348123f5c29a80 Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Wed, 5 Nov 2025 18:06:00 +0100 Subject: [PATCH 15/15] WIP: DO NOT MERGE: test ci failure --- .github/workflows/ci.yml | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25da5b2924e..bcc509ab51f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -344,8 +344,39 @@ jobs: next_era: [""] cardano_node_version: ["10.4.1", "10.5.1"] hard_fork_latest_era_at_epoch: [0] - run_id: ["#1", "#2", "#3"] - extra_args: ["--aggregate-signature-type=Concatenation"] + run_id: + [ + "#1", + "#2", + "#3", + "#4", + "#5", + "#6", + "#7", + "#8", + "#9", + "#10", + "#11", + "#12", + "#13", + "#14", + "#15", + "#16", + "#17", + "#18", + "#19", + "#20", + "#21", + "#22", + "#23", + "#24", + "#25", + ] + extra_args: + [ + "--aggregate-signature-type=Concatenation", + "--number-of-aggregators=2 --use-relays --relay-signer-registration-mode=passthrough --relay-signature-registration-mode=p2p --aggregate-signature-type=Concatenation", + ] include: # Include a test for partial decentralization with leader/follower signer registration and P2P signature registration