1#[cfg(feature = "fs")]
105use anyhow::Context;
106#[cfg(feature = "fs")]
107use slog::Logger;
108#[cfg(feature = "fs")]
109use std::path::{Path, PathBuf};
110use std::sync::Arc;
111use thiserror::Error;
112
113#[cfg(feature = "fs")]
114use mithril_common::entities::CompressionAlgorithm;
115
116#[cfg(feature = "fs")]
117use crate::feedback::FeedbackSender;
118#[cfg(feature = "fs")]
119use crate::file_downloader::{DownloadEvent, FileDownloader};
120#[cfg(feature = "fs")]
121use crate::utils::create_bootstrap_node_files;
122#[cfg(feature = "fs")]
123use crate::utils::{
124 ANCILLARIES_NOT_SIGNED_BY_MITHRIL, AncillaryVerifier, UnexpectedDownloadedFileVerifier,
125};
126use crate::{MithrilResult, Snapshot, SnapshotListItem};
127
128#[derive(Error, Debug)]
130pub enum SnapshotClientError {
131 #[error(
133 "Could not find a working download location for the snapshot digest '{digest}', tried location: {{'{locations}'}}."
134 )]
135 NoWorkingLocation {
136 digest: String,
138
139 locations: String,
141 },
142 #[error(
144 "Ancillary verifier is not set, please use `set_ancillary_verification_key` when creating the client"
145 )]
146 MissingAncillaryVerifier,
147}
148
149#[deprecated(since = "0.12.35", note = "superseded by `CardanoDatabaseClient`")]
151pub struct SnapshotClient {
152 aggregator_requester: Arc<dyn SnapshotAggregatorRequest>,
153 #[cfg(feature = "fs")]
154 http_file_downloader: Arc<dyn FileDownloader>,
155 #[cfg(feature = "fs")]
156 ancillary_verifier: Option<Arc<AncillaryVerifier>>,
157 #[cfg(feature = "fs")]
158 _feedback_sender: FeedbackSender,
159 #[cfg(feature = "fs")]
160 logger: Logger,
161}
162
163#[cfg_attr(test, mockall::automock)]
165#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
166#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
167pub trait SnapshotAggregatorRequest: Send + Sync {
168 async fn list_latest(&self) -> MithrilResult<Vec<SnapshotListItem>>;
170
171 async fn get_by_hash(&self, hash: &str) -> MithrilResult<Option<Snapshot>>;
173
174 async fn increment_snapshot_downloaded_statistic(
176 &self,
177 snapshot: Snapshot,
178 ) -> MithrilResult<()>;
179}
180
181impl SnapshotClient {
182 pub fn new(
184 aggregator_requester: Arc<dyn SnapshotAggregatorRequest>,
185 #[cfg(feature = "fs")] http_file_downloader: Arc<dyn FileDownloader>,
186 #[cfg(feature = "fs")] ancillary_verifier: Option<Arc<AncillaryVerifier>>,
187 #[cfg(feature = "fs")] feedback_sender: FeedbackSender,
188 #[cfg(feature = "fs")] logger: Logger,
189 ) -> Self {
190 Self {
191 aggregator_requester,
192 #[cfg(feature = "fs")]
193 http_file_downloader,
194 #[cfg(feature = "fs")]
195 ancillary_verifier,
196 #[cfg(feature = "fs")]
198 _feedback_sender: feedback_sender,
199 #[cfg(feature = "fs")]
200 logger: mithril_common::logging::LoggerExtensions::new_with_component_name::<Self>(
201 &logger,
202 ),
203 }
204 }
205
206 pub async fn list(&self) -> MithrilResult<Vec<SnapshotListItem>> {
208 self.aggregator_requester.list_latest().await
209 }
210
211 pub async fn get(&self, digest: &str) -> MithrilResult<Option<Snapshot>> {
213 self.aggregator_requester.get_by_hash(digest).await
214 }
215
216 cfg_fs! {
217 pub async fn download_unpack_full(
225 &self,
226 snapshot: &Snapshot,
227 target_dir: &Path,
228 ) -> MithrilResult<()> {
229 if self.ancillary_verifier.is_none() {
230 return Err(SnapshotClientError::MissingAncillaryVerifier.into());
231 }
232
233 let include_ancillary = true;
234 let expected_files_after_download = UnexpectedDownloadedFileVerifier::new(
235 target_dir,
236 include_ancillary,
237 snapshot.beacon.immutable_file_number,
238 &self.logger
239 )
240 .compute_expected_state_after_download()
241 .await?;
242
243 let result = self.run_download_unpack(snapshot, target_dir, include_ancillary).await;
245
246 expected_files_after_download
247 .remove_unexpected_files()
248 .await?;
249
250 result
251 }
252
253 pub async fn download_unpack(
261 &self,
262 snapshot: &Snapshot,
263 target_dir: &Path,
264 ) -> MithrilResult<()> {
265 slog::warn!(
266 self.logger,
267 "The fast bootstrap of the Cardano node is not available with the current parameters used in this command: the ledger state will be recomputed from genesis at startup of the Cardano node. Use the extra function download_unpack_full to allow it."
268 );
269
270 let include_ancillary = false;
271 let expected_files_after_download = UnexpectedDownloadedFileVerifier::new(
272 target_dir,
273 include_ancillary,
274 snapshot.beacon.immutable_file_number,
275 &self.logger
276 )
277 .compute_expected_state_after_download()
278 .await?;
279
280 let result = self.run_download_unpack(snapshot, target_dir, include_ancillary).await;
282
283 expected_files_after_download
284 .remove_unexpected_files()
285 .await?;
286
287 result
288 }
289
290 async fn run_download_unpack(
291 &self,
292 snapshot: &Snapshot,
293 target_dir: &Path,
294 include_ancillary: bool,
295 ) -> MithrilResult<()> {
296 use crate::feedback::MithrilEvent;
297
298 let download_id = MithrilEvent::new_snapshot_download_id();
299 self.download_unpack_immutables_files(snapshot, target_dir, &download_id)
300 .await?;
301 if include_ancillary {
302 self.download_unpack_ancillary(snapshot, target_dir, &download_id)
303 .await?;
304 }
305 create_bootstrap_node_files(
306 &self.logger,
307 target_dir,
308 &snapshot.network,
309 )?;
310 Ok(())
311 }
312
313 async fn download_unpack_immutables_files(
314 &self,
315 snapshot: &Snapshot,
316 target_dir: &Path,
317 download_id: &str,
318 ) -> MithrilResult<()> {
319 self.download_unpack_file(
320 &snapshot.digest,
321 &snapshot.locations,
322 snapshot.size,
323 target_dir,
324 snapshot.compression_algorithm,
325 DownloadEvent::Full {
326 download_id: download_id.to_string(),
327 digest: snapshot.digest.clone(),
328 },
329 )
330 .await?;
331
332 Ok(())
333 }
334
335 async fn download_unpack_ancillary(
336 &self,
337 snapshot: &Snapshot,
338 target_dir: &Path,
339 download_id: &str,
340 ) -> MithrilResult<()> {
341 slog::warn!(self.logger, "{}", ANCILLARIES_NOT_SIGNED_BY_MITHRIL);
342
343 match &snapshot.ancillary_locations {
344 None => Ok(()),
345 Some(ancillary_locations) => {
346 let temp_ancillary_unpack_dir = Self::ancillary_subdir(target_dir, download_id);
347 tokio::fs::create_dir(&temp_ancillary_unpack_dir)
348 .await
349 .with_context(|| {
350 format!(
351 "Snapshot Client can not create ancillary unpack directory '{}'",
352 temp_ancillary_unpack_dir.display()
353 )
354 })?;
355
356 let result = self
357 .download_unpack_verify_ancillary(
358 snapshot,
359 ancillary_locations,
360 snapshot.ancillary_size.unwrap_or(0),
361 target_dir,
362 &temp_ancillary_unpack_dir,
363 download_id,
364 )
365 .await;
366
367 if let Err(e) = std::fs::remove_dir_all(&temp_ancillary_unpack_dir) {
368 slog::warn!(
369 self.logger, "Failed to remove ancillary unpack directory '{}'", temp_ancillary_unpack_dir.display();
370 "error" => ?e
371 );
372 }
373
374 result
375 }
376 }
377 }
378
379 async fn download_unpack_verify_ancillary(
380 &self,
381 snapshot: &Snapshot,
382 ancillary_locations: &[String],
383 ancillary_size: u64,
384 target_dir: &Path,
385 temp_ancillary_unpack_dir: &Path,
386 download_id: &str,
387 ) -> MithrilResult<()> {
388 self.download_unpack_file(
389 &snapshot.digest,
390 ancillary_locations,
391 ancillary_size,
392 temp_ancillary_unpack_dir,
393 snapshot.compression_algorithm,
394 DownloadEvent::FullAncillary {
395 download_id: download_id.to_string(),
396 },
397 )
398 .await?;
399
400 let ancillary_verifier = self
401 .ancillary_verifier
402 .as_ref()
403 .ok_or(SnapshotClientError::MissingAncillaryVerifier)?;
404
405 let validated_manifest = ancillary_verifier.verify(temp_ancillary_unpack_dir).await?;
406 validated_manifest
407 .move_to_final_location(target_dir)
408 .await?;
409
410 Ok(())
411 }
412
413 async fn download_unpack_file(
414 &self,
415 digest: &str,
416 locations: &[String],
417 size: u64,
418 target_dir: &Path,
419 compression_algorithm: CompressionAlgorithm,
420 download_event: DownloadEvent,
421 ) -> MithrilResult<()> {
422 for location in locations {
423 let file_downloader_uri = location.to_owned().into();
424
425 match self
426 .http_file_downloader
427 .download_unpack(
428 &file_downloader_uri,
429 size,
430 target_dir,
431 Some(compression_algorithm),
432 download_event.clone(),
433 )
434 .await
435 { Err(error) => {
436 slog::warn!(self.logger, "Failed downloading snapshot from '{location}'"; "error" => ?error);
437 } _ => {
438 return Ok(());
439 }}
440 }
441
442 let locations = locations.join(", ");
443
444 Err(SnapshotClientError::NoWorkingLocation {
445 digest: digest.to_string(),
446 locations,
447 }
448 .into())
449 }
450
451 fn ancillary_subdir(target_dir: &Path, download_id: &str) -> PathBuf {
452 target_dir.join(format!("ancillary-{download_id}"))
453 }
454 }
455
456 pub async fn add_statistics(&self, snapshot: &Snapshot) -> MithrilResult<()> {
458 self.aggregator_requester
459 .increment_snapshot_downloaded_statistic(snapshot.clone())
460 .await
461 }
462}
463
464#[cfg(all(test, feature = "fs"))]
465mod tests {
466 use crate::{
467 common::CompressionAlgorithm,
468 feedback::MithrilEvent,
469 file_downloader::{MockFileDownloader, MockFileDownloaderBuilder},
470 test_utils::TestLogger,
471 };
472
473 use mithril_cardano_node_internal_database::IMMUTABLE_DIR;
474 use mithril_common::test::double::fake_keys;
475 use mithril_common::{assert_dir_eq, crypto_helper::ManifestSigner, temp_dir_create};
476
477 use crate::common::test::Dummy;
478
479 use super::*;
480
481 fn dummy_download_event() -> DownloadEvent {
482 DownloadEvent::Full {
483 download_id: MithrilEvent::new_snapshot_download_id(),
484 digest: "test-digest".to_string(),
485 }
486 }
487
488 fn setup_snapshot_client(
489 file_downloader: Arc<dyn FileDownloader>,
490 ancillary_verifier: Option<Arc<AncillaryVerifier>>,
491 ) -> SnapshotClient {
492 let aggregator_client = Arc::new(MockSnapshotAggregatorRequest::new());
493 let logger = TestLogger::stdout();
494
495 SnapshotClient::new(
496 aggregator_client,
497 file_downloader,
498 ancillary_verifier,
499 FeedbackSender::new(&[]),
500 logger.clone(),
501 )
502 }
503
504 mod fetch {
505 use mockall::predicate::eq;
506
507 use mithril_common::test::mock_extensions::MockBuilder;
508
509 use super::*;
510
511 #[tokio::test]
512 async fn get_snapshots_list() {
513 let requester = MockBuilder::<MockSnapshotAggregatorRequest>::configure(|mock| {
514 let messages = vec![
515 SnapshotListItem {
516 digest: "hash-123".to_string(),
517 ..Dummy::dummy()
518 },
519 SnapshotListItem {
520 digest: "hash-456".to_string(),
521 ..Dummy::dummy()
522 },
523 ];
524 mock.expect_list_latest().return_once(move || Ok(messages));
525 });
526 let client = SnapshotClient::new(
527 requester,
528 Arc::new(MockFileDownloader::new()),
529 None,
530 FeedbackSender::new(&[]),
531 TestLogger::stdout(),
532 );
533
534 let items = client.list().await.unwrap();
535
536 assert_eq!(2, items.len());
537 assert_eq!("hash-123".to_string(), items[0].digest);
538 assert_eq!("hash-456".to_string(), items[1].digest);
539 }
540
541 #[tokio::test]
542 async fn get_snapshot() {
543 let requester = MockBuilder::<MockSnapshotAggregatorRequest>::configure(|mock| {
544 let message = Snapshot {
545 digest: "hash".to_string(),
546 ancillary_size: Some(123),
547 ..Dummy::dummy()
548 };
549 mock.expect_get_by_hash()
550 .with(eq(message.digest.clone()))
551 .return_once(move |_| Ok(Some(message)));
552 });
553 let client = SnapshotClient::new(
554 requester,
555 Arc::new(MockFileDownloader::new()),
556 None,
557 FeedbackSender::new(&[]),
558 TestLogger::stdout(),
559 );
560
561 let snapshot = client.get("hash").await.unwrap().expect("should return a snapshot");
562
563 assert_eq!("hash", &snapshot.digest);
564 assert_eq!(Some(123), snapshot.ancillary_size);
565 }
566 }
567
568 mod download_unpack_file {
569 use super::*;
570
571 fn setup_snapshot_client(file_downloader: Arc<dyn FileDownloader>) -> SnapshotClient {
572 super::setup_snapshot_client(file_downloader, None)
573 }
574
575 #[tokio::test]
576 async fn log_warning_if_location_fails() {
577 let (logger, log_inspector) = TestLogger::memory();
578 let mock_downloader = MockFileDownloaderBuilder::default()
579 .with_file_uri("http://whatever.co/snapshot")
580 .with_failure()
581 .build();
582
583 let client = SnapshotClient {
584 logger,
585 ..setup_snapshot_client(Arc::new(mock_downloader))
586 };
587
588 let _result = client
589 .download_unpack_file(
590 "test-digest",
591 &["http://whatever.co/snapshot".to_string()],
592 19,
593 &PathBuf::from("/whatever"),
594 CompressionAlgorithm::Gzip,
595 dummy_download_event(),
596 )
597 .await;
598
599 assert!(log_inspector.contains_log("Failed downloading snapshot"));
600 }
601
602 #[tokio::test]
603 async fn error_contains_list_of_all_tried_locations_if_all_attempts_fails() {
604 let test_locations = vec![
605 "http://example.com/snapshot1".to_string(),
606 "http://example.com/snapshot2".to_string(),
607 ];
608 let mock_downloader = MockFileDownloaderBuilder::default()
609 .with_file_uri("http://example.com/snapshot1")
610 .with_failure()
611 .next_call()
612 .with_file_uri("http://example.com/snapshot2")
613 .with_failure()
614 .build();
615 let client = setup_snapshot_client(Arc::new(mock_downloader));
616
617 let error = client
618 .download_unpack_file(
619 "test-digest",
620 &test_locations,
621 19,
622 &PathBuf::from("/whatever"),
623 CompressionAlgorithm::Gzip,
624 dummy_download_event(),
625 )
626 .await
627 .expect_err("Should fail when all locations fail");
628
629 if let Some(SnapshotClientError::NoWorkingLocation { digest, locations }) =
630 error.downcast_ref::<SnapshotClientError>()
631 {
632 assert_eq!(digest, "test-digest");
633 assert_eq!(
634 locations,
635 "http://example.com/snapshot1, http://example.com/snapshot2"
636 );
637 } else {
638 panic!("Expected SnapshotClientError::NoWorkingLocation, but got: {error:?}");
639 }
640 }
641
642 #[tokio::test]
643 async fn fallback_to_another_location() {
644 let mock_downloader = MockFileDownloaderBuilder::default()
645 .with_file_uri("http://example.com/snapshot1")
646 .with_failure()
647 .next_call()
648 .with_file_uri("http://example.com/snapshot2")
649 .with_success()
650 .build();
651 let client = setup_snapshot_client(Arc::new(mock_downloader));
652
653 client
654 .download_unpack_file(
655 "test-digest",
656 &[
657 "http://example.com/snapshot1".to_string(),
658 "http://example.com/snapshot2".to_string(),
659 ],
660 19,
661 &PathBuf::from("/whatever"),
662 CompressionAlgorithm::Gzip,
663 dummy_download_event(),
664 )
665 .await
666 .expect("Should succeed when fallbacking to another location");
667 }
668
669 #[tokio::test]
670 async fn fail_if_location_list_is_empty() {
671 let client = setup_snapshot_client(Arc::new(MockFileDownloader::new()));
672
673 let error = client
674 .download_unpack_file(
675 "test-digest",
676 &Vec::new(),
677 19,
678 &PathBuf::from("/whatever"),
679 CompressionAlgorithm::Gzip,
680 dummy_download_event(),
681 )
682 .await
683 .expect_err("Should fail with empty location list");
684
685 if let Some(SnapshotClientError::NoWorkingLocation { locations, .. }) =
686 error.downcast_ref::<SnapshotClientError>()
687 {
688 assert_eq!(locations, "");
689 } else {
690 panic!("Expected SnapshotClientError::NoWorkingLocation, but got: {error:?}");
691 }
692 }
693 }
694
695 mod download_unpack_full {
696 use super::*;
697
698 #[tokio::test]
699 async fn fail_if_ancillary_verifier_is_not_set() {
700 let snapshot = Snapshot {
701 ancillary_locations: Some(vec!["http://example.com/ancillary".to_string()]),
702 ancillary_size: Some(123),
703 ..Snapshot::dummy()
704 };
705
706 let client = setup_snapshot_client(Arc::new(MockFileDownloader::new()), None);
707
708 let error = client
709 .download_unpack_full(&snapshot, &PathBuf::from("/whatever"))
710 .await
711 .expect_err("Should fail when ancillary verifier is not set");
712
713 assert!(
714 matches!(
715 error.downcast_ref::<SnapshotClientError>(),
716 Some(SnapshotClientError::MissingAncillaryVerifier)
717 ),
718 "Expected SnapshotClientError::MissingAncillaryVerifier, but got: {error:#?}"
719 );
720 }
721
722 #[tokio::test]
723 async fn remove_unexpected_downloaded_files_even_if_failing_to_verify_ancillary() {
724 let test_dir = temp_dir_create!();
725 let immutable_dir = test_dir.join(IMMUTABLE_DIR);
726 std::fs::create_dir(&immutable_dir).unwrap();
727 let snapshot = Snapshot::dummy();
728
729 let mut mock_downloader = MockFileDownloader::new();
730 mock_downloader
731 .expect_download_unpack()
732 .returning(move |_, _, _, _, _| {
733 std::fs::File::create(immutable_dir.join("unexpected.md")).unwrap();
735 Ok(())
736 });
737
738 let client = setup_snapshot_client(
739 Arc::new(mock_downloader),
740 Some(Arc::new(AncillaryVerifier::new(
741 fake_keys::manifest_verification_key()[0].try_into().unwrap(),
742 ))),
743 );
744
745 client.download_unpack_full(&snapshot, &test_dir).await.unwrap_err();
746 assert_dir_eq!(&test_dir, format!("* {IMMUTABLE_DIR}/"));
747 }
748 }
749
750 mod download_unpack {
751 use super::*;
752
753 #[tokio::test]
754 async fn warn_that_fast_boostrap_is_not_available_without_ancillary_files() {
755 let (logger, log_inspector) = TestLogger::memory();
756 let snapshot = Snapshot::dummy();
757
758 let mut mock_downloader = MockFileDownloader::new();
759 mock_downloader
760 .expect_download_unpack()
761 .returning(|_, _, _, _, _| Ok(()));
762
763 let client = SnapshotClient {
764 logger,
765 ..setup_snapshot_client(Arc::new(mock_downloader), None)
766 };
767
768 let _result = client.download_unpack(&snapshot, &PathBuf::from("/whatever")).await;
769
770 assert!(
771 log_inspector.contains_log("WARN The fast bootstrap of the Cardano node is not available with the current parameters used in this command: the ledger state will be recomputed from genesis at startup of the Cardano node. Use the extra function download_unpack_full to allow it."),
772 );
773 }
774
775 #[tokio::test]
776 async fn remove_unexpected_downloaded_files() {
777 let test_dir = temp_dir_create!();
778 let immutable_dir = test_dir.join(IMMUTABLE_DIR);
779 std::fs::create_dir(&immutable_dir).unwrap();
780 let snapshot = Snapshot::dummy();
781
782 let mut mock_downloader = MockFileDownloader::new();
783 mock_downloader
784 .expect_download_unpack()
785 .returning(move |_, _, _, _, _| {
786 std::fs::File::create(immutable_dir.join("unexpected.md")).unwrap();
788 Ok(())
789 });
790
791 let client = setup_snapshot_client(Arc::new(mock_downloader), None);
792
793 client.download_unpack(&snapshot, &test_dir).await.unwrap();
794 assert_dir_eq!(
795 &test_dir,
796 format!("* {IMMUTABLE_DIR}/\n* clean\n * protocolMagicId")
797 );
798 }
799 }
800
801 mod download_unpack_ancillary {
802 use crate::file_downloader::FakeAncillaryFileBuilder;
803
804 use super::*;
805
806 #[tokio::test]
807 async fn log_a_info_message_telling_that_the_feature_does_not_use_mithril_certification() {
808 let (logger, log_inspector) = TestLogger::memory();
809 let verification_key = fake_keys::manifest_verification_key()[0].try_into().unwrap();
810 let snapshot = Snapshot {
811 ancillary_locations: None,
812 ancillary_size: None,
813 ..Snapshot::dummy()
814 };
815
816 let client = SnapshotClient {
817 logger,
818 ..setup_snapshot_client(
819 Arc::new(MockFileDownloader::new()),
820 Some(Arc::new(AncillaryVerifier::new(verification_key))),
821 )
822 };
823
824 client
825 .download_unpack_ancillary(
826 &snapshot,
827 &PathBuf::from("/whatever"),
828 "test-download-id",
829 )
830 .await
831 .unwrap();
832
833 assert!(
834 log_inspector.contains_log(&format!("WARN {ANCILLARIES_NOT_SIGNED_BY_MITHRIL}")),
835 );
836 }
837
838 #[tokio::test]
839 async fn do_nothing_if_no_ancillary_locations_available_in_snapshot() {
840 let verification_key = fake_keys::manifest_verification_key()[0].try_into().unwrap();
841 let snapshot = Snapshot {
842 ancillary_locations: None,
843 ancillary_size: None,
844 ..Snapshot::dummy()
845 };
846
847 let client = setup_snapshot_client(
848 Arc::new(MockFileDownloader::new()),
849 Some(Arc::new(AncillaryVerifier::new(verification_key))),
850 );
851
852 client
853 .download_unpack_ancillary(
854 &snapshot,
855 &PathBuf::from("/whatever"),
856 "test-download-id",
857 )
858 .await
859 .expect("Should succeed when no ancillary locations are available");
860 }
861
862 #[tokio::test]
863 async fn delete_temporary_unpack_subfolder_if_download_fail() {
864 let test_dir = temp_dir_create!();
865 let mock_downloader = MockFileDownloaderBuilder::default()
866 .with_file_uri("http://example.com/ancillary")
867 .with_failure()
868 .build();
869 let verification_key = fake_keys::manifest_verification_key()[0].try_into().unwrap();
870
871 let client = setup_snapshot_client(
872 Arc::new(mock_downloader),
873 Some(Arc::new(AncillaryVerifier::new(verification_key))),
874 );
875 let snapshot = Snapshot {
876 ancillary_locations: Some(vec!["http://example.com/ancillary".to_string()]),
877 ancillary_size: Some(123),
878 ..Snapshot::dummy()
879 };
880
881 client
882 .download_unpack_ancillary(&snapshot, &test_dir, "test-download-id")
883 .await
884 .unwrap_err();
885
886 assert!(!SnapshotClient::ancillary_subdir(&test_dir, "test-download-id").exists());
887 }
888
889 #[tokio::test]
890 async fn delete_temporary_unpack_subfolder_if_verify_fail() {
891 let test_dir = temp_dir_create!();
892 let mock_downloader = MockFileDownloaderBuilder::default()
893 .with_file_uri("http://example.com/ancillary")
894 .with_success()
895 .build();
896 let verification_key = fake_keys::manifest_verification_key()[0].try_into().unwrap();
897
898 let client = setup_snapshot_client(
899 Arc::new(mock_downloader),
900 Some(Arc::new(AncillaryVerifier::new(verification_key))),
901 );
902 let snapshot = Snapshot {
903 ancillary_locations: Some(vec!["http://example.com/ancillary".to_string()]),
904 ancillary_size: Some(123),
905 ..Snapshot::dummy()
906 };
907
908 client
909 .download_unpack_ancillary(&snapshot, &test_dir, "test-download-id")
910 .await
911 .unwrap_err();
912
913 assert!(!SnapshotClient::ancillary_subdir(&test_dir, "test-download-id").exists());
914 }
915
916 #[tokio::test]
917 async fn move_file_in_manifest_then_delete_temporary_unpack_subfolder_if_verify_succeed() {
918 let test_dir = temp_dir_create!();
919 let ancillary_signer = ManifestSigner::create_deterministic_signer();
920 let verification_key = ancillary_signer.verification_key();
921 let mock_downloader = MockFileDownloaderBuilder::default()
922 .with_file_uri("http://example.com/ancillary")
923 .with_success_and_create_fake_ancillary_files(
924 FakeAncillaryFileBuilder::builder()
925 .files_in_manifest_to_create(vec!["dummy_ledger".to_string()])
926 .files_not_in_manifest_to_create(vec!["not_in_ancillary".to_string()])
927 .sign_manifest(ancillary_signer)
928 .build(),
929 )
930 .build();
931
932 let client = setup_snapshot_client(
933 Arc::new(mock_downloader),
934 Some(Arc::new(AncillaryVerifier::new(verification_key))),
935 );
936
937 let snapshot = Snapshot {
938 ancillary_locations: Some(vec!["http://example.com/ancillary".to_string()]),
939 ancillary_size: Some(123),
940 ..Snapshot::dummy()
941 };
942 client
943 .download_unpack_ancillary(&snapshot, &test_dir, "test-download-id")
944 .await
945 .expect("Should succeed when ancillary verification is successful");
946
947 assert_dir_eq!(&test_dir, "* dummy_ledger");
948 }
949 }
950}