1use {
2 crate::{
3 accounts_db::{
4 AccountShrinkThreshold, AccountsDbConfig, SnapshotStorage, SnapshotStorages,
5 },
6 accounts_index::AccountSecondaryIndexes,
7 accounts_update_notifier_interface::AccountsUpdateNotifier,
8 bank::{Bank, BankFieldsToDeserialize, BankSlotDelta},
9 builtins::Builtins,
10 hardened_unpack::{unpack_snapshot, ParallelSelector, UnpackError, UnpackedAppendVecMap},
11 serde_snapshot::{
12 bank_from_streams, bank_to_stream, fields_from_streams, SerdeStyle, SnapshotStreams,
13 },
14 shared_buffer_reader::{SharedBuffer, SharedBufferReader},
15 snapshot_archive_info::{
16 FullSnapshotArchiveInfo, IncrementalSnapshotArchiveInfo, SnapshotArchiveInfoGetter,
17 },
18 snapshot_package::{
19 AccountsPackage, PendingAccountsPackage, SnapshotPackage, SnapshotType,
20 },
21 status_cache,
22 },
23 bincode::{config::Options, serialize_into},
24 bzip2::bufread::BzDecoder,
25 flate2::read::GzDecoder,
26 lazy_static::lazy_static,
27 log::*,
28 rayon::prelude::*,
29 regex::Regex,
30 safecoin_measure::measure::Measure,
31 solana_sdk::{
32 clock::Slot,
33 genesis_config::GenesisConfig,
34 hash::Hash,
35 pubkey::Pubkey,
36 slot_history::{Check, SlotHistory},
37 },
38 std::{
39 cmp::Ordering,
40 collections::{HashMap, HashSet},
41 fmt,
42 fs::{self, File},
43 io::{BufReader, BufWriter, Error as IoError, ErrorKind, Read, Seek, Write},
44 path::{Path, PathBuf},
45 process::ExitStatus,
46 str::FromStr,
47 sync::Arc,
48 },
49 tar::{self, Archive},
50 tempfile::TempDir,
51 thiserror::Error,
52};
53
54mod archive_format;
55pub use archive_format::*;
56
57pub const SNAPSHOT_STATUS_CACHE_FILENAME: &str = "status_cache";
58pub const SNAPSHOT_ARCHIVE_DOWNLOAD_DIR: &str = "remote";
59pub const DEFAULT_FULL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS: Slot = 25_000;
60pub const DEFAULT_INCREMENTAL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS: Slot = 100;
61const MAX_SNAPSHOT_DATA_FILE_SIZE: u64 = 32 * 1024 * 1024 * 1024; const MAX_SNAPSHOT_VERSION_FILE_SIZE: u64 = 8; const VERSION_STRING_V1_2_0: &str = "1.2.0";
64pub(crate) const TMP_BANK_SNAPSHOT_PREFIX: &str = "tmp-bank-snapshot-";
65pub const TMP_SNAPSHOT_ARCHIVE_PREFIX: &str = "tmp-snapshot-archive-";
66pub const BANK_SNAPSHOT_PRE_FILENAME_EXTENSION: &str = "pre";
67pub const MAX_BANK_SNAPSHOTS_TO_RETAIN: usize = 8; pub const DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN: usize = 2;
69pub const DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN: usize = 4;
70pub const FULL_SNAPSHOT_ARCHIVE_FILENAME_REGEX: &str = r"^snapshot-(?P<slot>[[:digit:]]+)-(?P<hash>[[:alnum:]]+)\.(?P<ext>tar|tar\.bz2|tar\.zst|tar\.gz|tar\.lz4)$";
71pub const INCREMENTAL_SNAPSHOT_ARCHIVE_FILENAME_REGEX: &str = r"^incremental-snapshot-(?P<base>[[:digit:]]+)-(?P<slot>[[:digit:]]+)-(?P<hash>[[:alnum:]]+)\.(?P<ext>tar|tar\.bz2|tar\.zst|tar\.gz|tar\.lz4)$";
72
73#[derive(Copy, Clone, Eq, PartialEq, Debug)]
74pub enum SnapshotVersion {
75 V1_2_0,
76}
77
78impl Default for SnapshotVersion {
79 fn default() -> Self {
80 SnapshotVersion::V1_2_0
81 }
82}
83
84impl fmt::Display for SnapshotVersion {
85 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86 f.write_str(From::from(*self))
87 }
88}
89
90impl From<SnapshotVersion> for &'static str {
91 fn from(snapshot_version: SnapshotVersion) -> &'static str {
92 match snapshot_version {
93 SnapshotVersion::V1_2_0 => VERSION_STRING_V1_2_0,
94 }
95 }
96}
97
98impl FromStr for SnapshotVersion {
99 type Err = &'static str;
100
101 fn from_str(version_string: &str) -> std::result::Result<Self, Self::Err> {
102 let version_string = if version_string
104 .get(..1)
105 .map_or(false, |s| s.eq_ignore_ascii_case("v"))
106 {
107 &version_string[1..]
108 } else {
109 version_string
110 };
111 match version_string {
112 VERSION_STRING_V1_2_0 => Ok(SnapshotVersion::V1_2_0),
113 _ => Err("unsupported snapshot version"),
114 }
115 }
116}
117
118impl SnapshotVersion {
119 pub fn as_str(self) -> &'static str {
120 <&str as From<Self>>::from(self)
121 }
122
123 fn maybe_from_string(version_string: &str) -> Option<SnapshotVersion> {
124 version_string.parse::<Self>().ok()
125 }
126}
127
128#[derive(PartialEq, Eq, Debug)]
131pub struct BankSnapshotInfo {
132 pub slot: Slot,
134 pub snapshot_path: PathBuf,
136 pub snapshot_type: BankSnapshotType,
138}
139
140impl PartialOrd for BankSnapshotInfo {
141 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
142 Some(self.cmp(other))
143 }
144}
145
146impl Ord for BankSnapshotInfo {
148 fn cmp(&self, other: &Self) -> Ordering {
149 self.slot.cmp(&other.slot)
150 }
151}
152
153#[derive(Debug, Copy, Clone, Eq, PartialEq)]
162pub enum BankSnapshotType {
163 Pre,
165 Post,
167}
168
169#[derive(Debug)]
172struct SnapshotRootPaths {
173 full_snapshot_root_file_path: PathBuf,
174 incremental_snapshot_root_file_path: Option<PathBuf>,
175}
176
177#[derive(Debug)]
179struct UnarchivedSnapshot {
180 #[allow(dead_code)]
181 unpack_dir: TempDir,
182 unpacked_append_vec_map: UnpackedAppendVecMap,
183 unpacked_snapshots_dir_and_version: UnpackedSnapshotsDirAndVersion,
184 measure_untar: Measure,
185}
186
187#[derive(Debug)]
189struct UnpackedSnapshotsDirAndVersion {
190 unpacked_snapshots_dir: PathBuf,
191 snapshot_version: String,
192}
193
194#[derive(Error, Debug)]
195#[allow(clippy::large_enum_variant)]
196pub enum SnapshotError {
197 #[error("I/O error: {0}")]
198 Io(#[from] std::io::Error),
199
200 #[error("serialization error: {0}")]
201 Serialize(#[from] bincode::Error),
202
203 #[error("archive generation failure {0}")]
204 ArchiveGenerationFailure(ExitStatus),
205
206 #[error("storage path symlink is invalid")]
207 StoragePathSymlinkInvalid,
208
209 #[error("Unpack error: {0}")]
210 UnpackError(#[from] UnpackError),
211
212 #[error("source({1}) - I/O error: {0}")]
213 IoWithSource(std::io::Error, &'static str),
214
215 #[error("could not get file name from path: {}", .0.display())]
216 PathToFileNameError(PathBuf),
217
218 #[error("could not get str from file name: {}", .0.display())]
219 FileNameToStrError(PathBuf),
220
221 #[error("could not parse snapshot archive's file name: {0}")]
222 ParseSnapshotArchiveFileNameError(String),
223
224 #[error("snapshots are incompatible: full snapshot slot ({0}) and incremental snapshot base slot ({1}) do not match")]
225 MismatchedBaseSlot(Slot, Slot),
226
227 #[error("no snapshot archives to load from")]
228 NoSnapshotArchives,
229
230 #[error("snapshot has mismatch: deserialized bank: {:?}, snapshot archive info: {:?}", .0, .1)]
231 MismatchedSlotHash((Slot, Hash), (Slot, Hash)),
232
233 #[error("snapshot slot deltas are invalid: {0}")]
234 VerifySlotDeltas(#[from] VerifySlotDeltasError),
235}
236pub type Result<T> = std::result::Result<T, SnapshotError>;
237
238#[derive(Error, Debug, PartialEq, Eq)]
240pub enum VerifySlotDeltasError {
241 #[error("too many entries: {0} (max: {1})")]
242 TooManyEntries(usize, usize),
243
244 #[error("slot {0} is not a root")]
245 SlotIsNotRoot(Slot),
246
247 #[error("slot {0} is greater than bank slot {1}")]
248 SlotGreaterThanMaxRoot(Slot, Slot),
249
250 #[error("slot {0} has multiple entries")]
251 SlotHasMultipleEntries(Slot),
252
253 #[error("slot {0} was not found in slot history")]
254 SlotNotFoundInHistory(Slot),
255
256 #[error("slot {0} was in history but missing from slot deltas")]
257 SlotNotFoundInDeltas(Slot),
258
259 #[error("slot history is bad and cannot be used to verify slot deltas")]
260 BadSlotHistory,
261}
262
263pub fn remove_tmp_snapshot_archives(snapshot_archives_dir: impl AsRef<Path>) {
266 if let Ok(entries) = fs::read_dir(snapshot_archives_dir) {
267 for entry in entries.filter_map(|entry| entry.ok()) {
268 let file_name = entry
269 .file_name()
270 .into_string()
271 .unwrap_or_else(|_| String::new());
272 if file_name.starts_with(TMP_SNAPSHOT_ARCHIVE_PREFIX) {
273 if entry.path().is_file() {
274 fs::remove_file(entry.path())
275 } else {
276 fs::remove_dir_all(entry.path())
277 }
278 .unwrap_or_else(|err| {
279 warn!("Failed to remove {}: {}", entry.path().display(), err)
280 });
281 }
282 }
283 }
284}
285
286pub fn archive_snapshot_package(
288 snapshot_package: &SnapshotPackage,
289 full_snapshot_archives_dir: impl AsRef<Path>,
290 incremental_snapshot_archives_dir: impl AsRef<Path>,
291 maximum_full_snapshot_archives_to_retain: usize,
292 maximum_incremental_snapshot_archives_to_retain: usize,
293) -> Result<()> {
294 info!(
295 "Generating snapshot archive for slot {}",
296 snapshot_package.slot()
297 );
298
299 serialize_status_cache(
300 snapshot_package.slot(),
301 &snapshot_package.slot_deltas,
302 &snapshot_package
303 .snapshot_links
304 .path()
305 .join(SNAPSHOT_STATUS_CACHE_FILENAME),
306 )?;
307
308 let mut timer = Measure::start("snapshot_package-package_snapshots");
309 let tar_dir = snapshot_package
310 .path()
311 .parent()
312 .expect("Tar output path is invalid");
313
314 fs::create_dir_all(tar_dir)
315 .map_err(|e| SnapshotError::IoWithSource(e, "create archive path"))?;
316
317 let staging_dir_prefix = TMP_SNAPSHOT_ARCHIVE_PREFIX;
319 let staging_dir = tempfile::Builder::new()
320 .prefix(&format!(
321 "{}{}-",
322 staging_dir_prefix,
323 snapshot_package.slot()
324 ))
325 .tempdir_in(tar_dir)
326 .map_err(|e| SnapshotError::IoWithSource(e, "create archive tempdir"))?;
327
328 let staging_accounts_dir = staging_dir.path().join("accounts");
329 let staging_snapshots_dir = staging_dir.path().join("snapshots");
330 let staging_version_file = staging_dir.path().join("version");
331 fs::create_dir_all(&staging_accounts_dir)
332 .map_err(|e| SnapshotError::IoWithSource(e, "create staging path"))?;
333
334 symlink::symlink_dir(
336 snapshot_package.snapshot_links.path(),
337 &staging_snapshots_dir,
338 )
339 .map_err(|e| SnapshotError::IoWithSource(e, "create staging symlinks"))?;
340
341 for storage in snapshot_package.snapshot_storages.iter().flatten() {
343 storage.flush()?;
344 let storage_path = storage.get_path();
345 let output_path = staging_accounts_dir.join(crate::append_vec::AppendVec::file_name(
346 storage.slot(),
347 storage.append_vec_id(),
348 ));
349
350 let storage_path =
353 fs::canonicalize(storage_path).expect("Could not get absolute path for accounts");
354 symlink::symlink_file(storage_path, &output_path)
355 .map_err(|e| SnapshotError::IoWithSource(e, "create storage symlink"))?;
356 if !output_path.is_file() {
357 return Err(SnapshotError::StoragePathSymlinkInvalid);
358 }
359 }
360
361 {
363 let mut f = fs::File::create(staging_version_file)
364 .map_err(|e| SnapshotError::IoWithSource(e, "create version file"))?;
365 f.write_all(snapshot_package.snapshot_version.as_str().as_bytes())
366 .map_err(|e| SnapshotError::IoWithSource(e, "write version file"))?;
367 }
368
369 let archive_path = tar_dir.join(format!(
371 "{}{}.{}",
372 staging_dir_prefix,
373 snapshot_package.slot(),
374 snapshot_package.archive_format().extension(),
375 ));
376
377 {
378 let mut archive_file = fs::File::create(&archive_path)?;
379
380 let do_archive_files = |encoder: &mut dyn Write| -> Result<()> {
381 let mut archive = tar::Builder::new(encoder);
382 archive.append_path_with_name(staging_dir.as_ref().join("version"), "version")?;
385 for dir in ["snapshots", "accounts"] {
386 archive.append_dir_all(dir, staging_dir.as_ref().join(dir))?;
387 }
388 archive.into_inner()?;
389 Ok(())
390 };
391
392 match snapshot_package.archive_format() {
393 ArchiveFormat::TarBzip2 => {
394 let mut encoder =
395 bzip2::write::BzEncoder::new(archive_file, bzip2::Compression::best());
396 do_archive_files(&mut encoder)?;
397 encoder.finish()?;
398 }
399 ArchiveFormat::TarGzip => {
400 let mut encoder =
401 flate2::write::GzEncoder::new(archive_file, flate2::Compression::default());
402 do_archive_files(&mut encoder)?;
403 encoder.finish()?;
404 }
405 ArchiveFormat::TarZstd => {
406 let mut encoder = zstd::stream::Encoder::new(archive_file, 0)?;
407 do_archive_files(&mut encoder)?;
408 encoder.finish()?;
409 }
410 ArchiveFormat::TarLz4 => {
411 let mut encoder = lz4::EncoderBuilder::new().level(1).build(archive_file)?;
412 do_archive_files(&mut encoder)?;
413 let (_output, result) = encoder.finish();
414 result?
415 }
416 ArchiveFormat::Tar => {
417 do_archive_files(&mut archive_file)?;
418 }
419 };
420 }
421
422 let metadata = fs::metadata(&archive_path)
424 .map_err(|e| SnapshotError::IoWithSource(e, "archive path stat"))?;
425 fs::rename(&archive_path, snapshot_package.path())
426 .map_err(|e| SnapshotError::IoWithSource(e, "archive path rename"))?;
427
428 purge_old_snapshot_archives(
429 full_snapshot_archives_dir,
430 incremental_snapshot_archives_dir,
431 maximum_full_snapshot_archives_to_retain,
432 maximum_incremental_snapshot_archives_to_retain,
433 );
434
435 timer.stop();
436 info!(
437 "Successfully created {:?}. slot: {}, elapsed ms: {}, size={}",
438 snapshot_package.path(),
439 snapshot_package.slot(),
440 timer.as_ms(),
441 metadata.len()
442 );
443
444 datapoint_info!(
445 "archive-snapshot-package",
446 ("slot", snapshot_package.slot(), i64),
447 (
448 "archive_format",
449 snapshot_package.archive_format().to_string(),
450 String
451 ),
452 ("duration_ms", timer.as_ms(), i64),
453 (
454 if snapshot_package.snapshot_type.is_full_snapshot() {
455 "full-snapshot-archive-size"
456 } else {
457 "incremental-snapshot-archive-size"
458 },
459 metadata.len(),
460 i64
461 ),
462 );
463 Ok(())
464}
465
466pub fn get_bank_snapshots(bank_snapshots_dir: impl AsRef<Path>) -> Vec<BankSnapshotInfo> {
468 let mut bank_snapshots = Vec::default();
469 match fs::read_dir(&bank_snapshots_dir) {
470 Err(err) => {
471 info!(
472 "Unable to read bank snapshots directory {}: {}",
473 bank_snapshots_dir.as_ref().display(),
474 err
475 );
476 }
477 Ok(paths) => paths
478 .filter_map(|entry| {
479 entry
482 .ok()
483 .filter(|entry| entry.path().is_dir())
484 .and_then(|entry| {
485 entry
486 .path()
487 .file_name()
488 .and_then(|file_name| file_name.to_str())
489 .and_then(|file_name| file_name.parse::<Slot>().ok())
490 })
491 })
492 .for_each(|slot| {
493 let bank_snapshot_outer_dir = get_bank_snapshots_dir(&bank_snapshots_dir, slot);
496 let bank_snapshot_post_path =
497 bank_snapshot_outer_dir.join(get_snapshot_file_name(slot));
498 let mut bank_snapshot_pre_path = bank_snapshot_post_path.clone();
499 bank_snapshot_pre_path.set_extension(BANK_SNAPSHOT_PRE_FILENAME_EXTENSION);
500
501 if bank_snapshot_pre_path.is_file() {
502 bank_snapshots.push(BankSnapshotInfo {
503 slot,
504 snapshot_path: bank_snapshot_pre_path,
505 snapshot_type: BankSnapshotType::Pre,
506 });
507 }
508
509 if bank_snapshot_post_path.is_file() {
510 bank_snapshots.push(BankSnapshotInfo {
511 slot,
512 snapshot_path: bank_snapshot_post_path,
513 snapshot_type: BankSnapshotType::Post,
514 });
515 }
516 }),
517 }
518 bank_snapshots
519}
520
521pub fn get_bank_snapshots_pre(bank_snapshots_dir: impl AsRef<Path>) -> Vec<BankSnapshotInfo> {
525 let mut bank_snapshots = get_bank_snapshots(bank_snapshots_dir);
526 bank_snapshots.retain(|bank_snapshot| bank_snapshot.snapshot_type == BankSnapshotType::Pre);
527 bank_snapshots
528}
529
530pub fn get_bank_snapshots_post(bank_snapshots_dir: impl AsRef<Path>) -> Vec<BankSnapshotInfo> {
534 let mut bank_snapshots = get_bank_snapshots(bank_snapshots_dir);
535 bank_snapshots.retain(|bank_snapshot| bank_snapshot.snapshot_type == BankSnapshotType::Post);
536 bank_snapshots
537}
538
539pub fn get_highest_bank_snapshot_pre(
543 bank_snapshots_dir: impl AsRef<Path>,
544) -> Option<BankSnapshotInfo> {
545 do_get_highest_bank_snapshot(get_bank_snapshots_pre(bank_snapshots_dir))
546}
547
548pub fn get_highest_bank_snapshot_post(
552 bank_snapshots_dir: impl AsRef<Path>,
553) -> Option<BankSnapshotInfo> {
554 do_get_highest_bank_snapshot(get_bank_snapshots_post(bank_snapshots_dir))
555}
556
557fn do_get_highest_bank_snapshot(
558 mut bank_snapshots: Vec<BankSnapshotInfo>,
559) -> Option<BankSnapshotInfo> {
560 bank_snapshots.sort_unstable();
561 bank_snapshots.into_iter().rev().next()
562}
563
564pub fn serialize_snapshot_data_file<F>(data_file_path: &Path, serializer: F) -> Result<u64>
565where
566 F: FnOnce(&mut BufWriter<File>) -> Result<()>,
567{
568 serialize_snapshot_data_file_capped::<F>(
569 data_file_path,
570 MAX_SNAPSHOT_DATA_FILE_SIZE,
571 serializer,
572 )
573}
574
575pub fn deserialize_snapshot_data_file<T: Sized>(
576 data_file_path: &Path,
577 deserializer: impl FnOnce(&mut BufReader<File>) -> Result<T>,
578) -> Result<T> {
579 let wrapped_deserializer = move |streams: &mut SnapshotStreams<File>| -> Result<T> {
580 deserializer(streams.full_snapshot_stream)
581 };
582
583 let wrapped_data_file_path = SnapshotRootPaths {
584 full_snapshot_root_file_path: data_file_path.to_path_buf(),
585 incremental_snapshot_root_file_path: None,
586 };
587
588 deserialize_snapshot_data_files_capped(
589 &wrapped_data_file_path,
590 MAX_SNAPSHOT_DATA_FILE_SIZE,
591 wrapped_deserializer,
592 )
593}
594
595fn deserialize_snapshot_data_files<T: Sized>(
596 snapshot_root_paths: &SnapshotRootPaths,
597 deserializer: impl FnOnce(&mut SnapshotStreams<File>) -> Result<T>,
598) -> Result<T> {
599 deserialize_snapshot_data_files_capped(
600 snapshot_root_paths,
601 MAX_SNAPSHOT_DATA_FILE_SIZE,
602 deserializer,
603 )
604}
605
606fn serialize_snapshot_data_file_capped<F>(
607 data_file_path: &Path,
608 maximum_file_size: u64,
609 serializer: F,
610) -> Result<u64>
611where
612 F: FnOnce(&mut BufWriter<File>) -> Result<()>,
613{
614 let data_file = File::create(data_file_path)?;
615 let mut data_file_stream = BufWriter::new(data_file);
616 serializer(&mut data_file_stream)?;
617 data_file_stream.flush()?;
618
619 let consumed_size = data_file_stream.stream_position()?;
620 if consumed_size > maximum_file_size {
621 let error_message = format!(
622 "too large snapshot data file to serialize: {:?} has {} bytes",
623 data_file_path, consumed_size
624 );
625 return Err(get_io_error(&error_message));
626 }
627 Ok(consumed_size)
628}
629
630fn deserialize_snapshot_data_files_capped<T: Sized>(
631 snapshot_root_paths: &SnapshotRootPaths,
632 maximum_file_size: u64,
633 deserializer: impl FnOnce(&mut SnapshotStreams<File>) -> Result<T>,
634) -> Result<T> {
635 let (full_snapshot_file_size, mut full_snapshot_data_file_stream) =
636 create_snapshot_data_file_stream(
637 &snapshot_root_paths.full_snapshot_root_file_path,
638 maximum_file_size,
639 )?;
640
641 let (incremental_snapshot_file_size, mut incremental_snapshot_data_file_stream) =
642 if let Some(ref incremental_snapshot_root_file_path) =
643 snapshot_root_paths.incremental_snapshot_root_file_path
644 {
645 let (incremental_snapshot_file_size, incremental_snapshot_data_file_stream) =
646 create_snapshot_data_file_stream(
647 incremental_snapshot_root_file_path,
648 maximum_file_size,
649 )?;
650 (
651 Some(incremental_snapshot_file_size),
652 Some(incremental_snapshot_data_file_stream),
653 )
654 } else {
655 (None, None)
656 };
657
658 let mut snapshot_streams = SnapshotStreams {
659 full_snapshot_stream: &mut full_snapshot_data_file_stream,
660 incremental_snapshot_stream: incremental_snapshot_data_file_stream.as_mut(),
661 };
662 let ret = deserializer(&mut snapshot_streams)?;
663
664 check_deserialize_file_consumed(
665 full_snapshot_file_size,
666 &snapshot_root_paths.full_snapshot_root_file_path,
667 &mut full_snapshot_data_file_stream,
668 )?;
669
670 if let Some(ref incremental_snapshot_root_file_path) =
671 snapshot_root_paths.incremental_snapshot_root_file_path
672 {
673 check_deserialize_file_consumed(
674 incremental_snapshot_file_size.unwrap(),
675 incremental_snapshot_root_file_path,
676 incremental_snapshot_data_file_stream.as_mut().unwrap(),
677 )?;
678 }
679
680 Ok(ret)
681}
682
683fn create_snapshot_data_file_stream<P>(
686 snapshot_root_file_path: P,
687 maximum_file_size: u64,
688) -> Result<(u64, BufReader<File>)>
689where
690 P: AsRef<Path>,
691{
692 let snapshot_file_size = fs::metadata(&snapshot_root_file_path)?.len();
693
694 if snapshot_file_size > maximum_file_size {
695 let error_message =
696 format!(
697 "too large snapshot data file to deserialize: {} has {} bytes (max size is {} bytes)",
698 snapshot_root_file_path.as_ref().display(), snapshot_file_size, maximum_file_size
699 );
700 return Err(get_io_error(&error_message));
701 }
702
703 let snapshot_data_file = File::open(&snapshot_root_file_path)?;
704 let snapshot_data_file_stream = BufReader::new(snapshot_data_file);
705
706 Ok((snapshot_file_size, snapshot_data_file_stream))
707}
708
709fn check_deserialize_file_consumed<P>(
712 file_size: u64,
713 file_path: P,
714 file_stream: &mut BufReader<File>,
715) -> Result<()>
716where
717 P: AsRef<Path>,
718{
719 let consumed_size = file_stream.stream_position()?;
720
721 if consumed_size != file_size {
722 let error_message =
723 format!(
724 "invalid snapshot data file: {} has {} bytes, however consumed {} bytes to deserialize",
725 file_path.as_ref().display(), file_size, consumed_size
726 );
727 return Err(get_io_error(&error_message));
728 }
729
730 Ok(())
731}
732
733pub fn add_bank_snapshot<P: AsRef<Path>>(
735 bank_snapshots_dir: P,
736 bank: &Bank,
737 snapshot_storages: &[SnapshotStorage],
738 snapshot_version: SnapshotVersion,
739) -> Result<BankSnapshotInfo> {
740 let slot = bank.slot();
741 let bank_snapshots_dir = get_bank_snapshots_dir(bank_snapshots_dir, slot);
743 fs::create_dir_all(&bank_snapshots_dir)?;
744
745 let mut bank_snapshot_path = bank_snapshots_dir.join(get_snapshot_file_name(slot));
747 bank_snapshot_path.set_extension(BANK_SNAPSHOT_PRE_FILENAME_EXTENSION);
748
749 info!(
750 "Creating bank snapshot for slot {}, path: {}",
751 slot,
752 bank_snapshot_path.display(),
753 );
754
755 let mut bank_serialize = Measure::start("bank-serialize-ms");
756 let bank_snapshot_serializer = move |stream: &mut BufWriter<File>| -> Result<()> {
757 let serde_style = match snapshot_version {
758 SnapshotVersion::V1_2_0 => SerdeStyle::Newer,
759 };
760 bank_to_stream(serde_style, stream.by_ref(), bank, snapshot_storages)?;
761 Ok(())
762 };
763 let consumed_size =
764 serialize_snapshot_data_file(&bank_snapshot_path, bank_snapshot_serializer)?;
765 bank_serialize.stop();
766
767 datapoint_info!(
769 "snapshot-bank-file",
770 ("slot", slot, i64),
771 ("size", consumed_size, i64)
772 );
773
774 inc_new_counter_info!("bank-serialize-ms", bank_serialize.as_ms() as usize);
775
776 info!(
777 "{} for slot {} at {}",
778 bank_serialize,
779 slot,
780 bank_snapshot_path.display(),
781 );
782
783 Ok(BankSnapshotInfo {
784 slot,
785 snapshot_path: bank_snapshot_path,
786 snapshot_type: BankSnapshotType::Pre,
787 })
788}
789
790fn serialize_status_cache(
791 slot: Slot,
792 slot_deltas: &[BankSlotDelta],
793 status_cache_path: &Path,
794) -> Result<()> {
795 let mut status_cache_serialize = Measure::start("status_cache_serialize-ms");
796 let consumed_size = serialize_snapshot_data_file(status_cache_path, |stream| {
797 serialize_into(stream, slot_deltas)?;
798 Ok(())
799 })?;
800 status_cache_serialize.stop();
801
802 datapoint_info!(
804 "snapshot-status-cache-file",
805 ("slot", slot, i64),
806 ("size", consumed_size, i64)
807 );
808
809 inc_new_counter_info!(
810 "serialize-status-cache-ms",
811 status_cache_serialize.as_ms() as usize
812 );
813 Ok(())
814}
815
816pub fn remove_bank_snapshot<P>(slot: Slot, bank_snapshots_dir: P) -> Result<()>
818where
819 P: AsRef<Path>,
820{
821 let bank_snapshot_dir = get_bank_snapshots_dir(&bank_snapshots_dir, slot);
822 fs::remove_dir_all(bank_snapshot_dir)?;
823 Ok(())
824}
825
826#[derive(Debug, Default)]
827pub struct BankFromArchiveTimings {
828 pub rebuild_bank_from_snapshots_us: u64,
829 pub full_snapshot_untar_us: u64,
830 pub incremental_snapshot_untar_us: u64,
831 pub verify_snapshot_bank_us: u64,
832}
833
834const PARALLEL_UNTAR_READERS_DEFAULT: usize = 4;
836
837fn verify_and_unarchive_snapshots(
838 bank_snapshots_dir: impl AsRef<Path>,
839 full_snapshot_archive_info: &FullSnapshotArchiveInfo,
840 incremental_snapshot_archive_info: Option<&IncrementalSnapshotArchiveInfo>,
841 account_paths: &[PathBuf],
842) -> Result<(UnarchivedSnapshot, Option<UnarchivedSnapshot>)> {
843 check_are_snapshots_compatible(
844 full_snapshot_archive_info,
845 incremental_snapshot_archive_info,
846 )?;
847
848 let parallel_divisions = std::cmp::min(
849 PARALLEL_UNTAR_READERS_DEFAULT,
850 std::cmp::max(1, num_cpus::get() / 4),
851 );
852
853 let unarchived_full_snapshot = unarchive_snapshot(
854 &bank_snapshots_dir,
855 TMP_SNAPSHOT_ARCHIVE_PREFIX,
856 full_snapshot_archive_info.path(),
857 "snapshot untar",
858 account_paths,
859 full_snapshot_archive_info.archive_format(),
860 parallel_divisions,
861 )?;
862
863 let unarchived_incremental_snapshot =
864 if let Some(incremental_snapshot_archive_info) = incremental_snapshot_archive_info {
865 let unarchived_incremental_snapshot = unarchive_snapshot(
866 &bank_snapshots_dir,
867 TMP_SNAPSHOT_ARCHIVE_PREFIX,
868 incremental_snapshot_archive_info.path(),
869 "incremental snapshot untar",
870 account_paths,
871 incremental_snapshot_archive_info.archive_format(),
872 parallel_divisions,
873 )?;
874 Some(unarchived_incremental_snapshot)
875 } else {
876 None
877 };
878
879 Ok((unarchived_full_snapshot, unarchived_incremental_snapshot))
880}
881
882pub fn bank_fields_from_snapshot_archives(
885 bank_snapshots_dir: impl AsRef<Path>,
886 full_snapshot_archives_dir: impl AsRef<Path>,
887 incremental_snapshot_archives_dir: impl AsRef<Path>,
888) -> Result<BankFieldsToDeserialize> {
889 let full_snapshot_archive_info =
890 get_highest_full_snapshot_archive_info(&full_snapshot_archives_dir)
891 .ok_or(SnapshotError::NoSnapshotArchives)?;
892
893 let incremental_snapshot_archive_info = get_highest_incremental_snapshot_archive_info(
894 &incremental_snapshot_archives_dir,
895 full_snapshot_archive_info.slot(),
896 );
897
898 let temp_dir = tempfile::Builder::new()
899 .prefix("dummy-accounts-path")
900 .tempdir()?;
901
902 let account_paths = vec![temp_dir.path().to_path_buf()];
903
904 let (unarchived_full_snapshot, unarchived_incremental_snapshot) =
905 verify_and_unarchive_snapshots(
906 &bank_snapshots_dir,
907 &full_snapshot_archive_info,
908 incremental_snapshot_archive_info.as_ref(),
909 &account_paths,
910 )?;
911
912 bank_fields_from_snapshots(
913 &unarchived_full_snapshot.unpacked_snapshots_dir_and_version,
914 unarchived_incremental_snapshot
915 .as_ref()
916 .map(|unarchive_preparation_result| {
917 &unarchive_preparation_result.unpacked_snapshots_dir_and_version
918 }),
919 )
920}
921
922#[allow(clippy::too_many_arguments)]
925pub fn bank_from_snapshot_archives(
926 account_paths: &[PathBuf],
927 bank_snapshots_dir: impl AsRef<Path>,
928 full_snapshot_archive_info: &FullSnapshotArchiveInfo,
929 incremental_snapshot_archive_info: Option<&IncrementalSnapshotArchiveInfo>,
930 genesis_config: &GenesisConfig,
931 debug_keys: Option<Arc<HashSet<Pubkey>>>,
932 additional_builtins: Option<&Builtins>,
933 account_secondary_indexes: AccountSecondaryIndexes,
934 accounts_db_caching_enabled: bool,
935 limit_load_slot_count_from_snapshot: Option<usize>,
936 shrink_ratio: AccountShrinkThreshold,
937 test_hash_calculation: bool,
938 accounts_db_skip_shrink: bool,
939 verify_index: bool,
940 accounts_db_config: Option<AccountsDbConfig>,
941 accounts_update_notifier: Option<AccountsUpdateNotifier>,
942) -> Result<(Bank, BankFromArchiveTimings)> {
943 let (unarchived_full_snapshot, mut unarchived_incremental_snapshot) =
944 verify_and_unarchive_snapshots(
945 bank_snapshots_dir,
946 full_snapshot_archive_info,
947 incremental_snapshot_archive_info,
948 account_paths,
949 )?;
950
951 let mut unpacked_append_vec_map = unarchived_full_snapshot.unpacked_append_vec_map;
952 if let Some(ref mut unarchive_preparation_result) = unarchived_incremental_snapshot {
953 let incremental_snapshot_unpacked_append_vec_map =
954 std::mem::take(&mut unarchive_preparation_result.unpacked_append_vec_map);
955 unpacked_append_vec_map.extend(incremental_snapshot_unpacked_append_vec_map.into_iter());
956 }
957
958 let mut measure_rebuild = Measure::start("rebuild bank from snapshots");
959 let bank = rebuild_bank_from_snapshots(
960 &unarchived_full_snapshot.unpacked_snapshots_dir_and_version,
961 unarchived_incremental_snapshot
962 .as_ref()
963 .map(|unarchive_preparation_result| {
964 &unarchive_preparation_result.unpacked_snapshots_dir_and_version
965 }),
966 account_paths,
967 unpacked_append_vec_map,
968 genesis_config,
969 debug_keys,
970 additional_builtins,
971 account_secondary_indexes,
972 accounts_db_caching_enabled,
973 limit_load_slot_count_from_snapshot,
974 shrink_ratio,
975 verify_index,
976 accounts_db_config,
977 accounts_update_notifier,
978 )?;
979 measure_rebuild.stop();
980 info!("{}", measure_rebuild);
981
982 let mut measure_verify = Measure::start("verify");
983 if !bank.verify_snapshot_bank(
984 test_hash_calculation,
985 accounts_db_skip_shrink || !full_snapshot_archive_info.is_remote(),
986 Some(full_snapshot_archive_info.slot()),
987 ) && limit_load_slot_count_from_snapshot.is_none()
988 {
989 panic!("Snapshot bank for slot {} failed to verify", bank.slot());
990 }
991 measure_verify.stop();
992
993 let timings = BankFromArchiveTimings {
994 rebuild_bank_from_snapshots_us: measure_rebuild.as_us(),
995 full_snapshot_untar_us: unarchived_full_snapshot.measure_untar.as_us(),
996 incremental_snapshot_untar_us: unarchived_incremental_snapshot
997 .map_or(0, |unarchive_preparation_result| {
998 unarchive_preparation_result.measure_untar.as_us()
999 }),
1000 verify_snapshot_bank_us: measure_verify.as_us(),
1001 };
1002 Ok((bank, timings))
1003}
1004
1005#[allow(clippy::too_many_arguments)]
1008pub fn bank_from_latest_snapshot_archives(
1009 bank_snapshots_dir: impl AsRef<Path>,
1010 full_snapshot_archives_dir: impl AsRef<Path>,
1011 incremental_snapshot_archives_dir: impl AsRef<Path>,
1012 account_paths: &[PathBuf],
1013 genesis_config: &GenesisConfig,
1014 debug_keys: Option<Arc<HashSet<Pubkey>>>,
1015 additional_builtins: Option<&Builtins>,
1016 account_secondary_indexes: AccountSecondaryIndexes,
1017 accounts_db_caching_enabled: bool,
1018 limit_load_slot_count_from_snapshot: Option<usize>,
1019 shrink_ratio: AccountShrinkThreshold,
1020 test_hash_calculation: bool,
1021 accounts_db_skip_shrink: bool,
1022 verify_index: bool,
1023 accounts_db_config: Option<AccountsDbConfig>,
1024 accounts_update_notifier: Option<AccountsUpdateNotifier>,
1025) -> Result<(
1026 Bank,
1027 FullSnapshotArchiveInfo,
1028 Option<IncrementalSnapshotArchiveInfo>,
1029)> {
1030 let full_snapshot_archive_info =
1031 get_highest_full_snapshot_archive_info(&full_snapshot_archives_dir)
1032 .ok_or(SnapshotError::NoSnapshotArchives)?;
1033
1034 let incremental_snapshot_archive_info = get_highest_incremental_snapshot_archive_info(
1035 &incremental_snapshot_archives_dir,
1036 full_snapshot_archive_info.slot(),
1037 );
1038
1039 info!(
1040 "Loading bank from full snapshot: {}, and incremental snapshot: {:?}",
1041 full_snapshot_archive_info.path().display(),
1042 incremental_snapshot_archive_info
1043 .as_ref()
1044 .map(
1045 |incremental_snapshot_archive_info| incremental_snapshot_archive_info
1046 .path()
1047 .display()
1048 )
1049 );
1050
1051 let (bank, timings) = bank_from_snapshot_archives(
1052 account_paths,
1053 bank_snapshots_dir.as_ref(),
1054 &full_snapshot_archive_info,
1055 incremental_snapshot_archive_info.as_ref(),
1056 genesis_config,
1057 debug_keys,
1058 additional_builtins,
1059 account_secondary_indexes,
1060 accounts_db_caching_enabled,
1061 limit_load_slot_count_from_snapshot,
1062 shrink_ratio,
1063 test_hash_calculation,
1064 accounts_db_skip_shrink,
1065 verify_index,
1066 accounts_db_config,
1067 accounts_update_notifier,
1068 )?;
1069
1070 datapoint_info!(
1071 "bank_from_snapshot_archives",
1072 (
1073 "full_snapshot_untar_us",
1074 timings.full_snapshot_untar_us,
1075 i64
1076 ),
1077 (
1078 "incremental_snapshot_untar_us",
1079 timings.incremental_snapshot_untar_us,
1080 i64
1081 ),
1082 (
1083 "rebuild_bank_from_snapshots_us",
1084 timings.rebuild_bank_from_snapshots_us,
1085 i64
1086 ),
1087 (
1088 "verify_snapshot_bank_us",
1089 timings.verify_snapshot_bank_us,
1090 i64
1091 ),
1092 );
1093
1094 verify_bank_against_expected_slot_hash(
1095 &bank,
1096 incremental_snapshot_archive_info.as_ref().map_or(
1097 full_snapshot_archive_info.slot(),
1098 |incremental_snapshot_archive_info| incremental_snapshot_archive_info.slot(),
1099 ),
1100 incremental_snapshot_archive_info.as_ref().map_or(
1101 *full_snapshot_archive_info.hash(),
1102 |incremental_snapshot_archive_info| *incremental_snapshot_archive_info.hash(),
1103 ),
1104 )?;
1105
1106 Ok((
1107 bank,
1108 full_snapshot_archive_info,
1109 incremental_snapshot_archive_info,
1110 ))
1111}
1112
1113fn verify_bank_against_expected_slot_hash(
1116 bank: &Bank,
1117 expected_slot: Slot,
1118 expected_hash: Hash,
1119) -> Result<()> {
1120 let bank_slot = bank.slot();
1121 let bank_hash = bank.get_accounts_hash();
1122
1123 if bank_slot != expected_slot || bank_hash != expected_hash {
1124 return Err(SnapshotError::MismatchedSlotHash(
1125 (bank_slot, bank_hash),
1126 (expected_slot, expected_hash),
1127 ));
1128 }
1129
1130 Ok(())
1131}
1132
1133fn unarchive_snapshot<P, Q>(
1137 bank_snapshots_dir: P,
1138 unpacked_snapshots_dir_prefix: &'static str,
1139 snapshot_archive_path: Q,
1140 measure_name: &'static str,
1141 account_paths: &[PathBuf],
1142 archive_format: ArchiveFormat,
1143 parallel_divisions: usize,
1144) -> Result<UnarchivedSnapshot>
1145where
1146 P: AsRef<Path>,
1147 Q: AsRef<Path>,
1148{
1149 let unpack_dir = tempfile::Builder::new()
1150 .prefix(unpacked_snapshots_dir_prefix)
1151 .tempdir_in(bank_snapshots_dir)?;
1152 let unpacked_snapshots_dir = unpack_dir.path().join("snapshots");
1153
1154 let mut measure_untar = Measure::start(measure_name);
1155 let unpacked_append_vec_map = untar_snapshot_in(
1156 snapshot_archive_path,
1157 unpack_dir.path(),
1158 account_paths,
1159 archive_format,
1160 parallel_divisions,
1161 )?;
1162 measure_untar.stop();
1163 info!("{}", measure_untar);
1164
1165 let unpacked_version_file = unpack_dir.path().join("version");
1166 let snapshot_version = snapshot_version_from_file(&unpacked_version_file)?;
1167
1168 Ok(UnarchivedSnapshot {
1169 unpack_dir,
1170 unpacked_append_vec_map,
1171 unpacked_snapshots_dir_and_version: UnpackedSnapshotsDirAndVersion {
1172 unpacked_snapshots_dir,
1173 snapshot_version,
1174 },
1175 measure_untar,
1176 })
1177}
1178
1179fn snapshot_version_from_file(path: impl AsRef<Path>) -> Result<String> {
1183 let file_size = fs::metadata(&path)?.len();
1185 if file_size > MAX_SNAPSHOT_VERSION_FILE_SIZE {
1186 let error_message = format!(
1187 "snapshot version file too large: {} has {} bytes (max size is {} bytes)",
1188 path.as_ref().display(),
1189 file_size,
1190 MAX_SNAPSHOT_VERSION_FILE_SIZE,
1191 );
1192 return Err(get_io_error(&error_message));
1193 }
1194
1195 let mut snapshot_version = String::new();
1197 File::open(path).and_then(|mut f| f.read_to_string(&mut snapshot_version))?;
1198 Ok(snapshot_version.trim().to_string())
1199}
1200
1201fn check_are_snapshots_compatible(
1204 full_snapshot_archive_info: &FullSnapshotArchiveInfo,
1205 incremental_snapshot_archive_info: Option<&IncrementalSnapshotArchiveInfo>,
1206) -> Result<()> {
1207 if incremental_snapshot_archive_info.is_none() {
1208 return Ok(());
1209 }
1210
1211 let incremental_snapshot_archive_info = incremental_snapshot_archive_info.unwrap();
1212
1213 (full_snapshot_archive_info.slot() == incremental_snapshot_archive_info.base_slot())
1214 .then(|| ())
1215 .ok_or_else(|| {
1216 SnapshotError::MismatchedBaseSlot(
1217 full_snapshot_archive_info.slot(),
1218 incremental_snapshot_archive_info.base_slot(),
1219 )
1220 })
1221}
1222
1223pub fn path_to_file_name_str(path: &Path) -> Result<&str> {
1225 path.file_name()
1226 .ok_or_else(|| SnapshotError::PathToFileNameError(path.to_path_buf()))?
1227 .to_str()
1228 .ok_or_else(|| SnapshotError::FileNameToStrError(path.to_path_buf()))
1229}
1230
1231pub fn build_snapshot_archives_remote_dir(snapshot_archives_dir: impl AsRef<Path>) -> PathBuf {
1232 snapshot_archives_dir
1233 .as_ref()
1234 .join(SNAPSHOT_ARCHIVE_DOWNLOAD_DIR)
1235}
1236
1237pub fn build_full_snapshot_archive_path(
1240 full_snapshot_archives_dir: impl AsRef<Path>,
1241 slot: Slot,
1242 hash: &Hash,
1243 archive_format: ArchiveFormat,
1244) -> PathBuf {
1245 full_snapshot_archives_dir.as_ref().join(format!(
1246 "snapshot-{}-{}.{}",
1247 slot,
1248 hash,
1249 archive_format.extension(),
1250 ))
1251}
1252
1253pub fn build_incremental_snapshot_archive_path(
1257 incremental_snapshot_archives_dir: impl AsRef<Path>,
1258 base_slot: Slot,
1259 slot: Slot,
1260 hash: &Hash,
1261 archive_format: ArchiveFormat,
1262) -> PathBuf {
1263 incremental_snapshot_archives_dir.as_ref().join(format!(
1264 "incremental-snapshot-{}-{}-{}.{}",
1265 base_slot,
1266 slot,
1267 hash,
1268 archive_format.extension(),
1269 ))
1270}
1271
1272pub(crate) fn parse_full_snapshot_archive_filename(
1274 archive_filename: &str,
1275) -> Result<(Slot, Hash, ArchiveFormat)> {
1276 lazy_static! {
1277 static ref RE: Regex = Regex::new(FULL_SNAPSHOT_ARCHIVE_FILENAME_REGEX).unwrap();
1278 }
1279
1280 let do_parse = || {
1281 RE.captures(archive_filename).and_then(|captures| {
1282 let slot = captures
1283 .name("slot")
1284 .map(|x| x.as_str().parse::<Slot>())?
1285 .ok()?;
1286 let hash = captures
1287 .name("hash")
1288 .map(|x| x.as_str().parse::<Hash>())?
1289 .ok()?;
1290 let archive_format = captures
1291 .name("ext")
1292 .map(|x| x.as_str().parse::<ArchiveFormat>())?
1293 .ok()?;
1294
1295 Some((slot, hash, archive_format))
1296 })
1297 };
1298
1299 do_parse().ok_or_else(|| {
1300 SnapshotError::ParseSnapshotArchiveFileNameError(archive_filename.to_string())
1301 })
1302}
1303
1304pub(crate) fn parse_incremental_snapshot_archive_filename(
1306 archive_filename: &str,
1307) -> Result<(Slot, Slot, Hash, ArchiveFormat)> {
1308 lazy_static! {
1309 static ref RE: Regex = Regex::new(INCREMENTAL_SNAPSHOT_ARCHIVE_FILENAME_REGEX).unwrap();
1310 }
1311
1312 let do_parse = || {
1313 RE.captures(archive_filename).and_then(|captures| {
1314 let base_slot = captures
1315 .name("base")
1316 .map(|x| x.as_str().parse::<Slot>())?
1317 .ok()?;
1318 let slot = captures
1319 .name("slot")
1320 .map(|x| x.as_str().parse::<Slot>())?
1321 .ok()?;
1322 let hash = captures
1323 .name("hash")
1324 .map(|x| x.as_str().parse::<Hash>())?
1325 .ok()?;
1326 let archive_format = captures
1327 .name("ext")
1328 .map(|x| x.as_str().parse::<ArchiveFormat>())?
1329 .ok()?;
1330
1331 Some((base_slot, slot, hash, archive_format))
1332 })
1333 };
1334
1335 do_parse().ok_or_else(|| {
1336 SnapshotError::ParseSnapshotArchiveFileNameError(archive_filename.to_string())
1337 })
1338}
1339
1340fn get_snapshot_archives<T, F>(snapshot_archives_dir: &Path, cb: F) -> Vec<T>
1342where
1343 F: Fn(PathBuf) -> Result<T>,
1344{
1345 let walk_dir = |dir: &Path| -> Vec<T> {
1346 let entry_iter = fs::read_dir(dir);
1347 match entry_iter {
1348 Err(err) => {
1349 info!(
1350 "Unable to read snapshot archives directory: err: {}, path: {}",
1351 err,
1352 dir.display()
1353 );
1354 vec![]
1355 }
1356 Ok(entries) => entries
1357 .filter_map(|entry| entry.map_or(None, |entry| cb(entry.path()).ok()))
1358 .collect(),
1359 }
1360 };
1361
1362 let mut ret = walk_dir(snapshot_archives_dir);
1363 let remote_dir = build_snapshot_archives_remote_dir(snapshot_archives_dir);
1364 if remote_dir.exists() {
1365 ret.append(&mut walk_dir(remote_dir.as_ref()));
1366 }
1367 ret
1368}
1369
1370pub fn get_full_snapshot_archives(
1372 full_snapshot_archives_dir: impl AsRef<Path>,
1373) -> Vec<FullSnapshotArchiveInfo> {
1374 get_snapshot_archives(
1375 full_snapshot_archives_dir.as_ref(),
1376 FullSnapshotArchiveInfo::new_from_path,
1377 )
1378}
1379
1380pub fn get_incremental_snapshot_archives(
1382 incremental_snapshot_archives_dir: impl AsRef<Path>,
1383) -> Vec<IncrementalSnapshotArchiveInfo> {
1384 get_snapshot_archives(
1385 incremental_snapshot_archives_dir.as_ref(),
1386 IncrementalSnapshotArchiveInfo::new_from_path,
1387 )
1388}
1389
1390pub fn get_highest_full_snapshot_archive_slot(
1392 full_snapshot_archives_dir: impl AsRef<Path>,
1393) -> Option<Slot> {
1394 get_highest_full_snapshot_archive_info(full_snapshot_archives_dir)
1395 .map(|full_snapshot_archive_info| full_snapshot_archive_info.slot())
1396}
1397
1398pub fn get_highest_incremental_snapshot_archive_slot(
1401 incremental_snapshot_archives_dir: impl AsRef<Path>,
1402 full_snapshot_slot: Slot,
1403) -> Option<Slot> {
1404 get_highest_incremental_snapshot_archive_info(
1405 incremental_snapshot_archives_dir,
1406 full_snapshot_slot,
1407 )
1408 .map(|incremental_snapshot_archive_info| incremental_snapshot_archive_info.slot())
1409}
1410
1411pub fn get_highest_full_snapshot_archive_info(
1413 full_snapshot_archives_dir: impl AsRef<Path>,
1414) -> Option<FullSnapshotArchiveInfo> {
1415 let mut full_snapshot_archives = get_full_snapshot_archives(full_snapshot_archives_dir);
1416 full_snapshot_archives.sort_unstable();
1417 full_snapshot_archives.into_iter().rev().next()
1418}
1419
1420pub fn get_highest_incremental_snapshot_archive_info(
1423 incremental_snapshot_archives_dir: impl AsRef<Path>,
1424 full_snapshot_slot: Slot,
1425) -> Option<IncrementalSnapshotArchiveInfo> {
1426 let mut incremental_snapshot_archives =
1430 get_incremental_snapshot_archives(incremental_snapshot_archives_dir)
1431 .into_iter()
1432 .filter(|incremental_snapshot_archive_info| {
1433 incremental_snapshot_archive_info.base_slot() == full_snapshot_slot
1434 })
1435 .collect::<Vec<_>>();
1436 incremental_snapshot_archives.sort_unstable();
1437 incremental_snapshot_archives.into_iter().rev().next()
1438}
1439
1440pub fn purge_old_snapshot_archives(
1441 full_snapshot_archives_dir: impl AsRef<Path>,
1442 incremental_snapshot_archives_dir: impl AsRef<Path>,
1443 maximum_full_snapshot_archives_to_retain: usize,
1444 maximum_incremental_snapshot_archives_to_retain: usize,
1445) {
1446 info!(
1447 "Purging old full snapshot archives in {}, retaining up to {} full snapshots",
1448 full_snapshot_archives_dir.as_ref().display(),
1449 maximum_full_snapshot_archives_to_retain
1450 );
1451
1452 let mut full_snapshot_archives = get_full_snapshot_archives(&full_snapshot_archives_dir);
1453 full_snapshot_archives.sort_unstable();
1454 full_snapshot_archives.reverse();
1455
1456 let num_to_retain = full_snapshot_archives.len().min(
1457 maximum_full_snapshot_archives_to_retain
1458 .max(1 ),
1459 );
1460 trace!(
1461 "There are {} full snapshot archives, retaining {}",
1462 full_snapshot_archives.len(),
1463 num_to_retain,
1464 );
1465
1466 let (full_snapshot_archives_to_retain, full_snapshot_archives_to_remove) =
1467 if full_snapshot_archives.is_empty() {
1468 None
1469 } else {
1470 Some(full_snapshot_archives.split_at(num_to_retain))
1471 }
1472 .unwrap_or_default();
1473
1474 let retained_full_snapshot_slots = full_snapshot_archives_to_retain
1475 .iter()
1476 .map(|ai| ai.slot())
1477 .collect::<HashSet<_>>();
1478
1479 fn remove_archives<T: SnapshotArchiveInfoGetter>(archives: &[T]) {
1480 for path in archives.iter().map(|a| a.path()) {
1481 trace!("Removing snapshot archive: {}", path.display());
1482 fs::remove_file(path)
1483 .unwrap_or_else(|err| info!("Failed to remove {}: {}", path.display(), err));
1484 }
1485 }
1486 remove_archives(full_snapshot_archives_to_remove);
1487
1488 info!(
1489 "Purging old incremental snapshot archives in {}, retaining up to {} incremental snapshots",
1490 incremental_snapshot_archives_dir.as_ref().display(),
1491 maximum_incremental_snapshot_archives_to_retain
1492 );
1493 let mut incremental_snapshot_archives_by_base_slot = HashMap::<Slot, Vec<_>>::new();
1494 for incremental_snapshot_archive in
1495 get_incremental_snapshot_archives(&incremental_snapshot_archives_dir)
1496 {
1497 incremental_snapshot_archives_by_base_slot
1498 .entry(incremental_snapshot_archive.base_slot())
1499 .or_default()
1500 .push(incremental_snapshot_archive)
1501 }
1502
1503 let highest_full_snapshot_slot = retained_full_snapshot_slots.iter().max().copied();
1504 for (base_slot, mut incremental_snapshot_archives) in incremental_snapshot_archives_by_base_slot
1505 {
1506 incremental_snapshot_archives.sort_unstable();
1507 let num_to_retain = if Some(base_slot) == highest_full_snapshot_slot {
1508 maximum_incremental_snapshot_archives_to_retain
1509 } else {
1510 usize::from(retained_full_snapshot_slots.contains(&base_slot))
1511 };
1512 trace!(
1513 "There are {} incremental snapshot archives for base slot {}, removing {} of them",
1514 incremental_snapshot_archives.len(),
1515 base_slot,
1516 incremental_snapshot_archives
1517 .len()
1518 .saturating_sub(num_to_retain),
1519 );
1520
1521 incremental_snapshot_archives.truncate(
1522 incremental_snapshot_archives
1523 .len()
1524 .saturating_sub(num_to_retain),
1525 );
1526 remove_archives(&incremental_snapshot_archives);
1527 }
1528}
1529
1530fn unpack_snapshot_local(
1531 shared_buffer: SharedBuffer,
1532 ledger_dir: &Path,
1533 account_paths: &[PathBuf],
1534 parallel_divisions: usize,
1535) -> Result<UnpackedAppendVecMap> {
1536 assert!(parallel_divisions > 0);
1537
1538 let readers = (0..parallel_divisions)
1540 .into_iter()
1541 .map(|_| SharedBufferReader::new(&shared_buffer))
1542 .collect::<Vec<_>>();
1543
1544 let all_unpacked_append_vec_map = readers
1546 .into_par_iter()
1547 .enumerate()
1548 .map(|(index, reader)| {
1549 let parallel_selector = Some(ParallelSelector {
1550 index,
1551 divisions: parallel_divisions,
1552 });
1553 let mut archive = Archive::new(reader);
1554 unpack_snapshot(&mut archive, ledger_dir, account_paths, parallel_selector)
1555 })
1556 .collect::<Vec<_>>();
1557
1558 let mut unpacked_append_vec_map = UnpackedAppendVecMap::new();
1559 for h in all_unpacked_append_vec_map {
1560 unpacked_append_vec_map.extend(h?);
1561 }
1562
1563 Ok(unpacked_append_vec_map)
1564}
1565
1566fn untar_snapshot_create_shared_buffer(
1567 snapshot_tar: &Path,
1568 archive_format: ArchiveFormat,
1569) -> SharedBuffer {
1570 let open_file = || File::open(snapshot_tar).unwrap();
1571 match archive_format {
1572 ArchiveFormat::TarBzip2 => SharedBuffer::new(BzDecoder::new(BufReader::new(open_file()))),
1573 ArchiveFormat::TarGzip => SharedBuffer::new(GzDecoder::new(BufReader::new(open_file()))),
1574 ArchiveFormat::TarZstd => SharedBuffer::new(
1575 zstd::stream::read::Decoder::new(BufReader::new(open_file())).unwrap(),
1576 ),
1577 ArchiveFormat::TarLz4 => {
1578 SharedBuffer::new(lz4::Decoder::new(BufReader::new(open_file())).unwrap())
1579 }
1580 ArchiveFormat::Tar => SharedBuffer::new(BufReader::new(open_file())),
1581 }
1582}
1583
1584fn untar_snapshot_in<P: AsRef<Path>>(
1585 snapshot_tar: P,
1586 unpack_dir: &Path,
1587 account_paths: &[PathBuf],
1588 archive_format: ArchiveFormat,
1589 parallel_divisions: usize,
1590) -> Result<UnpackedAppendVecMap> {
1591 let shared_buffer = untar_snapshot_create_shared_buffer(snapshot_tar.as_ref(), archive_format);
1592 unpack_snapshot_local(shared_buffer, unpack_dir, account_paths, parallel_divisions)
1593}
1594
1595fn verify_unpacked_snapshots_dir_and_version(
1596 unpacked_snapshots_dir_and_version: &UnpackedSnapshotsDirAndVersion,
1597) -> Result<(SnapshotVersion, BankSnapshotInfo)> {
1598 info!(
1599 "snapshot version: {}",
1600 &unpacked_snapshots_dir_and_version.snapshot_version
1601 );
1602
1603 let snapshot_version =
1604 SnapshotVersion::maybe_from_string(&unpacked_snapshots_dir_and_version.snapshot_version)
1605 .ok_or_else(|| {
1606 get_io_error(&format!(
1607 "unsupported snapshot version: {}",
1608 &unpacked_snapshots_dir_and_version.snapshot_version,
1609 ))
1610 })?;
1611 let mut bank_snapshots =
1612 get_bank_snapshots_post(&unpacked_snapshots_dir_and_version.unpacked_snapshots_dir);
1613 if bank_snapshots.len() > 1 {
1614 return Err(get_io_error("invalid snapshot format"));
1615 }
1616 let root_paths = bank_snapshots
1617 .pop()
1618 .ok_or_else(|| get_io_error("No snapshots found in snapshots directory"))?;
1619 Ok((snapshot_version, root_paths))
1620}
1621
1622fn bank_fields_from_snapshots(
1623 full_snapshot_unpacked_snapshots_dir_and_version: &UnpackedSnapshotsDirAndVersion,
1624 incremental_snapshot_unpacked_snapshots_dir_and_version: Option<
1625 &UnpackedSnapshotsDirAndVersion,
1626 >,
1627) -> Result<BankFieldsToDeserialize> {
1628 let (full_snapshot_version, full_snapshot_root_paths) =
1629 verify_unpacked_snapshots_dir_and_version(
1630 full_snapshot_unpacked_snapshots_dir_and_version,
1631 )?;
1632 let (incremental_snapshot_version, incremental_snapshot_root_paths) =
1633 if let Some(snapshot_unpacked_snapshots_dir_and_version) =
1634 incremental_snapshot_unpacked_snapshots_dir_and_version
1635 {
1636 let (snapshot_version, bank_snapshot_info) = verify_unpacked_snapshots_dir_and_version(
1637 snapshot_unpacked_snapshots_dir_and_version,
1638 )?;
1639 (Some(snapshot_version), Some(bank_snapshot_info))
1640 } else {
1641 (None, None)
1642 };
1643 info!(
1644 "Loading bank from full snapshot {} and incremental snapshot {:?}",
1645 full_snapshot_root_paths.snapshot_path.display(),
1646 incremental_snapshot_root_paths
1647 .as_ref()
1648 .map(|paths| paths.snapshot_path.display()),
1649 );
1650
1651 let snapshot_root_paths = SnapshotRootPaths {
1652 full_snapshot_root_file_path: full_snapshot_root_paths.snapshot_path,
1653 incremental_snapshot_root_file_path: incremental_snapshot_root_paths
1654 .map(|root_paths| root_paths.snapshot_path),
1655 };
1656
1657 deserialize_snapshot_data_files(&snapshot_root_paths, |snapshot_streams| {
1658 Ok(
1659 match incremental_snapshot_version.unwrap_or(full_snapshot_version) {
1660 SnapshotVersion::V1_2_0 => fields_from_streams(SerdeStyle::Newer, snapshot_streams)
1661 .map(|(bank_fields, _accountsdb_fields)| bank_fields),
1662 }?,
1663 )
1664 })
1665}
1666
1667#[allow(clippy::too_many_arguments)]
1668fn rebuild_bank_from_snapshots(
1669 full_snapshot_unpacked_snapshots_dir_and_version: &UnpackedSnapshotsDirAndVersion,
1670 incremental_snapshot_unpacked_snapshots_dir_and_version: Option<
1671 &UnpackedSnapshotsDirAndVersion,
1672 >,
1673 account_paths: &[PathBuf],
1674 unpacked_append_vec_map: UnpackedAppendVecMap,
1675 genesis_config: &GenesisConfig,
1676 debug_keys: Option<Arc<HashSet<Pubkey>>>,
1677 additional_builtins: Option<&Builtins>,
1678 account_secondary_indexes: AccountSecondaryIndexes,
1679 accounts_db_caching_enabled: bool,
1680 limit_load_slot_count_from_snapshot: Option<usize>,
1681 shrink_ratio: AccountShrinkThreshold,
1682 verify_index: bool,
1683 accounts_db_config: Option<AccountsDbConfig>,
1684 accounts_update_notifier: Option<AccountsUpdateNotifier>,
1685) -> Result<Bank> {
1686 let (full_snapshot_version, full_snapshot_root_paths) =
1687 verify_unpacked_snapshots_dir_and_version(
1688 full_snapshot_unpacked_snapshots_dir_and_version,
1689 )?;
1690 let (incremental_snapshot_version, incremental_snapshot_root_paths) =
1691 if let Some(snapshot_unpacked_snapshots_dir_and_version) =
1692 incremental_snapshot_unpacked_snapshots_dir_and_version
1693 {
1694 let (snapshot_version, bank_snapshot_info) = verify_unpacked_snapshots_dir_and_version(
1695 snapshot_unpacked_snapshots_dir_and_version,
1696 )?;
1697 (Some(snapshot_version), Some(bank_snapshot_info))
1698 } else {
1699 (None, None)
1700 };
1701 info!(
1702 "Loading bank from full snapshot {} and incremental snapshot {:?}",
1703 full_snapshot_root_paths.snapshot_path.display(),
1704 incremental_snapshot_root_paths
1705 .as_ref()
1706 .map(|paths| paths.snapshot_path.display()),
1707 );
1708
1709 let snapshot_root_paths = SnapshotRootPaths {
1710 full_snapshot_root_file_path: full_snapshot_root_paths.snapshot_path,
1711 incremental_snapshot_root_file_path: incremental_snapshot_root_paths
1712 .map(|root_paths| root_paths.snapshot_path),
1713 };
1714
1715 let bank = deserialize_snapshot_data_files(&snapshot_root_paths, |snapshot_streams| {
1716 Ok(
1717 match incremental_snapshot_version.unwrap_or(full_snapshot_version) {
1718 SnapshotVersion::V1_2_0 => bank_from_streams(
1719 SerdeStyle::Newer,
1720 snapshot_streams,
1721 account_paths,
1722 unpacked_append_vec_map,
1723 genesis_config,
1724 debug_keys,
1725 additional_builtins,
1726 account_secondary_indexes,
1727 accounts_db_caching_enabled,
1728 limit_load_slot_count_from_snapshot,
1729 shrink_ratio,
1730 verify_index,
1731 accounts_db_config,
1732 accounts_update_notifier,
1733 ),
1734 }?,
1735 )
1736 })?;
1737
1738 let status_cache_path = incremental_snapshot_unpacked_snapshots_dir_and_version
1741 .map_or_else(
1742 || {
1743 full_snapshot_unpacked_snapshots_dir_and_version
1744 .unpacked_snapshots_dir
1745 .as_path()
1746 },
1747 |unpacked_snapshots_dir_and_version| {
1748 unpacked_snapshots_dir_and_version
1749 .unpacked_snapshots_dir
1750 .as_path()
1751 },
1752 )
1753 .join(SNAPSHOT_STATUS_CACHE_FILENAME);
1754 let slot_deltas = deserialize_snapshot_data_file(&status_cache_path, |stream| {
1755 info!(
1756 "Rebuilding status cache from {}",
1757 status_cache_path.display()
1758 );
1759 let slot_deltas: Vec<BankSlotDelta> = bincode::options()
1760 .with_limit(MAX_SNAPSHOT_DATA_FILE_SIZE)
1761 .with_fixint_encoding()
1762 .allow_trailing_bytes()
1763 .deserialize_from(stream)?;
1764 Ok(slot_deltas)
1765 })?;
1766
1767 verify_slot_deltas(slot_deltas.as_slice(), &bank)?;
1768
1769 bank.status_cache.write().unwrap().append(&slot_deltas);
1770
1771 bank.prepare_rewrites_for_hash();
1772
1773 info!("Loaded bank for slot: {}", bank.slot());
1774 Ok(bank)
1775}
1776
1777fn verify_slot_deltas(
1779 slot_deltas: &[BankSlotDelta],
1780 bank: &Bank,
1781) -> std::result::Result<(), VerifySlotDeltasError> {
1782 let info = verify_slot_deltas_structural(slot_deltas, bank.slot())?;
1783 verify_slot_deltas_with_history(&info.slots, &bank.get_slot_history(), bank.slot())
1784}
1785
1786fn verify_slot_deltas_structural(
1789 slot_deltas: &[BankSlotDelta],
1790 bank_slot: Slot,
1791) -> std::result::Result<VerifySlotDeltasStructuralInfo, VerifySlotDeltasError> {
1792 let num_entries = slot_deltas.len();
1794 if num_entries > status_cache::MAX_CACHE_ENTRIES {
1795 return Err(VerifySlotDeltasError::TooManyEntries(
1796 num_entries,
1797 status_cache::MAX_CACHE_ENTRIES,
1798 ));
1799 }
1800
1801 let mut slots_seen_so_far = HashSet::new();
1802 for &(slot, is_root, ..) in slot_deltas {
1803 if !is_root {
1805 return Err(VerifySlotDeltasError::SlotIsNotRoot(slot));
1806 }
1807
1808 if slot > bank_slot {
1810 return Err(VerifySlotDeltasError::SlotGreaterThanMaxRoot(
1811 slot, bank_slot,
1812 ));
1813 }
1814
1815 let is_duplicate = !slots_seen_so_far.insert(slot);
1817 if is_duplicate {
1818 return Err(VerifySlotDeltasError::SlotHasMultipleEntries(slot));
1819 }
1820 }
1821
1822 assert_eq!(slots_seen_so_far.len(), slot_deltas.len());
1824
1825 Ok(VerifySlotDeltasStructuralInfo {
1826 slots: slots_seen_so_far,
1827 })
1828}
1829
1830#[derive(Debug, PartialEq, Eq)]
1832struct VerifySlotDeltasStructuralInfo {
1833 slots: HashSet<Slot>,
1835}
1836
1837fn verify_slot_deltas_with_history(
1840 slots_from_slot_deltas: &HashSet<Slot>,
1841 slot_history: &SlotHistory,
1842 bank_slot: Slot,
1843) -> std::result::Result<(), VerifySlotDeltasError> {
1844 if slot_history.newest() != bank_slot {
1847 return Err(VerifySlotDeltasError::BadSlotHistory);
1848 }
1849
1850 let slot_missing_from_history = slots_from_slot_deltas
1852 .iter()
1853 .find(|slot| slot_history.check(**slot) != Check::Found);
1854 if let Some(slot) = slot_missing_from_history {
1855 return Err(VerifySlotDeltasError::SlotNotFoundInHistory(*slot));
1856 }
1857
1858 let slot_missing_from_deltas = (slot_history.oldest()..=slot_history.newest())
1866 .rev()
1867 .filter(|slot| slot_history.check(*slot) == Check::Found)
1868 .take(status_cache::MAX_CACHE_ENTRIES)
1869 .find(|slot| !slots_from_slot_deltas.contains(slot));
1870 if let Some(slot) = slot_missing_from_deltas {
1871 return Err(VerifySlotDeltasError::SlotNotFoundInDeltas(slot));
1872 }
1873
1874 Ok(())
1875}
1876
1877pub(crate) fn get_snapshot_file_name(slot: Slot) -> String {
1878 slot.to_string()
1879}
1880
1881pub(crate) fn get_bank_snapshots_dir<P: AsRef<Path>>(path: P, slot: Slot) -> PathBuf {
1882 path.as_ref().join(slot.to_string())
1883}
1884
1885fn get_io_error(error: &str) -> SnapshotError {
1886 warn!("Snapshot Error: {:?}", error);
1887 SnapshotError::Io(IoError::new(ErrorKind::Other, error))
1888}
1889
1890#[derive(Debug, Copy, Clone)]
1891pub enum VerifyBank {
1893 Deterministic,
1895 NonDeterministic(Slot),
1898}
1899
1900pub fn verify_snapshot_archive<P, Q, R>(
1901 snapshot_archive: P,
1902 snapshots_to_verify: Q,
1903 storages_to_verify: R,
1904 archive_format: ArchiveFormat,
1905 verify_bank: VerifyBank,
1906) where
1907 P: AsRef<Path>,
1908 Q: AsRef<Path>,
1909 R: AsRef<Path>,
1910{
1911 let temp_dir = tempfile::TempDir::new().unwrap();
1912 let unpack_dir = temp_dir.path();
1913 untar_snapshot_in(
1914 snapshot_archive,
1915 unpack_dir,
1916 &[unpack_dir.to_path_buf()],
1917 archive_format,
1918 1,
1919 )
1920 .unwrap();
1921
1922 let unpacked_snapshots = unpack_dir.join("snapshots");
1924 if let VerifyBank::NonDeterministic(slot) = verify_bank {
1925 let slot = slot.to_string();
1927 let p1 = snapshots_to_verify.as_ref().join(&slot).join(&slot);
1928 let p2 = unpacked_snapshots.join(&slot).join(&slot);
1929
1930 assert!(crate::serde_snapshot::compare_two_serialized_banks(&p1, &p2).unwrap());
1931 std::fs::remove_file(p1).unwrap();
1932 std::fs::remove_file(p2).unwrap();
1933 }
1934
1935 assert!(!dir_diff::is_different(&snapshots_to_verify, unpacked_snapshots).unwrap());
1936
1937 let unpacked_accounts = unpack_dir.join("accounts");
1939 assert!(!dir_diff::is_different(&storages_to_verify, unpacked_accounts).unwrap());
1940}
1941
1942pub fn purge_old_bank_snapshots(bank_snapshots_dir: impl AsRef<Path>) {
1944 let do_purge = |mut bank_snapshots: Vec<BankSnapshotInfo>| {
1945 bank_snapshots.sort_unstable();
1946 bank_snapshots
1947 .into_iter()
1948 .rev()
1949 .skip(MAX_BANK_SNAPSHOTS_TO_RETAIN)
1950 .for_each(|bank_snapshot| {
1951 let r = remove_bank_snapshot(bank_snapshot.slot, &bank_snapshots_dir);
1952 if r.is_err() {
1953 warn!(
1954 "Couldn't remove bank snapshot at: {}",
1955 bank_snapshot.snapshot_path.display()
1956 );
1957 }
1958 })
1959 };
1960
1961 do_purge(get_bank_snapshots_pre(&bank_snapshots_dir));
1962 do_purge(get_bank_snapshots_post(&bank_snapshots_dir));
1963}
1964
1965#[allow(clippy::too_many_arguments)]
1972pub fn snapshot_bank(
1973 root_bank: &Bank,
1974 status_cache_slot_deltas: Vec<BankSlotDelta>,
1975 pending_accounts_package: &PendingAccountsPackage,
1976 bank_snapshots_dir: impl AsRef<Path>,
1977 full_snapshot_archives_dir: impl AsRef<Path>,
1978 incremental_snapshot_archives_dir: impl AsRef<Path>,
1979 snapshot_version: SnapshotVersion,
1980 archive_format: ArchiveFormat,
1981 hash_for_testing: Option<Hash>,
1982 snapshot_type: Option<SnapshotType>,
1983) -> Result<()> {
1984 let snapshot_storages = get_snapshot_storages(root_bank);
1985
1986 let mut add_snapshot_time = Measure::start("add-snapshot-ms");
1987 let bank_snapshot_info = add_bank_snapshot(
1988 &bank_snapshots_dir,
1989 root_bank,
1990 &snapshot_storages,
1991 snapshot_version,
1992 )?;
1993 add_snapshot_time.stop();
1994 inc_new_counter_info!("add-snapshot-ms", add_snapshot_time.as_ms() as usize);
1995
1996 let accounts_package = AccountsPackage::new(
1997 root_bank,
1998 &bank_snapshot_info,
1999 bank_snapshots_dir,
2000 status_cache_slot_deltas,
2001 full_snapshot_archives_dir,
2002 incremental_snapshot_archives_dir,
2003 snapshot_storages,
2004 archive_format,
2005 snapshot_version,
2006 hash_for_testing,
2007 snapshot_type,
2008 )
2009 .expect("failed to hard link bank snapshot into a tmpdir");
2010
2011 if can_submit_accounts_package(&accounts_package, pending_accounts_package) {
2012 let old_accounts_package = pending_accounts_package
2013 .lock()
2014 .unwrap()
2015 .replace(accounts_package);
2016 if let Some(old_accounts_package) = old_accounts_package {
2017 debug!(
2018 "The pending AccountsPackage has been overwritten: \
2019 \nNew AccountsPackage slot: {}, snapshot type: {:?} \
2020 \nOld AccountsPackage slot: {}, snapshot type: {:?}",
2021 root_bank.slot(),
2022 snapshot_type,
2023 old_accounts_package.slot,
2024 old_accounts_package.snapshot_type,
2025 );
2026 }
2027 }
2028
2029 Ok(())
2030}
2031
2032fn get_snapshot_storages(bank: &Bank) -> SnapshotStorages {
2034 let mut measure_snapshot_storages = Measure::start("snapshot-storages");
2035 let snapshot_storages = bank.get_snapshot_storages(None);
2036 measure_snapshot_storages.stop();
2037 let snapshot_storages_count = snapshot_storages.iter().map(Vec::len).sum::<usize>();
2038 datapoint_info!(
2039 "get_snapshot_storages",
2040 ("snapshot-storages-count", snapshot_storages_count, i64),
2041 (
2042 "snapshot-storages-time-ms",
2043 measure_snapshot_storages.as_ms(),
2044 i64
2045 ),
2046 );
2047
2048 snapshot_storages
2049}
2050
2051pub fn bank_to_full_snapshot_archive(
2058 bank_snapshots_dir: impl AsRef<Path>,
2059 bank: &Bank,
2060 snapshot_version: Option<SnapshotVersion>,
2061 full_snapshot_archives_dir: impl AsRef<Path>,
2062 incremental_snapshot_archives_dir: impl AsRef<Path>,
2063 archive_format: ArchiveFormat,
2064 maximum_full_snapshot_archives_to_retain: usize,
2065 maximum_incremental_snapshot_archives_to_retain: usize,
2066) -> Result<FullSnapshotArchiveInfo> {
2067 let snapshot_version = snapshot_version.unwrap_or_default();
2068
2069 assert!(bank.is_complete());
2070 bank.squash(); bank.force_flush_accounts_cache();
2072 bank.clean_accounts(true, false, Some(bank.slot()));
2073 bank.update_accounts_hash();
2074 bank.rehash(); let temp_dir = tempfile::tempdir_in(bank_snapshots_dir)?;
2077 let snapshot_storages = bank.get_snapshot_storages(None);
2078 let bank_snapshot_info =
2079 add_bank_snapshot(&temp_dir, bank, &snapshot_storages, snapshot_version)?;
2080
2081 package_and_archive_full_snapshot(
2082 bank,
2083 &bank_snapshot_info,
2084 &temp_dir,
2085 full_snapshot_archives_dir,
2086 incremental_snapshot_archives_dir,
2087 snapshot_storages,
2088 archive_format,
2089 snapshot_version,
2090 maximum_full_snapshot_archives_to_retain,
2091 maximum_incremental_snapshot_archives_to_retain,
2092 )
2093}
2094
2095pub fn bank_to_incremental_snapshot_archive(
2103 bank_snapshots_dir: impl AsRef<Path>,
2104 bank: &Bank,
2105 full_snapshot_slot: Slot,
2106 snapshot_version: Option<SnapshotVersion>,
2107 full_snapshot_archives_dir: impl AsRef<Path>,
2108 incremental_snapshot_archives_dir: impl AsRef<Path>,
2109 archive_format: ArchiveFormat,
2110 maximum_full_snapshot_archives_to_retain: usize,
2111 maximum_incremental_snapshot_archives_to_retain: usize,
2112) -> Result<IncrementalSnapshotArchiveInfo> {
2113 let snapshot_version = snapshot_version.unwrap_or_default();
2114
2115 assert!(bank.is_complete());
2116 assert!(bank.slot() > full_snapshot_slot);
2117 bank.squash(); bank.force_flush_accounts_cache();
2119 bank.clean_accounts(true, false, Some(full_snapshot_slot));
2120 bank.update_accounts_hash();
2121 bank.rehash(); let temp_dir = tempfile::tempdir_in(bank_snapshots_dir)?;
2124 let snapshot_storages = bank.get_snapshot_storages(Some(full_snapshot_slot));
2125 let bank_snapshot_info =
2126 add_bank_snapshot(&temp_dir, bank, &snapshot_storages, snapshot_version)?;
2127
2128 package_and_archive_incremental_snapshot(
2129 bank,
2130 full_snapshot_slot,
2131 &bank_snapshot_info,
2132 &temp_dir,
2133 full_snapshot_archives_dir,
2134 incremental_snapshot_archives_dir,
2135 snapshot_storages,
2136 archive_format,
2137 snapshot_version,
2138 maximum_full_snapshot_archives_to_retain,
2139 maximum_incremental_snapshot_archives_to_retain,
2140 )
2141}
2142
2143#[allow(clippy::too_many_arguments)]
2145pub fn package_and_archive_full_snapshot(
2146 bank: &Bank,
2147 bank_snapshot_info: &BankSnapshotInfo,
2148 bank_snapshots_dir: impl AsRef<Path>,
2149 full_snapshot_archives_dir: impl AsRef<Path>,
2150 incremental_snapshot_archives_dir: impl AsRef<Path>,
2151 snapshot_storages: SnapshotStorages,
2152 archive_format: ArchiveFormat,
2153 snapshot_version: SnapshotVersion,
2154 maximum_full_snapshot_archives_to_retain: usize,
2155 maximum_incremental_snapshot_archives_to_retain: usize,
2156) -> Result<FullSnapshotArchiveInfo> {
2157 let slot_deltas = bank.status_cache.read().unwrap().root_slot_deltas();
2158 let accounts_package = AccountsPackage::new(
2159 bank,
2160 bank_snapshot_info,
2161 bank_snapshots_dir,
2162 slot_deltas,
2163 &full_snapshot_archives_dir,
2164 &incremental_snapshot_archives_dir,
2165 snapshot_storages,
2166 archive_format,
2167 snapshot_version,
2168 None,
2169 Some(SnapshotType::FullSnapshot),
2170 )?;
2171
2172 crate::serde_snapshot::reserialize_bank_with_new_accounts_hash(
2173 accounts_package.snapshot_links.path(),
2174 accounts_package.slot,
2175 &bank.get_accounts_hash(),
2176 None,
2177 );
2178
2179 let snapshot_package = SnapshotPackage::new(accounts_package, bank.get_accounts_hash());
2180 archive_snapshot_package(
2181 &snapshot_package,
2182 full_snapshot_archives_dir,
2183 incremental_snapshot_archives_dir,
2184 maximum_full_snapshot_archives_to_retain,
2185 maximum_incremental_snapshot_archives_to_retain,
2186 )?;
2187
2188 Ok(FullSnapshotArchiveInfo::new(
2189 snapshot_package.snapshot_archive_info,
2190 ))
2191}
2192
2193#[allow(clippy::too_many_arguments)]
2195pub fn package_and_archive_incremental_snapshot(
2196 bank: &Bank,
2197 incremental_snapshot_base_slot: Slot,
2198 bank_snapshot_info: &BankSnapshotInfo,
2199 bank_snapshots_dir: impl AsRef<Path>,
2200 full_snapshot_archives_dir: impl AsRef<Path>,
2201 incremental_snapshot_archives_dir: impl AsRef<Path>,
2202 snapshot_storages: SnapshotStorages,
2203 archive_format: ArchiveFormat,
2204 snapshot_version: SnapshotVersion,
2205 maximum_full_snapshot_archives_to_retain: usize,
2206 maximum_incremental_snapshot_archives_to_retain: usize,
2207) -> Result<IncrementalSnapshotArchiveInfo> {
2208 let slot_deltas = bank.status_cache.read().unwrap().root_slot_deltas();
2209 let accounts_package = AccountsPackage::new(
2210 bank,
2211 bank_snapshot_info,
2212 bank_snapshots_dir,
2213 slot_deltas,
2214 &full_snapshot_archives_dir,
2215 &incremental_snapshot_archives_dir,
2216 snapshot_storages,
2217 archive_format,
2218 snapshot_version,
2219 None,
2220 Some(SnapshotType::IncrementalSnapshot(
2221 incremental_snapshot_base_slot,
2222 )),
2223 )?;
2224
2225 crate::serde_snapshot::reserialize_bank_with_new_accounts_hash(
2226 accounts_package.snapshot_links.path(),
2227 accounts_package.slot,
2228 &bank.get_accounts_hash(),
2229 None,
2230 );
2231
2232 let snapshot_package = SnapshotPackage::new(accounts_package, bank.get_accounts_hash());
2233 archive_snapshot_package(
2234 &snapshot_package,
2235 full_snapshot_archives_dir,
2236 incremental_snapshot_archives_dir,
2237 maximum_full_snapshot_archives_to_retain,
2238 maximum_incremental_snapshot_archives_to_retain,
2239 )?;
2240
2241 Ok(IncrementalSnapshotArchiveInfo::new(
2242 incremental_snapshot_base_slot,
2243 snapshot_package.snapshot_archive_info,
2244 ))
2245}
2246
2247pub fn should_take_full_snapshot(
2248 block_height: Slot,
2249 full_snapshot_archive_interval_slots: Slot,
2250) -> bool {
2251 block_height % full_snapshot_archive_interval_slots == 0
2252}
2253
2254pub fn should_take_incremental_snapshot(
2255 block_height: Slot,
2256 incremental_snapshot_archive_interval_slots: Slot,
2257 last_full_snapshot_slot: Option<Slot>,
2258) -> bool {
2259 block_height % incremental_snapshot_archive_interval_slots == 0
2260 && last_full_snapshot_slot.is_some()
2261}
2262
2263fn can_submit_accounts_package(
2273 accounts_package: &AccountsPackage,
2274 pending_accounts_package: &PendingAccountsPackage,
2275) -> bool {
2276 match accounts_package.snapshot_type {
2277 Some(SnapshotType::FullSnapshot) => true,
2278 Some(SnapshotType::IncrementalSnapshot(_)) => pending_accounts_package
2279 .lock()
2280 .unwrap()
2281 .as_ref()
2282 .and_then(|old_accounts_package| old_accounts_package.snapshot_type)
2283 .map(|old_snapshot_type| !old_snapshot_type.is_full_snapshot())
2284 .unwrap_or(true),
2285 None => pending_accounts_package
2286 .lock()
2287 .unwrap()
2288 .as_ref()
2289 .map(|old_accounts_package| old_accounts_package.snapshot_type.is_none())
2290 .unwrap_or(true),
2291 }
2292}
2293
2294#[cfg(test)]
2295mod tests {
2296 use {
2297 super::*,
2298 crate::{accounts_db::ACCOUNTS_DB_CONFIG_FOR_TESTING, status_cache::Status},
2299 assert_matches::assert_matches,
2300 bincode::{deserialize_from, serialize_into},
2301 solana_sdk::{
2302 genesis_config::create_genesis_config,
2303 native_token::sol_to_lamports,
2304 signature::{Keypair, Signer},
2305 slot_history::SlotHistory,
2306 system_transaction,
2307 transaction::SanitizedTransaction,
2308 },
2309 std::{convert::TryFrom, mem::size_of},
2310 tempfile::NamedTempFile,
2311 };
2312
2313 #[test]
2314 fn test_serialize_snapshot_data_file_under_limit() {
2315 let temp_dir = tempfile::TempDir::new().unwrap();
2316 let expected_consumed_size = size_of::<u32>() as u64;
2317 let consumed_size = serialize_snapshot_data_file_capped(
2318 &temp_dir.path().join("data-file"),
2319 expected_consumed_size,
2320 |stream| {
2321 serialize_into(stream, &2323_u32)?;
2322 Ok(())
2323 },
2324 )
2325 .unwrap();
2326 assert_eq!(consumed_size, expected_consumed_size);
2327 }
2328
2329 #[test]
2330 fn test_serialize_snapshot_data_file_over_limit() {
2331 let temp_dir = tempfile::TempDir::new().unwrap();
2332 let expected_consumed_size = size_of::<u32>() as u64;
2333 let result = serialize_snapshot_data_file_capped(
2334 &temp_dir.path().join("data-file"),
2335 expected_consumed_size - 1,
2336 |stream| {
2337 serialize_into(stream, &2323_u32)?;
2338 Ok(())
2339 },
2340 );
2341 assert_matches!(result, Err(SnapshotError::Io(ref message)) if message.to_string().starts_with("too large snapshot data file to serialize"));
2342 }
2343
2344 #[test]
2345 fn test_deserialize_snapshot_data_file_under_limit() {
2346 let expected_data = 2323_u32;
2347 let expected_consumed_size = size_of::<u32>() as u64;
2348
2349 let temp_dir = tempfile::TempDir::new().unwrap();
2350 serialize_snapshot_data_file_capped(
2351 &temp_dir.path().join("data-file"),
2352 expected_consumed_size,
2353 |stream| {
2354 serialize_into(stream, &expected_data)?;
2355 Ok(())
2356 },
2357 )
2358 .unwrap();
2359
2360 let snapshot_root_paths = SnapshotRootPaths {
2361 full_snapshot_root_file_path: temp_dir.path().join("data-file"),
2362 incremental_snapshot_root_file_path: None,
2363 };
2364
2365 let actual_data = deserialize_snapshot_data_files_capped(
2366 &snapshot_root_paths,
2367 expected_consumed_size,
2368 |stream| {
2369 Ok(deserialize_from::<_, u32>(
2370 &mut stream.full_snapshot_stream,
2371 )?)
2372 },
2373 )
2374 .unwrap();
2375 assert_eq!(actual_data, expected_data);
2376 }
2377
2378 #[test]
2379 fn test_deserialize_snapshot_data_file_over_limit() {
2380 let expected_data = 2323_u32;
2381 let expected_consumed_size = size_of::<u32>() as u64;
2382
2383 let temp_dir = tempfile::TempDir::new().unwrap();
2384 serialize_snapshot_data_file_capped(
2385 &temp_dir.path().join("data-file"),
2386 expected_consumed_size,
2387 |stream| {
2388 serialize_into(stream, &expected_data)?;
2389 Ok(())
2390 },
2391 )
2392 .unwrap();
2393
2394 let snapshot_root_paths = SnapshotRootPaths {
2395 full_snapshot_root_file_path: temp_dir.path().join("data-file"),
2396 incremental_snapshot_root_file_path: None,
2397 };
2398
2399 let result = deserialize_snapshot_data_files_capped(
2400 &snapshot_root_paths,
2401 expected_consumed_size - 1,
2402 |stream| {
2403 Ok(deserialize_from::<_, u32>(
2404 &mut stream.full_snapshot_stream,
2405 )?)
2406 },
2407 );
2408 assert_matches!(result, Err(SnapshotError::Io(ref message)) if message.to_string().starts_with("too large snapshot data file to deserialize"));
2409 }
2410
2411 #[test]
2412 fn test_deserialize_snapshot_data_file_extra_data() {
2413 let expected_data = 2323_u32;
2414 let expected_consumed_size = size_of::<u32>() as u64;
2415
2416 let temp_dir = tempfile::TempDir::new().unwrap();
2417 serialize_snapshot_data_file_capped(
2418 &temp_dir.path().join("data-file"),
2419 expected_consumed_size * 2,
2420 |stream| {
2421 serialize_into(stream.by_ref(), &expected_data)?;
2422 serialize_into(stream.by_ref(), &expected_data)?;
2423 Ok(())
2424 },
2425 )
2426 .unwrap();
2427
2428 let snapshot_root_paths = SnapshotRootPaths {
2429 full_snapshot_root_file_path: temp_dir.path().join("data-file"),
2430 incremental_snapshot_root_file_path: None,
2431 };
2432
2433 let result = deserialize_snapshot_data_files_capped(
2434 &snapshot_root_paths,
2435 expected_consumed_size * 2,
2436 |stream| {
2437 Ok(deserialize_from::<_, u32>(
2438 &mut stream.full_snapshot_stream,
2439 )?)
2440 },
2441 );
2442 assert_matches!(result, Err(SnapshotError::Io(ref message)) if message.to_string().starts_with("invalid snapshot data file"));
2443 }
2444
2445 #[test]
2446 fn test_snapshot_version_from_file_under_limit() {
2447 let file_content = SnapshotVersion::default().as_str();
2448 let mut file = NamedTempFile::new().unwrap();
2449 file.write_all(file_content.as_bytes()).unwrap();
2450 let version_from_file = snapshot_version_from_file(file.path()).unwrap();
2451 assert_eq!(version_from_file, file_content);
2452 }
2453
2454 #[test]
2455 fn test_snapshot_version_from_file_over_limit() {
2456 let over_limit_size = usize::try_from(MAX_SNAPSHOT_VERSION_FILE_SIZE + 1).unwrap();
2457 let file_content = vec![7u8; over_limit_size];
2458 let mut file = NamedTempFile::new().unwrap();
2459 file.write_all(&file_content).unwrap();
2460 assert_matches!(
2461 snapshot_version_from_file(file.path()),
2462 Err(SnapshotError::Io(ref message)) if message.to_string().starts_with("snapshot version file too large")
2463 );
2464 }
2465
2466 #[test]
2467 fn test_parse_full_snapshot_archive_filename() {
2468 assert_eq!(
2469 parse_full_snapshot_archive_filename(&format!(
2470 "snapshot-42-{}.tar.bz2",
2471 Hash::default()
2472 ))
2473 .unwrap(),
2474 (42, Hash::default(), ArchiveFormat::TarBzip2)
2475 );
2476 assert_eq!(
2477 parse_full_snapshot_archive_filename(&format!(
2478 "snapshot-43-{}.tar.zst",
2479 Hash::default()
2480 ))
2481 .unwrap(),
2482 (43, Hash::default(), ArchiveFormat::TarZstd)
2483 );
2484 assert_eq!(
2485 parse_full_snapshot_archive_filename(&format!("snapshot-44-{}.tar", Hash::default()))
2486 .unwrap(),
2487 (44, Hash::default(), ArchiveFormat::Tar)
2488 );
2489 assert_eq!(
2490 parse_full_snapshot_archive_filename(&format!(
2491 "snapshot-45-{}.tar.lz4",
2492 Hash::default()
2493 ))
2494 .unwrap(),
2495 (45, Hash::default(), ArchiveFormat::TarLz4)
2496 );
2497
2498 assert!(parse_full_snapshot_archive_filename("invalid").is_err());
2499 assert!(
2500 parse_full_snapshot_archive_filename("snapshot-bad!slot-bad!hash.bad!ext").is_err()
2501 );
2502
2503 assert!(
2504 parse_full_snapshot_archive_filename("snapshot-12345678-bad!hash.bad!ext").is_err()
2505 );
2506 assert!(parse_full_snapshot_archive_filename(&format!(
2507 "snapshot-12345678-{}.bad!ext",
2508 Hash::new_unique()
2509 ))
2510 .is_err());
2511 assert!(parse_full_snapshot_archive_filename("snapshot-12345678-bad!hash.tar").is_err());
2512
2513 assert!(parse_full_snapshot_archive_filename(&format!(
2514 "snapshot-bad!slot-{}.bad!ext",
2515 Hash::new_unique()
2516 ))
2517 .is_err());
2518 assert!(parse_full_snapshot_archive_filename(&format!(
2519 "snapshot-12345678-{}.bad!ext",
2520 Hash::new_unique()
2521 ))
2522 .is_err());
2523 assert!(parse_full_snapshot_archive_filename(&format!(
2524 "snapshot-bad!slot-{}.tar",
2525 Hash::new_unique()
2526 ))
2527 .is_err());
2528
2529 assert!(parse_full_snapshot_archive_filename("snapshot-bad!slot-bad!hash.tar").is_err());
2530 assert!(parse_full_snapshot_archive_filename("snapshot-12345678-bad!hash.tar").is_err());
2531 assert!(parse_full_snapshot_archive_filename(&format!(
2532 "snapshot-bad!slot-{}.tar",
2533 Hash::new_unique()
2534 ))
2535 .is_err());
2536 }
2537
2538 #[test]
2539 fn test_parse_incremental_snapshot_archive_filename() {
2540 solana_logger::setup();
2541 assert_eq!(
2542 parse_incremental_snapshot_archive_filename(&format!(
2543 "incremental-snapshot-42-123-{}.tar.bz2",
2544 Hash::default()
2545 ))
2546 .unwrap(),
2547 (42, 123, Hash::default(), ArchiveFormat::TarBzip2)
2548 );
2549 assert_eq!(
2550 parse_incremental_snapshot_archive_filename(&format!(
2551 "incremental-snapshot-43-234-{}.tar.zst",
2552 Hash::default()
2553 ))
2554 .unwrap(),
2555 (43, 234, Hash::default(), ArchiveFormat::TarZstd)
2556 );
2557 assert_eq!(
2558 parse_incremental_snapshot_archive_filename(&format!(
2559 "incremental-snapshot-44-345-{}.tar",
2560 Hash::default()
2561 ))
2562 .unwrap(),
2563 (44, 345, Hash::default(), ArchiveFormat::Tar)
2564 );
2565 assert_eq!(
2566 parse_incremental_snapshot_archive_filename(&format!(
2567 "incremental-snapshot-45-456-{}.tar.lz4",
2568 Hash::default()
2569 ))
2570 .unwrap(),
2571 (45, 456, Hash::default(), ArchiveFormat::TarLz4)
2572 );
2573
2574 assert!(parse_incremental_snapshot_archive_filename("invalid").is_err());
2575 assert!(parse_incremental_snapshot_archive_filename(&format!(
2576 "snapshot-42-{}.tar",
2577 Hash::new_unique()
2578 ))
2579 .is_err());
2580 assert!(parse_incremental_snapshot_archive_filename(
2581 "incremental-snapshot-bad!slot-bad!slot-bad!hash.bad!ext"
2582 )
2583 .is_err());
2584
2585 assert!(parse_incremental_snapshot_archive_filename(&format!(
2586 "incremental-snapshot-bad!slot-56785678-{}.tar",
2587 Hash::new_unique()
2588 ))
2589 .is_err());
2590
2591 assert!(parse_incremental_snapshot_archive_filename(&format!(
2592 "incremental-snapshot-12345678-bad!slot-{}.tar",
2593 Hash::new_unique()
2594 ))
2595 .is_err());
2596
2597 assert!(parse_incremental_snapshot_archive_filename(
2598 "incremental-snapshot-12341234-56785678-bad!HASH.tar"
2599 )
2600 .is_err());
2601
2602 assert!(parse_incremental_snapshot_archive_filename(&format!(
2603 "incremental-snapshot-12341234-56785678-{}.bad!ext",
2604 Hash::new_unique()
2605 ))
2606 .is_err());
2607 }
2608
2609 #[test]
2610 fn test_check_are_snapshots_compatible() {
2611 solana_logger::setup();
2612 let slot1: Slot = 1234;
2613 let slot2: Slot = 5678;
2614 let slot3: Slot = 999_999;
2615
2616 let full_snapshot_archive_info = FullSnapshotArchiveInfo::new_from_path(PathBuf::from(
2617 format!("/dir/snapshot-{}-{}.tar", slot1, Hash::new_unique()),
2618 ))
2619 .unwrap();
2620
2621 assert!(check_are_snapshots_compatible(&full_snapshot_archive_info, None,).is_ok());
2622
2623 let incremental_snapshot_archive_info =
2624 IncrementalSnapshotArchiveInfo::new_from_path(PathBuf::from(format!(
2625 "/dir/incremental-snapshot-{}-{}-{}.tar",
2626 slot1,
2627 slot2,
2628 Hash::new_unique()
2629 )))
2630 .unwrap();
2631
2632 assert!(check_are_snapshots_compatible(
2633 &full_snapshot_archive_info,
2634 Some(&incremental_snapshot_archive_info)
2635 )
2636 .is_ok());
2637
2638 let incremental_snapshot_archive_info =
2639 IncrementalSnapshotArchiveInfo::new_from_path(PathBuf::from(format!(
2640 "/dir/incremental-snapshot-{}-{}-{}.tar",
2641 slot2,
2642 slot3,
2643 Hash::new_unique()
2644 )))
2645 .unwrap();
2646
2647 assert!(check_are_snapshots_compatible(
2648 &full_snapshot_archive_info,
2649 Some(&incremental_snapshot_archive_info)
2650 )
2651 .is_err());
2652 }
2653
2654 fn common_create_bank_snapshot_files(
2656 bank_snapshots_dir: &Path,
2657 min_slot: Slot,
2658 max_slot: Slot,
2659 ) {
2660 for slot in min_slot..max_slot {
2661 let snapshot_dir = get_bank_snapshots_dir(bank_snapshots_dir, slot);
2662 fs::create_dir_all(&snapshot_dir).unwrap();
2663
2664 let snapshot_filename = get_snapshot_file_name(slot);
2665 let snapshot_path = snapshot_dir.join(snapshot_filename);
2666 File::create(snapshot_path).unwrap();
2667 }
2668 }
2669
2670 #[test]
2671 fn test_get_bank_snapshots() {
2672 solana_logger::setup();
2673 let temp_snapshots_dir = tempfile::TempDir::new().unwrap();
2674 let min_slot = 10;
2675 let max_slot = 20;
2676 common_create_bank_snapshot_files(temp_snapshots_dir.path(), min_slot, max_slot);
2677
2678 let bank_snapshots = get_bank_snapshots(temp_snapshots_dir.path());
2679 assert_eq!(bank_snapshots.len() as Slot, max_slot - min_slot);
2680 }
2681
2682 #[test]
2683 fn test_get_highest_bank_snapshot_post() {
2684 solana_logger::setup();
2685 let temp_snapshots_dir = tempfile::TempDir::new().unwrap();
2686 let min_slot = 99;
2687 let max_slot = 123;
2688 common_create_bank_snapshot_files(temp_snapshots_dir.path(), min_slot, max_slot);
2689
2690 let highest_bank_snapshot = get_highest_bank_snapshot_post(temp_snapshots_dir.path());
2691 assert!(highest_bank_snapshot.is_some());
2692 assert_eq!(highest_bank_snapshot.unwrap().slot, max_slot - 1);
2693 }
2694
2695 fn common_create_snapshot_archive_files(
2701 full_snapshot_archives_dir: &Path,
2702 incremental_snapshot_archives_dir: &Path,
2703 min_full_snapshot_slot: Slot,
2704 max_full_snapshot_slot: Slot,
2705 min_incremental_snapshot_slot: Slot,
2706 max_incremental_snapshot_slot: Slot,
2707 ) {
2708 fs::create_dir_all(full_snapshot_archives_dir).unwrap();
2709 fs::create_dir_all(incremental_snapshot_archives_dir).unwrap();
2710 for full_snapshot_slot in min_full_snapshot_slot..max_full_snapshot_slot {
2711 for incremental_snapshot_slot in
2712 min_incremental_snapshot_slot..max_incremental_snapshot_slot
2713 {
2714 let snapshot_filename = format!(
2715 "incremental-snapshot-{}-{}-{}.tar",
2716 full_snapshot_slot,
2717 incremental_snapshot_slot,
2718 Hash::default()
2719 );
2720 let snapshot_filepath = incremental_snapshot_archives_dir.join(snapshot_filename);
2721 File::create(snapshot_filepath).unwrap();
2722 }
2723
2724 let snapshot_filename =
2725 format!("snapshot-{}-{}.tar", full_snapshot_slot, Hash::default());
2726 let snapshot_filepath = full_snapshot_archives_dir.join(snapshot_filename);
2727 File::create(snapshot_filepath).unwrap();
2728
2729 let bad_filename = format!(
2731 "incremental-snapshot-{}-{}-bad!hash.tar",
2732 full_snapshot_slot,
2733 max_incremental_snapshot_slot + 1,
2734 );
2735 let bad_filepath = incremental_snapshot_archives_dir.join(bad_filename);
2736 File::create(bad_filepath).unwrap();
2737 }
2738
2739 let bad_filename = format!("snapshot-{}-bad!hash.tar", max_full_snapshot_slot + 1);
2742 let bad_filepath = full_snapshot_archives_dir.join(bad_filename);
2743 File::create(bad_filepath).unwrap();
2744 }
2745
2746 #[test]
2747 fn test_get_full_snapshot_archives() {
2748 solana_logger::setup();
2749 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2750 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2751 let min_slot = 123;
2752 let max_slot = 456;
2753 common_create_snapshot_archive_files(
2754 full_snapshot_archives_dir.path(),
2755 incremental_snapshot_archives_dir.path(),
2756 min_slot,
2757 max_slot,
2758 0,
2759 0,
2760 );
2761
2762 let snapshot_archives = get_full_snapshot_archives(full_snapshot_archives_dir);
2763 assert_eq!(snapshot_archives.len() as Slot, max_slot - min_slot);
2764 }
2765
2766 #[test]
2767 fn test_get_full_snapshot_archives_remote() {
2768 solana_logger::setup();
2769 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2770 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2771 let min_slot = 123;
2772 let max_slot = 456;
2773 common_create_snapshot_archive_files(
2774 &full_snapshot_archives_dir.path().join("remote"),
2775 &incremental_snapshot_archives_dir.path().join("remote"),
2776 min_slot,
2777 max_slot,
2778 0,
2779 0,
2780 );
2781
2782 let snapshot_archives = get_full_snapshot_archives(full_snapshot_archives_dir);
2783 assert_eq!(snapshot_archives.len() as Slot, max_slot - min_slot);
2784 assert!(snapshot_archives.iter().all(|info| info.is_remote()));
2785 }
2786
2787 #[test]
2788 fn test_get_incremental_snapshot_archives() {
2789 solana_logger::setup();
2790 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2791 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2792 let min_full_snapshot_slot = 12;
2793 let max_full_snapshot_slot = 23;
2794 let min_incremental_snapshot_slot = 34;
2795 let max_incremental_snapshot_slot = 45;
2796 common_create_snapshot_archive_files(
2797 full_snapshot_archives_dir.path(),
2798 incremental_snapshot_archives_dir.path(),
2799 min_full_snapshot_slot,
2800 max_full_snapshot_slot,
2801 min_incremental_snapshot_slot,
2802 max_incremental_snapshot_slot,
2803 );
2804
2805 let incremental_snapshot_archives =
2806 get_incremental_snapshot_archives(incremental_snapshot_archives_dir);
2807 assert_eq!(
2808 incremental_snapshot_archives.len() as Slot,
2809 (max_full_snapshot_slot - min_full_snapshot_slot)
2810 * (max_incremental_snapshot_slot - min_incremental_snapshot_slot)
2811 );
2812 }
2813
2814 #[test]
2815 fn test_get_incremental_snapshot_archives_remote() {
2816 solana_logger::setup();
2817 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2818 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2819 let min_full_snapshot_slot = 12;
2820 let max_full_snapshot_slot = 23;
2821 let min_incremental_snapshot_slot = 34;
2822 let max_incremental_snapshot_slot = 45;
2823 common_create_snapshot_archive_files(
2824 &full_snapshot_archives_dir.path().join("remote"),
2825 &incremental_snapshot_archives_dir.path().join("remote"),
2826 min_full_snapshot_slot,
2827 max_full_snapshot_slot,
2828 min_incremental_snapshot_slot,
2829 max_incremental_snapshot_slot,
2830 );
2831
2832 let incremental_snapshot_archives =
2833 get_incremental_snapshot_archives(incremental_snapshot_archives_dir);
2834 assert_eq!(
2835 incremental_snapshot_archives.len() as Slot,
2836 (max_full_snapshot_slot - min_full_snapshot_slot)
2837 * (max_incremental_snapshot_slot - min_incremental_snapshot_slot)
2838 );
2839 assert!(incremental_snapshot_archives
2840 .iter()
2841 .all(|info| info.is_remote()));
2842 }
2843
2844 #[test]
2845 fn test_get_highest_full_snapshot_archive_slot() {
2846 solana_logger::setup();
2847 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2848 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2849 let min_slot = 123;
2850 let max_slot = 456;
2851 common_create_snapshot_archive_files(
2852 full_snapshot_archives_dir.path(),
2853 incremental_snapshot_archives_dir.path(),
2854 min_slot,
2855 max_slot,
2856 0,
2857 0,
2858 );
2859
2860 assert_eq!(
2861 get_highest_full_snapshot_archive_slot(full_snapshot_archives_dir.path()),
2862 Some(max_slot - 1)
2863 );
2864 }
2865
2866 #[test]
2867 fn test_get_highest_incremental_snapshot_slot() {
2868 solana_logger::setup();
2869 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2870 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2871 let min_full_snapshot_slot = 12;
2872 let max_full_snapshot_slot = 23;
2873 let min_incremental_snapshot_slot = 34;
2874 let max_incremental_snapshot_slot = 45;
2875 common_create_snapshot_archive_files(
2876 full_snapshot_archives_dir.path(),
2877 incremental_snapshot_archives_dir.path(),
2878 min_full_snapshot_slot,
2879 max_full_snapshot_slot,
2880 min_incremental_snapshot_slot,
2881 max_incremental_snapshot_slot,
2882 );
2883
2884 for full_snapshot_slot in min_full_snapshot_slot..max_full_snapshot_slot {
2885 assert_eq!(
2886 get_highest_incremental_snapshot_archive_slot(
2887 incremental_snapshot_archives_dir.path(),
2888 full_snapshot_slot
2889 ),
2890 Some(max_incremental_snapshot_slot - 1)
2891 );
2892 }
2893
2894 assert_eq!(
2895 get_highest_incremental_snapshot_archive_slot(
2896 incremental_snapshot_archives_dir.path(),
2897 max_full_snapshot_slot
2898 ),
2899 None
2900 );
2901 }
2902
2903 fn common_test_purge_old_snapshot_archives(
2904 snapshot_names: &[&String],
2905 maximum_full_snapshot_archives_to_retain: usize,
2906 maximum_incremental_snapshot_archives_to_retain: usize,
2907 expected_snapshots: &[&String],
2908 ) {
2909 let temp_snap_dir = tempfile::TempDir::new().unwrap();
2910
2911 for snap_name in snapshot_names {
2912 let snap_path = temp_snap_dir.path().join(snap_name);
2913 let mut _snap_file = File::create(snap_path);
2914 }
2915 purge_old_snapshot_archives(
2916 temp_snap_dir.path(),
2917 temp_snap_dir.path(),
2918 maximum_full_snapshot_archives_to_retain,
2919 maximum_incremental_snapshot_archives_to_retain,
2920 );
2921
2922 let mut retained_snaps = HashSet::new();
2923 for entry in fs::read_dir(temp_snap_dir.path()).unwrap() {
2924 let entry_path_buf = entry.unwrap().path();
2925 let entry_path = entry_path_buf.as_path();
2926 let snapshot_name = entry_path
2927 .file_name()
2928 .unwrap()
2929 .to_str()
2930 .unwrap()
2931 .to_string();
2932 retained_snaps.insert(snapshot_name);
2933 }
2934
2935 for snap_name in expected_snapshots {
2936 assert!(
2937 retained_snaps.contains(snap_name.as_str()),
2938 "{} not found",
2939 snap_name
2940 );
2941 }
2942 assert_eq!(retained_snaps.len(), expected_snapshots.len());
2943 }
2944
2945 #[test]
2946 fn test_purge_old_full_snapshot_archives() {
2947 let snap1_name = format!("snapshot-1-{}.tar.zst", Hash::default());
2948 let snap2_name = format!("snapshot-3-{}.tar.zst", Hash::default());
2949 let snap3_name = format!("snapshot-50-{}.tar.zst", Hash::default());
2950 let snapshot_names = vec![&snap1_name, &snap2_name, &snap3_name];
2951
2952 let expected_snapshots = vec![&snap3_name];
2954 common_test_purge_old_snapshot_archives(
2955 &snapshot_names,
2956 1,
2957 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2958 &expected_snapshots,
2959 );
2960
2961 common_test_purge_old_snapshot_archives(
2963 &snapshot_names,
2964 0,
2965 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2966 &expected_snapshots,
2967 );
2968
2969 let expected_snapshots = vec![&snap2_name, &snap3_name];
2971 common_test_purge_old_snapshot_archives(
2972 &snapshot_names,
2973 2,
2974 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2975 &expected_snapshots,
2976 );
2977
2978 let expected_snapshots = vec![&snap1_name, &snap2_name, &snap3_name];
2980 common_test_purge_old_snapshot_archives(
2981 &snapshot_names,
2982 3,
2983 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2984 &expected_snapshots,
2985 );
2986 }
2987
2988 #[test]
2992 fn test_purge_old_full_snapshot_archives_in_the_loop() {
2993 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2994 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2995 let maximum_snapshots_to_retain = 5;
2996 let starting_slot: Slot = 42;
2997
2998 for slot in (starting_slot..).take(100) {
2999 let full_snapshot_archive_file_name =
3000 format!("snapshot-{}-{}.tar", slot, Hash::default());
3001 let full_snapshot_archive_path = full_snapshot_archives_dir
3002 .as_ref()
3003 .join(full_snapshot_archive_file_name);
3004 File::create(full_snapshot_archive_path).unwrap();
3005
3006 if slot < starting_slot + maximum_snapshots_to_retain as Slot {
3008 continue;
3009 }
3010
3011 if slot % (maximum_snapshots_to_retain as Slot * 2) != 0 {
3013 continue;
3014 }
3015
3016 purge_old_snapshot_archives(
3017 &full_snapshot_archives_dir,
3018 &incremental_snapshot_archives_dir,
3019 maximum_snapshots_to_retain,
3020 usize::MAX,
3021 );
3022 let mut full_snapshot_archives =
3023 get_full_snapshot_archives(&full_snapshot_archives_dir);
3024 full_snapshot_archives.sort_unstable();
3025 assert_eq!(full_snapshot_archives.len(), maximum_snapshots_to_retain);
3026 assert_eq!(full_snapshot_archives.last().unwrap().slot(), slot);
3027 for (i, full_snapshot_archive) in full_snapshot_archives.iter().rev().enumerate() {
3028 assert_eq!(full_snapshot_archive.slot(), slot - i as Slot);
3029 }
3030 }
3031 }
3032
3033 #[test]
3034 fn test_purge_old_incremental_snapshot_archives() {
3035 solana_logger::setup();
3036 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3037 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3038 let starting_slot = 100_000;
3039
3040 let maximum_incremental_snapshot_archives_to_retain =
3041 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN;
3042 let maximum_full_snapshot_archives_to_retain = DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN;
3043
3044 let incremental_snapshot_interval = 100;
3045 let num_incremental_snapshots_per_full_snapshot =
3046 maximum_incremental_snapshot_archives_to_retain * 2;
3047 let full_snapshot_interval =
3048 incremental_snapshot_interval * num_incremental_snapshots_per_full_snapshot;
3049
3050 let mut snapshot_filenames = vec![];
3051 (starting_slot..)
3052 .step_by(full_snapshot_interval)
3053 .take(maximum_full_snapshot_archives_to_retain * 2)
3054 .for_each(|full_snapshot_slot| {
3055 let snapshot_filename =
3056 format!("snapshot-{}-{}.tar", full_snapshot_slot, Hash::default());
3057 let snapshot_path = full_snapshot_archives_dir.path().join(&snapshot_filename);
3058 File::create(snapshot_path).unwrap();
3059 snapshot_filenames.push(snapshot_filename);
3060
3061 (full_snapshot_slot..)
3062 .step_by(incremental_snapshot_interval)
3063 .take(num_incremental_snapshots_per_full_snapshot)
3064 .skip(1)
3065 .for_each(|incremental_snapshot_slot| {
3066 let snapshot_filename = format!(
3067 "incremental-snapshot-{}-{}-{}.tar",
3068 full_snapshot_slot,
3069 incremental_snapshot_slot,
3070 Hash::default()
3071 );
3072 let snapshot_path = incremental_snapshot_archives_dir
3073 .path()
3074 .join(&snapshot_filename);
3075 File::create(snapshot_path).unwrap();
3076 snapshot_filenames.push(snapshot_filename);
3077 });
3078 });
3079
3080 purge_old_snapshot_archives(
3081 full_snapshot_archives_dir.path(),
3082 incremental_snapshot_archives_dir.path(),
3083 maximum_full_snapshot_archives_to_retain,
3084 maximum_incremental_snapshot_archives_to_retain,
3085 );
3086
3087 let mut remaining_full_snapshot_archives =
3090 get_full_snapshot_archives(full_snapshot_archives_dir.path());
3091 assert_eq!(
3092 remaining_full_snapshot_archives.len(),
3093 maximum_full_snapshot_archives_to_retain,
3094 );
3095 remaining_full_snapshot_archives.sort_unstable();
3096 let latest_full_snapshot_archive_slot =
3097 remaining_full_snapshot_archives.last().unwrap().slot();
3098
3099 let mut remaining_incremental_snapshot_archives =
3101 get_incremental_snapshot_archives(incremental_snapshot_archives_dir.path());
3102 assert_eq!(
3103 remaining_incremental_snapshot_archives.len(),
3104 maximum_incremental_snapshot_archives_to_retain
3105 + maximum_full_snapshot_archives_to_retain.saturating_sub(1)
3106 );
3107 remaining_incremental_snapshot_archives.sort_unstable();
3108 remaining_incremental_snapshot_archives.reverse();
3109
3110 for i in (1..maximum_full_snapshot_archives_to_retain).rev() {
3112 let incremental_snapshot_archive =
3113 remaining_incremental_snapshot_archives.pop().unwrap();
3114
3115 let expected_base_slot =
3116 latest_full_snapshot_archive_slot - (i * full_snapshot_interval) as u64;
3117 assert_eq!(incremental_snapshot_archive.base_slot(), expected_base_slot);
3118 let expected_slot = expected_base_slot
3119 + (full_snapshot_interval - incremental_snapshot_interval) as u64;
3120 assert_eq!(incremental_snapshot_archive.slot(), expected_slot);
3121 }
3122
3123 for incremental_snapshot_archive in &remaining_incremental_snapshot_archives {
3125 assert_eq!(
3126 incremental_snapshot_archive.base_slot(),
3127 latest_full_snapshot_archive_slot
3128 );
3129 }
3130
3131 let expected_remaing_incremental_snapshot_archive_slots =
3133 (latest_full_snapshot_archive_slot..)
3134 .step_by(incremental_snapshot_interval)
3135 .take(num_incremental_snapshots_per_full_snapshot)
3136 .skip(
3137 num_incremental_snapshots_per_full_snapshot
3138 - maximum_incremental_snapshot_archives_to_retain,
3139 )
3140 .collect::<HashSet<_>>();
3141
3142 let actual_remaining_incremental_snapshot_archive_slots =
3143 remaining_incremental_snapshot_archives
3144 .iter()
3145 .map(|snapshot| snapshot.slot())
3146 .collect::<HashSet<_>>();
3147 assert_eq!(
3148 actual_remaining_incremental_snapshot_archive_slots,
3149 expected_remaing_incremental_snapshot_archive_slots
3150 );
3151 }
3152
3153 #[test]
3154 fn test_purge_all_incremental_snapshot_archives_when_no_full_snapshot_archives() {
3155 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3156 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3157
3158 for snapshot_filenames in [
3159 format!("incremental-snapshot-100-120-{}.tar", Hash::default()),
3160 format!("incremental-snapshot-100-140-{}.tar", Hash::default()),
3161 format!("incremental-snapshot-100-160-{}.tar", Hash::default()),
3162 format!("incremental-snapshot-100-180-{}.tar", Hash::default()),
3163 format!("incremental-snapshot-200-220-{}.tar", Hash::default()),
3164 format!("incremental-snapshot-200-240-{}.tar", Hash::default()),
3165 format!("incremental-snapshot-200-260-{}.tar", Hash::default()),
3166 format!("incremental-snapshot-200-280-{}.tar", Hash::default()),
3167 ] {
3168 let snapshot_path = incremental_snapshot_archives_dir
3169 .path()
3170 .join(&snapshot_filenames);
3171 File::create(snapshot_path).unwrap();
3172 }
3173
3174 purge_old_snapshot_archives(
3175 full_snapshot_archives_dir.path(),
3176 incremental_snapshot_archives_dir.path(),
3177 usize::MAX,
3178 usize::MAX,
3179 );
3180
3181 let remaining_incremental_snapshot_archives =
3182 get_incremental_snapshot_archives(incremental_snapshot_archives_dir.path());
3183 assert!(remaining_incremental_snapshot_archives.is_empty());
3184 }
3185
3186 #[test]
3189 fn test_roundtrip_bank_to_and_from_full_snapshot_simple() {
3190 solana_logger::setup();
3191 let genesis_config = GenesisConfig::default();
3192 let original_bank = Bank::new_for_tests(&genesis_config);
3193
3194 while !original_bank.is_complete() {
3195 original_bank.register_tick(&Hash::new_unique());
3196 }
3197
3198 let accounts_dir = tempfile::TempDir::new().unwrap();
3199 let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
3200 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3201 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3202 let snapshot_archive_format = ArchiveFormat::Tar;
3203
3204 let snapshot_archive_info = bank_to_full_snapshot_archive(
3205 &bank_snapshots_dir,
3206 &original_bank,
3207 None,
3208 full_snapshot_archives_dir.path(),
3209 incremental_snapshot_archives_dir.path(),
3210 snapshot_archive_format,
3211 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3212 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3213 )
3214 .unwrap();
3215
3216 let (roundtrip_bank, _) = bank_from_snapshot_archives(
3217 &[PathBuf::from(accounts_dir.path())],
3218 bank_snapshots_dir.path(),
3219 &snapshot_archive_info,
3220 None,
3221 &genesis_config,
3222 None,
3223 None,
3224 AccountSecondaryIndexes::default(),
3225 false,
3226 None,
3227 AccountShrinkThreshold::default(),
3228 false,
3229 false,
3230 false,
3231 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
3232 None,
3233 )
3234 .unwrap();
3235
3236 assert_eq!(original_bank, roundtrip_bank);
3237 }
3238
3239 #[test]
3243 fn test_roundtrip_bank_to_and_from_snapshot_complex() {
3244 solana_logger::setup();
3245 let collector = Pubkey::new_unique();
3246 let key1 = Keypair::new();
3247 let key2 = Keypair::new();
3248 let key3 = Keypair::new();
3249 let key4 = Keypair::new();
3250 let key5 = Keypair::new();
3251
3252 let (genesis_config, mint_keypair) = create_genesis_config(sol_to_lamports(1_000_000.));
3253 let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
3254 bank0
3255 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3256 .unwrap();
3257 bank0
3258 .transfer(sol_to_lamports(2.), &mint_keypair, &key2.pubkey())
3259 .unwrap();
3260 bank0
3261 .transfer(sol_to_lamports(3.), &mint_keypair, &key3.pubkey())
3262 .unwrap();
3263 while !bank0.is_complete() {
3264 bank0.register_tick(&Hash::new_unique());
3265 }
3266
3267 let slot = 1;
3268 let bank1 = Arc::new(Bank::new_from_parent(&bank0, &collector, slot));
3269 bank1
3270 .transfer(sol_to_lamports(3.), &mint_keypair, &key3.pubkey())
3271 .unwrap();
3272 bank1
3273 .transfer(sol_to_lamports(4.), &mint_keypair, &key4.pubkey())
3274 .unwrap();
3275 bank1
3276 .transfer(sol_to_lamports(5.), &mint_keypair, &key5.pubkey())
3277 .unwrap();
3278 while !bank1.is_complete() {
3279 bank1.register_tick(&Hash::new_unique());
3280 }
3281
3282 let slot = slot + 1;
3283 let bank2 = Arc::new(Bank::new_from_parent(&bank1, &collector, slot));
3284 bank2
3285 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3286 .unwrap();
3287 while !bank2.is_complete() {
3288 bank2.register_tick(&Hash::new_unique());
3289 }
3290
3291 let slot = slot + 1;
3292 let bank3 = Arc::new(Bank::new_from_parent(&bank2, &collector, slot));
3293 bank3
3294 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3295 .unwrap();
3296 while !bank3.is_complete() {
3297 bank3.register_tick(&Hash::new_unique());
3298 }
3299
3300 let slot = slot + 1;
3301 let bank4 = Arc::new(Bank::new_from_parent(&bank3, &collector, slot));
3302 bank4
3303 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3304 .unwrap();
3305 while !bank4.is_complete() {
3306 bank4.register_tick(&Hash::new_unique());
3307 }
3308
3309 let accounts_dir = tempfile::TempDir::new().unwrap();
3310 let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
3311 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3312 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3313 let snapshot_archive_format = ArchiveFormat::TarGzip;
3314
3315 let full_snapshot_archive_info = bank_to_full_snapshot_archive(
3316 bank_snapshots_dir.path(),
3317 &bank4,
3318 None,
3319 full_snapshot_archives_dir.path(),
3320 incremental_snapshot_archives_dir.path(),
3321 snapshot_archive_format,
3322 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3323 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3324 )
3325 .unwrap();
3326
3327 let (roundtrip_bank, _) = bank_from_snapshot_archives(
3328 &[PathBuf::from(accounts_dir.path())],
3329 bank_snapshots_dir.path(),
3330 &full_snapshot_archive_info,
3331 None,
3332 &genesis_config,
3333 None,
3334 None,
3335 AccountSecondaryIndexes::default(),
3336 false,
3337 None,
3338 AccountShrinkThreshold::default(),
3339 false,
3340 false,
3341 false,
3342 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
3343 None,
3344 )
3345 .unwrap();
3346
3347 assert_eq!(*bank4, roundtrip_bank);
3348 }
3349
3350 #[test]
3360 fn test_roundtrip_bank_to_and_from_incremental_snapshot() {
3361 solana_logger::setup();
3362 let collector = Pubkey::new_unique();
3363 let key1 = Keypair::new();
3364 let key2 = Keypair::new();
3365 let key3 = Keypair::new();
3366 let key4 = Keypair::new();
3367 let key5 = Keypair::new();
3368
3369 let (genesis_config, mint_keypair) = create_genesis_config(sol_to_lamports(1_000_000.));
3370 let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
3371 bank0
3372 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3373 .unwrap();
3374 bank0
3375 .transfer(sol_to_lamports(2.), &mint_keypair, &key2.pubkey())
3376 .unwrap();
3377 bank0
3378 .transfer(sol_to_lamports(3.), &mint_keypair, &key3.pubkey())
3379 .unwrap();
3380 while !bank0.is_complete() {
3381 bank0.register_tick(&Hash::new_unique());
3382 }
3383
3384 let slot = 1;
3385 let bank1 = Arc::new(Bank::new_from_parent(&bank0, &collector, slot));
3386 bank1
3387 .transfer(sol_to_lamports(3.), &mint_keypair, &key3.pubkey())
3388 .unwrap();
3389 bank1
3390 .transfer(sol_to_lamports(4.), &mint_keypair, &key4.pubkey())
3391 .unwrap();
3392 bank1
3393 .transfer(sol_to_lamports(5.), &mint_keypair, &key5.pubkey())
3394 .unwrap();
3395 while !bank1.is_complete() {
3396 bank1.register_tick(&Hash::new_unique());
3397 }
3398
3399 let accounts_dir = tempfile::TempDir::new().unwrap();
3400 let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
3401 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3402 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3403 let snapshot_archive_format = ArchiveFormat::TarZstd;
3404
3405 let full_snapshot_slot = slot;
3406 let full_snapshot_archive_info = bank_to_full_snapshot_archive(
3407 bank_snapshots_dir.path(),
3408 &bank1,
3409 None,
3410 full_snapshot_archives_dir.path(),
3411 incremental_snapshot_archives_dir.path(),
3412 snapshot_archive_format,
3413 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3414 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3415 )
3416 .unwrap();
3417
3418 let slot = slot + 1;
3419 let bank2 = Arc::new(Bank::new_from_parent(&bank1, &collector, slot));
3420 bank2
3421 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3422 .unwrap();
3423 while !bank2.is_complete() {
3424 bank2.register_tick(&Hash::new_unique());
3425 }
3426
3427 let slot = slot + 1;
3428 let bank3 = Arc::new(Bank::new_from_parent(&bank2, &collector, slot));
3429 bank3
3430 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3431 .unwrap();
3432 while !bank3.is_complete() {
3433 bank3.register_tick(&Hash::new_unique());
3434 }
3435
3436 let slot = slot + 1;
3437 let bank4 = Arc::new(Bank::new_from_parent(&bank3, &collector, slot));
3438 bank4
3439 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3440 .unwrap();
3441 while !bank4.is_complete() {
3442 bank4.register_tick(&Hash::new_unique());
3443 }
3444
3445 let incremental_snapshot_archive_info = bank_to_incremental_snapshot_archive(
3446 bank_snapshots_dir.path(),
3447 &bank4,
3448 full_snapshot_slot,
3449 None,
3450 full_snapshot_archives_dir.path(),
3451 incremental_snapshot_archives_dir.path(),
3452 snapshot_archive_format,
3453 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3454 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3455 )
3456 .unwrap();
3457
3458 let (roundtrip_bank, _) = bank_from_snapshot_archives(
3459 &[PathBuf::from(accounts_dir.path())],
3460 bank_snapshots_dir.path(),
3461 &full_snapshot_archive_info,
3462 Some(&incremental_snapshot_archive_info),
3463 &genesis_config,
3464 None,
3465 None,
3466 AccountSecondaryIndexes::default(),
3467 false,
3468 None,
3469 AccountShrinkThreshold::default(),
3470 false,
3471 false,
3472 false,
3473 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
3474 None,
3475 )
3476 .unwrap();
3477
3478 assert_eq!(*bank4, roundtrip_bank);
3479 }
3480
3481 #[test]
3483 fn test_bank_from_latest_snapshot_archives() {
3484 solana_logger::setup();
3485 let collector = Pubkey::new_unique();
3486 let key1 = Keypair::new();
3487 let key2 = Keypair::new();
3488 let key3 = Keypair::new();
3489
3490 let (genesis_config, mint_keypair) = create_genesis_config(sol_to_lamports(1_000_000.));
3491 let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
3492 bank0
3493 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3494 .unwrap();
3495 bank0
3496 .transfer(sol_to_lamports(2.), &mint_keypair, &key2.pubkey())
3497 .unwrap();
3498 bank0
3499 .transfer(sol_to_lamports(3.), &mint_keypair, &key3.pubkey())
3500 .unwrap();
3501 while !bank0.is_complete() {
3502 bank0.register_tick(&Hash::new_unique());
3503 }
3504
3505 let slot = 1;
3506 let bank1 = Arc::new(Bank::new_from_parent(&bank0, &collector, slot));
3507 bank1
3508 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3509 .unwrap();
3510 bank1
3511 .transfer(sol_to_lamports(2.), &mint_keypair, &key2.pubkey())
3512 .unwrap();
3513 bank1
3514 .transfer(sol_to_lamports(3.), &mint_keypair, &key3.pubkey())
3515 .unwrap();
3516 while !bank1.is_complete() {
3517 bank1.register_tick(&Hash::new_unique());
3518 }
3519
3520 let accounts_dir = tempfile::TempDir::new().unwrap();
3521 let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
3522 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3523 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3524 let snapshot_archive_format = ArchiveFormat::Tar;
3525
3526 let full_snapshot_slot = slot;
3527 bank_to_full_snapshot_archive(
3528 &bank_snapshots_dir,
3529 &bank1,
3530 None,
3531 &full_snapshot_archives_dir,
3532 &incremental_snapshot_archives_dir,
3533 snapshot_archive_format,
3534 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3535 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3536 )
3537 .unwrap();
3538
3539 let slot = slot + 1;
3540 let bank2 = Arc::new(Bank::new_from_parent(&bank1, &collector, slot));
3541 bank2
3542 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3543 .unwrap();
3544 while !bank2.is_complete() {
3545 bank2.register_tick(&Hash::new_unique());
3546 }
3547
3548 let slot = slot + 1;
3549 let bank3 = Arc::new(Bank::new_from_parent(&bank2, &collector, slot));
3550 bank3
3551 .transfer(sol_to_lamports(2.), &mint_keypair, &key2.pubkey())
3552 .unwrap();
3553 while !bank3.is_complete() {
3554 bank3.register_tick(&Hash::new_unique());
3555 }
3556
3557 let slot = slot + 1;
3558 let bank4 = Arc::new(Bank::new_from_parent(&bank3, &collector, slot));
3559 bank4
3560 .transfer(sol_to_lamports(3.), &mint_keypair, &key3.pubkey())
3561 .unwrap();
3562 while !bank4.is_complete() {
3563 bank4.register_tick(&Hash::new_unique());
3564 }
3565
3566 bank_to_incremental_snapshot_archive(
3567 &bank_snapshots_dir,
3568 &bank4,
3569 full_snapshot_slot,
3570 None,
3571 &full_snapshot_archives_dir,
3572 &incremental_snapshot_archives_dir,
3573 snapshot_archive_format,
3574 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3575 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3576 )
3577 .unwrap();
3578
3579 let (deserialized_bank, ..) = bank_from_latest_snapshot_archives(
3580 &bank_snapshots_dir,
3581 &full_snapshot_archives_dir,
3582 &incremental_snapshot_archives_dir,
3583 &[accounts_dir.as_ref().to_path_buf()],
3584 &genesis_config,
3585 None,
3586 None,
3587 AccountSecondaryIndexes::default(),
3588 false,
3589 None,
3590 AccountShrinkThreshold::default(),
3591 false,
3592 false,
3593 false,
3594 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
3595 None,
3596 )
3597 .unwrap();
3598
3599 assert_eq!(deserialized_bank, *bank4);
3600 }
3601
3602 #[test]
3625 fn test_incremental_snapshots_handle_zero_lamport_accounts() {
3626 solana_logger::setup();
3627
3628 let collector = Pubkey::new_unique();
3629 let key1 = Keypair::new();
3630 let key2 = Keypair::new();
3631
3632 let accounts_dir = tempfile::TempDir::new().unwrap();
3633 let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
3634 let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3635 let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
3636 let snapshot_archive_format = ArchiveFormat::Tar;
3637
3638 let (genesis_config, mint_keypair) = create_genesis_config(sol_to_lamports(1_000_000.));
3639
3640 let lamports_to_transfer = sol_to_lamports(123_456.);
3641 let bank0 = Arc::new(Bank::new_with_paths_for_tests(
3642 &genesis_config,
3643 vec![accounts_dir.path().to_path_buf()],
3644 None,
3645 None,
3646 AccountSecondaryIndexes::default(),
3647 false,
3648 AccountShrinkThreshold::default(),
3649 false,
3650 None,
3651 ));
3652 bank0
3653 .transfer(lamports_to_transfer, &mint_keypair, &key2.pubkey())
3654 .unwrap();
3655 while !bank0.is_complete() {
3656 bank0.register_tick(&Hash::new_unique());
3657 }
3658
3659 let slot = 1;
3660 let bank1 = Arc::new(Bank::new_from_parent(&bank0, &collector, slot));
3661 bank1
3662 .transfer(lamports_to_transfer, &key2, &key1.pubkey())
3663 .unwrap();
3664 while !bank1.is_complete() {
3665 bank1.register_tick(&Hash::new_unique());
3666 }
3667
3668 let full_snapshot_slot = slot;
3669 let full_snapshot_archive_info = bank_to_full_snapshot_archive(
3670 bank_snapshots_dir.path(),
3671 &bank1,
3672 None,
3673 full_snapshot_archives_dir.path(),
3674 incremental_snapshot_archives_dir.path(),
3675 snapshot_archive_format,
3676 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3677 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3678 )
3679 .unwrap();
3680
3681 let slot = slot + 1;
3682 let bank2 = Arc::new(Bank::new_from_parent(&bank1, &collector, slot));
3683 let blockhash = bank2.last_blockhash();
3684 let tx = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer(
3685 &key1,
3686 &key2.pubkey(),
3687 lamports_to_transfer,
3688 blockhash,
3689 ));
3690 let fee = bank2.get_fee_for_message(tx.message()).unwrap();
3691 let tx = system_transaction::transfer(
3692 &key1,
3693 &key2.pubkey(),
3694 lamports_to_transfer - fee,
3695 blockhash,
3696 );
3697 bank2.process_transaction(&tx).unwrap();
3698 assert_eq!(
3699 bank2.get_balance(&key1.pubkey()),
3700 0,
3701 "Ensure Account1's balance is zero"
3702 );
3703 while !bank2.is_complete() {
3704 bank2.register_tick(&Hash::new_unique());
3705 }
3706
3707 let incremental_snapshot_archive_info = bank_to_incremental_snapshot_archive(
3710 bank_snapshots_dir.path(),
3711 &bank2,
3712 full_snapshot_slot,
3713 None,
3714 full_snapshot_archives_dir.path(),
3715 incremental_snapshot_archives_dir.path(),
3716 snapshot_archive_format,
3717 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3718 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3719 )
3720 .unwrap();
3721 let (deserialized_bank, _) = bank_from_snapshot_archives(
3722 &[accounts_dir.path().to_path_buf()],
3723 bank_snapshots_dir.path(),
3724 &full_snapshot_archive_info,
3725 Some(&incremental_snapshot_archive_info),
3726 &genesis_config,
3727 None,
3728 None,
3729 AccountSecondaryIndexes::default(),
3730 false,
3731 None,
3732 AccountShrinkThreshold::default(),
3733 false,
3734 false,
3735 false,
3736 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
3737 None,
3738 )
3739 .unwrap();
3740 assert_eq!(
3741 deserialized_bank, *bank2,
3742 "Ensure rebuilding from an incremental snapshot works"
3743 );
3744
3745 let slot = slot + 1;
3746 let bank3 = Arc::new(Bank::new_from_parent(&bank2, &collector, slot));
3747 bank3
3749 .transfer(lamports_to_transfer, &mint_keypair, &key2.pubkey())
3750 .unwrap();
3751 while !bank3.is_complete() {
3752 bank3.register_tick(&Hash::new_unique());
3753 }
3754
3755 let slot = slot + 1;
3756 let bank4 = Arc::new(Bank::new_from_parent(&bank3, &collector, slot));
3757 while !bank4.is_complete() {
3758 bank4.register_tick(&Hash::new_unique());
3759 }
3760
3761 bank4.squash();
3763 bank4.clean_accounts(true, false, Some(full_snapshot_slot));
3764 assert!(
3765 bank4.get_account_modified_slot(&key1.pubkey()).is_none(),
3766 "Ensure Account1 has been cleaned and purged from AccountsDb"
3767 );
3768
3769 let incremental_snapshot_archive_info = bank_to_incremental_snapshot_archive(
3772 bank_snapshots_dir.path(),
3773 &bank4,
3774 full_snapshot_slot,
3775 None,
3776 full_snapshot_archives_dir.path(),
3777 incremental_snapshot_archives_dir.path(),
3778 snapshot_archive_format,
3779 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3780 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3781 )
3782 .unwrap();
3783
3784 let (deserialized_bank, _) = bank_from_snapshot_archives(
3785 &[accounts_dir.path().to_path_buf()],
3786 bank_snapshots_dir.path(),
3787 &full_snapshot_archive_info,
3788 Some(&incremental_snapshot_archive_info),
3789 &genesis_config,
3790 None,
3791 None,
3792 AccountSecondaryIndexes::default(),
3793 false,
3794 None,
3795 AccountShrinkThreshold::default(),
3796 false,
3797 false,
3798 false,
3799 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
3800 None,
3801 )
3802 .unwrap();
3803 assert_eq!(
3804 deserialized_bank, *bank4,
3805 "Ensure rebuilding from an incremental snapshot works",
3806 );
3807 assert!(
3808 deserialized_bank
3809 .get_account_modified_slot(&key1.pubkey())
3810 .is_none(),
3811 "Ensure Account1 has not been brought back from the dead"
3812 );
3813 }
3814
3815 #[test]
3816 fn test_bank_fields_from_snapshot() {
3817 solana_logger::setup();
3818 let collector = Pubkey::new_unique();
3819 let key1 = Keypair::new();
3820
3821 let (genesis_config, mint_keypair) = create_genesis_config(sol_to_lamports(1_000_000.));
3822 let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
3823 while !bank0.is_complete() {
3824 bank0.register_tick(&Hash::new_unique());
3825 }
3826
3827 let slot = 1;
3828 let bank1 = Arc::new(Bank::new_from_parent(&bank0, &collector, slot));
3829 while !bank1.is_complete() {
3830 bank1.register_tick(&Hash::new_unique());
3831 }
3832
3833 let all_snapshots_dir = tempfile::TempDir::new().unwrap();
3834 let snapshot_archive_format = ArchiveFormat::Tar;
3835
3836 let full_snapshot_slot = slot;
3837 bank_to_full_snapshot_archive(
3838 &all_snapshots_dir,
3839 &bank1,
3840 None,
3841 &all_snapshots_dir,
3842 &all_snapshots_dir,
3843 snapshot_archive_format,
3844 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3845 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3846 )
3847 .unwrap();
3848
3849 let slot = slot + 1;
3850 let bank2 = Arc::new(Bank::new_from_parent(&bank1, &collector, slot));
3851 bank2
3852 .transfer(sol_to_lamports(1.), &mint_keypair, &key1.pubkey())
3853 .unwrap();
3854 while !bank2.is_complete() {
3855 bank2.register_tick(&Hash::new_unique());
3856 }
3857
3858 bank_to_incremental_snapshot_archive(
3859 &all_snapshots_dir,
3860 &bank2,
3861 full_snapshot_slot,
3862 None,
3863 &all_snapshots_dir,
3864 &all_snapshots_dir,
3865 snapshot_archive_format,
3866 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3867 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3868 )
3869 .unwrap();
3870
3871 let bank_fields = bank_fields_from_snapshot_archives(
3872 &all_snapshots_dir,
3873 &all_snapshots_dir,
3874 &all_snapshots_dir,
3875 )
3876 .unwrap();
3877 assert_eq!(bank_fields.slot, bank2.slot());
3878 assert_eq!(bank_fields.parent_slot, bank2.parent_slot());
3879 }
3880
3881 #[test]
3897 fn test_can_submit_accounts_package() {
3898 fn new_accounts_package_with(snapshot_type: Option<SnapshotType>) -> AccountsPackage {
3900 AccountsPackage {
3901 slot: Slot::default(),
3902 block_height: Slot::default(),
3903 slot_deltas: Vec::default(),
3904 snapshot_links: TempDir::new().unwrap(),
3905 snapshot_storages: SnapshotStorages::default(),
3906 archive_format: ArchiveFormat::Tar,
3907 snapshot_version: SnapshotVersion::default(),
3908 full_snapshot_archives_dir: PathBuf::default(),
3909 incremental_snapshot_archives_dir: PathBuf::default(),
3910 expected_capitalization: u64::default(),
3911 accounts_hash_for_testing: None,
3912 cluster_type: solana_sdk::genesis_config::ClusterType::Development,
3913 snapshot_type,
3914 accounts: Arc::new(crate::accounts::Accounts::default_for_tests()),
3915 epoch_schedule: solana_sdk::epoch_schedule::EpochSchedule::default(),
3916 rent_collector: crate::rent_collector::RentCollector::default(),
3917 }
3918 }
3919
3920 let pending_accounts_package = PendingAccountsPackage::default();
3921 for (new_snapshot_type, old_snapshot_type, expected_result) in [
3922 (
3923 Some(SnapshotType::FullSnapshot),
3924 Some(SnapshotType::FullSnapshot),
3925 true,
3926 ),
3927 (
3928 Some(SnapshotType::FullSnapshot),
3929 Some(SnapshotType::IncrementalSnapshot(0)),
3930 true,
3931 ),
3932 (Some(SnapshotType::FullSnapshot), None, true),
3933 (
3934 Some(SnapshotType::IncrementalSnapshot(0)),
3935 Some(SnapshotType::FullSnapshot),
3936 false,
3937 ),
3938 (
3939 Some(SnapshotType::IncrementalSnapshot(0)),
3940 Some(SnapshotType::IncrementalSnapshot(0)),
3941 true,
3942 ),
3943 (Some(SnapshotType::IncrementalSnapshot(0)), None, true),
3944 (None, Some(SnapshotType::FullSnapshot), false),
3945 (None, Some(SnapshotType::IncrementalSnapshot(0)), false),
3946 (None, None, true),
3947 ] {
3948 let new_accounts_package = new_accounts_package_with(new_snapshot_type);
3949 let old_accounts_package = new_accounts_package_with(old_snapshot_type);
3950 pending_accounts_package
3951 .lock()
3952 .unwrap()
3953 .replace(old_accounts_package);
3954
3955 let actual_result =
3956 can_submit_accounts_package(&new_accounts_package, &pending_accounts_package);
3957 assert_eq!(expected_result, actual_result);
3958 }
3959 }
3960
3961 #[test]
3962 fn test_verify_slot_deltas_structural_good() {
3963 let slot_deltas = vec![
3965 (222, true, Status::default()),
3966 (333, true, Status::default()),
3967 (111, true, Status::default()),
3968 ];
3969
3970 let bank_slot = 333;
3971 let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot);
3972 assert_eq!(
3973 result,
3974 Ok(VerifySlotDeltasStructuralInfo {
3975 slots: HashSet::from([111, 222, 333])
3976 })
3977 );
3978 }
3979
3980 #[test]
3981 fn test_verify_slot_deltas_structural_bad_too_many_entries() {
3982 let bank_slot = status_cache::MAX_CACHE_ENTRIES as Slot + 1;
3983 let slot_deltas: Vec<_> = (0..bank_slot)
3984 .map(|slot| (slot, true, Status::default()))
3985 .collect();
3986
3987 let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot);
3988 assert_eq!(
3989 result,
3990 Err(VerifySlotDeltasError::TooManyEntries(
3991 status_cache::MAX_CACHE_ENTRIES + 1,
3992 status_cache::MAX_CACHE_ENTRIES
3993 )),
3994 );
3995 }
3996
3997 #[test]
3998 fn test_verify_slot_deltas_structural_bad_slot_not_root() {
3999 let slot_deltas = vec![
4000 (111, true, Status::default()),
4001 (222, false, Status::default()), (333, true, Status::default()),
4003 ];
4004
4005 let bank_slot = 333;
4006 let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot);
4007 assert_eq!(result, Err(VerifySlotDeltasError::SlotIsNotRoot(222)));
4008 }
4009
4010 #[test]
4011 fn test_verify_slot_deltas_structural_bad_slot_greater_than_bank() {
4012 let slot_deltas = vec![
4013 (222, true, Status::default()),
4014 (111, true, Status::default()),
4015 (555, true, Status::default()), ];
4017
4018 let bank_slot = 444;
4019 let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot);
4020 assert_eq!(
4021 result,
4022 Err(VerifySlotDeltasError::SlotGreaterThanMaxRoot(
4023 555, bank_slot
4024 )),
4025 );
4026 }
4027
4028 #[test]
4029 fn test_verify_slot_deltas_structural_bad_slot_has_multiple_entries() {
4030 let slot_deltas = vec![
4031 (111, true, Status::default()),
4032 (222, true, Status::default()),
4033 (111, true, Status::default()), ];
4035
4036 let bank_slot = 222;
4037 let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot);
4038 assert_eq!(
4039 result,
4040 Err(VerifySlotDeltasError::SlotHasMultipleEntries(111)),
4041 );
4042 }
4043
4044 #[test]
4045 fn test_verify_slot_deltas_with_history_good() {
4046 let mut slots_from_slot_deltas = HashSet::default();
4047 let mut slot_history = SlotHistory::default();
4048 for slot in [0, 111, 222, 333, 444] {
4050 slots_from_slot_deltas.insert(slot);
4051 slot_history.add(slot);
4052 }
4053
4054 let bank_slot = 444;
4055 let result =
4056 verify_slot_deltas_with_history(&slots_from_slot_deltas, &slot_history, bank_slot);
4057 assert_eq!(result, Ok(()));
4058 }
4059
4060 #[test]
4061 fn test_verify_slot_deltas_with_history_bad_slot_history() {
4062 let bank_slot = 444;
4063 let result = verify_slot_deltas_with_history(
4064 &HashSet::default(),
4065 &SlotHistory::default(), bank_slot,
4067 );
4068 assert_eq!(result, Err(VerifySlotDeltasError::BadSlotHistory));
4069 }
4070
4071 #[test]
4072 fn test_verify_slot_deltas_with_history_bad_slot_not_in_history() {
4073 let slots_from_slot_deltas = HashSet::from([
4074 0, 444, 222,
4076 ]);
4077 let mut slot_history = SlotHistory::default();
4078 slot_history.add(444); let bank_slot = 444;
4081 let result =
4082 verify_slot_deltas_with_history(&slots_from_slot_deltas, &slot_history, bank_slot);
4083
4084 assert_eq!(
4085 result,
4086 Err(VerifySlotDeltasError::SlotNotFoundInHistory(222)),
4087 );
4088 }
4089
4090 #[test]
4091 fn test_verify_slot_deltas_with_history_bad_slot_not_in_deltas() {
4092 let slots_from_slot_deltas = HashSet::from([
4093 0, 444, 222,
4095 ]);
4097 let mut slot_history = SlotHistory::default();
4098 slot_history.add(222);
4099 slot_history.add(333);
4100 slot_history.add(444);
4101
4102 let bank_slot = 444;
4103 let result =
4104 verify_slot_deltas_with_history(&slots_from_slot_deltas, &slot_history, bank_slot);
4105
4106 assert_eq!(
4107 result,
4108 Err(VerifySlotDeltasError::SlotNotFoundInDeltas(333)),
4109 );
4110 }
4111}