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