From aca6b11da4d7ef4796e472d096268c412a535578 Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Thu, 3 Aug 2023 10:08:47 +0200 Subject: [PATCH 1/5] Enhance apispec so it output the given response when failing --- mithril-common/src/test_utils/apispec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mithril-common/src/test_utils/apispec.rs b/mithril-common/src/test_utils/apispec.rs index b9d0b4c4530..4916ee41a3f 100644 --- a/mithril-common/src/test_utils/apispec.rs +++ b/mithril-common/src/test_utils/apispec.rs @@ -33,10 +33,10 @@ impl<'a> APISpec<'a> { .path(path) .content_type(content_type) .validate_request(request_body) - .map_err(|e| panic!("OpenAPI invalid request in {spec_file}, reason: {e}")) + .map_err(|e| panic!("OpenAPI invalid request in {spec_file}, reason: {e}\nresponse: {response:#?}")) .unwrap() .validate_response(response) - .map_err(|e| panic!("OpenAPI invalid response in {spec_file}, reason: {e}")) + .map_err(|e| panic!("OpenAPI invalid response in {spec_file}, reason: {e}\nresponse: {response:#?}")) .unwrap(); } } From d4686f517a71b3ba4f460b931fd64c2e35889263 Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Thu, 3 Aug 2023 10:14:54 +0200 Subject: [PATCH 2/5] Add `default-run` to e2e Cargo.toml so it run by default the e2e tests --- mithril-test-lab/mithril-end-to-end/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/mithril-test-lab/mithril-end-to-end/Cargo.toml b/mithril-test-lab/mithril-end-to-end/Cargo.toml index a5efd59b40f..778e060c54d 100644 --- a/mithril-test-lab/mithril-end-to-end/Cargo.toml +++ b/mithril-test-lab/mithril-end-to-end/Cargo.toml @@ -7,6 +7,7 @@ documentation = { workspace = true } homepage = { workspace = true } license = { workspace = true } repository = { workspace = true } +default-run = "mithril-end-to-end" [[bin]] name = "load-aggregator" From 61317d2bf05a3daf0bbf810cf1a4df26af9d9632 Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Wed, 2 Aug 2023 17:07:16 +0200 Subject: [PATCH 3/5] Add method to vkey store to get registered stake distribution for an epoch --- .../database/provider/signer_registration.rs | 22 +++++- .../src/store/verification_key_store.rs | 67 ++++++++++++++++++- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/mithril-aggregator/src/database/provider/signer_registration.rs b/mithril-aggregator/src/database/provider/signer_registration.rs index a123c78ffe1..30decc17b4b 100644 --- a/mithril-aggregator/src/database/provider/signer_registration.rs +++ b/mithril-aggregator/src/database/provider/signer_registration.rs @@ -8,7 +8,7 @@ use mithril_common::{ crypto_helper::KESPeriod, entities::{ Epoch, HexEncodedOpCert, HexEncodedVerificationKey, HexEncodedVerificationKeySignature, - PartyId, Signer, SignerWithStake, Stake, + PartyId, Signer, SignerWithStake, Stake, StakeDistribution, }, sqlite::{ EntityCursor, HydrationError, Projection, Provider, SourceAlias, SqLiteEntity, @@ -458,6 +458,26 @@ impl VerificationKeyStorer for SignerRegistrationStore { Ok(()) } + + async fn get_stake_distribution_for_epoch( + &self, + epoch: Epoch, + ) -> Result, StoreError> { + let connection = &*self.connection.lock().await; + let provider = SignerRegistrationRecordProvider::new(connection); + let cursor = provider + .get_by_epoch(&epoch) + .map_err(|e| AdapterError::GeneralError(format!("{e}")))?; + + let stake_distribution = StakeDistribution::from_iter( + cursor.map(|r| (r.signer_id, r.stake.unwrap_or_default())), + ); + + match stake_distribution.is_empty() { + true => Ok(None), + false => Ok(Some(stake_distribution)), + } + } } #[cfg(test)] diff --git a/mithril-aggregator/src/store/verification_key_store.rs b/mithril-aggregator/src/store/verification_key_store.rs index cec79170a28..78c2949a6cc 100644 --- a/mithril-aggregator/src/store/verification_key_store.rs +++ b/mithril-aggregator/src/store/verification_key_store.rs @@ -2,12 +2,15 @@ use async_trait::async_trait; use std::collections::HashMap; use tokio::sync::RwLock; -use mithril_common::entities::{Epoch, PartyId, Signer, SignerWithStake}; +use mithril_common::entities::{Epoch, PartyId, Signer, SignerWithStake, StakeDistribution}; use mithril_common::store::{adapter::StoreAdapter, StoreError}; type Adapter = Box>>; /// Store and get signers verification keys for given epoch. +/// +/// Important note: This store works on the **recording** epoch, the epoch at which the signers +/// are signed into a certificate so they can sign single signatures at the next epoch. #[async_trait] pub trait VerificationKeyStorer: Sync + Send { /// Save the verification key, for the given [Signer] for the given [Epoch], returns the @@ -26,6 +29,12 @@ pub trait VerificationKeyStorer: Sync + Send { /// Prune all verification keys that are at or below the given epoch. async fn prune_verification_keys(&self, max_epoch_to_prune: Epoch) -> Result<(), StoreError>; + + /// Return the parties that are stored at the given epoch. + async fn get_stake_distribution_for_epoch( + &self, + epoch: Epoch, + ) -> Result, StoreError>; } /// Store for the `VerificationKey`. @@ -85,6 +94,19 @@ impl VerificationKeyStorer for VerificationKeyStore { Ok(()) } + + async fn get_stake_distribution_for_epoch( + &self, + epoch: Epoch, + ) -> Result, StoreError> { + let record = self.adapter.read().await.get_record(&epoch).await?; + Ok(record.map(|r| { + StakeDistribution::from_iter( + r.into_iter() + .map(|(party_id, signer)| (party_id, signer.stake)), + ) + })) + } } /// Macro that generate tests that a [VerificationKeyStorer] must pass @@ -110,11 +132,21 @@ macro_rules! test_verification_key_storer { test_suite::get_verification_keys_for_empty_epoch(&$store_builder).await; } + #[tokio::test] + async fn get_stake_distribution_for_empty_epoch() { + test_suite::get_stake_distribution_for_empty_epoch(&$store_builder).await; + } + #[tokio::test] async fn get_verification_keys_for_existing_epoch() { test_suite::get_verification_keys_for_existing_epoch(&$store_builder).await; } + #[tokio::test] + async fn get_stake_distribution_for_existing_epoch() { + test_suite::get_stake_distribution_for_existing_epoch(&$store_builder).await; + } + #[tokio::test] async fn can_prune_keys_from_given_epoch_retention_limit() { test_suite::can_prune_keys_from_given_epoch_retention_limit(&$store_builder).await; @@ -129,7 +161,7 @@ pub(crate) use test_verification_key_storer; #[macro_use] #[cfg(test)] pub mod test_suite { - use mithril_common::entities::{Epoch, PartyId, Signer, SignerWithStake}; + use mithril_common::entities::{Epoch, PartyId, Signer, SignerWithStake, StakeDistribution}; use mithril_common::test_utils::fake_keys; use std::collections::{BTreeMap, HashMap}; use std::sync::Arc; @@ -233,6 +265,17 @@ pub mod test_suite { assert!(res.is_none()); } + pub async fn get_stake_distribution_for_empty_epoch(store_builder: &StoreBuilder) { + let signers = build_signers(2, 1); + let store = store_builder(signers); + let res = store + .get_stake_distribution_for_epoch(Epoch(0)) + .await + .unwrap(); + + assert!(res.is_none()); + } + pub async fn get_verification_keys_for_existing_epoch(store_builder: &StoreBuilder) { let signers = build_signers(2, 2); let store = store_builder(signers.clone()); @@ -253,6 +296,26 @@ pub mod test_suite { assert_eq!(expected_signers, res); } + pub async fn get_stake_distribution_for_existing_epoch(store_builder: &StoreBuilder) { + let signers = build_signers(2, 2); + let store = store_builder(signers.clone()); + + let expected: Option = signers + .into_iter() + .filter(|(e, _)| e == 1) + .map(|(_, signers)| { + StakeDistribution::from_iter(signers.into_iter().map(|(p, s)| (p, s.stake))) + }) + .next(); + let res = store + .get_stake_distribution_for_epoch(Epoch(1)) + .await + .unwrap() + .map(|x| BTreeMap::from_iter(x.into_iter())); + + assert_eq!(expected, res); + } + pub async fn can_prune_keys_from_given_epoch_retention_limit(store_builder: &StoreBuilder) { let signers = build_signers(6, 2); let store = store_builder(signers); From b22b80ae170f5dee6348cc2408124f434d8ff9ab Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Thu, 3 Aug 2023 10:07:48 +0200 Subject: [PATCH 4/5] Define & add `/signers/registered/{epoch}` to aggregator --- mithril-aggregator/src/entities/mod.rs | 4 + .../entities/signer_registration_message.rs | 41 ++++ .../src/http_server/routes/middlewares.rs | 9 +- .../src/http_server/routes/signer_routes.rs | 205 +++++++++++++++++- mithril-aggregator/src/store/mod.rs | 2 + .../src/store/verification_key_store.rs | 4 + mithril-common/src/entities/epoch.rs | 9 + openapi.yaml | 107 +++++++-- 8 files changed, 352 insertions(+), 29 deletions(-) create mode 100644 mithril-aggregator/src/entities/signer_registration_message.rs diff --git a/mithril-aggregator/src/entities/mod.rs b/mithril-aggregator/src/entities/mod.rs index b9f7ce8b728..53017088d18 100644 --- a/mithril-aggregator/src/entities/mod.rs +++ b/mithril-aggregator/src/entities/mod.rs @@ -2,5 +2,9 @@ //! //! This module provide domain entities for the services & state machine. mod open_message; +mod signer_registration_message; pub use open_message::OpenMessage; +pub use signer_registration_message::{ + SignerRegistrationsListItemMessage, SignerRegistrationsMessage, +}; diff --git a/mithril-aggregator/src/entities/signer_registration_message.rs b/mithril-aggregator/src/entities/signer_registration_message.rs new file mode 100644 index 00000000000..0b5e5efc80c --- /dev/null +++ b/mithril-aggregator/src/entities/signer_registration_message.rs @@ -0,0 +1,41 @@ +use mithril_common::entities::{Epoch, PartyId, Stake, StakeDistribution}; +use serde::{Deserialize, Serialize}; + +/// Message structure of signer registrations for an epoch. +#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +pub struct SignerRegistrationsMessage { + /// The epoch at which the registration was sent. + pub registered_at: Epoch, + + /// The epoch at which the registration was able to send signatures. + pub signing_at: Epoch, + + /// The signer registrations + pub registrations: Vec, +} + +/// Message structure of a signer registration +#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +pub struct SignerRegistrationsListItemMessage { + /// The registered signer party id + pub party_id: PartyId, + + /// The registered signer stake + pub stake: Stake, +} + +impl SignerRegistrationsMessage { + /// Build a [SignerRegistrationsMessage] from a [stake distribution][StakeDistribution]. + pub fn new(registered_at: Epoch, stake_distribution: StakeDistribution) -> Self { + let registrations: Vec = stake_distribution + .into_iter() + .map(|(party_id, stake)| SignerRegistrationsListItemMessage { party_id, stake }) + .collect(); + + Self { + registered_at, + signing_at: registered_at.offset_to_signer_signing_offset(), + registrations, + } + } +} diff --git a/mithril-aggregator/src/http_server/routes/middlewares.rs b/mithril-aggregator/src/http_server/routes/middlewares.rs index 8650797b1c3..0dda6956f36 100644 --- a/mithril-aggregator/src/http_server/routes/middlewares.rs +++ b/mithril-aggregator/src/http_server/routes/middlewares.rs @@ -4,7 +4,7 @@ use crate::{ services::CertifierService, services::{SignedEntityService, TickerService}, CertificatePendingStore, Configuration, DependencyContainer, ProtocolParametersStorer, - SignerRegisterer, + SignerRegisterer, VerificationKeyStorer, }; use mithril_common::BeaconProvider; @@ -81,3 +81,10 @@ pub fn with_signed_entity_service( ) -> impl Filter,), Error = Infallible> + Clone { warp::any().map(move || dependency_manager.signed_entity_service.clone()) } + +/// With verification key store +pub fn with_verification_key_store( + dependency_manager: Arc, +) -> impl Filter,), Error = Infallible> + Clone { + warp::any().map(move || dependency_manager.verification_key_store.clone()) +} diff --git a/mithril-aggregator/src/http_server/routes/signer_routes.rs b/mithril-aggregator/src/http_server/routes/signer_routes.rs index 8cc205af727..2cf77af75d3 100644 --- a/mithril-aggregator/src/http_server/routes/signer_routes.rs +++ b/mithril-aggregator/src/http_server/routes/signer_routes.rs @@ -8,7 +8,7 @@ const MITHRIL_SIGNER_VERSION_HEADER: &str = "signer-node-version"; pub fn routes( dependency_manager: Arc, ) -> impl Filter + Clone { - register_signer(dependency_manager) + register_signer(dependency_manager.clone()).or(registered_signers(dependency_manager)) } /// POST /register-signer @@ -31,10 +31,22 @@ fn register_signer( .and_then(handlers::register_signer) } +/// Get /signers/registered/:epoch +fn registered_signers( + dependency_manager: Arc, +) -> impl Filter + Clone { + warp::path!("signers" / "registered" / String) + .and(warp::get()) + .and(middlewares::with_verification_key_store(dependency_manager)) + .and_then(handlers::registered_signers) +} + mod handlers { + use crate::entities::SignerRegistrationsMessage; use crate::event_store::{EventMessage, TransmitterService}; - use crate::FromRegisterSignerAdapter; use crate::{http_server::routes::reply, SignerRegisterer, SignerRegistrationError}; + use crate::{FromRegisterSignerAdapter, VerificationKeyStorer}; + use mithril_common::entities::Epoch; use mithril_common::messages::{RegisterSignerMessage, TryFromMessageAdapter}; use mithril_common::BeaconProvider; use slog_scope::{debug, warn}; @@ -139,21 +151,68 @@ mod handlers { } } } + + /// Get Registered Signers for a given epoch + pub async fn registered_signers( + registered_at: String, + verification_key_store: Arc, + ) -> Result { + debug!("⇄ HTTP SERVER: signers/registered/{:?}", registered_at); + + let registered_at = match registered_at.parse::() { + Ok(epoch) => Epoch(epoch), + Err(err) => { + warn!("registered_signers::invalid_epoch"; "error" => ?err); + return Ok(reply::bad_request( + "invalid_epoch".to_string(), + err.to_string(), + )); + } + }; + + // The given epoch is the epoch at which the signer registered, the store works on + // the recording epoch so we need to offset. + match verification_key_store + .get_stake_distribution_for_epoch(registered_at.offset_to_recording_epoch()) + .await + { + Ok(Some(stake_distribution)) => { + let message = SignerRegistrationsMessage::new(registered_at, stake_distribution); + Ok(reply::json(&message, StatusCode::OK)) + } + Ok(None) => { + warn!("registered_signers::not_found"); + Ok(reply::empty(StatusCode::NOT_FOUND)) + } + Err(err) => { + warn!("registered_signers::error"; "error" => ?err); + Ok(reply::internal_server_error(err.to_string())) + } + } + } } #[cfg(test)] mod tests { - use mithril_common::crypto_helper::ProtocolRegistrationError; - use mithril_common::messages::RegisterSignerMessage; - use mithril_common::test_utils::apispec::APISpec; - use mithril_common::test_utils::fake_data; - use warp::http::Method; - use warp::test::request; + use mithril_common::entities::Epoch; + use mithril_common::{ + crypto_helper::ProtocolRegistrationError, + entities::StakeDistribution, + messages::RegisterSignerMessage, + store::adapter::AdapterError, + test_utils::{apispec::APISpec, fake_data}, + }; + use mockall::predicate::eq; + use serde_json::Value::Null; + use warp::{http::Method, test::request}; + + use crate::{ + http_server::SERVER_BASE_PATH, initialize_dependencies, + signer_registerer::MockSignerRegisterer, store::MockVerificationKeyStorer, + SignerRegistrationError, + }; use super::*; - use crate::http_server::SERVER_BASE_PATH; - use crate::signer_registerer::MockSignerRegisterer; - use crate::{initialize_dependencies, SignerRegistrationError}; fn setup_router( dependency_manager: Arc, @@ -316,4 +375,128 @@ mod tests { &response, ); } + + #[tokio::test] + async fn test_registered_signers_get_offset_given_epoch_to_registration_epoch() { + let asked_epoch = Epoch(1); + let expected_retrieval_epoch = asked_epoch.offset_to_recording_epoch(); + let stake_distribution = StakeDistribution::from_iter( + fake_data::signers_with_stakes(3) + .into_iter() + .map(|s| (s.party_id, s.stake)), + ); + let mut mock_verification_key_store = MockVerificationKeyStorer::new(); + mock_verification_key_store + .expect_get_stake_distribution_for_epoch() + .with(eq(expected_retrieval_epoch)) + .return_once(|_| Ok(Some(stake_distribution))) + .once(); + let mut dependency_manager = initialize_dependencies().await; + dependency_manager.verification_key_store = Arc::new(mock_verification_key_store); + + let method = Method::GET.as_str(); + let base_path = "/signers/registered"; + + let response = request() + .method(method) + .path(&format!("/{SERVER_BASE_PATH}{base_path}/{}", asked_epoch)) + .reply(&setup_router(Arc::new(dependency_manager))) + .await; + + assert!( + response.status().is_success(), + "expected the response to succeed, was: {response:#?}" + ); + } + + #[tokio::test] + async fn test_registered_signers_get_ok() { + let stake_distribution = StakeDistribution::from_iter( + fake_data::signers_with_stakes(3) + .into_iter() + .map(|s| (s.party_id, s.stake)), + ); + let mut mock_verification_key_store = MockVerificationKeyStorer::new(); + mock_verification_key_store + .expect_get_stake_distribution_for_epoch() + .return_once(|_| Ok(Some(stake_distribution))) + .once(); + let mut dependency_manager = initialize_dependencies().await; + dependency_manager.verification_key_store = Arc::new(mock_verification_key_store); + + let base_path = "/signers/registered"; + let method = Method::GET.as_str(); + + let response = request() + .method(method) + .path(&format!("/{SERVER_BASE_PATH}{base_path}/1")) + .reply(&setup_router(Arc::new(dependency_manager))) + .await; + + APISpec::verify_conformity( + APISpec::get_all_spec_files(), + method, + &format!("{base_path}/{{epoch}}"), + "application/json", + &Null, + &response, + ); + } + + #[tokio::test] + async fn test_registered_signers_get_ok_noregistration() { + let mut mock_verification_key_store = MockVerificationKeyStorer::new(); + mock_verification_key_store + .expect_get_stake_distribution_for_epoch() + .return_once(|_| Ok(None)) + .once(); + let mut dependency_manager = initialize_dependencies().await; + dependency_manager.verification_key_store = Arc::new(mock_verification_key_store); + + let method = Method::GET.as_str(); + let base_path = "/signers/registered"; + + let response = request() + .method(method) + .path(&format!("/{SERVER_BASE_PATH}{base_path}/3")) + .reply(&setup_router(Arc::new(dependency_manager))) + .await; + + APISpec::verify_conformity( + APISpec::get_all_spec_files(), + method, + &format!("{base_path}/{{epoch}}"), + "application/json", + &Null, + &response, + ); + } + + #[tokio::test] + async fn test_registered_signers_get_ko() { + let mut mock_verification_key_store = MockVerificationKeyStorer::new(); + mock_verification_key_store + .expect_get_stake_distribution_for_epoch() + .return_once(|_| Err(AdapterError::GeneralError("invalid query".to_string()).into())); + let mut dependency_manager = initialize_dependencies().await; + dependency_manager.verification_key_store = Arc::new(mock_verification_key_store); + + let method = Method::GET.as_str(); + let base_path = "/signers/registered"; + + let response = request() + .method(method) + .path(&format!("/{SERVER_BASE_PATH}{base_path}/1")) + .reply(&setup_router(Arc::new(dependency_manager))) + .await; + + APISpec::verify_conformity( + APISpec::get_all_spec_files(), + method, + &format!("{base_path}/{{epoch}}"), + "application/json", + &Null, + &response, + ); + } } diff --git a/mithril-aggregator/src/store/mod.rs b/mithril-aggregator/src/store/mod.rs index adb28960fe4..b4705c9af47 100644 --- a/mithril-aggregator/src/store/mod.rs +++ b/mithril-aggregator/src/store/mod.rs @@ -10,3 +10,5 @@ pub use verification_key_store::{VerificationKeyStore, VerificationKeyStorer}; pub use verification_key_store::test_suite as verification_key_store_test_suite; #[cfg(test)] pub(crate) use verification_key_store::test_verification_key_storer; +#[cfg(test)] +pub use verification_key_store::MockVerificationKeyStorer; diff --git a/mithril-aggregator/src/store/verification_key_store.rs b/mithril-aggregator/src/store/verification_key_store.rs index 78c2949a6cc..d4c1aa02182 100644 --- a/mithril-aggregator/src/store/verification_key_store.rs +++ b/mithril-aggregator/src/store/verification_key_store.rs @@ -5,12 +5,16 @@ use tokio::sync::RwLock; use mithril_common::entities::{Epoch, PartyId, Signer, SignerWithStake, StakeDistribution}; use mithril_common::store::{adapter::StoreAdapter, StoreError}; +#[cfg(test)] +use mockall::automock; + type Adapter = Box>>; /// Store and get signers verification keys for given epoch. /// /// Important note: This store works on the **recording** epoch, the epoch at which the signers /// are signed into a certificate so they can sign single signatures at the next epoch. +#[cfg_attr(test, automock)] #[async_trait] pub trait VerificationKeyStorer: Sync + Send { /// Save the verification key, for the given [Signer] for the given [Epoch], returns the diff --git a/mithril-common/src/entities/epoch.rs b/mithril-common/src/entities/epoch.rs index 07000180e1c..e000e2ea6eb 100644 --- a/mithril-common/src/entities/epoch.rs +++ b/mithril-common/src/entities/epoch.rs @@ -29,6 +29,10 @@ impl Epoch { /// The epoch offset used for aggregator protocol parameters recording. pub const PROTOCOL_PARAMETERS_RECORDING_OFFSET: u64 = 2; + /// The epoch offset used to retrieve, given the epoch at which a signer registered, the epoch + /// at which the signer can send single signatures. + pub const SIGNER_SIGNING_OFFSET: u64 = 2; + /// Computes a new Epoch by applying an epoch offset. /// /// Will fail if the computed epoch is negative. @@ -60,6 +64,11 @@ impl Epoch { *self + Self::PROTOCOL_PARAMETERS_RECORDING_OFFSET } + /// Apply the [signer signing offset][Self::SIGNER_SIGNING_OFFSET] to this epoch + pub fn offset_to_signer_signing_offset(&self) -> Self { + *self + Self::SIGNER_SIGNING_OFFSET + } + /// Computes the next Epoch pub fn next(&self) -> Self { *self + 1 diff --git a/openapi.yaml b/openapi.yaml index 92decb68abb..33d2faafde6 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -45,6 +45,7 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /certificate-pending: get: summary: Get current pending certificate information @@ -71,6 +72,7 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /certificates: get: summary: Get most recent certificates @@ -91,6 +93,7 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /certificate/{certificate_hash}: get: summary: Get certificate by hash @@ -122,6 +125,7 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /artifact/snapshots: get: summary: Get most recent snapshots @@ -142,6 +146,7 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /artifact/snapshot/{digest}: get: summary: Get snapshot information @@ -173,6 +178,7 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /artifact/snapshot/{digest}/download: get: summary: Download the snapshot @@ -205,6 +211,7 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /artifact/mithril-stake-distributions: get: summary: Get most recent Mithril stake distributions @@ -225,6 +232,7 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /artifact/mithril-stake-distribution/{hash}: get: summary: Get Mithril stake distribution information @@ -256,6 +264,39 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + + /signers/registered/{epoch}: + get: + summary: Get registered signers for an epoch + description: | + Returns the signers that registered at a given Epoch + parameters: + - name: epoch + in: path + description: Cardano Epoch at which the signer registrations are registered + required: true + schema: + type: integer + format: int64 + example: 419 + responses: + "200": + description: Registered Signers found + content: + application/json: + schema: + $ref: "#/components/schemas/SignerRegistrationsMessage" + "404": + description: Registed Signers not found + "412": + description: API version mismatch + default: + description: Registered Signers retrieval error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /register-signer: post: summary: Registers signer @@ -285,6 +326,7 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + /register-signatures: post: summary: Registers signatures @@ -318,8 +360,14 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + components: schemas: + Epoch: + description: Cardano chain epoch number + type: integer + format: int64 + EpochSettingsMessage: description: Epoch settings type: object @@ -330,9 +378,7 @@ components: - next_protocol properties: epoch: - description: Cardano chain epoch number - type: integer - format: int64 + $ref: "#/components/schemas/Epoch" protocol: $ref: "#/components/schemas/ProtocolParameters" next_protocol: @@ -379,9 +425,7 @@ components: description: Cardano network type: string epoch: - description: Cardano chain epoch number - type: integer - format: int64 + $ref: "#/components/schemas/Epoch" immutable_file_number: description: Number of the last immutable file that should be included the snapshot type: integer @@ -525,9 +569,7 @@ components: additionalProperties: true properties: epoch: - description: Cardano chain epoch number - type: integer - format: int64 + $ref: "#/components/schemas/Epoch" allOf: - $ref: "#/components/schemas/Signer" example: @@ -555,6 +597,43 @@ components: "kes_period": 123, "stake": 1234 } + + SignerRegistrationsMessage: + description: | + This message holds the registered signers at a given epoch. + type: object + additionalProperties: false + properties: + registered_at: + $ref: "#/components/schemas/Epoch" + signing_at: + $ref: "#/components/schemas/Epoch" + registrations: + type: array + items: + $ref: "#/components/schemas/SignerRegistrationsListItemMessage" + example: + { + "registered_at": 420, + "signing_at": 422, + "registrations": [ + { + "party_id": "1234567890", + "stake": 1234 + } + ] + } + + SignerRegistrationsListItemMessage: + description: represents an item of a SignerRegistrationsMessage registration + type: object + additionalProperties: true + allOf: + - $ref: "#/components/schemas/Stake" + properties: + party_id: + description: The unique identifier of the signer + type: string RegisterSingleSignatureMessage: description: | @@ -731,7 +810,6 @@ components: metadata: $ref: "#/components/schemas/CertificateListItemMessageMetadata" protocol_message: - description: Protocol message $ref: "#/components/schemas/ProtocolMessage" signed_message: description: Hash of the protocol message that is signed by the signer participants @@ -856,7 +934,6 @@ components: metadata: $ref: "#/components/schemas/CertificateMetadata" protocol_message: - description: Protocol message $ref: "#/components/schemas/ProtocolMessage" signed_message: description: Hash of the protocol message that is signed by the signer participants @@ -1045,9 +1122,7 @@ components: - created_at properties: epoch: - description: Cardano chain epoch number - type: integer - format: int64 + $ref: "#/components/schemas/Epoch" hash: description: Hash of the Mithril stake distribution type: string @@ -1080,9 +1155,7 @@ components: - protocol_parameters properties: epoch: - description: Cardano chain epoch number - type: integer - format: int64 + $ref: "#/components/schemas/Epoch" hash: description: Hash of the Mithril stake distribution type: string From b282f9427e7fab414d07da7243bebc5bdd8941d5 Mon Sep 17 00:00:00 2001 From: DJO <790521+Alenar@users.noreply.github.com> Date: Thu, 3 Aug 2023 12:14:01 +0200 Subject: [PATCH 5/5] Update versions --- Cargo.lock | 6 +++--- mithril-aggregator/Cargo.toml | 2 +- mithril-common/Cargo.toml | 2 +- mithril-test-lab/mithril-end-to-end/Cargo.toml | 2 +- openapi.yaml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4e8d6f19ca..5c21655b808 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2102,7 +2102,7 @@ dependencies = [ [[package]] name = "mithril-aggregator" -version = "0.3.65" +version = "0.3.66" dependencies = [ "async-trait", "chrono", @@ -2175,7 +2175,7 @@ dependencies = [ [[package]] name = "mithril-common" -version = "0.2.88" +version = "0.2.89" dependencies = [ "anyhow", "async-trait", @@ -2223,7 +2223,7 @@ dependencies = [ [[package]] name = "mithril-end-to-end" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "async-recursion", diff --git a/mithril-aggregator/Cargo.toml b/mithril-aggregator/Cargo.toml index ec21af74068..afeed89dcee 100644 --- a/mithril-aggregator/Cargo.toml +++ b/mithril-aggregator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-aggregator" -version = "0.3.65" +version = "0.3.66" description = "A Mithril Aggregator server" authors = { workspace = true } edition = { workspace = true } diff --git a/mithril-common/Cargo.toml b/mithril-common/Cargo.toml index 5cb5fd7b194..f78940a38d0 100644 --- a/mithril-common/Cargo.toml +++ b/mithril-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-common" -version = "0.2.88" +version = "0.2.89" authors = { workspace = true } edition = { workspace = true } documentation = { workspace = true } diff --git a/mithril-test-lab/mithril-end-to-end/Cargo.toml b/mithril-test-lab/mithril-end-to-end/Cargo.toml index 778e060c54d..8556127c355 100644 --- a/mithril-test-lab/mithril-end-to-end/Cargo.toml +++ b/mithril-test-lab/mithril-end-to-end/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-end-to-end" -version = "0.2.0" +version = "0.2.1" authors = { workspace = true } edition = { workspace = true } documentation = { workspace = true } diff --git a/openapi.yaml b/openapi.yaml index 33d2faafde6..09fa5d7e92a 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -4,7 +4,7 @@ info: # `mithril-common/src/lib.rs` file. If you plan to update it # here to reflect changes in the API, please also update the constant in the # Rust file. - version: 0.1.8 + version: 0.1.9 title: Mithril Aggregator Server description: | The REST API provided by a Mithril Aggregator Node in a Mithril network.