solana_runtime/
snapshot_package.rs

1use {
2    crate::{
3        bank::{Bank, BankFieldsToSerialize, BankSlotDelta},
4        serde_snapshot::BankIncrementalSnapshotPersistence,
5        snapshot_hash::SnapshotHash,
6    },
7    log::*,
8    solana_accounts_db::{
9        account_storage::meta::StoredMetaWriteVersion,
10        accounts::Accounts,
11        accounts_db::{stats::BankHashStats, AccountStorageEntry},
12        accounts_hash::{AccountsDeltaHash, AccountsHash, AccountsHashKind},
13        epoch_accounts_hash::EpochAccountsHash,
14    },
15    solana_sdk::{
16        clock::Slot, hash::Hash, rent_collector::RentCollector,
17        sysvar::epoch_schedule::EpochSchedule,
18    },
19    std::{
20        sync::{atomic::Ordering, Arc},
21        time::Instant,
22    },
23};
24
25mod compare;
26pub use compare::*;
27
28/// This struct packages up fields to send from AccountsBackgroundService to AccountsHashVerifier
29pub struct AccountsPackage {
30    pub package_kind: AccountsPackageKind,
31    pub slot: Slot,
32    pub block_height: Slot,
33    pub snapshot_storages: Vec<Arc<AccountStorageEntry>>,
34    pub expected_capitalization: u64,
35    pub accounts_hash_for_testing: Option<AccountsHash>,
36    pub accounts: Arc<Accounts>,
37    pub epoch_schedule: EpochSchedule,
38    pub rent_collector: RentCollector,
39
40    /// Supplemental information needed for snapshots
41    pub snapshot_info: Option<SupplementalSnapshotInfo>,
42
43    /// The instant this accounts package was send to the queue.
44    /// Used to track how long accounts packages wait before processing.
45    pub enqueued: Instant,
46}
47
48impl AccountsPackage {
49    /// Package up bank files, storages, and slot deltas for a snapshot
50    pub fn new_for_snapshot(
51        package_kind: AccountsPackageKind,
52        bank: &Bank,
53        snapshot_storages: Vec<Arc<AccountStorageEntry>>,
54        status_cache_slot_deltas: Vec<BankSlotDelta>,
55        accounts_hash_for_testing: Option<AccountsHash>,
56    ) -> Self {
57        let slot = bank.slot();
58        if let AccountsPackageKind::Snapshot(snapshot_kind) = package_kind {
59            info!(
60                "Package snapshot for bank {} has {} account storage entries (snapshot kind: {:?})",
61                slot,
62                snapshot_storages.len(),
63                snapshot_kind,
64            );
65            if let SnapshotKind::IncrementalSnapshot(incremental_snapshot_base_slot) = snapshot_kind
66            {
67                assert!(
68                    slot > incremental_snapshot_base_slot,
69                    "Incremental snapshot base slot must be less than the bank being snapshotted!"
70                );
71            }
72        }
73
74        let snapshot_info = {
75            let accounts_db = &bank.rc.accounts.accounts_db;
76            let write_version = accounts_db.write_version.load(Ordering::Acquire);
77            // SAFETY: There *must* be an accounts delta hash for this slot.
78            // Since we only snapshot rooted slots, and we know rooted slots must be frozen,
79            // that guarantees this slot will have an accounts delta hash.
80            let accounts_delta_hash = accounts_db.get_accounts_delta_hash(slot).unwrap();
81            // SAFETY: Every slot *must* have a BankHashStats entry in AccountsDb.
82            let bank_hash_stats = accounts_db.get_bank_hash_stats(slot).unwrap();
83            let bank_fields_to_serialize = bank.get_fields_to_serialize();
84            SupplementalSnapshotInfo {
85                status_cache_slot_deltas,
86                bank_fields_to_serialize,
87                bank_hash_stats,
88                accounts_delta_hash,
89                epoch_accounts_hash: bank.get_epoch_accounts_hash_to_serialize(),
90                write_version,
91            }
92        };
93
94        Self::_new(
95            package_kind,
96            bank,
97            snapshot_storages,
98            accounts_hash_for_testing,
99            Some(snapshot_info),
100        )
101    }
102
103    /// Package up fields needed to verify an accounts hash
104    #[must_use]
105    pub fn new_for_accounts_hash_verifier(
106        package_kind: AccountsPackageKind,
107        bank: &Bank,
108        snapshot_storages: Vec<Arc<AccountStorageEntry>>,
109        accounts_hash_for_testing: Option<AccountsHash>,
110    ) -> Self {
111        assert_eq!(package_kind, AccountsPackageKind::AccountsHashVerifier);
112        Self::_new(
113            package_kind,
114            bank,
115            snapshot_storages,
116            accounts_hash_for_testing,
117            None,
118        )
119    }
120
121    /// Package up fields needed to compute an EpochAccountsHash
122    #[must_use]
123    pub fn new_for_epoch_accounts_hash(
124        package_kind: AccountsPackageKind,
125        bank: &Bank,
126        snapshot_storages: Vec<Arc<AccountStorageEntry>>,
127        accounts_hash_for_testing: Option<AccountsHash>,
128    ) -> Self {
129        assert_eq!(package_kind, AccountsPackageKind::EpochAccountsHash);
130        Self::_new(
131            package_kind,
132            bank,
133            snapshot_storages,
134            accounts_hash_for_testing,
135            None,
136        )
137    }
138
139    fn _new(
140        package_kind: AccountsPackageKind,
141        bank: &Bank,
142        snapshot_storages: Vec<Arc<AccountStorageEntry>>,
143        accounts_hash_for_testing: Option<AccountsHash>,
144        snapshot_info: Option<SupplementalSnapshotInfo>,
145    ) -> Self {
146        Self {
147            package_kind,
148            slot: bank.slot(),
149            block_height: bank.block_height(),
150            snapshot_storages,
151            expected_capitalization: bank.capitalization(),
152            accounts_hash_for_testing,
153            accounts: bank.accounts(),
154            epoch_schedule: bank.epoch_schedule().clone(),
155            rent_collector: bank.rent_collector().clone(),
156            snapshot_info,
157            enqueued: Instant::now(),
158        }
159    }
160
161    /// Create a new Accounts Package where basically every field is defaulted.
162    /// Only use for tests; many of the fields are invalid!
163    #[cfg(feature = "dev-context-only-utils")]
164    pub fn default_for_tests() -> Self {
165        use solana_accounts_db::accounts_db::AccountsDb;
166        let accounts_db = AccountsDb::default_for_tests();
167        let accounts = Accounts::new(Arc::new(accounts_db));
168        Self {
169            package_kind: AccountsPackageKind::AccountsHashVerifier,
170            slot: Slot::default(),
171            block_height: Slot::default(),
172            snapshot_storages: Vec::default(),
173            expected_capitalization: u64::default(),
174            accounts_hash_for_testing: Option::default(),
175            accounts: Arc::new(accounts),
176            epoch_schedule: EpochSchedule::default(),
177            rent_collector: RentCollector::default(),
178            snapshot_info: Some(SupplementalSnapshotInfo {
179                status_cache_slot_deltas: Vec::default(),
180                bank_fields_to_serialize: BankFieldsToSerialize::default_for_tests(),
181                bank_hash_stats: BankHashStats::default(),
182                accounts_delta_hash: AccountsDeltaHash(Hash::default()),
183                epoch_accounts_hash: Option::default(),
184                write_version: StoredMetaWriteVersion::default(),
185            }),
186            enqueued: Instant::now(),
187        }
188    }
189}
190
191impl std::fmt::Debug for AccountsPackage {
192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193        f.debug_struct("AccountsPackage")
194            .field("kind", &self.package_kind)
195            .field("slot", &self.slot)
196            .field("block_height", &self.block_height)
197            .finish_non_exhaustive()
198    }
199}
200
201/// Supplemental information needed for snapshots
202pub struct SupplementalSnapshotInfo {
203    pub status_cache_slot_deltas: Vec<BankSlotDelta>,
204    pub bank_fields_to_serialize: BankFieldsToSerialize,
205    pub bank_hash_stats: BankHashStats,
206    pub accounts_delta_hash: AccountsDeltaHash,
207    pub epoch_accounts_hash: Option<EpochAccountsHash>,
208    pub write_version: StoredMetaWriteVersion,
209}
210
211/// Accounts packages are sent to the Accounts Hash Verifier for processing.  There are multiple
212/// types of accounts packages, which are specified as variants in this enum.  All accounts
213/// packages do share some processing: such as calculating the accounts hash.
214#[derive(Debug, Copy, Clone, Eq, PartialEq)]
215pub enum AccountsPackageKind {
216    AccountsHashVerifier,
217    Snapshot(SnapshotKind),
218    EpochAccountsHash,
219}
220
221/// This struct packages up fields to send from AccountsHashVerifier to SnapshotPackagerService
222pub struct SnapshotPackage {
223    pub snapshot_kind: SnapshotKind,
224    pub slot: Slot,
225    pub block_height: Slot,
226    pub hash: SnapshotHash,
227    pub snapshot_storages: Vec<Arc<AccountStorageEntry>>,
228    pub status_cache_slot_deltas: Vec<BankSlotDelta>,
229    pub bank_fields_to_serialize: BankFieldsToSerialize,
230    pub bank_hash_stats: BankHashStats,
231    pub accounts_delta_hash: AccountsDeltaHash,
232    pub accounts_hash: AccountsHash,
233    pub epoch_accounts_hash: Option<EpochAccountsHash>,
234    pub write_version: StoredMetaWriteVersion,
235    pub bank_incremental_snapshot_persistence: Option<BankIncrementalSnapshotPersistence>,
236
237    /// The instant this snapshot package was sent to the queue.
238    /// Used to track how long snapshot packages wait before handling.
239    pub enqueued: Instant,
240}
241
242impl SnapshotPackage {
243    pub fn new(
244        accounts_package: AccountsPackage,
245        accounts_hash_kind: AccountsHashKind,
246        bank_incremental_snapshot_persistence: Option<BankIncrementalSnapshotPersistence>,
247    ) -> Self {
248        let AccountsPackageKind::Snapshot(kind) = accounts_package.package_kind else {
249            panic!(
250                "The AccountsPackage must be of kind Snapshot in order to make a SnapshotPackage!"
251            );
252        };
253        let Some(snapshot_info) = accounts_package.snapshot_info else {
254            panic!(
255                "The AccountsPackage must have snapshot info in order to make a SnapshotPackage!"
256            );
257        };
258
259        let accounts_hash = match accounts_hash_kind {
260            AccountsHashKind::Full(accounts_hash) => accounts_hash,
261            AccountsHashKind::Incremental(_) => {
262                // The accounts hash is only needed when serializing a full snapshot.
263                // When serializing an incremental snapshot, there will not be a full accounts hash
264                // at `slot`.  In that case, use the default, because it doesn't actually get used.
265                // The incremental snapshot will use the BankIncrementalSnapshotPersistence
266                // field, so ensure it is Some.
267                assert!(bank_incremental_snapshot_persistence.is_some());
268                AccountsHash(Hash::default())
269            }
270        };
271
272        Self {
273            snapshot_kind: kind,
274            slot: accounts_package.slot,
275            block_height: accounts_package.block_height,
276            hash: SnapshotHash::new(
277                &accounts_hash_kind,
278                snapshot_info.epoch_accounts_hash.as_ref(),
279            ),
280            snapshot_storages: accounts_package.snapshot_storages,
281            status_cache_slot_deltas: snapshot_info.status_cache_slot_deltas,
282            bank_fields_to_serialize: snapshot_info.bank_fields_to_serialize,
283            accounts_delta_hash: snapshot_info.accounts_delta_hash,
284            bank_hash_stats: snapshot_info.bank_hash_stats,
285            accounts_hash,
286            epoch_accounts_hash: snapshot_info.epoch_accounts_hash,
287            bank_incremental_snapshot_persistence,
288            write_version: snapshot_info.write_version,
289            enqueued: Instant::now(),
290        }
291    }
292}
293
294#[cfg(feature = "dev-context-only-utils")]
295impl SnapshotPackage {
296    /// Create a new SnapshotPackage where basically every field is defaulted.
297    /// Only use for tests; many of the fields are invalid!
298    pub fn default_for_tests() -> Self {
299        Self {
300            snapshot_kind: SnapshotKind::FullSnapshot,
301            slot: Slot::default(),
302            block_height: Slot::default(),
303            hash: SnapshotHash(Hash::default()),
304            snapshot_storages: Vec::default(),
305            status_cache_slot_deltas: Vec::default(),
306            bank_fields_to_serialize: BankFieldsToSerialize::default_for_tests(),
307            accounts_delta_hash: AccountsDeltaHash(Hash::default()),
308            bank_hash_stats: BankHashStats::default(),
309            accounts_hash: AccountsHash(Hash::default()),
310            epoch_accounts_hash: None,
311            bank_incremental_snapshot_persistence: None,
312            write_version: StoredMetaWriteVersion::default(),
313            enqueued: Instant::now(),
314        }
315    }
316}
317
318impl std::fmt::Debug for SnapshotPackage {
319    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320        f.debug_struct("SnapshotPackage")
321            .field("kind", &self.snapshot_kind)
322            .field("slot", &self.slot)
323            .field("block_height", &self.block_height)
324            .finish_non_exhaustive()
325    }
326}
327
328/// Snapshots come in two kinds, Full and Incremental.  The IncrementalSnapshot has a Slot field,
329/// which is the incremental snapshot base slot.
330#[derive(Clone, Copy, Debug, Eq, PartialEq)]
331pub enum SnapshotKind {
332    FullSnapshot,
333    IncrementalSnapshot(Slot),
334}
335
336impl SnapshotKind {
337    pub fn is_full_snapshot(&self) -> bool {
338        matches!(self, SnapshotKind::FullSnapshot)
339    }
340    pub fn is_incremental_snapshot(&self) -> bool {
341        matches!(self, SnapshotKind::IncrementalSnapshot(_))
342    }
343}