mithril_client/
cardano_stake_distribution_client.rs

1//! A client to retrieve Cardano stake distributions data from an aggregator.
2//!
3//! In order to do so it defines a [CardanoStakeDistributionClient] which exposes the following features:
4//!  - [get][CardanoStakeDistributionClient::get]: get a Cardano stake distribution data from its hash
5//!  - [get_by_epoch][CardanoStakeDistributionClient::get_by_epoch]: get a Cardano stake distribution data from its epoch
6//!  - [list][CardanoStakeDistributionClient::list]: get the list of available Cardano stake distribution
7//!
8//! # Get a Cardano stake distribution
9//!
10//! To get a Cardano stake distribution using the [ClientBuilder][crate::client::ClientBuilder].
11//!
12//! ```no_run
13//! # async fn run() -> mithril_client::MithrilResult<()> {
14//! use mithril_client::ClientBuilder;
15//!
16//! let client = ClientBuilder::aggregator("YOUR_AGGREGATOR_ENDPOINT", "YOUR_GENESIS_VERIFICATION_KEY").build()?;
17//! let cardano_stake_distribution = client.cardano_stake_distribution().get("CARDANO_STAKE_DISTRIBUTION_HASH").await?.unwrap();
18//!
19//! println!(
20//!     "Cardano stake distribution hash={}, epoch={}, stake_distribution={:?}",
21//!     cardano_stake_distribution.hash,
22//!     cardano_stake_distribution.epoch,
23//!     cardano_stake_distribution.stake_distribution
24//! );
25//! #    Ok(())
26//! # }
27//! ```
28//!
29//! # List available Cardano stake distributions
30//!
31//! To list available Cardano stake distributions using the [ClientBuilder][crate::client::ClientBuilder].
32//!
33//! ```no_run
34//! # async fn run() -> mithril_client::MithrilResult<()> {
35//! use mithril_client::ClientBuilder;
36//!
37//! let client = ClientBuilder::aggregator("YOUR_AGGREGATOR_ENDPOINT", "YOUR_GENESIS_VERIFICATION_KEY").build()?;
38//! let cardano_stake_distributions = client.cardano_stake_distribution().list().await?;
39//!
40//! for cardano_stake_distribution in cardano_stake_distributions {
41//!     println!("Cardano stake distribution hash={}, epoch={}", cardano_stake_distribution.hash, cardano_stake_distribution.epoch);
42//! }
43//! #    Ok(())
44//! # }
45//! ```
46//!
47//! # Get a Cardano stake distribution by epoch
48//!
49//! To get a Cardano stake distribution by epoch using the [ClientBuilder][crate::client::ClientBuilder].
50//!
51//! **Note:** The epoch represents the epoch at the end of which the Cardano stake distribution is computed by the Cardano node
52//!
53//! ```no_run
54//! # async fn run() -> mithril_client::MithrilResult<()> {
55//! use mithril_client::{ClientBuilder, common::Epoch};
56//!
57//! let client = ClientBuilder::aggregator("YOUR_AGGREGATOR_ENDPOINT", "YOUR_GENESIS_VERIFICATION_KEY").build()?;
58//! // For a specific epoch
59//! let cardano_stake_distribution = client.cardano_stake_distribution().get_by_epoch(Epoch(500)).await?.unwrap();
60//! // For the latest epoch known by the Mithril aggregator
61//! let cardano_stake_distribution = client.cardano_stake_distribution().get_for_latest_epoch().await?.unwrap();
62//! // For the latest epoch known by the Mithril aggregator with an offset
63//! let cardano_stake_distribution = client.cardano_stake_distribution().get_for_latest_epoch_with_offset(4).await?.unwrap();
64//!
65//! println!(
66//!     "Cardano stake distribution hash={}, epoch={}, stake_distribution={:?}",
67//!     cardano_stake_distribution.hash,
68//!     cardano_stake_distribution.epoch,
69//!     cardano_stake_distribution.stake_distribution
70//! );
71//! #    Ok(())
72//! # }
73//! ```
74
75use std::sync::Arc;
76
77use crate::common::{Epoch, EpochSpecifier};
78use crate::{CardanoStakeDistribution, CardanoStakeDistributionListItem, MithrilResult};
79
80/// HTTP client for CardanoStakeDistribution API from the aggregator
81pub struct CardanoStakeDistributionClient {
82    aggregator_requester: Arc<dyn CardanoStakeDistributionAggregatorRequest>,
83}
84
85/// Define the requests against an aggregator related to Cardano stake distribution.
86#[cfg_attr(test, mockall::automock)]
87#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
88#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
89pub trait CardanoStakeDistributionAggregatorRequest: Send + Sync {
90    /// Get the list of the latest Cardano stake distributions from the aggregator.
91    async fn list_latest(&self) -> MithrilResult<Vec<CardanoStakeDistributionListItem>>;
92
93    /// Get a Cardano stake distribution for a given hash from the aggregator.
94    async fn get_by_hash(&self, hash: &str) -> MithrilResult<Option<CardanoStakeDistribution>>;
95
96    /// Get a Cardano stake distribution for an [EpochSpecifier] from the aggregator.
97    async fn get_by_epoch(
98        &self,
99        specifier: EpochSpecifier,
100    ) -> MithrilResult<Option<CardanoStakeDistribution>>;
101}
102
103impl CardanoStakeDistributionClient {
104    /// Constructs a new `CardanoStakeDistribution`.
105    pub fn new(aggregator_requester: Arc<dyn CardanoStakeDistributionAggregatorRequest>) -> Self {
106        Self {
107            aggregator_requester,
108        }
109    }
110
111    /// Fetch a list of signed CardanoStakeDistribution
112    pub async fn list(&self) -> MithrilResult<Vec<CardanoStakeDistributionListItem>> {
113        self.aggregator_requester.list_latest().await
114    }
115
116    /// Get the given Cardano stake distribution data by hash.
117    pub async fn get(&self, hash: &str) -> MithrilResult<Option<CardanoStakeDistribution>> {
118        self.aggregator_requester.get_by_hash(hash).await
119    }
120
121    /// Get the given Cardano stake distribution data by epoch.
122    pub async fn get_by_epoch(
123        &self,
124        epoch: Epoch,
125    ) -> MithrilResult<Option<CardanoStakeDistribution>> {
126        self.aggregator_requester
127            .get_by_epoch(EpochSpecifier::Number(epoch))
128            .await
129    }
130
131    /// Get the given Cardano stake distribution data by epoch.
132    pub async fn get_for_latest_epoch(&self) -> MithrilResult<Option<CardanoStakeDistribution>> {
133        self.aggregator_requester.get_by_epoch(EpochSpecifier::Latest).await
134    }
135
136    /// Get the given Cardano stake distribution data by epoch.
137    pub async fn get_for_latest_epoch_with_offset(
138        &self,
139        offset: u64,
140    ) -> MithrilResult<Option<CardanoStakeDistribution>> {
141        self.aggregator_requester
142            .get_by_epoch(EpochSpecifier::LatestMinusOffset(offset))
143            .await
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use mockall::predicate::eq;
150
151    use mithril_common::test::mock_extensions::MockBuilder;
152
153    use crate::common::test::Dummy;
154
155    use super::*;
156
157    #[tokio::test]
158    async fn list_cardano_stake_distributions_returns_messages() {
159        let aggregator_requester =
160            MockBuilder::<MockCardanoStakeDistributionAggregatorRequest>::configure(|mock| {
161                let messages = vec![
162                    CardanoStakeDistributionListItem {
163                        hash: "hash-123".to_string(),
164                        ..Dummy::dummy()
165                    },
166                    CardanoStakeDistributionListItem {
167                        hash: "hash-456".to_string(),
168                        ..Dummy::dummy()
169                    },
170                ];
171                mock.expect_list_latest().return_once(|| Ok(messages));
172            });
173        let client = CardanoStakeDistributionClient::new(aggregator_requester);
174
175        let messages = client.list().await.unwrap();
176
177        assert_eq!(2, messages.len());
178        assert_eq!("hash-123".to_string(), messages[0].hash);
179        assert_eq!("hash-456".to_string(), messages[1].hash);
180    }
181
182    #[tokio::test]
183    async fn get_cardano_stake_distribution_returns_message() {
184        let aggregator_requester =
185            MockBuilder::<MockCardanoStakeDistributionAggregatorRequest>::configure(|mock| {
186                let message = CardanoStakeDistribution {
187                    hash: "hash_1".to_string(),
188                    certificate_hash: "certificate_123".to_string(),
189                    ..Dummy::dummy()
190                };
191                mock.expect_get_by_hash()
192                    .with(eq(message.hash.clone()))
193                    .return_once(|_| Ok(Some(message)));
194            });
195        let client = CardanoStakeDistributionClient::new(aggregator_requester);
196
197        let cardano_stake_distribution = client
198            .get("hash_1")
199            .await
200            .unwrap()
201            .expect("This test returns a Cardano stake distribution");
202
203        assert_eq!("hash_1", &cardano_stake_distribution.hash);
204        assert_eq!(
205            "certificate_123",
206            &cardano_stake_distribution.certificate_hash
207        );
208    }
209
210    #[tokio::test]
211    async fn get_cardano_stake_distribution_by_epoch_returns_message() {
212        let aggregator_requester =
213            MockBuilder::<MockCardanoStakeDistributionAggregatorRequest>::configure(|mock| {
214                let message = CardanoStakeDistribution {
215                    hash: "hash_2".to_string(),
216                    epoch: Epoch(2),
217                    ..Dummy::dummy()
218                };
219                mock.expect_get_by_epoch()
220                    .with(eq(EpochSpecifier::Number(Epoch(2))))
221                    .return_once(|_| Ok(Some(message)));
222            });
223        let client = CardanoStakeDistributionClient::new(aggregator_requester);
224
225        let cardano_stake_distribution = client
226            .get_by_epoch(Epoch(2))
227            .await
228            .unwrap()
229            .expect("This test returns a Cardano stake distribution");
230
231        assert_eq!("hash_2", &cardano_stake_distribution.hash);
232        assert_eq!(Epoch(2), &cardano_stake_distribution.epoch);
233    }
234
235    #[tokio::test]
236    async fn get_cardano_stake_distribution_for_latest_epoch_returns_message() {
237        let aggregator_requester =
238            MockBuilder::<MockCardanoStakeDistributionAggregatorRequest>::configure(|mock| {
239                let message = CardanoStakeDistribution {
240                    hash: "hash_3".to_string(),
241                    epoch: Epoch(3),
242                    ..Dummy::dummy()
243                };
244                mock.expect_get_by_epoch()
245                    .with(eq(EpochSpecifier::Latest))
246                    .return_once(|_| Ok(Some(message)));
247            });
248        let client = CardanoStakeDistributionClient::new(aggregator_requester);
249
250        let cardano_stake_distribution = client
251            .get_for_latest_epoch()
252            .await
253            .unwrap()
254            .expect("This test returns a Cardano stake distribution");
255
256        assert_eq!("hash_3", &cardano_stake_distribution.hash);
257        assert_eq!(Epoch(3), &cardano_stake_distribution.epoch);
258    }
259
260    #[tokio::test]
261    async fn get_cardano_stake_distribution_for_latest_with_offset_epoch_returns_message() {
262        let aggregator_requester =
263            MockBuilder::<MockCardanoStakeDistributionAggregatorRequest>::configure(|mock| {
264                let message = CardanoStakeDistribution {
265                    hash: "hash_4".to_string(),
266                    epoch: Epoch(4),
267                    ..Dummy::dummy()
268                };
269                mock.expect_get_by_epoch()
270                    .with(eq(EpochSpecifier::LatestMinusOffset(4)))
271                    .return_once(|_| Ok(Some(message)));
272            });
273        let client = CardanoStakeDistributionClient::new(aggregator_requester);
274
275        let cardano_stake_distribution = client
276            .get_for_latest_epoch_with_offset(4)
277            .await
278            .unwrap()
279            .expect("This test returns a Cardano stake distribution");
280
281        assert_eq!("hash_4", &cardano_stake_distribution.hash);
282        assert_eq!(Epoch(4), &cardano_stake_distribution.epoch);
283    }
284}