Skip to content

Commit

Permalink
Merge pull request #1847 from input-output-hk/dlachaume/1832/implemen…
Browse files Browse the repository at this point in the history
…t-signable-artifact-builders-for-cardano-stake-distribution

Implement signable and artifact builders for Cardano Stake Distribution
  • Loading branch information
dlachaume authored Jul 29, 2024
2 parents 52a7beb + 4829708 commit eaaa3dd
Show file tree
Hide file tree
Showing 33 changed files with 889 additions and 72 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ As a minor extension, we have adopted a slightly different versioning convention

## Mithril Distribution [XXXX] - UNRELEASED

- **UNSTABLE** Cardano stake distribution certification:

- Implement the signable and artifact builders for the signed entity type `CardanoStakeDistribution`

- Crates versions:

| Crate | Version |
Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion internal/mithril-persistence/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mithril-persistence"
version = "0.2.21"
version = "0.2.22"
description = "Common types, interfaces, and utilities to persist data for Mithril nodes."
authors = { workspace = true }
edition = { workspace = true }
Expand Down
34 changes: 34 additions & 0 deletions internal/mithril-persistence/src/store/stake_store.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use async_trait::async_trait;
use mithril_common::entities::{Epoch, StakeDistribution};
use mithril_common::signable_builder::StakeDistributionRetriever;
use mithril_common::StdResult;
use tokio::sync::RwLock;

Expand Down Expand Up @@ -79,6 +80,15 @@ impl StakeStorer for StakeStore {
}
}

#[async_trait]
impl StakeDistributionRetriever for StakeStore {
async fn retrieve(&self, epoch: Epoch) -> StdResult<Option<StakeDistribution>> {
let stake_distribution = self.get_stakes(epoch).await?;

Ok(stake_distribution)
}
}

#[cfg(test)]
mod tests {
use super::super::adapter::MemoryAdapter;
Expand Down Expand Up @@ -167,4 +177,28 @@ mod tests {
.unwrap();
assert!(store.get_stakes(Epoch(1)).await.unwrap().is_none());
}

#[tokio::test]
async fn retrieve_with_no_stakes_returns_none() {
let store = init_store(0, 0, None);

let result = store.retrieve(Epoch(1)).await.unwrap();

assert!(result.is_none());
}

#[tokio::test]
async fn retrieve_returns_stake_distribution() {
let stake_distribution_to_retrieve =
StakeDistribution::from([("pool-123".to_string(), 123)]);
let store = init_store(0, 0, None);
store
.save_stakes(Epoch(1), stake_distribution_to_retrieve.clone())
.await
.unwrap();

let stake_distribution = store.retrieve(Epoch(1)).await.unwrap();

assert_eq!(stake_distribution, Some(stake_distribution_to_retrieve));
}
}
2 changes: 1 addition & 1 deletion mithril-aggregator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mithril-aggregator"
version = "0.5.50"
version = "0.5.51"
description = "A Mithril Aggregator server"
authors = { workspace = true }
edition = { workspace = true }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use anyhow::anyhow;
use async_trait::async_trait;
use std::sync::Arc;

use mithril_common::{
entities::{CardanoStakeDistribution, Certificate, Epoch},
signable_builder::StakeDistributionRetriever,
StdResult,
};

use crate::ArtifactBuilder;

/// A [CardanoStakeDistributionArtifact] builder
pub struct CardanoStakeDistributionArtifactBuilder {
stake_distribution_retriever: Arc<dyn StakeDistributionRetriever>,
}

impl CardanoStakeDistributionArtifactBuilder {
/// CardanoStakeDistribution artifact builder factory
pub fn new(stake_distribution_retriever: Arc<dyn StakeDistributionRetriever>) -> Self {
Self {
stake_distribution_retriever,
}
}
}

#[async_trait]
impl ArtifactBuilder<Epoch, CardanoStakeDistribution> for CardanoStakeDistributionArtifactBuilder {
async fn compute_artifact(
&self,
epoch: Epoch,
_certificate: &Certificate,
) -> StdResult<CardanoStakeDistribution> {
let stake_distribution = self
.stake_distribution_retriever
.retrieve(epoch.offset_to_recording_epoch())
.await?
.ok_or_else(|| anyhow!("No stake distribution found for epoch '{}'", epoch))?;

Ok(CardanoStakeDistribution::new(epoch, stake_distribution))
}
}

#[cfg(test)]
mod tests {
use mithril_common::{entities::StakeDistribution, test_utils::fake_data};
use mockall::{mock, predicate::eq};

use super::*;

mock! {
pub StakeDistributionRetrieverImpl {}

#[async_trait]
impl StakeDistributionRetriever for StakeDistributionRetrieverImpl {
async fn retrieve(&self, epoch: Epoch) -> StdResult<Option<StakeDistribution>>;
}
}

#[tokio::test]
async fn compute_artifact_returns_valid_artifact_and_retrieve_with_epoch_offset() {
let epoch = Epoch(1);
let epoch_to_retrieve = Epoch(2);
let certificate = fake_data::certificate("whatever".to_string());
let stake_distribution = StakeDistribution::from([("pool-123".to_string(), 123)]);
let stake_distribution_clone = stake_distribution.clone();
let mut mock_storer = MockStakeDistributionRetrieverImpl::new();
mock_storer
.expect_retrieve()
.with(eq(epoch_to_retrieve))
.return_once(move |_| Ok(Some(stake_distribution_clone)));
let builder = CardanoStakeDistributionArtifactBuilder::new(Arc::new(mock_storer));

let cardano_stake_distribution =
builder.compute_artifact(epoch, &certificate).await.unwrap();

let expected = CardanoStakeDistribution::new(epoch, stake_distribution);
assert_eq!(cardano_stake_distribution, expected);
}

#[tokio::test]
async fn compute_artifact_returns_error_if_no_stakes_found_for_epoch() {
let epoch = Epoch(1);
let epoch_to_retrieve = Epoch(2);
let certificate = fake_data::certificate("whatever".to_string());
let mut mock_storer = MockStakeDistributionRetrieverImpl::new();
mock_storer
.expect_retrieve()
.with(eq(epoch_to_retrieve))
.return_once(move |_| Ok(None));
let builder = CardanoStakeDistributionArtifactBuilder::new(Arc::new(mock_storer));

builder
.compute_artifact(epoch, &certificate)
.await
.expect_err("Should return error");
}
}
2 changes: 2 additions & 0 deletions mithril-aggregator/src/artifact_builder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! The module used for building artifact
mod cardano_immutable_files_full;
mod cardano_stake_distribution;
mod cardano_transactions;
mod interface;
mod mithril_stake_distribution;

pub use cardano_immutable_files_full::*;
pub use cardano_stake_distribution::*;
pub use cardano_transactions::*;
pub use interface::*;
pub use mithril_stake_distribution::*;
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl OpenMessageRepository {
) -> StdResult<Option<OpenMessageWithSingleSignaturesRecord>> {
self.connection.fetch_first(
GetOpenMessageWithSingleSignaturesQuery::by_epoch_and_signed_entity_type(
signed_entity_type.get_epoch(),
signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
signed_entity_type,
)?,
)
Expand Down
36 changes: 36 additions & 0 deletions mithril-aggregator/src/database/repository/stake_pool_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use anyhow::Context;
use async_trait::async_trait;

use mithril_common::entities::{Epoch, StakeDistribution};
use mithril_common::signable_builder::StakeDistributionRetriever;
use mithril_common::StdResult;
use mithril_persistence::sqlite::{ConnectionExtensions, SqliteConnection};
use mithril_persistence::store::adapter::AdapterError;
Expand Down Expand Up @@ -87,6 +88,15 @@ impl StakeStorer for StakePoolStore {
}
}

#[async_trait]
impl StakeDistributionRetriever for StakePoolStore {
async fn retrieve(&self, epoch: Epoch) -> StdResult<Option<StakeDistribution>> {
let stake_distribution = self.get_stakes(epoch).await?;

Ok(stake_distribution)
}
}

#[cfg(test)]
mod tests {
use crate::database::test_helper::{insert_stake_pool, main_db_connection};
Expand Down Expand Up @@ -120,4 +130,30 @@ mod tests {
"Stakes at epoch 2 should still exist",
);
}

#[tokio::test]
async fn retrieve_with_no_stakes_returns_none() {
let connection = main_db_connection().unwrap();
let store = StakePoolStore::new(Arc::new(connection), None);

let result = store.retrieve(Epoch(1)).await.unwrap();

assert!(result.is_none());
}

#[tokio::test]
async fn retrieve_returns_stake_distribution() {
let stake_distribution_to_retrieve =
StakeDistribution::from([("pool-123".to_string(), 123)]);
let connection = main_db_connection().unwrap();
let store = StakePoolStore::new(Arc::new(connection), None);
store
.save_stakes(Epoch(1), stake_distribution_to_retrieve.clone())
.await
.unwrap();

let stake_distribution = store.retrieve(Epoch(1)).await.unwrap();

assert_eq!(stake_distribution, Some(stake_distribution_to_retrieve));
}
}
18 changes: 13 additions & 5 deletions mithril-aggregator/src/dependency_injection/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ use mithril_common::{
EraChecker, EraMarker, EraReader, EraReaderAdapter, SupportedEra,
},
signable_builder::{
CardanoImmutableFilesFullSignableBuilder, CardanoTransactionsSignableBuilder,
MithrilSignableBuilderService, MithrilStakeDistributionSignableBuilder,
SignableBuilderService, TransactionsImporter,
CardanoImmutableFilesFullSignableBuilder, CardanoStakeDistributionSignableBuilder,
CardanoTransactionsSignableBuilder, MithrilSignableBuilderService,
MithrilStakeDistributionSignableBuilder, SignableBuilderService, TransactionsImporter,
},
signed_entity_type_lock::SignedEntityTypeLock,
MithrilTickerService, TickerService,
Expand All @@ -52,8 +52,8 @@ use mithril_persistence::{

use crate::{
artifact_builder::{
CardanoImmutableFilesFullArtifactBuilder, CardanoTransactionsArtifactBuilder,
MithrilStakeDistributionArtifactBuilder,
CardanoImmutableFilesFullArtifactBuilder, CardanoStakeDistributionArtifactBuilder,
CardanoTransactionsArtifactBuilder, MithrilStakeDistributionArtifactBuilder,
},
configuration::ExecutionEnvironment,
database::repository::{
Expand Down Expand Up @@ -1090,10 +1090,14 @@ impl DependenciesBuilder {
block_range_root_retriever,
self.get_logger()?,
));
let cardano_stake_distribution_builder = Arc::new(
CardanoStakeDistributionSignableBuilder::new(self.get_stake_store().await?),
);
let signable_builder_service = Arc::new(MithrilSignableBuilderService::new(
mithril_stake_distribution_builder,
immutable_signable_builder,
cardano_transactions_builder,
cardano_stake_distribution_builder,
));

Ok(signable_builder_service)
Expand Down Expand Up @@ -1130,12 +1134,16 @@ impl DependenciesBuilder {
let cardano_transactions_artifact_builder = Arc::new(
CardanoTransactionsArtifactBuilder::new(prover_service.clone()),
);
let stake_store = self.get_stake_store().await?;
let cardano_stake_distribution_artifact_builder =
Arc::new(CardanoStakeDistributionArtifactBuilder::new(stake_store));
let signed_entity_service = Arc::new(MithrilSignedEntityService::new(
signed_entity_storer,
mithril_stake_distribution_artifact_builder,
cardano_immutable_files_full_artifact_builder,
cardano_transactions_artifact_builder,
self.get_signed_entity_lock().await?,
cardano_stake_distribution_artifact_builder,
));

// Compute the cache pool for prover service
Expand Down
14 changes: 10 additions & 4 deletions mithril-aggregator/src/http_server/routes/signatures_routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,17 @@ mod handlers {

let signed_entity_type = match message.signed_entity_type.clone() {
Some(signed_entity_type) => Ok(signed_entity_type),
// The '.unwrap()' inside the 'None' branch is safe
// This case does not occur anymore, as there are no signers using the Mithril Signer node
// version that sends a 'RegisterSignatureMessage' without the 'signed_entity_type' field.
// TODO: Make the 'signed_entity_type' field mandatory in the 'RegisterSignatureMessage'.
None => ticker_service.get_current_time_point().await.map(|t| {
signed_entity_config.time_point_to_signed_entity(
SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
&t,
)
signed_entity_config
.time_point_to_signed_entity(
SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
&t,
)
.unwrap()
}),
};

Expand Down
Loading

0 comments on commit eaaa3dd

Please sign in to comment.