mithril_client/cardano_database_client/
api.rs1#[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};
20use crate::common::Epoch;
21#[cfg(feature = "fs")]
22use crate::feedback::FeedbackSender;
23#[cfg(feature = "fs")]
24use crate::file_downloader::FileDownloader;
25#[cfg(feature = "fs")]
26use crate::utils::{AncillaryVerifier, TempDirectoryProvider};
27use crate::{CardanoDatabaseSnapshot, CardanoDatabaseSnapshotListItem, MithrilResult};
28
29use super::fetch::InternalArtifactRetriever;
30use super::statistics::InternalStatisticsSender;
31#[cfg(feature = "fs")]
32use super::{
33 DownloadUnpackOptions, ImmutableFileRange, download_unpack::InternalArtifactDownloader,
34 proving::InternalArtifactProver,
35};
36
37pub struct CardanoDatabaseClient {
39 pub(super) artifact_retriever: InternalArtifactRetriever,
40 #[cfg(feature = "fs")]
41 pub(super) artifact_downloader: InternalArtifactDownloader,
42 #[cfg(feature = "fs")]
43 pub(super) artifact_prover: InternalArtifactProver,
44 pub(super) statistics_sender: InternalStatisticsSender,
45}
46
47impl CardanoDatabaseClient {
48 pub fn new(
50 aggregator_client: Arc<dyn AggregatorClient>,
51 #[cfg(feature = "fs")] http_file_downloader: Arc<dyn FileDownloader>,
52 #[cfg(feature = "fs")] ancillary_verifier: Option<Arc<AncillaryVerifier>>,
53 #[cfg(feature = "fs")] feedback_sender: FeedbackSender,
54 #[cfg(feature = "fs")] temp_directory_provider: Arc<dyn TempDirectoryProvider>,
55 #[cfg(feature = "fs")] logger: Logger,
56 ) -> Self {
57 #[cfg(feature = "fs")]
58 let logger =
59 mithril_common::logging::LoggerExtensions::new_with_component_name::<Self>(&logger);
60 Self {
61 artifact_retriever: InternalArtifactRetriever::new(aggregator_client.clone()),
62 #[cfg(feature = "fs")]
63 artifact_downloader: InternalArtifactDownloader::new(
64 http_file_downloader.clone(),
65 ancillary_verifier,
66 feedback_sender.clone(),
67 logger.clone(),
68 ),
69 #[cfg(feature = "fs")]
70 artifact_prover: InternalArtifactProver::new(
71 http_file_downloader.clone(),
72 temp_directory_provider.clone(),
73 logger.clone(),
74 ),
75 statistics_sender: InternalStatisticsSender::new(aggregator_client.clone()),
76 }
77 }
78
79 pub async fn list(&self) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
81 self.artifact_retriever.list().await
82 }
83
84 pub async fn list_by_epoch(
86 &self,
87 epoch: Epoch,
88 ) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
89 self.artifact_retriever.list_by_epoch(epoch).await
90 }
91
92 pub async fn list_for_latest_epoch(
94 &self,
95 ) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
96 self.artifact_retriever.list_for_latest_epoch().await
97 }
98
99 pub async fn list_for_latest_epoch_with_offset(
101 &self,
102 offset: u64,
103 ) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
104 self.artifact_retriever
105 .list_for_latest_epoch_with_offset(offset)
106 .await
107 }
108
109 pub async fn get(&self, hash: &str) -> MithrilResult<Option<CardanoDatabaseSnapshot>> {
111 self.artifact_retriever.get(hash).await
112 }
113
114 #[cfg(feature = "fs")]
116 pub async fn download_unpack(
117 &self,
118 cardano_database_snapshot: &CardanoDatabaseSnapshotMessage,
119 immutable_file_range: &ImmutableFileRange,
120 target_dir: &Path,
121 download_unpack_options: DownloadUnpackOptions,
122 ) -> MithrilResult<()> {
123 self.artifact_downloader
124 .download_unpack(
125 cardano_database_snapshot,
126 immutable_file_range,
127 target_dir,
128 download_unpack_options,
129 )
130 .await
131 }
132
133 #[cfg(feature = "fs")]
135 pub async fn download_and_verify_digests(
136 &self,
137 certificate: &CertificateMessage,
138 cardano_database_snapshot: &CardanoDatabaseSnapshotMessage,
139 ) -> MithrilResult<VerifiedDigests> {
140 self.artifact_prover
141 .download_and_verify_digests(certificate, cardano_database_snapshot)
142 .await
143 }
144
145 #[cfg(feature = "fs")]
147 pub async fn verify_cardano_database(
148 &self,
149 certificate: &CertificateMessage,
150 cardano_database_snapshot: &CardanoDatabaseSnapshotMessage,
151 immutable_file_range: &ImmutableFileRange,
152 allow_missing: bool,
153 database_dir: &Path,
154 verified_digests: &VerifiedDigests,
155 ) -> Result<MKProof, CardanoDatabaseVerificationError> {
156 self.artifact_prover
157 .verify_cardano_database(
158 certificate,
159 cardano_database_snapshot,
160 immutable_file_range,
161 allow_missing,
162 database_dir,
163 verified_digests,
164 )
165 .await
166 }
167
168 #[cfg(feature = "fs")]
170 pub fn check_has_immutables(&self, database_dir: &Path) -> MithrilResult<()> {
171 ImmutableFile::at_least_one_immutable_files_exist_in_dir(database_dir)?;
172 Ok(())
173 }
174
175 pub async fn add_statistics(
177 &self,
178 full_restoration: bool,
179 include_ancillary: bool,
180 number_of_immutable_files_restored: u64,
181 ) -> MithrilResult<()> {
182 self.statistics_sender
183 .add_statistics(
184 full_restoration,
185 include_ancillary,
186 number_of_immutable_files_restored,
187 )
188 .await
189 }
190}
191
192#[cfg(test)]
193pub(crate) mod test_dependency_injector {
194 use super::*;
195
196 #[cfg(feature = "fs")]
197 use mithril_common::crypto_helper::ManifestVerifierVerificationKey;
198
199 use crate::aggregator_client::MockAggregatorClient;
200 #[cfg(feature = "fs")]
201 use crate::file_downloader::{FileDownloader, MockFileDownloaderBuilder};
202 #[cfg(feature = "fs")]
203 use crate::utils::TimestampTempDirectoryProvider;
204 #[cfg(feature = "fs")]
205 use crate::{feedback::FeedbackReceiver, test_utils::TestLogger};
206
207 pub(crate) struct CardanoDatabaseClientDependencyInjector {
209 aggregator_client: MockAggregatorClient,
210 #[cfg(feature = "fs")]
211 http_file_downloader: Arc<dyn FileDownloader>,
212 #[cfg(feature = "fs")]
213 ancillary_verifier: Option<Arc<AncillaryVerifier>>,
214 #[cfg(feature = "fs")]
215 feedback_receivers: Vec<Arc<dyn FeedbackReceiver>>,
216 #[cfg(feature = "fs")]
217 temp_directory_provider: Arc<dyn TempDirectoryProvider>,
218 #[cfg(feature = "fs")]
219 logger: Logger,
220 }
221
222 impl CardanoDatabaseClientDependencyInjector {
223 pub(crate) fn new() -> Self {
224 Self {
225 aggregator_client: MockAggregatorClient::new(),
226 #[cfg(feature = "fs")]
227 http_file_downloader: Arc::new(
228 MockFileDownloaderBuilder::default()
229 .with_compression(None)
230 .with_success()
231 .with_times(0)
232 .build(),
233 ),
234 #[cfg(feature = "fs")]
235 ancillary_verifier: None,
236 #[cfg(feature = "fs")]
237 feedback_receivers: vec![],
238 #[cfg(feature = "fs")]
239 temp_directory_provider: Arc::new(TimestampTempDirectoryProvider::new(
240 "cardano_database_client_test",
241 )),
242 #[cfg(feature = "fs")]
243 logger: TestLogger::stdout(),
244 }
245 }
246
247 #[cfg(feature = "fs")]
248 pub(crate) fn with_logger(self, logger: Logger) -> Self {
249 #[cfg(feature = "fs")]
250 Self { logger, ..self }
251 }
252
253 pub(crate) fn with_aggregator_client_mock_config<F>(mut self, config: F) -> Self
254 where
255 F: FnOnce(&mut MockAggregatorClient),
256 {
257 config(&mut self.aggregator_client);
258
259 self
260 }
261
262 #[cfg(feature = "fs")]
263 pub(crate) fn with_http_file_downloader(
264 self,
265 http_file_downloader: Arc<dyn FileDownloader>,
266 ) -> Self {
267 Self {
268 http_file_downloader,
269 ..self
270 }
271 }
272
273 #[cfg(feature = "fs")]
274 pub(crate) fn with_ancillary_verifier<T>(self, ancillary_verification_key: T) -> Self
275 where
276 T: TryInto<ManifestVerifierVerificationKey>,
277 T::Error: std::fmt::Debug,
278 {
279 Self {
280 ancillary_verifier: Some(Arc::new(AncillaryVerifier::new(
281 ancillary_verification_key.try_into().unwrap(),
282 ))),
283 ..self
284 }
285 }
286
287 #[cfg(feature = "fs")]
288 pub(crate) fn with_feedback_receivers(
289 self,
290 feedback_receivers: &[Arc<dyn FeedbackReceiver>],
291 ) -> Self {
292 Self {
293 feedback_receivers: feedback_receivers.to_vec(),
294 ..self
295 }
296 }
297
298 #[cfg(feature = "fs")]
299 pub(crate) fn with_temp_directory_provider(
300 self,
301 temp_directory_provider: Arc<dyn TempDirectoryProvider>,
302 ) -> Self {
303 Self {
304 temp_directory_provider,
305 ..self
306 }
307 }
308
309 #[cfg(feature = "fs")]
310 pub(crate) fn build_cardano_database_client(self) -> CardanoDatabaseClient {
311 CardanoDatabaseClient::new(
312 Arc::new(self.aggregator_client),
313 self.http_file_downloader,
314 self.ancillary_verifier,
315 FeedbackSender::new(&self.feedback_receivers),
316 self.temp_directory_provider,
317 self.logger,
318 )
319 }
320
321 #[cfg(not(feature = "fs"))]
322 pub(crate) fn build_cardano_database_client(self) -> CardanoDatabaseClient {
323 CardanoDatabaseClient::new(Arc::new(self.aggregator_client))
324 }
325 }
326
327 mod tests {
328 use mithril_common::test::double::Dummy;
329 use mockall::predicate;
330
331 use crate::aggregator_client::AggregatorRequest;
332 #[cfg(feature = "fs")]
333 use crate::feedback::StackFeedbackReceiver;
334
335 use super::*;
336
337 #[cfg(feature = "fs")]
338 #[test]
339 fn test_cardano_database_client_dependency_injector_builds() {
340 let _ = CardanoDatabaseClientDependencyInjector::new()
341 .with_aggregator_client_mock_config(|http_client| {
342 let message = vec![CardanoDatabaseSnapshotListItem {
343 hash: "hash-123".to_string(),
344 ..CardanoDatabaseSnapshotListItem::dummy()
345 }];
346 http_client
347 .expect_get_content()
348 .with(predicate::eq(
349 AggregatorRequest::ListCardanoDatabaseSnapshots,
350 ))
351 .return_once(move |_| Ok(serde_json::to_string(&message).unwrap()));
352 })
353 .with_http_file_downloader(Arc::new(
354 MockFileDownloaderBuilder::default()
355 .with_success()
356 .with_times(0)
357 .build(),
358 ))
359 .with_feedback_receivers(&[Arc::new(StackFeedbackReceiver::new())])
360 .build_cardano_database_client();
361 }
362
363 #[cfg(not(feature = "fs"))]
364 #[test]
365 fn test_cardano_database_client_dependency_injector_builds() {
366 let _ = CardanoDatabaseClientDependencyInjector::new()
367 .with_aggregator_client_mock_config(|http_client| {
368 let message = vec![CardanoDatabaseSnapshotListItem {
369 hash: "hash-123".to_string(),
370 ..CardanoDatabaseSnapshotListItem::dummy()
371 }];
372 http_client
373 .expect_get_content()
374 .with(predicate::eq(
375 AggregatorRequest::ListCardanoDatabaseSnapshots,
376 ))
377 .return_once(move |_| Ok(serde_json::to_string(&message).unwrap()));
378 })
379 .build_cardano_database_client();
380 }
381 }
382}