solana_download_utils/
lib.rs

1#![cfg_attr(
2    not(feature = "agave-unstable-api"),
3    deprecated(
4        since = "3.1.0",
5        note = "This crate has been marked for formal inclusion in the Agave Unstable API. From \
6                v4.0.0 onward, the `agave-unstable-api` crate feature must be specified to \
7                acknowledge use of an interface that may break without warning."
8    )
9)]
10pub use solana_file_download::DownloadProgressRecord;
11use {
12    agave_snapshots::{
13        paths as snapshot_paths, snapshot_hash::SnapshotHash, ArchiveFormat, SnapshotKind,
14        ZstdConfig,
15    },
16    log::*,
17    solana_clock::Slot,
18    solana_file_download::{download_file, DownloadProgressCallbackOption},
19    solana_genesis_config::DEFAULT_GENESIS_ARCHIVE,
20    solana_runtime::snapshot_utils,
21    std::{
22        fs,
23        net::SocketAddr,
24        num::NonZeroUsize,
25        path::{Path, PathBuf},
26    },
27};
28
29pub fn download_genesis_if_missing(
30    rpc_addr: &SocketAddr,
31    genesis_package: &Path,
32    use_progress_bar: bool,
33) -> Result<PathBuf, String> {
34    if !genesis_package.exists() {
35        let tmp_genesis_path = genesis_package.parent().unwrap().join("tmp-genesis");
36        let tmp_genesis_package = tmp_genesis_path.join(DEFAULT_GENESIS_ARCHIVE);
37
38        let _ignored = fs::remove_dir_all(&tmp_genesis_path);
39        download_file(
40            &format!("http://{rpc_addr}/{DEFAULT_GENESIS_ARCHIVE}"),
41            &tmp_genesis_package,
42            use_progress_bar,
43            &mut None,
44        )?;
45
46        Ok(tmp_genesis_package)
47    } else {
48        Err("genesis already exists".to_string())
49    }
50}
51
52/// Download a snapshot archive from `rpc_addr`.  Use `snapshot_kind` to specify downloading either
53/// a full snapshot or an incremental snapshot.
54pub fn download_snapshot_archive(
55    rpc_addr: &SocketAddr,
56    full_snapshot_archives_dir: &Path,
57    incremental_snapshot_archives_dir: &Path,
58    desired_snapshot_hash: (Slot, SnapshotHash),
59    snapshot_kind: SnapshotKind,
60    maximum_full_snapshot_archives_to_retain: NonZeroUsize,
61    maximum_incremental_snapshot_archives_to_retain: NonZeroUsize,
62    use_progress_bar: bool,
63    progress_notify_callback: &mut DownloadProgressCallbackOption<'_>,
64) -> Result<(), String> {
65    snapshot_utils::purge_old_snapshot_archives(
66        full_snapshot_archives_dir,
67        incremental_snapshot_archives_dir,
68        maximum_full_snapshot_archives_to_retain,
69        maximum_incremental_snapshot_archives_to_retain,
70    );
71
72    let snapshot_archives_remote_dir =
73        snapshot_paths::build_snapshot_archives_remote_dir(match snapshot_kind {
74            SnapshotKind::FullSnapshot => full_snapshot_archives_dir,
75            SnapshotKind::IncrementalSnapshot(_) => incremental_snapshot_archives_dir,
76        });
77    fs::create_dir_all(&snapshot_archives_remote_dir).unwrap();
78
79    for archive_format in [
80        ArchiveFormat::TarZstd {
81            config: ZstdConfig::default(),
82        },
83        ArchiveFormat::TarLz4,
84    ] {
85        let destination_path = match snapshot_kind {
86            SnapshotKind::FullSnapshot => snapshot_paths::build_full_snapshot_archive_path(
87                &snapshot_archives_remote_dir,
88                desired_snapshot_hash.0,
89                &desired_snapshot_hash.1,
90                archive_format,
91            ),
92            SnapshotKind::IncrementalSnapshot(base_slot) => {
93                snapshot_paths::build_incremental_snapshot_archive_path(
94                    &snapshot_archives_remote_dir,
95                    base_slot,
96                    desired_snapshot_hash.0,
97                    &desired_snapshot_hash.1,
98                    archive_format,
99                )
100            }
101        };
102
103        if destination_path.is_file() {
104            return Ok(());
105        }
106
107        match download_file(
108            &format!(
109                "http://{}/{}",
110                rpc_addr,
111                destination_path.file_name().unwrap().to_str().unwrap()
112            ),
113            &destination_path,
114            use_progress_bar,
115            progress_notify_callback,
116        ) {
117            Ok(()) => return Ok(()),
118            Err(err) => info!("{err}"),
119        }
120    }
121    Err(format!(
122        "Failed to download a snapshot archive for slot {} from {}",
123        desired_snapshot_hash.0, rpc_addr
124    ))
125}