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