Skip to content

Commit aa82f6c

Browse files
committed
feature(aggregator, openapi): introduce a new aggregator route to retrieve protocol settings by epoch
1 parent effe078 commit aa82f6c

File tree

8 files changed

+311
-4
lines changed

8 files changed

+311
-4
lines changed

mithril-aggregator/src/dependency_injection/builder/enablers/misc.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@ impl DependenciesBuilder {
4141
self.get_sqlite_connection().await?,
4242
));
4343
let signed_entity_storer = self.get_signed_entity_storer().await?;
44+
let epoch_settings_storer = self.get_epoch_settings_store().await?;
4445
let immutable_file_digest_mapper = self.get_immutable_file_digest_mapper().await?;
4546
let epoch_service = self.get_epoch_service().await?;
4647
let service = MithrilMessageService::new(
4748
certificate_repository,
4849
signed_entity_storer,
50+
epoch_settings_storer,
4951
immutable_file_digest_mapper,
5052
epoch_service,
5153
);

mithril-aggregator/src/http_server/routes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod certificate_routes;
33
mod epoch_routes;
44
mod middlewares;
55
mod proof_routes;
6+
mod protocol_configuration_routes;
67
pub(crate) mod reply;
78
mod root_routes;
89
pub mod router;
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
use warp::Filter;
2+
3+
use crate::http_server::routes::middlewares;
4+
use crate::http_server::routes::router::RouterState;
5+
6+
pub fn routes(
7+
router_state: &RouterState,
8+
) -> impl Filter<Extract = (impl warp::Reply + use<>,), Error = warp::Rejection> + Clone + use<> {
9+
protocol_configuration(router_state)
10+
}
11+
12+
/// GET /protocol-configuration
13+
fn protocol_configuration(
14+
router_state: &RouterState,
15+
) -> impl Filter<Extract = (impl warp::Reply + use<>,), Error = warp::Rejection> + Clone + use<> {
16+
warp::path!("protocol-configuration" / String)
17+
.and(warp::get())
18+
.and(middlewares::with_logger(router_state))
19+
.and(middlewares::with_http_message_service(router_state))
20+
.and(middlewares::extract_config(router_state, |config| {
21+
config.allowed_discriminants.clone()
22+
}))
23+
.and_then(handlers::protocol_configuration)
24+
}
25+
26+
mod handlers {
27+
use slog::{Logger, warn};
28+
use std::{collections::BTreeSet, convert::Infallible, sync::Arc};
29+
30+
use mithril_common::entities::{Epoch, SignedEntityTypeDiscriminants};
31+
32+
use crate::{http_server::routes::reply, services::MessageService};
33+
34+
/// Protocol Configuration
35+
pub async fn protocol_configuration(
36+
epoch: String,
37+
logger: Logger,
38+
http_message_service: Arc<dyn MessageService>,
39+
allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
40+
) -> Result<impl warp::Reply, Infallible> {
41+
let epoch = match epoch.parse::<u64>() {
42+
Ok(epoch) => Epoch(epoch),
43+
Err(err) => {
44+
warn!(logger, "protocol_configuration::invalid_epoch"; "error" => ?err);
45+
return Ok(reply::bad_request(
46+
"invalid_epoch".to_string(),
47+
err.to_string(),
48+
));
49+
}
50+
};
51+
52+
let protocol_configuration_message = http_message_service
53+
.get_protocol_configuration_message(epoch, allowed_discriminants)
54+
.await;
55+
56+
match protocol_configuration_message {
57+
Ok(message) => Ok(reply::json(&message, warp::http::StatusCode::OK)),
58+
Err(err) => {
59+
slog::warn!(logger, "protocol_configuration::error"; "error" => ?err);
60+
Ok(reply::server_error(err))
61+
}
62+
}
63+
}
64+
}
65+
66+
#[cfg(test)]
67+
mod tests {
68+
use anyhow::anyhow;
69+
use serde_json::Value::Null;
70+
use std::sync::Arc;
71+
use warp::{
72+
http::{Method, StatusCode},
73+
test::request,
74+
};
75+
76+
use mithril_api_spec::APISpec;
77+
use mithril_common::messages::ProtocolConfigurationMessage;
78+
use mithril_common::test::double::Dummy;
79+
80+
use crate::{initialize_dependencies, services::MockMessageService};
81+
82+
use super::*;
83+
84+
fn setup_router(
85+
state: RouterState,
86+
) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
87+
let cors = warp::cors()
88+
.allow_any_origin()
89+
.allow_headers(vec!["content-type"])
90+
.allow_methods(vec![Method::GET, Method::POST, Method::OPTIONS]);
91+
92+
warp::any().and(routes(&state).with(cors))
93+
}
94+
95+
#[tokio::test]
96+
async fn test_protocol_configuration_get_ok() {
97+
let method = Method::GET.as_str();
98+
let base_path = "/protocol-configuration";
99+
let mut dependency_manager = initialize_dependencies!().await;
100+
let mut mock_http_message_service = MockMessageService::new();
101+
mock_http_message_service
102+
.expect_get_protocol_configuration_message()
103+
.return_once(|_, _| Ok(ProtocolConfigurationMessage::dummy()))
104+
.once();
105+
dependency_manager.message_service = Arc::new(mock_http_message_service);
106+
107+
let response = request()
108+
.method(method)
109+
.path(&format!("{base_path}/42"))
110+
.reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
111+
dependency_manager,
112+
))))
113+
.await;
114+
115+
APISpec::verify_conformity(
116+
APISpec::get_default_spec_file_from(crate::http_server::API_SPEC_LOCATION),
117+
method,
118+
&format!("{base_path}/{{epoch}}"),
119+
"application/json",
120+
&Null,
121+
&response,
122+
&StatusCode::OK,
123+
)
124+
.unwrap();
125+
}
126+
127+
#[tokio::test]
128+
async fn test_protocol_configuration_get_ko_500() {
129+
let method = Method::GET.as_str();
130+
let base_path = "/protocol-configuration";
131+
let mut dependency_manager = initialize_dependencies!().await;
132+
let mut mock_http_message_service = MockMessageService::new();
133+
mock_http_message_service
134+
.expect_get_protocol_configuration_message()
135+
.return_once(|_, _| Err(anyhow!("an error")))
136+
.once();
137+
dependency_manager.message_service = Arc::new(mock_http_message_service);
138+
139+
let response = request()
140+
.method(method)
141+
.path(&format!("{base_path}/42"))
142+
.reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
143+
dependency_manager,
144+
))))
145+
.await;
146+
147+
APISpec::verify_conformity(
148+
APISpec::get_default_spec_file_from(crate::http_server::API_SPEC_LOCATION),
149+
method,
150+
&format!("{base_path}/{{epoch}}"),
151+
"application/json",
152+
&Null,
153+
&response,
154+
&StatusCode::INTERNAL_SERVER_ERROR,
155+
)
156+
.unwrap();
157+
}
158+
}

mithril-aggregator/src/services/message.rs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ use mithril_common::{
1414
CardanoStakeDistributionListMessage, CardanoStakeDistributionMessage,
1515
CardanoTransactionSnapshotListMessage, CardanoTransactionSnapshotMessage,
1616
CertificateListMessage, CertificateMessage, EpochSettingsMessage,
17-
MithrilStakeDistributionListMessage, MithrilStakeDistributionMessage, SignerMessagePart,
18-
SnapshotListMessage, SnapshotMessage,
17+
MithrilStakeDistributionListMessage, MithrilStakeDistributionMessage,
18+
ProtocolConfigurationMessage, SignerMessagePart, SnapshotListMessage, SnapshotMessage,
1919
},
2020
};
2121

2222
use crate::{
23-
ImmutableFileDigestMapper,
23+
EpochSettingsStorer, ImmutableFileDigestMapper,
2424
database::repository::{CertificateRepository, SignedEntityStorer},
2525
dependency_injection::EpochServiceWrapper,
2626
};
@@ -35,6 +35,13 @@ pub trait MessageService: Sync + Send {
3535
allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
3636
) -> StdResult<EpochSettingsMessage>;
3737

38+
///Return the protocol configuration message for the given epoch if it exists.
39+
async fn get_protocol_configuration_message(
40+
&self,
41+
epoch: Epoch,
42+
allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
43+
) -> StdResult<ProtocolConfigurationMessage>;
44+
3845
/// Return the message representation of a certificate if it exists.
3946
async fn get_certificate_message(
4047
&self,
@@ -130,6 +137,7 @@ pub trait MessageService: Sync + Send {
130137
pub struct MithrilMessageService {
131138
certificate_repository: Arc<CertificateRepository>,
132139
signed_entity_storer: Arc<dyn SignedEntityStorer>,
140+
epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
133141
immutable_file_digest_mapper: Arc<dyn ImmutableFileDigestMapper>,
134142
epoch_service: EpochServiceWrapper,
135143
}
@@ -139,12 +147,14 @@ impl MithrilMessageService {
139147
pub fn new(
140148
certificate_repository: Arc<CertificateRepository>,
141149
signed_entity_storer: Arc<dyn SignedEntityStorer>,
150+
epoch_settings_storer: Arc<dyn EpochSettingsStorer>,
142151
immutable_file_digest_mapper: Arc<dyn ImmutableFileDigestMapper>,
143152
epoch_service: EpochServiceWrapper,
144153
) -> Self {
145154
Self {
146155
certificate_repository,
147156
signed_entity_storer,
157+
epoch_settings_storer,
148158
immutable_file_digest_mapper,
149159
epoch_service,
150160
}
@@ -184,6 +194,27 @@ impl MessageService for MithrilMessageService {
184194
Ok(epoch_settings_message)
185195
}
186196

197+
async fn get_protocol_configuration_message(
198+
&self,
199+
epoch: Epoch,
200+
enabled_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
201+
) -> StdResult<ProtocolConfigurationMessage> {
202+
let epoch_settings = self.epoch_settings_storer.get_epoch_settings(epoch).await?.unwrap();
203+
204+
let cardano_transactions_discriminant =
205+
enabled_discriminants.get(&SignedEntityTypeDiscriminants::CardanoTransactions);
206+
207+
let cardano_transactions_signing_config = cardano_transactions_discriminant
208+
.map(|_| epoch_settings.cardano_transactions_signing_config);
209+
210+
let protocol_configuration_message = ProtocolConfigurationMessage {
211+
protocol_parameters: epoch_settings.protocol_parameters,
212+
cardano_transactions_signing_config,
213+
available_signed_entity_types: enabled_discriminants,
214+
};
215+
Ok(protocol_configuration_message)
216+
}
217+
187218
async fn get_certificate_message(
188219
&self,
189220
certificate_hash: &str,
@@ -362,7 +393,9 @@ mod tests {
362393
use tokio::sync::RwLock;
363394

364395
use crate::database::record::SignedEntityRecord;
365-
use crate::database::repository::{ImmutableFileDigestRepository, SignedEntityStore};
396+
use crate::database::repository::{
397+
EpochSettingsStore, ImmutableFileDigestRepository, SignedEntityStore,
398+
};
366399
use crate::database::test_helper::main_db_connection;
367400
use crate::services::FakeEpochService;
368401

@@ -419,6 +452,7 @@ mod tests {
419452
let connection = Arc::new(main_db_connection().unwrap());
420453
let certificate_repository = CertificateRepository::new(connection.clone());
421454
let signed_entity_store = SignedEntityStore::new(connection.clone());
455+
let epoch_settings_storer = EpochSettingsStore::new(connection.clone(), None);
422456
let immutable_file_digest_mapper =
423457
ImmutableFileDigestRepository::new(connection.clone());
424458
let epoch_service = self.epoch_service.unwrap_or(FakeEpochService::without_data());
@@ -444,6 +478,7 @@ mod tests {
444478
MithrilMessageService::new(
445479
Arc::new(certificate_repository),
446480
Arc::new(signed_entity_store),
481+
Arc::new(epoch_settings_storer),
447482
Arc::new(immutable_file_digest_mapper),
448483
Arc::new(RwLock::new(epoch_service)),
449484
)

mithril-common/src/messages/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mod interface;
1818
mod message_parts;
1919
mod mithril_stake_distribution;
2020
mod mithril_stake_distribution_list;
21+
mod protocol_configuration;
2122
mod register_signature;
2223
mod register_signer;
2324
mod snapshot;
@@ -61,6 +62,7 @@ pub use mithril_stake_distribution::MithrilStakeDistributionMessage;
6162
pub use mithril_stake_distribution_list::{
6263
MithrilStakeDistributionListItemMessage, MithrilStakeDistributionListMessage,
6364
};
65+
pub use protocol_configuration::ProtocolConfigurationMessage;
6466
pub use register_signature::{RegisterSignatureMessageDmq, RegisterSignatureMessageHttp};
6567
pub use register_signer::RegisterSignerMessage;
6668
pub use snapshot::SnapshotMessage;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use std::collections::BTreeSet;
2+
3+
use serde::{Deserialize, Serialize};
4+
5+
use crate::entities::{
6+
CardanoTransactionsSigningConfig, Epoch, ProtocolParameters, SignedEntityTypeDiscriminants,
7+
};
8+
9+
/// ProtocolConfiguration represents the protocol configuration of an epoch
10+
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
11+
pub struct ProtocolConfigurationMessage {
12+
/// Signer Registration Protocol parameters
13+
pub protocol_parameters: ProtocolParameters,
14+
15+
/// Cardano transactions signing configuration for the current epoch
16+
#[serde(skip_serializing_if = "Option::is_none")]
17+
pub cardano_transactions_signing_config: Option<CardanoTransactionsSigningConfig>,
18+
19+
/// Aggregator enabled signed entity types
20+
pub available_signed_entity_types: BTreeSet<SignedEntityTypeDiscriminants>,
21+
}

mithril-common/src/test/double/dummies.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,23 @@ mod messages {
420420
}
421421
}
422422

423+
impl Dummy for ProtocolConfigurationMessage {
424+
/// Return a dummy [ProtocolConfigurationMessage] (test-only).
425+
fn dummy() -> Self {
426+
Self {
427+
protocol_parameters: ProtocolParameters {
428+
k: 5,
429+
m: 100,
430+
phi_f: 0.65,
431+
},
432+
cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig::dummy()),
433+
available_signed_entity_types: BTreeSet::from([
434+
SignedEntityTypeDiscriminants::MithrilStakeDistribution,
435+
]),
436+
}
437+
}
438+
}
439+
423440
impl Dummy for MithrilStakeDistributionMessage {
424441
/// Return a dummy [MithrilStakeDistributionMessage] (test-only).
425442
fn dummy() -> Self {

0 commit comments

Comments
 (0)