solana_genesis_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)]
10use {
11    agave_snapshots::unpack_genesis_archive,
12    log::*,
13    solana_download_utils::download_genesis_if_missing,
14    solana_genesis_config::{GenesisConfig, DEFAULT_GENESIS_ARCHIVE},
15    solana_hash::Hash,
16    solana_rpc_client::rpc_client::RpcClient,
17    std::net::SocketAddr,
18};
19
20mod open;
21
22fn check_genesis_hash(
23    genesis_config: &GenesisConfig,
24    expected_genesis_hash: Option<Hash>,
25) -> Result<(), String> {
26    let genesis_hash = genesis_config.hash();
27
28    if let Some(expected_genesis_hash) = expected_genesis_hash {
29        if expected_genesis_hash != genesis_hash {
30            return Err(format!(
31                "Genesis hash mismatch: expected {expected_genesis_hash} but downloaded genesis \
32                 hash is {genesis_hash}",
33            ));
34        }
35    }
36
37    Ok(())
38}
39
40fn load_local_genesis(
41    ledger_path: &std::path::Path,
42    expected_genesis_hash: Option<Hash>,
43) -> Result<GenesisConfig, String> {
44    let existing_genesis = GenesisConfig::load(ledger_path)
45        .map_err(|err| format!("Failed to load genesis config: {err}"))?;
46    check_genesis_hash(&existing_genesis, expected_genesis_hash)?;
47
48    Ok(existing_genesis)
49}
50
51fn get_genesis_config(
52    rpc_addr: &SocketAddr,
53    ledger_path: &std::path::Path,
54    expected_genesis_hash: Option<Hash>,
55    max_genesis_archive_unpacked_size: u64,
56    no_genesis_fetch: bool,
57    use_progress_bar: bool,
58) -> Result<GenesisConfig, String> {
59    if no_genesis_fetch {
60        return load_local_genesis(ledger_path, expected_genesis_hash);
61    }
62
63    let genesis_package = ledger_path.join(DEFAULT_GENESIS_ARCHIVE);
64    if let Ok(tmp_genesis_package) =
65        download_genesis_if_missing(rpc_addr, &genesis_package, use_progress_bar)
66    {
67        unpack_genesis_archive(
68            &tmp_genesis_package,
69            ledger_path,
70            max_genesis_archive_unpacked_size,
71        )
72        .map_err(|err| format!("Failed to unpack downloaded genesis config: {err}"))?;
73
74        let downloaded_genesis = GenesisConfig::load(ledger_path)
75            .map_err(|err| format!("Failed to load downloaded genesis config: {err}"))?;
76
77        check_genesis_hash(&downloaded_genesis, expected_genesis_hash)?;
78        std::fs::rename(tmp_genesis_package, genesis_package)
79            .map_err(|err| format!("Unable to rename: {err:?}"))?;
80
81        Ok(downloaded_genesis)
82    } else {
83        load_local_genesis(ledger_path, expected_genesis_hash)
84    }
85}
86
87fn set_and_verify_expected_genesis_hash(
88    genesis_config: GenesisConfig,
89    expected_genesis_hash: &mut Option<Hash>,
90    rpc_client: &RpcClient,
91) -> Result<(), String> {
92    let genesis_hash = genesis_config.hash();
93    if expected_genesis_hash.is_none() {
94        info!("Expected genesis hash set to {genesis_hash}");
95        *expected_genesis_hash = Some(genesis_hash);
96    }
97    let expected_genesis_hash = expected_genesis_hash.unwrap();
98
99    // Sanity check that the RPC node is using the expected genesis hash before
100    // downloading a snapshot from it
101    let rpc_genesis_hash = rpc_client
102        .get_genesis_hash()
103        .map_err(|err| format!("Failed to get genesis hash: {err}"))?;
104
105    if expected_genesis_hash != rpc_genesis_hash {
106        return Err(format!(
107            "Genesis hash mismatch: expected {expected_genesis_hash} but RPC node genesis hash is \
108             {rpc_genesis_hash}"
109        ));
110    }
111
112    Ok(())
113}
114
115pub fn download_then_check_genesis_hash(
116    rpc_addr: &SocketAddr,
117    ledger_path: &std::path::Path,
118    expected_genesis_hash: &mut Option<Hash>,
119    max_genesis_archive_unpacked_size: u64,
120    no_genesis_fetch: bool,
121    use_progress_bar: bool,
122    rpc_client: &RpcClient,
123) -> Result<(), String> {
124    let genesis_config = get_genesis_config(
125        rpc_addr,
126        ledger_path,
127        *expected_genesis_hash,
128        max_genesis_archive_unpacked_size,
129        no_genesis_fetch,
130        use_progress_bar,
131    )?;
132
133    set_and_verify_expected_genesis_hash(genesis_config, expected_genesis_hash, rpc_client)
134}
135
136pub use open::{open_genesis_config, OpenGenesisConfigError, MAX_GENESIS_ARCHIVE_UNPACKED_SIZE};