mithril_client/cardano_database_client/
api.rs

1#[cfg(feature = "fs")]
2use std::path::Path;
3use std::sync::Arc;
4
5#[cfg(feature = "fs")]
6use slog::Logger;
7
8#[cfg(feature = "fs")]
9use mithril_common::{
10    crypto_helper::MKProof,
11    messages::{CardanoDatabaseSnapshotMessage, CertificateMessage},
12};
13
14use crate::aggregator_client::AggregatorClient;
15#[cfg(feature = "fs")]
16use crate::feedback::FeedbackSender;
17#[cfg(feature = "fs")]
18use crate::file_downloader::FileDownloader;
19#[cfg(feature = "fs")]
20use crate::utils::AncillaryVerifier;
21use crate::{CardanoDatabaseSnapshot, CardanoDatabaseSnapshotListItem, MithrilResult};
22
23use super::fetch::InternalArtifactRetriever;
24use super::statistics::InternalStatisticsSender;
25#[cfg(feature = "fs")]
26use super::{
27    download_unpack::InternalArtifactDownloader, proving::InternalArtifactProver,
28    DownloadUnpackOptions, ImmutableFileRange,
29};
30
31/// HTTP client for CardanoDatabase API from the Aggregator
32pub struct CardanoDatabaseClient {
33    pub(super) artifact_retriever: InternalArtifactRetriever,
34    #[cfg(feature = "fs")]
35    pub(super) artifact_downloader: InternalArtifactDownloader,
36    #[cfg(feature = "fs")]
37    pub(super) artifact_prover: InternalArtifactProver,
38    pub(super) statistics_sender: InternalStatisticsSender,
39}
40
41impl CardanoDatabaseClient {
42    /// Constructs a new `CardanoDatabase`.
43    pub fn new(
44        aggregator_client: Arc<dyn AggregatorClient>,
45        #[cfg(feature = "fs")] http_file_downloader: Arc<dyn FileDownloader>,
46        #[cfg(feature = "fs")] ancillary_verifier: Option<Arc<AncillaryVerifier>>,
47        #[cfg(feature = "fs")] feedback_sender: FeedbackSender,
48        #[cfg(feature = "fs")] logger: Logger,
49    ) -> Self {
50        #[cfg(feature = "fs")]
51        let logger =
52            mithril_common::logging::LoggerExtensions::new_with_component_name::<Self>(&logger);
53        Self {
54            artifact_retriever: InternalArtifactRetriever::new(aggregator_client.clone()),
55            #[cfg(feature = "fs")]
56            artifact_downloader: InternalArtifactDownloader::new(
57                http_file_downloader.clone(),
58                ancillary_verifier,
59                feedback_sender.clone(),
60                logger.clone(),
61            ),
62            #[cfg(feature = "fs")]
63            artifact_prover: InternalArtifactProver::new(
64                http_file_downloader.clone(),
65                logger.clone(),
66            ),
67            statistics_sender: InternalStatisticsSender::new(aggregator_client.clone()),
68        }
69    }
70
71    /// Fetch a list of signed CardanoDatabase
72    pub async fn list(&self) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
73        self.artifact_retriever.list().await
74    }
75
76    /// Get the given Cardano database data by hash
77    pub async fn get(&self, hash: &str) -> MithrilResult<Option<CardanoDatabaseSnapshot>> {
78        self.artifact_retriever.get(hash).await
79    }
80
81    /// Download and unpack the given Cardano database parts data by hash.
82    #[cfg(feature = "fs")]
83    pub async fn download_unpack(
84        &self,
85        cardano_database_snapshot: &CardanoDatabaseSnapshotMessage,
86        immutable_file_range: &ImmutableFileRange,
87        target_dir: &Path,
88        download_unpack_options: DownloadUnpackOptions,
89    ) -> MithrilResult<()> {
90        self.artifact_downloader
91            .download_unpack(
92                cardano_database_snapshot,
93                immutable_file_range,
94                target_dir,
95                download_unpack_options,
96            )
97            .await
98    }
99
100    /// Compute the Merkle proof of membership for the given immutable file range.
101    #[cfg(feature = "fs")]
102    pub async fn compute_merkle_proof(
103        &self,
104        certificate: &CertificateMessage,
105        cardano_database_snapshot: &CardanoDatabaseSnapshotMessage,
106        immutable_file_range: &ImmutableFileRange,
107        database_dir: &Path,
108    ) -> MithrilResult<MKProof> {
109        self.artifact_prover
110            .compute_merkle_proof(
111                certificate,
112                cardano_database_snapshot,
113                immutable_file_range,
114                database_dir,
115            )
116            .await
117    }
118
119    /// Increments the aggregator Cardano database snapshot download statistics
120    pub async fn add_statistics(
121        &self,
122        full_restoration: bool,
123        include_ancillary: bool,
124        number_of_immutable_files_restored: u64,
125    ) -> MithrilResult<()> {
126        self.statistics_sender
127            .add_statistics(
128                full_restoration,
129                include_ancillary,
130                number_of_immutable_files_restored,
131            )
132            .await
133    }
134}
135
136#[cfg(test)]
137pub(crate) mod test_dependency_injector {
138    use super::*;
139
140    #[cfg(feature = "fs")]
141    use mithril_common::crypto_helper::ManifestVerifierVerificationKey;
142
143    use crate::aggregator_client::MockAggregatorClient;
144    #[cfg(feature = "fs")]
145    use crate::file_downloader::{FileDownloader, MockFileDownloaderBuilder};
146    #[cfg(feature = "fs")]
147    use crate::{feedback::FeedbackReceiver, test_utils::TestLogger};
148
149    /// Dependency injector for `CardanoDatabaseClient` for testing purposes.
150    pub(crate) struct CardanoDatabaseClientDependencyInjector {
151        aggregator_client: MockAggregatorClient,
152        #[cfg(feature = "fs")]
153        http_file_downloader: Arc<dyn FileDownloader>,
154        #[cfg(feature = "fs")]
155        ancillary_verifier: Option<Arc<AncillaryVerifier>>,
156        #[cfg(feature = "fs")]
157        feedback_receivers: Vec<Arc<dyn FeedbackReceiver>>,
158    }
159
160    impl CardanoDatabaseClientDependencyInjector {
161        pub(crate) fn new() -> Self {
162            Self {
163                aggregator_client: MockAggregatorClient::new(),
164                #[cfg(feature = "fs")]
165                http_file_downloader: Arc::new(
166                    MockFileDownloaderBuilder::default()
167                        .with_compression(None)
168                        .with_success()
169                        .with_times(0)
170                        .build(),
171                ),
172                #[cfg(feature = "fs")]
173                ancillary_verifier: None,
174                #[cfg(feature = "fs")]
175                feedback_receivers: vec![],
176            }
177        }
178
179        pub(crate) fn with_aggregator_client_mock_config<F>(mut self, config: F) -> Self
180        where
181            F: FnOnce(&mut MockAggregatorClient),
182        {
183            config(&mut self.aggregator_client);
184
185            self
186        }
187
188        #[cfg(feature = "fs")]
189        pub(crate) fn with_http_file_downloader(
190            self,
191            http_file_downloader: Arc<dyn FileDownloader>,
192        ) -> Self {
193            Self {
194                http_file_downloader,
195                ..self
196            }
197        }
198
199        #[cfg(feature = "fs")]
200        pub(crate) fn with_ancillary_verifier<T>(self, ancillary_verification_key: T) -> Self
201        where
202            T: TryInto<ManifestVerifierVerificationKey>,
203            T::Error: std::fmt::Debug,
204        {
205            Self {
206                ancillary_verifier: Some(Arc::new(AncillaryVerifier::new(
207                    ancillary_verification_key.try_into().unwrap(),
208                ))),
209                ..self
210            }
211        }
212
213        #[cfg(feature = "fs")]
214        pub(crate) fn with_feedback_receivers(
215            self,
216            feedback_receivers: &[Arc<dyn FeedbackReceiver>],
217        ) -> Self {
218            Self {
219                feedback_receivers: feedback_receivers.to_vec(),
220                ..self
221            }
222        }
223
224        #[cfg(feature = "fs")]
225        pub(crate) fn build_cardano_database_client(self) -> CardanoDatabaseClient {
226            CardanoDatabaseClient::new(
227                Arc::new(self.aggregator_client),
228                self.http_file_downloader,
229                self.ancillary_verifier,
230                FeedbackSender::new(&self.feedback_receivers),
231                TestLogger::stdout(),
232            )
233        }
234
235        #[cfg(not(feature = "fs"))]
236        pub(crate) fn build_cardano_database_client(self) -> CardanoDatabaseClient {
237            CardanoDatabaseClient::new(Arc::new(self.aggregator_client))
238        }
239    }
240
241    mod tests {
242        use mockall::predicate;
243
244        use crate::aggregator_client::AggregatorRequest;
245        #[cfg(feature = "fs")]
246        use crate::feedback::StackFeedbackReceiver;
247
248        use super::*;
249
250        #[cfg(feature = "fs")]
251        #[test]
252        fn test_cardano_database_client_dependency_injector_builds() {
253            let _ = CardanoDatabaseClientDependencyInjector::new()
254                .with_aggregator_client_mock_config(|http_client| {
255                    let message = vec![CardanoDatabaseSnapshotListItem {
256                        hash: "hash-123".to_string(),
257                        ..CardanoDatabaseSnapshotListItem::dummy()
258                    }];
259                    http_client
260                        .expect_get_content()
261                        .with(predicate::eq(
262                            AggregatorRequest::ListCardanoDatabaseSnapshots,
263                        ))
264                        .return_once(move |_| Ok(serde_json::to_string(&message).unwrap()));
265                })
266                .with_http_file_downloader(Arc::new(
267                    MockFileDownloaderBuilder::default()
268                        .with_success()
269                        .with_times(0)
270                        .build(),
271                ))
272                .with_feedback_receivers(&[Arc::new(StackFeedbackReceiver::new())])
273                .build_cardano_database_client();
274        }
275
276        #[cfg(not(feature = "fs"))]
277        #[test]
278        fn test_cardano_database_client_dependency_injector_builds() {
279            let _ = CardanoDatabaseClientDependencyInjector::new()
280                .with_aggregator_client_mock_config(|http_client| {
281                    let message = vec![CardanoDatabaseSnapshotListItem {
282                        hash: "hash-123".to_string(),
283                        ..CardanoDatabaseSnapshotListItem::dummy()
284                    }];
285                    http_client
286                        .expect_get_content()
287                        .with(predicate::eq(
288                            AggregatorRequest::ListCardanoDatabaseSnapshots,
289                        ))
290                        .return_once(move |_| Ok(serde_json::to_string(&message).unwrap()));
291                })
292                .build_cardano_database_client();
293        }
294    }
295}