mithril_client/
mithril_stake_distribution_client.rs

1//! A client to retrieve Mithril stake distributions data from an Aggregator.
2//!
3//! In order to do so it defines a [MithrilStakeDistributionClient] which exposes the following features:
4//!  - [get][MithrilStakeDistributionClient::get]: get a Mithril stake distribution data from its hash
5//!  - [list][MithrilStakeDistributionClient::list]: get the list of available Mithril stake distribution
6//!
7//! # Get a Mithril stake distribution
8//!
9//! To get a Mithril stake distribution using the [ClientBuilder][crate::client::ClientBuilder].
10//!
11//! ```no_run
12//! # async fn run() -> mithril_client::MithrilResult<()> {
13//! use mithril_client::ClientBuilder;
14//!
15//! let client = ClientBuilder::aggregator("YOUR_AGGREGATOR_ENDPOINT", "YOUR_GENESIS_VERIFICATION_KEY").build()?;
16//! let mithril_stake_distribution = client.mithril_stake_distribution().get("MITHRIL_STAKE_DISTRIBUTION_HASH").await?.unwrap();
17//!
18//! println!("Mithril stake distribution hash={}, epoch={}", mithril_stake_distribution.hash, mithril_stake_distribution.epoch);
19//! #    Ok(())
20//! # }
21//! ```
22//!
23//! # List available Mithril stake distributions
24//!
25//! To list available Mithril stake distributions using the [ClientBuilder][crate::client::ClientBuilder].
26//!
27//! ```no_run
28//! # async fn run() -> mithril_client::MithrilResult<()> {
29//! use mithril_client::ClientBuilder;
30//!
31//! let client = ClientBuilder::aggregator("YOUR_AGGREGATOR_ENDPOINT", "YOUR_GENESIS_VERIFICATION_KEY").build()?;
32//! let mithril_stake_distributions = client.mithril_stake_distribution().list().await?;
33//!
34//! for mithril_stake_distribution in mithril_stake_distributions {
35//!     println!("Mithril stake distribution hash={}, epoch={}", mithril_stake_distribution.hash, mithril_stake_distribution.epoch);
36//! }
37//! #    Ok(())
38//! # }
39//! ```
40
41use std::sync::Arc;
42
43use crate::aggregator_client::{AggregatorClient, AggregatorClientError, AggregatorRequest};
44use anyhow::Context;
45
46use crate::{MithrilResult, MithrilStakeDistribution, MithrilStakeDistributionListItem};
47
48/// HTTP client for MithrilStakeDistribution API from the Aggregator
49pub struct MithrilStakeDistributionClient {
50    aggregator_client: Arc<dyn AggregatorClient>,
51}
52
53impl MithrilStakeDistributionClient {
54    /// Constructs a new `MithrilStakeDistributionClient`.
55    pub fn new(aggregator_client: Arc<dyn AggregatorClient>) -> Self {
56        Self { aggregator_client }
57    }
58
59    /// Fetch a list of signed MithrilStakeDistribution
60    pub async fn list(&self) -> MithrilResult<Vec<MithrilStakeDistributionListItem>> {
61        let response = self
62            .aggregator_client
63            .get_content(AggregatorRequest::ListMithrilStakeDistributions)
64            .await
65            .with_context(|| "MithrilStakeDistribution Client can not get the artifact list")?;
66        let items = serde_json::from_str::<Vec<MithrilStakeDistributionListItem>>(&response)
67            .with_context(|| "MithrilStakeDistribution Client can not deserialize artifact list")?;
68
69        Ok(items)
70    }
71
72    /// Get the given stake distribution data. If it cannot be found, a None is returned.
73    pub async fn get(&self, hash: &str) -> MithrilResult<Option<MithrilStakeDistribution>> {
74        match self
75            .aggregator_client
76            .get_content(AggregatorRequest::GetMithrilStakeDistribution {
77                hash: hash.to_string(),
78            })
79            .await
80        {
81            Ok(content) => {
82                let stake_distribution_entity: MithrilStakeDistribution =
83                    serde_json::from_str(&content).with_context(|| {
84                        "MithrilStakeDistribution Client can not deserialize artifact"
85                    })?;
86
87                Ok(Some(stake_distribution_entity))
88            }
89            Err(AggregatorClientError::RemoteServerLogical(_)) => Ok(None),
90            Err(e) => Err(e.into()),
91        }
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use chrono::{DateTime, Utc};
98    use mithril_common::test_utils::fake_data;
99
100    use crate::aggregator_client::MockAggregatorClient;
101    use crate::common::Epoch;
102    use crate::MithrilSigner;
103
104    use super::*;
105
106    fn fake_messages() -> Vec<MithrilStakeDistributionListItem> {
107        vec![
108            MithrilStakeDistributionListItem {
109                epoch: Epoch(1),
110                hash: "hash-123".to_string(),
111                certificate_hash: "cert-hash-123".to_string(),
112                created_at: DateTime::parse_from_rfc3339("2023-01-19T13:43:05.618857482Z")
113                    .unwrap()
114                    .with_timezone(&Utc),
115            },
116            MithrilStakeDistributionListItem {
117                epoch: Epoch(2),
118                hash: "hash-456".to_string(),
119                certificate_hash: "cert-hash-456".to_string(),
120                created_at: DateTime::parse_from_rfc3339("2023-01-19T13:43:05.618857482Z")
121                    .unwrap()
122                    .with_timezone(&Utc),
123            },
124        ]
125    }
126
127    #[tokio::test]
128    async fn get_mithril_stake_distribution_list() {
129        let message = fake_messages();
130        let mut http_client = MockAggregatorClient::new();
131        http_client
132            .expect_get_content()
133            .return_once(move |_| Ok(serde_json::to_string(&message).unwrap()));
134        let client = MithrilStakeDistributionClient::new(Arc::new(http_client));
135        let items = client.list().await.unwrap();
136
137        assert_eq!(2, items.len());
138        assert_eq!("hash-123".to_string(), items[0].hash);
139        assert_eq!("hash-456".to_string(), items[1].hash);
140    }
141
142    #[tokio::test]
143    async fn get_mithril_stake_distribution() {
144        let mut http_client = MockAggregatorClient::new();
145        let message = MithrilStakeDistribution {
146            certificate_hash: "certificate-hash-123".to_string(),
147            epoch: Epoch(1),
148            signers_with_stake: MithrilSigner::from_signers(fake_data::signers_with_stakes(2)),
149            hash: "hash".to_string(),
150            created_at: DateTime::<Utc>::default(),
151            protocol_parameters: fake_data::protocol_parameters(),
152        };
153        http_client
154            .expect_get_content()
155            .return_once(move |_| Ok(serde_json::to_string(&message).unwrap()));
156        let client = MithrilStakeDistributionClient::new(Arc::new(http_client));
157        let stake_distribution_entity = client
158            .get("hash")
159            .await
160            .unwrap()
161            .expect("This test returns a stake distribution");
162
163        assert_eq!("hash".to_string(), stake_distribution_entity.hash);
164        assert_eq!(2, stake_distribution_entity.signers_with_stake.len(),);
165    }
166}