1use {
2 crate::{
3 bank::{Bank, BankFieldsToSerialize, BankHashStats, BankSlotDelta},
4 serde_snapshot::BankIncrementalSnapshotPersistence,
5 snapshot_hash::SnapshotHash,
6 },
7 agave_feature_set as feature_set,
8 log::*,
9 solana_accounts_db::{
10 account_storage::meta::StoredMetaWriteVersion,
11 accounts::Accounts,
12 accounts_db::AccountStorageEntry,
13 accounts_hash::{
14 AccountsDeltaHash, AccountsHash, AccountsHashKind, MerkleOrLatticeAccountsHash,
15 },
16 epoch_accounts_hash::EpochAccountsHash,
17 },
18 solana_sdk::{
19 clock::Slot, hash::Hash, rent_collector::RentCollector,
20 sysvar::epoch_schedule::EpochSchedule,
21 },
22 std::{
23 sync::{atomic::Ordering, Arc},
24 time::Instant,
25 },
26};
27
28mod compare;
29pub use compare::*;
30
31pub struct AccountsPackage {
33 pub package_kind: AccountsPackageKind,
34 pub slot: Slot,
35 pub block_height: Slot,
36 pub snapshot_storages: Vec<Arc<AccountStorageEntry>>,
37 pub expected_capitalization: u64,
38 pub accounts_hash_for_testing: Option<AccountsHash>,
39 pub accounts: Arc<Accounts>,
40 pub epoch_schedule: EpochSchedule,
41 pub rent_collector: RentCollector,
42 pub accounts_hash_algorithm: AccountsHashAlgorithm,
43
44 pub snapshot_info: Option<SupplementalSnapshotInfo>,
46
47 pub enqueued: Instant,
50}
51
52impl AccountsPackage {
53 pub fn new_for_snapshot(
55 package_kind: AccountsPackageKind,
56 bank: &Bank,
57 snapshot_storages: Vec<Arc<AccountStorageEntry>>,
58 status_cache_slot_deltas: Vec<BankSlotDelta>,
59 accounts_hash_for_testing: Option<AccountsHash>,
60 ) -> Self {
61 let slot = bank.slot();
62 if let AccountsPackageKind::Snapshot(snapshot_kind) = package_kind {
63 info!(
64 "Package snapshot for bank {} has {} account storage entries (snapshot kind: {:?})",
65 slot,
66 snapshot_storages.len(),
67 snapshot_kind,
68 );
69 if let SnapshotKind::IncrementalSnapshot(incremental_snapshot_base_slot) = snapshot_kind
70 {
71 assert!(
72 slot > incremental_snapshot_base_slot,
73 "Incremental snapshot base slot must be less than the bank being snapshotted!"
74 );
75 }
76 }
77
78 let snapshot_info = {
79 let accounts_db = &bank.rc.accounts.accounts_db;
80 let write_version = accounts_db.write_version.load(Ordering::Acquire);
81 let accounts_delta_hash = if bank
82 .feature_set
83 .is_active(&feature_set::remove_accounts_delta_hash::id())
84 {
85 AccountsDeltaHash(Hash::default())
86 } else {
87 accounts_db.get_accounts_delta_hash(slot).unwrap()
91 };
92 let bank_hash_stats = bank.get_bank_hash_stats();
93 let bank_fields_to_serialize = bank.get_fields_to_serialize();
94 SupplementalSnapshotInfo {
95 status_cache_slot_deltas,
96 bank_fields_to_serialize,
97 bank_hash_stats,
98 accounts_delta_hash,
99 must_include_epoch_accounts_hash: bank
100 .must_include_epoch_accounts_hash_in_snapshot(),
101 write_version,
102 }
103 };
104
105 let accounts_hash_algorithm = if bank.is_snapshots_lt_hash_enabled() {
106 AccountsHashAlgorithm::Lattice
107 } else {
108 AccountsHashAlgorithm::Merkle
109 };
110 Self::_new(
111 package_kind,
112 bank,
113 snapshot_storages,
114 accounts_hash_for_testing,
115 accounts_hash_algorithm,
116 Some(snapshot_info),
117 )
118 }
119
120 #[must_use]
122 pub fn new_for_accounts_hash_verifier(
123 package_kind: AccountsPackageKind,
124 bank: &Bank,
125 snapshot_storages: Vec<Arc<AccountStorageEntry>>,
126 accounts_hash_for_testing: Option<AccountsHash>,
127 ) -> Self {
128 assert_eq!(package_kind, AccountsPackageKind::AccountsHashVerifier);
129 Self::_new(
130 package_kind,
131 bank,
132 snapshot_storages,
133 accounts_hash_for_testing,
134 AccountsHashAlgorithm::Merkle,
135 None,
136 )
137 }
138
139 #[must_use]
141 pub fn new_for_epoch_accounts_hash(
142 package_kind: AccountsPackageKind,
143 bank: &Bank,
144 snapshot_storages: Vec<Arc<AccountStorageEntry>>,
145 accounts_hash_for_testing: Option<AccountsHash>,
146 ) -> Self {
147 assert_eq!(package_kind, AccountsPackageKind::EpochAccountsHash);
148 Self::_new(
149 package_kind,
150 bank,
151 snapshot_storages,
152 accounts_hash_for_testing,
153 AccountsHashAlgorithm::Merkle,
154 None,
155 )
156 }
157
158 fn _new(
159 package_kind: AccountsPackageKind,
160 bank: &Bank,
161 snapshot_storages: Vec<Arc<AccountStorageEntry>>,
162 accounts_hash_for_testing: Option<AccountsHash>,
163 accounts_hash_algorithm: AccountsHashAlgorithm,
164 snapshot_info: Option<SupplementalSnapshotInfo>,
165 ) -> Self {
166 Self {
167 package_kind,
168 slot: bank.slot(),
169 block_height: bank.block_height(),
170 snapshot_storages,
171 expected_capitalization: bank.capitalization(),
172 accounts_hash_for_testing,
173 accounts: bank.accounts(),
174 epoch_schedule: bank.epoch_schedule().clone(),
175 rent_collector: bank.rent_collector().clone(),
176 accounts_hash_algorithm,
177 snapshot_info,
178 enqueued: Instant::now(),
179 }
180 }
181
182 #[cfg(feature = "dev-context-only-utils")]
185 pub fn default_for_tests() -> Self {
186 use solana_accounts_db::accounts_db::AccountsDb;
187 let accounts_db = AccountsDb::default_for_tests();
188 let accounts = Accounts::new(Arc::new(accounts_db));
189 Self {
190 package_kind: AccountsPackageKind::AccountsHashVerifier,
191 slot: Slot::default(),
192 block_height: Slot::default(),
193 snapshot_storages: Vec::default(),
194 expected_capitalization: u64::default(),
195 accounts_hash_for_testing: Option::default(),
196 accounts: Arc::new(accounts),
197 epoch_schedule: EpochSchedule::default(),
198 rent_collector: RentCollector::default(),
199 accounts_hash_algorithm: AccountsHashAlgorithm::Merkle,
200 snapshot_info: Some(SupplementalSnapshotInfo {
201 status_cache_slot_deltas: Vec::default(),
202 bank_fields_to_serialize: BankFieldsToSerialize::default_for_tests(),
203 bank_hash_stats: BankHashStats::default(),
204 accounts_delta_hash: AccountsDeltaHash(Hash::default()),
205 must_include_epoch_accounts_hash: false,
206 write_version: StoredMetaWriteVersion::default(),
207 }),
208 enqueued: Instant::now(),
209 }
210 }
211}
212
213impl std::fmt::Debug for AccountsPackage {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 f.debug_struct("AccountsPackage")
216 .field("kind", &self.package_kind)
217 .field("slot", &self.slot)
218 .field("block_height", &self.block_height)
219 .field("accounts_hash_algorithm", &self.accounts_hash_algorithm)
220 .finish_non_exhaustive()
221 }
222}
223
224pub struct SupplementalSnapshotInfo {
226 pub status_cache_slot_deltas: Vec<BankSlotDelta>,
227 pub bank_fields_to_serialize: BankFieldsToSerialize,
228 pub bank_hash_stats: BankHashStats,
229 pub accounts_delta_hash: AccountsDeltaHash,
230 pub must_include_epoch_accounts_hash: bool,
231 pub write_version: StoredMetaWriteVersion,
232}
233
234#[derive(Debug, Copy, Clone, Eq, PartialEq)]
238pub enum AccountsPackageKind {
239 AccountsHashVerifier,
240 Snapshot(SnapshotKind),
241 EpochAccountsHash,
242}
243
244pub struct SnapshotPackage {
246 pub snapshot_kind: SnapshotKind,
247 pub slot: Slot,
248 pub block_height: Slot,
249 pub hash: SnapshotHash,
250 pub snapshot_storages: Vec<Arc<AccountStorageEntry>>,
251 pub status_cache_slot_deltas: Vec<BankSlotDelta>,
252 pub bank_fields_to_serialize: BankFieldsToSerialize,
253 pub bank_hash_stats: BankHashStats,
254 pub accounts_delta_hash: AccountsDeltaHash,
255 pub accounts_hash: AccountsHash,
256 pub epoch_accounts_hash: Option<EpochAccountsHash>,
257 pub write_version: StoredMetaWriteVersion,
258 pub bank_incremental_snapshot_persistence: Option<BankIncrementalSnapshotPersistence>,
259
260 pub enqueued: Instant,
263}
264
265impl SnapshotPackage {
266 pub fn new(
267 accounts_package: AccountsPackage,
268 merkle_or_lattice_accounts_hash: MerkleOrLatticeAccountsHash,
269 bank_incremental_snapshot_persistence: Option<BankIncrementalSnapshotPersistence>,
270 ) -> Self {
271 let AccountsPackageKind::Snapshot(kind) = accounts_package.package_kind else {
272 panic!(
273 "The AccountsPackage must be of kind Snapshot in order to make a SnapshotPackage!"
274 );
275 };
276 let Some(snapshot_info) = accounts_package.snapshot_info else {
277 panic!(
278 "The AccountsPackage must have snapshot info in order to make a SnapshotPackage!"
279 );
280 };
281
282 let accounts_hash = match merkle_or_lattice_accounts_hash {
283 MerkleOrLatticeAccountsHash::Merkle(accounts_hash_kind) => {
284 match accounts_hash_kind {
285 AccountsHashKind::Full(accounts_hash) => accounts_hash,
286 AccountsHashKind::Incremental(_) => {
287 assert!(bank_incremental_snapshot_persistence.is_some());
293 AccountsHash(Hash::default())
294 }
295 }
296 }
297 MerkleOrLatticeAccountsHash::Lattice => {
298 AccountsHash(Hash::default())
301 }
302 };
303
304 let epoch_accounts_hash = snapshot_info.must_include_epoch_accounts_hash.then(|| {
305 accounts_package
311 .accounts
312 .accounts_db
313 .epoch_accounts_hash_manager
314 .try_get_epoch_accounts_hash()
315 .unwrap()
316 });
317
318 Self {
319 snapshot_kind: kind,
320 slot: accounts_package.slot,
321 block_height: accounts_package.block_height,
322 hash: SnapshotHash::new(
323 &merkle_or_lattice_accounts_hash,
324 epoch_accounts_hash.as_ref(),
325 snapshot_info
326 .bank_fields_to_serialize
327 .accounts_lt_hash
328 .as_ref()
329 .map(|accounts_lt_hash| accounts_lt_hash.0.checksum()),
330 ),
331 snapshot_storages: accounts_package.snapshot_storages,
332 status_cache_slot_deltas: snapshot_info.status_cache_slot_deltas,
333 bank_fields_to_serialize: snapshot_info.bank_fields_to_serialize,
334 accounts_delta_hash: snapshot_info.accounts_delta_hash,
335 bank_hash_stats: snapshot_info.bank_hash_stats,
336 accounts_hash,
337 epoch_accounts_hash,
338 bank_incremental_snapshot_persistence,
339 write_version: snapshot_info.write_version,
340 enqueued: Instant::now(),
341 }
342 }
343}
344
345#[cfg(feature = "dev-context-only-utils")]
346impl SnapshotPackage {
347 pub fn default_for_tests() -> Self {
350 Self {
351 snapshot_kind: SnapshotKind::FullSnapshot,
352 slot: Slot::default(),
353 block_height: Slot::default(),
354 hash: SnapshotHash(Hash::default()),
355 snapshot_storages: Vec::default(),
356 status_cache_slot_deltas: Vec::default(),
357 bank_fields_to_serialize: BankFieldsToSerialize::default_for_tests(),
358 accounts_delta_hash: AccountsDeltaHash(Hash::default()),
359 bank_hash_stats: BankHashStats::default(),
360 accounts_hash: AccountsHash(Hash::default()),
361 epoch_accounts_hash: None,
362 bank_incremental_snapshot_persistence: None,
363 write_version: StoredMetaWriteVersion::default(),
364 enqueued: Instant::now(),
365 }
366 }
367}
368
369impl std::fmt::Debug for SnapshotPackage {
370 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
371 f.debug_struct("SnapshotPackage")
372 .field("kind", &self.snapshot_kind)
373 .field("slot", &self.slot)
374 .field("block_height", &self.block_height)
375 .finish_non_exhaustive()
376 }
377}
378
379#[derive(Clone, Copy, Debug, Eq, PartialEq)]
382pub enum SnapshotKind {
383 FullSnapshot,
384 IncrementalSnapshot(Slot),
385}
386
387impl SnapshotKind {
388 pub fn is_full_snapshot(&self) -> bool {
389 matches!(self, SnapshotKind::FullSnapshot)
390 }
391 pub fn is_incremental_snapshot(&self) -> bool {
392 matches!(self, SnapshotKind::IncrementalSnapshot(_))
393 }
394}
395
396#[derive(Debug, Copy, Clone, Eq, PartialEq)]
398pub enum AccountsHashAlgorithm {
399 Merkle,
401 Lattice,
403}