1use {
2 crate::{
3 accounts_db::{AccountShrinkThreshold, AccountsDbConfig},
4 accounts_index::AccountSecondaryIndexes,
5 bank::{Bank, BankSlotDelta},
6 builtins::Builtins,
7 hardened_unpack::{unpack_snapshot, ParallelSelector, UnpackError, UnpackedAppendVecMap},
8 serde_snapshot::{
9 bank_from_streams, bank_to_stream, SerdeStyle, SnapshotStorage, SnapshotStorages,
10 SnapshotStreams,
11 },
12 shared_buffer_reader::{SharedBuffer, SharedBufferReader},
13 snapshot_archive_info::{
14 FullSnapshotArchiveInfo, IncrementalSnapshotArchiveInfo, SnapshotArchiveInfoGetter,
15 },
16 snapshot_package::{
17 AccountsPackage, AccountsPackageSendError, AccountsPackageSender, SnapshotPackage,
18 SnapshotType,
19 },
20 },
21 bincode::{config::Options, serialize_into},
22 bzip2::bufread::BzDecoder,
23 flate2::read::GzDecoder,
24 lazy_static::lazy_static,
25 log::*,
26 rayon::prelude::*,
27 regex::Regex,
28 gemachain_measure::measure::Measure,
29 gemachain_sdk::{clock::Slot, genesis_config::GenesisConfig, hash::Hash, pubkey::Pubkey},
30 std::{
31 cmp::{max, Ordering},
32 collections::HashSet,
33 fmt,
34 fs::{self, File},
35 io::{BufReader, BufWriter, Error as IoError, ErrorKind, Read, Seek, Write},
36 path::{Path, PathBuf},
37 process::ExitStatus,
38 str::FromStr,
39 sync::Arc,
40 },
41 tar::{self, Archive},
42 tempfile::TempDir,
43 thiserror::Error,
44};
45
46pub const SNAPSHOT_STATUS_CACHE_FILE_NAME: &str = "status_cache";
47pub const DEFAULT_FULL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS: Slot = 100_000;
48pub const DEFAULT_INCREMENTAL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS: Slot = 100;
49const MAX_SNAPSHOT_DATA_FILE_SIZE: u64 = 32 * 1024 * 1024 * 1024; const VERSION_STRING_V1_2_0: &str = "1.2.0";
51const DEFAULT_SNAPSHOT_VERSION: SnapshotVersion = SnapshotVersion::V1_2_0;
52pub(crate) const TMP_BANK_SNAPSHOT_PREFIX: &str = "tmp-bank-snapshot-";
53pub const TMP_SNAPSHOT_ARCHIVE_PREFIX: &str = "tmp-snapshot-archive-";
54pub const MAX_BANK_SNAPSHOTS_TO_RETAIN: usize = 8; pub const DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN: usize = 2;
56pub const DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN: usize = 4;
57pub const FULL_SNAPSHOT_ARCHIVE_FILENAME_REGEX: &str = r"^snapshot-(?P<slot>[[:digit:]]+)-(?P<hash>[[:alnum:]]+)\.(?P<ext>tar|tar\.bz2|tar\.zst|tar\.gz)$";
58pub 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)$";
59
60#[derive(Copy, Clone, Eq, PartialEq, Debug)]
61pub enum SnapshotVersion {
62 V1_2_0,
63}
64
65impl Default for SnapshotVersion {
66 fn default() -> Self {
67 DEFAULT_SNAPSHOT_VERSION
68 }
69}
70
71impl fmt::Display for SnapshotVersion {
72 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73 f.write_str(From::from(*self))
74 }
75}
76
77impl From<SnapshotVersion> for &'static str {
78 fn from(snapshot_version: SnapshotVersion) -> &'static str {
79 match snapshot_version {
80 SnapshotVersion::V1_2_0 => VERSION_STRING_V1_2_0,
81 }
82 }
83}
84
85impl FromStr for SnapshotVersion {
86 type Err = &'static str;
87
88 fn from_str(version_string: &str) -> std::result::Result<Self, Self::Err> {
89 let version_string = if version_string
91 .get(..1)
92 .map_or(false, |s| s.eq_ignore_ascii_case("v"))
93 {
94 &version_string[1..]
95 } else {
96 version_string
97 };
98 match version_string {
99 VERSION_STRING_V1_2_0 => Ok(SnapshotVersion::V1_2_0),
100 _ => Err("unsupported snapshot version"),
101 }
102 }
103}
104
105impl SnapshotVersion {
106 pub fn as_str(self) -> &'static str {
107 <&str as From<Self>>::from(self)
108 }
109
110 fn maybe_from_string(version_string: &str) -> Option<SnapshotVersion> {
111 version_string.parse::<Self>().ok()
112 }
113}
114
115#[derive(Copy, Clone, Debug, Eq, PartialEq)]
117pub enum ArchiveFormat {
118 TarBzip2,
119 TarGzip,
120 TarZstd,
121 Tar,
122}
123
124#[derive(PartialEq, Eq, Debug)]
126pub struct BankSnapshotInfo {
127 pub slot: Slot,
128 pub snapshot_path: PathBuf,
129}
130
131impl PartialOrd for BankSnapshotInfo {
132 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
133 Some(self.cmp(other))
134 }
135}
136
137impl Ord for BankSnapshotInfo {
139 fn cmp(&self, other: &Self) -> Ordering {
140 self.slot.cmp(&other.slot)
141 }
142}
143
144#[derive(Debug)]
147struct SnapshotRootPaths {
148 full_snapshot_root_file_path: PathBuf,
149 incremental_snapshot_root_file_path: Option<PathBuf>,
150}
151
152#[derive(Debug)]
154struct UnarchivedSnapshot {
155 unpack_dir: TempDir,
156 unpacked_append_vec_map: UnpackedAppendVecMap,
157 unpacked_snapshots_dir_and_version: UnpackedSnapshotsDirAndVersion,
158 measure_untar: Measure,
159}
160
161#[derive(Debug)]
163struct UnpackedSnapshotsDirAndVersion {
164 unpacked_snapshots_dir: PathBuf,
165 snapshot_version: String,
166}
167
168#[derive(Error, Debug)]
169pub enum SnapshotError {
170 #[error("I/O error: {0}")]
171 Io(#[from] std::io::Error),
172
173 #[error("serialization error: {0}")]
174 Serialize(#[from] bincode::Error),
175
176 #[error("archive generation failure {0}")]
177 ArchiveGenerationFailure(ExitStatus),
178
179 #[error("storage path symlink is invalid")]
180 StoragePathSymlinkInvalid,
181
182 #[error("Unpack error: {0}")]
183 UnpackError(#[from] UnpackError),
184
185 #[error("accounts package send error")]
186 AccountsPackageSendError(#[from] AccountsPackageSendError),
187
188 #[error("source({1}) - I/O error: {0}")]
189 IoWithSource(std::io::Error, &'static str),
190
191 #[error("could not get file name from path: {}", .0.display())]
192 PathToFileNameError(PathBuf),
193
194 #[error("could not get str from file name: {}", .0.display())]
195 FileNameToStrError(PathBuf),
196
197 #[error("could not parse snapshot archive's file name: {0}")]
198 ParseSnapshotArchiveFileNameError(String),
199
200 #[error("snapshots are incompatible: full snapshot slot ({0}) and incremental snapshot base slot ({1}) do not match")]
201 MismatchedBaseSlot(Slot, Slot),
202
203 #[error("no snapshot archives to load from")]
204 NoSnapshotArchives,
205
206 #[error("snapshot has mismatch: deserialized bank: {:?}, snapshot archive info: {:?}", .0, .1)]
207 MismatchedSlotHash((Slot, Hash), (Slot, Hash)),
208}
209pub type Result<T> = std::result::Result<T, SnapshotError>;
210
211fn get_archive_ext(archive_format: ArchiveFormat) -> &'static str {
212 match archive_format {
213 ArchiveFormat::TarBzip2 => "tar.bz2",
214 ArchiveFormat::TarGzip => "tar.gz",
215 ArchiveFormat::TarZstd => "tar.zst",
216 ArchiveFormat::Tar => "tar",
217 }
218}
219
220pub fn remove_tmp_snapshot_archives(snapshot_archives_dir: impl AsRef<Path>) {
223 if let Ok(entries) = fs::read_dir(snapshot_archives_dir) {
224 for entry in entries.filter_map(|entry| entry.ok()) {
225 let file_name = entry
226 .file_name()
227 .into_string()
228 .unwrap_or_else(|_| String::new());
229 if file_name.starts_with(TMP_SNAPSHOT_ARCHIVE_PREFIX) {
230 if entry.path().is_file() {
231 fs::remove_file(entry.path())
232 } else {
233 fs::remove_dir_all(entry.path())
234 }
235 .unwrap_or_else(|err| {
236 warn!("Failed to remove {}: {}", entry.path().display(), err)
237 });
238 }
239 }
240 }
241}
242
243pub fn archive_snapshot_package(
245 snapshot_package: &SnapshotPackage,
246 maximum_full_snapshot_archives_to_retain: usize,
247 maximum_incremental_snapshot_archives_to_retain: usize,
248) -> Result<()> {
249 info!(
250 "Generating snapshot archive for slot {}",
251 snapshot_package.slot()
252 );
253
254 serialize_status_cache(
255 snapshot_package.slot(),
256 &snapshot_package.slot_deltas,
257 &snapshot_package
258 .snapshot_links
259 .path()
260 .join(SNAPSHOT_STATUS_CACHE_FILE_NAME),
261 )?;
262
263 let mut timer = Measure::start("snapshot_package-package_snapshots");
264 let tar_dir = snapshot_package
265 .path()
266 .parent()
267 .expect("Tar output path is invalid");
268
269 fs::create_dir_all(tar_dir)
270 .map_err(|e| SnapshotError::IoWithSource(e, "create archive path"))?;
271
272 let staging_dir_prefix = TMP_SNAPSHOT_ARCHIVE_PREFIX;
274 let staging_dir = tempfile::Builder::new()
275 .prefix(&format!(
276 "{}{}-",
277 staging_dir_prefix,
278 snapshot_package.slot()
279 ))
280 .tempdir_in(tar_dir)
281 .map_err(|e| SnapshotError::IoWithSource(e, "create archive tempdir"))?;
282
283 let staging_accounts_dir = staging_dir.path().join("accounts");
284 let staging_snapshots_dir = staging_dir.path().join("snapshots");
285 let staging_version_file = staging_dir.path().join("version");
286 fs::create_dir_all(&staging_accounts_dir)
287 .map_err(|e| SnapshotError::IoWithSource(e, "create staging path"))?;
288
289 symlink::symlink_dir(
291 snapshot_package.snapshot_links.path(),
292 &staging_snapshots_dir,
293 )
294 .map_err(|e| SnapshotError::IoWithSource(e, "create staging symlinks"))?;
295
296 for storage in snapshot_package.snapshot_storages.iter().flatten() {
298 storage.flush()?;
299 let storage_path = storage.get_path();
300 let output_path = staging_accounts_dir.join(crate::append_vec::AppendVec::file_name(
301 storage.slot(),
302 storage.append_vec_id(),
303 ));
304
305 let storage_path =
308 fs::canonicalize(storage_path).expect("Could not get absolute path for accounts");
309 symlink::symlink_file(storage_path, &output_path)
310 .map_err(|e| SnapshotError::IoWithSource(e, "create storage symlink"))?;
311 if !output_path.is_file() {
312 return Err(SnapshotError::StoragePathSymlinkInvalid);
313 }
314 }
315
316 {
318 let mut f = fs::File::create(staging_version_file)
319 .map_err(|e| SnapshotError::IoWithSource(e, "create version file"))?;
320 f.write_all(snapshot_package.snapshot_version.as_str().as_bytes())
321 .map_err(|e| SnapshotError::IoWithSource(e, "write version file"))?;
322 }
323
324 let file_ext = get_archive_ext(snapshot_package.archive_format());
325
326 let archive_path = tar_dir.join(format!(
328 "{}{}.{}",
329 staging_dir_prefix,
330 snapshot_package.slot(),
331 file_ext
332 ));
333
334 {
335 let mut archive_file = fs::File::create(&archive_path)?;
336
337 let do_archive_files = |encoder: &mut dyn Write| -> Result<()> {
338 let mut archive = tar::Builder::new(encoder);
339 for dir in ["accounts", "snapshots"] {
340 archive.append_dir_all(dir, staging_dir.as_ref().join(dir))?;
341 }
342 archive.append_path_with_name(staging_dir.as_ref().join("version"), "version")?;
343 archive.into_inner()?;
344 Ok(())
345 };
346
347 match snapshot_package.archive_format() {
348 ArchiveFormat::TarBzip2 => {
349 let mut encoder =
350 bzip2::write::BzEncoder::new(archive_file, bzip2::Compression::best());
351 do_archive_files(&mut encoder)?;
352 encoder.finish()?;
353 }
354 ArchiveFormat::TarGzip => {
355 let mut encoder =
356 flate2::write::GzEncoder::new(archive_file, flate2::Compression::default());
357 do_archive_files(&mut encoder)?;
358 encoder.finish()?;
359 }
360 ArchiveFormat::TarZstd => {
361 let mut encoder = zstd::stream::Encoder::new(archive_file, 0)?;
362 do_archive_files(&mut encoder)?;
363 encoder.finish()?;
364 }
365 ArchiveFormat::Tar => {
366 do_archive_files(&mut archive_file)?;
367 }
368 };
369 }
370
371 let metadata = fs::metadata(&archive_path)
373 .map_err(|e| SnapshotError::IoWithSource(e, "archive path stat"))?;
374 fs::rename(&archive_path, &snapshot_package.path())
375 .map_err(|e| SnapshotError::IoWithSource(e, "archive path rename"))?;
376
377 purge_old_snapshot_archives(
378 tar_dir,
379 maximum_full_snapshot_archives_to_retain,
380 maximum_incremental_snapshot_archives_to_retain,
381 );
382
383 timer.stop();
384 info!(
385 "Successfully created {:?}. slot: {}, elapsed ms: {}, size={}",
386 snapshot_package.path(),
387 snapshot_package.slot(),
388 timer.as_ms(),
389 metadata.len()
390 );
391 datapoint_info!(
392 "snapshot-package",
393 ("slot", snapshot_package.slot(), i64),
394 ("duration_ms", timer.as_ms(), i64),
395 ("size", metadata.len(), i64)
396 );
397 Ok(())
398}
399
400pub fn get_bank_snapshots<P>(bank_snapshots_dir: P) -> Vec<BankSnapshotInfo>
402where
403 P: AsRef<Path>,
404{
405 match fs::read_dir(&bank_snapshots_dir) {
406 Ok(paths) => paths
407 .filter_map(|entry| {
408 entry.ok().and_then(|e| {
409 e.path()
410 .file_name()
411 .and_then(|n| n.to_str().map(|s| s.parse::<Slot>().ok()))
412 .unwrap_or(None)
413 })
414 })
415 .map(|slot| {
416 let snapshot_file_name = get_snapshot_file_name(slot);
417 let snapshot_path = bank_snapshots_dir
420 .as_ref()
421 .join(&snapshot_file_name)
422 .join(snapshot_file_name);
423 BankSnapshotInfo {
424 slot,
425 snapshot_path,
426 }
427 })
428 .collect::<Vec<BankSnapshotInfo>>(),
429 Err(err) => {
430 info!(
431 "Unable to read snapshots directory {}: {}",
432 bank_snapshots_dir.as_ref().display(),
433 err
434 );
435 vec![]
436 }
437 }
438}
439
440pub fn get_highest_bank_snapshot_info<P>(bank_snapshots_dir: P) -> Option<BankSnapshotInfo>
442where
443 P: AsRef<Path>,
444{
445 let mut bank_snapshot_infos = get_bank_snapshots(bank_snapshots_dir);
446 bank_snapshot_infos.sort_unstable();
447 bank_snapshot_infos.into_iter().rev().next()
448}
449
450pub fn serialize_snapshot_data_file<F>(data_file_path: &Path, serializer: F) -> Result<u64>
451where
452 F: FnOnce(&mut BufWriter<File>) -> Result<()>,
453{
454 serialize_snapshot_data_file_capped::<F>(
455 data_file_path,
456 MAX_SNAPSHOT_DATA_FILE_SIZE,
457 serializer,
458 )
459}
460
461pub fn deserialize_snapshot_data_file<T: Sized>(
462 data_file_path: &Path,
463 deserializer: impl FnOnce(&mut BufReader<File>) -> Result<T>,
464) -> Result<T> {
465 let wrapped_deserializer = move |streams: &mut SnapshotStreams<File>| -> Result<T> {
466 deserializer(&mut streams.full_snapshot_stream)
467 };
468
469 let wrapped_data_file_path = SnapshotRootPaths {
470 full_snapshot_root_file_path: data_file_path.to_path_buf(),
471 incremental_snapshot_root_file_path: None,
472 };
473
474 deserialize_snapshot_data_files_capped(
475 &wrapped_data_file_path,
476 MAX_SNAPSHOT_DATA_FILE_SIZE,
477 wrapped_deserializer,
478 )
479}
480
481fn deserialize_snapshot_data_files<T: Sized>(
482 snapshot_root_paths: &SnapshotRootPaths,
483 deserializer: impl FnOnce(&mut SnapshotStreams<File>) -> Result<T>,
484) -> Result<T> {
485 deserialize_snapshot_data_files_capped(
486 snapshot_root_paths,
487 MAX_SNAPSHOT_DATA_FILE_SIZE,
488 deserializer,
489 )
490}
491
492fn serialize_snapshot_data_file_capped<F>(
493 data_file_path: &Path,
494 maximum_file_size: u64,
495 serializer: F,
496) -> Result<u64>
497where
498 F: FnOnce(&mut BufWriter<File>) -> Result<()>,
499{
500 let data_file = File::create(data_file_path)?;
501 let mut data_file_stream = BufWriter::new(data_file);
502 serializer(&mut data_file_stream)?;
503 data_file_stream.flush()?;
504
505 let consumed_size = data_file_stream.stream_position()?;
506 if consumed_size > maximum_file_size {
507 let error_message = format!(
508 "too large snapshot data file to serialize: {:?} has {} bytes",
509 data_file_path, consumed_size
510 );
511 return Err(get_io_error(&error_message));
512 }
513 Ok(consumed_size)
514}
515
516fn deserialize_snapshot_data_files_capped<T: Sized>(
517 snapshot_root_paths: &SnapshotRootPaths,
518 maximum_file_size: u64,
519 deserializer: impl FnOnce(&mut SnapshotStreams<File>) -> Result<T>,
520) -> Result<T> {
521 let (full_snapshot_file_size, mut full_snapshot_data_file_stream) =
522 create_snapshot_data_file_stream(
523 &snapshot_root_paths.full_snapshot_root_file_path,
524 maximum_file_size,
525 )?;
526
527 let (incremental_snapshot_file_size, mut incremental_snapshot_data_file_stream) =
528 if let Some(ref incremental_snapshot_root_file_path) =
529 snapshot_root_paths.incremental_snapshot_root_file_path
530 {
531 let (incremental_snapshot_file_size, incremental_snapshot_data_file_stream) =
532 create_snapshot_data_file_stream(
533 incremental_snapshot_root_file_path,
534 maximum_file_size,
535 )?;
536 (
537 Some(incremental_snapshot_file_size),
538 Some(incremental_snapshot_data_file_stream),
539 )
540 } else {
541 (None, None)
542 };
543
544 let mut snapshot_streams = SnapshotStreams {
545 full_snapshot_stream: &mut full_snapshot_data_file_stream,
546 incremental_snapshot_stream: incremental_snapshot_data_file_stream.as_mut(),
547 };
548 let ret = deserializer(&mut snapshot_streams)?;
549
550 check_deserialize_file_consumed(
551 full_snapshot_file_size,
552 &snapshot_root_paths.full_snapshot_root_file_path,
553 &mut full_snapshot_data_file_stream,
554 )?;
555
556 if let Some(ref incremental_snapshot_root_file_path) =
557 snapshot_root_paths.incremental_snapshot_root_file_path
558 {
559 check_deserialize_file_consumed(
560 incremental_snapshot_file_size.unwrap(),
561 incremental_snapshot_root_file_path,
562 incremental_snapshot_data_file_stream.as_mut().unwrap(),
563 )?;
564 }
565
566 Ok(ret)
567}
568
569fn create_snapshot_data_file_stream<P>(
572 snapshot_root_file_path: P,
573 maximum_file_size: u64,
574) -> Result<(u64, BufReader<File>)>
575where
576 P: AsRef<Path>,
577{
578 let snapshot_file_size = fs::metadata(&snapshot_root_file_path)?.len();
579
580 if snapshot_file_size > maximum_file_size {
581 let error_message =
582 format!(
583 "too large snapshot data file to deserialize: {} has {} bytes (max size is {} bytes)",
584 snapshot_root_file_path.as_ref().display(), snapshot_file_size, maximum_file_size
585 );
586 return Err(get_io_error(&error_message));
587 }
588
589 let snapshot_data_file = File::open(&snapshot_root_file_path)?;
590 let snapshot_data_file_stream = BufReader::new(snapshot_data_file);
591
592 Ok((snapshot_file_size, snapshot_data_file_stream))
593}
594
595fn check_deserialize_file_consumed<P>(
598 file_size: u64,
599 file_path: P,
600 file_stream: &mut BufReader<File>,
601) -> Result<()>
602where
603 P: AsRef<Path>,
604{
605 let consumed_size = file_stream.stream_position()?;
606
607 if consumed_size != file_size {
608 let error_message =
609 format!(
610 "invalid snapshot data file: {} has {} bytes, however consumed {} bytes to deserialize",
611 file_path.as_ref().display(), file_size, consumed_size
612 );
613 return Err(get_io_error(&error_message));
614 }
615
616 Ok(())
617}
618
619pub fn add_bank_snapshot<P: AsRef<Path>>(
621 bank_snapshots_dir: P,
622 bank: &Bank,
623 snapshot_storages: &[SnapshotStorage],
624 snapshot_version: SnapshotVersion,
625) -> Result<BankSnapshotInfo> {
626 let slot = bank.slot();
627 let bank_snapshots_dir = get_bank_snapshots_dir(bank_snapshots_dir, slot);
629 fs::create_dir_all(&bank_snapshots_dir)?;
630
631 let snapshot_bank_file_path = bank_snapshots_dir.join(get_snapshot_file_name(slot));
633 info!(
634 "Creating snapshot for slot {}, path: {:?}",
635 slot, snapshot_bank_file_path,
636 );
637
638 let mut bank_serialize = Measure::start("bank-serialize-ms");
639 let bank_snapshot_serializer = move |stream: &mut BufWriter<File>| -> Result<()> {
640 let serde_style = match snapshot_version {
641 SnapshotVersion::V1_2_0 => SerdeStyle::Newer,
642 };
643 bank_to_stream(serde_style, stream.by_ref(), bank, snapshot_storages)?;
644 Ok(())
645 };
646 let consumed_size =
647 serialize_snapshot_data_file(&snapshot_bank_file_path, bank_snapshot_serializer)?;
648 bank_serialize.stop();
649
650 datapoint_info!(
652 "snapshot-bank-file",
653 ("slot", slot, i64),
654 ("size", consumed_size, i64)
655 );
656
657 inc_new_counter_info!("bank-serialize-ms", bank_serialize.as_ms() as usize);
658
659 info!(
660 "{} for slot {} at {:?}",
661 bank_serialize, slot, snapshot_bank_file_path,
662 );
663
664 Ok(BankSnapshotInfo {
665 slot,
666 snapshot_path: snapshot_bank_file_path,
667 })
668}
669
670fn serialize_status_cache(
671 slot: Slot,
672 slot_deltas: &[BankSlotDelta],
673 status_cache_path: &Path,
674) -> Result<()> {
675 let mut status_cache_serialize = Measure::start("status_cache_serialize-ms");
676 let consumed_size = serialize_snapshot_data_file(status_cache_path, |stream| {
677 serialize_into(stream, slot_deltas)?;
678 Ok(())
679 })?;
680 status_cache_serialize.stop();
681
682 datapoint_info!(
684 "snapshot-status-cache-file",
685 ("slot", slot, i64),
686 ("size", consumed_size, i64)
687 );
688
689 inc_new_counter_info!(
690 "serialize-status-cache-ms",
691 status_cache_serialize.as_ms() as usize
692 );
693 Ok(())
694}
695
696pub fn remove_bank_snapshot<P>(slot: Slot, bank_snapshots_dir: P) -> Result<()>
698where
699 P: AsRef<Path>,
700{
701 let bank_snapshot_dir = get_bank_snapshots_dir(&bank_snapshots_dir, slot);
702 fs::remove_dir_all(bank_snapshot_dir)?;
703 Ok(())
704}
705
706#[derive(Debug, Default)]
707pub struct BankFromArchiveTimings {
708 pub rebuild_bank_from_snapshots_us: u64,
709 pub full_snapshot_untar_us: u64,
710 pub incremental_snapshot_untar_us: u64,
711 pub verify_snapshot_bank_us: u64,
712}
713
714const PARALLEL_UNTAR_READERS_DEFAULT: usize = 4;
716
717#[allow(clippy::too_many_arguments)]
720pub fn bank_from_snapshot_archives(
721 account_paths: &[PathBuf],
722 frozen_account_pubkeys: &[Pubkey],
723 bank_snapshots_dir: impl AsRef<Path>,
724 full_snapshot_archive_info: &FullSnapshotArchiveInfo,
725 incremental_snapshot_archive_info: Option<&IncrementalSnapshotArchiveInfo>,
726 genesis_config: &GenesisConfig,
727 debug_keys: Option<Arc<HashSet<Pubkey>>>,
728 additional_builtins: Option<&Builtins>,
729 account_secondary_indexes: AccountSecondaryIndexes,
730 accounts_db_caching_enabled: bool,
731 limit_load_slot_count_from_snapshot: Option<usize>,
732 shrink_ratio: AccountShrinkThreshold,
733 test_hash_calculation: bool,
734 accounts_db_skip_shrink: bool,
735 verify_index: bool,
736 accounts_db_config: Option<AccountsDbConfig>,
737) -> Result<(Bank, BankFromArchiveTimings)> {
738 check_are_snapshots_compatible(
739 full_snapshot_archive_info,
740 incremental_snapshot_archive_info,
741 )?;
742
743 let parallel_divisions = std::cmp::min(
744 PARALLEL_UNTAR_READERS_DEFAULT,
745 std::cmp::max(1, num_cpus::get() / 4),
746 );
747
748 let unarchived_full_snapshot = unarchive_snapshot(
749 &bank_snapshots_dir,
750 TMP_SNAPSHOT_ARCHIVE_PREFIX,
751 full_snapshot_archive_info.path(),
752 "snapshot untar",
753 account_paths,
754 full_snapshot_archive_info.archive_format(),
755 parallel_divisions,
756 )?;
757
758 let mut unarchived_incremental_snapshot =
759 if let Some(incremental_snapshot_archive_info) = incremental_snapshot_archive_info {
760 let unarchived_incremental_snapshot = unarchive_snapshot(
761 &bank_snapshots_dir,
762 TMP_SNAPSHOT_ARCHIVE_PREFIX,
763 incremental_snapshot_archive_info.path(),
764 "incremental snapshot untar",
765 account_paths,
766 incremental_snapshot_archive_info.archive_format(),
767 parallel_divisions,
768 )?;
769 Some(unarchived_incremental_snapshot)
770 } else {
771 None
772 };
773
774 let mut unpacked_append_vec_map = unarchived_full_snapshot.unpacked_append_vec_map;
775 if let Some(ref mut unarchive_preparation_result) = unarchived_incremental_snapshot {
776 let incremental_snapshot_unpacked_append_vec_map =
777 std::mem::take(&mut unarchive_preparation_result.unpacked_append_vec_map);
778 unpacked_append_vec_map.extend(incremental_snapshot_unpacked_append_vec_map.into_iter());
779 }
780
781 let mut measure_rebuild = Measure::start("rebuild bank from snapshots");
782 let bank = rebuild_bank_from_snapshots(
783 &unarchived_full_snapshot.unpacked_snapshots_dir_and_version,
784 unarchived_incremental_snapshot
785 .as_ref()
786 .map(|unarchive_preparation_result| {
787 &unarchive_preparation_result.unpacked_snapshots_dir_and_version
788 }),
789 frozen_account_pubkeys,
790 account_paths,
791 unpacked_append_vec_map,
792 genesis_config,
793 debug_keys,
794 additional_builtins,
795 account_secondary_indexes,
796 accounts_db_caching_enabled,
797 limit_load_slot_count_from_snapshot,
798 shrink_ratio,
799 verify_index,
800 accounts_db_config,
801 )?;
802 measure_rebuild.stop();
803 info!("{}", measure_rebuild);
804
805 let mut measure_verify = Measure::start("verify");
806 if !bank.verify_snapshot_bank(
807 test_hash_calculation,
808 accounts_db_skip_shrink,
809 Some(full_snapshot_archive_info.slot()),
810 ) && limit_load_slot_count_from_snapshot.is_none()
811 {
812 panic!("Snapshot bank for slot {} failed to verify", bank.slot());
813 }
814 measure_verify.stop();
815
816 let timings = BankFromArchiveTimings {
817 rebuild_bank_from_snapshots_us: measure_rebuild.as_us(),
818 full_snapshot_untar_us: unarchived_full_snapshot.measure_untar.as_us(),
819 incremental_snapshot_untar_us: unarchived_incremental_snapshot
820 .map_or(0, |unarchive_preparation_result| {
821 unarchive_preparation_result.measure_untar.as_us()
822 }),
823 verify_snapshot_bank_us: measure_verify.as_us(),
824 };
825 Ok((bank, timings))
826}
827
828#[allow(clippy::too_many_arguments)]
831pub fn bank_from_latest_snapshot_archives(
832 bank_snapshots_dir: impl AsRef<Path>,
833 snapshot_archives_dir: impl AsRef<Path>,
834 account_paths: &[PathBuf],
835 frozen_account_pubkeys: &[Pubkey],
836 genesis_config: &GenesisConfig,
837 debug_keys: Option<Arc<HashSet<Pubkey>>>,
838 additional_builtins: Option<&Builtins>,
839 account_secondary_indexes: AccountSecondaryIndexes,
840 accounts_db_caching_enabled: bool,
841 limit_load_slot_count_from_snapshot: Option<usize>,
842 shrink_ratio: AccountShrinkThreshold,
843 test_hash_calculation: bool,
844 accounts_db_skip_shrink: bool,
845 verify_index: bool,
846 accounts_db_config: Option<AccountsDbConfig>,
847) -> Result<(
848 Bank,
849 BankFromArchiveTimings,
850 FullSnapshotArchiveInfo,
851 Option<IncrementalSnapshotArchiveInfo>,
852)> {
853 let full_snapshot_archive_info = get_highest_full_snapshot_archive_info(&snapshot_archives_dir)
854 .ok_or(SnapshotError::NoSnapshotArchives)?;
855
856 let incremental_snapshot_archive_info = get_highest_incremental_snapshot_archive_info(
857 &snapshot_archives_dir,
858 full_snapshot_archive_info.slot(),
859 );
860
861 info!(
862 "Loading bank from full snapshot: {}, and incremental snapshot: {:?}",
863 full_snapshot_archive_info.path().display(),
864 incremental_snapshot_archive_info
865 .as_ref()
866 .map(
867 |incremental_snapshot_archive_info| incremental_snapshot_archive_info
868 .path()
869 .display()
870 )
871 );
872
873 let (bank, timings) = bank_from_snapshot_archives(
874 account_paths,
875 frozen_account_pubkeys,
876 bank_snapshots_dir.as_ref(),
877 &full_snapshot_archive_info,
878 incremental_snapshot_archive_info.as_ref(),
879 genesis_config,
880 debug_keys,
881 additional_builtins,
882 account_secondary_indexes,
883 accounts_db_caching_enabled,
884 limit_load_slot_count_from_snapshot,
885 shrink_ratio,
886 test_hash_calculation,
887 accounts_db_skip_shrink,
888 verify_index,
889 accounts_db_config,
890 )?;
891
892 verify_bank_against_expected_slot_hash(
893 &bank,
894 incremental_snapshot_archive_info.as_ref().map_or(
895 full_snapshot_archive_info.slot(),
896 |incremental_snapshot_archive_info| incremental_snapshot_archive_info.slot(),
897 ),
898 incremental_snapshot_archive_info.as_ref().map_or(
899 *full_snapshot_archive_info.hash(),
900 |incremental_snapshot_archive_info| *incremental_snapshot_archive_info.hash(),
901 ),
902 )?;
903
904 Ok((
905 bank,
906 timings,
907 full_snapshot_archive_info,
908 incremental_snapshot_archive_info,
909 ))
910}
911
912fn verify_bank_against_expected_slot_hash(
915 bank: &Bank,
916 expected_slot: Slot,
917 expected_hash: Hash,
918) -> Result<()> {
919 let bank_slot = bank.slot();
920 let bank_hash = bank.get_accounts_hash();
921
922 if bank_slot != expected_slot || bank_hash != expected_hash {
923 return Err(SnapshotError::MismatchedSlotHash(
924 (bank_slot, bank_hash),
925 (expected_slot, expected_hash),
926 ));
927 }
928
929 Ok(())
930}
931
932fn unarchive_snapshot<P, Q>(
936 bank_snapshots_dir: P,
937 unpacked_snapshots_dir_prefix: &'static str,
938 snapshot_archive_path: Q,
939 measure_name: &'static str,
940 account_paths: &[PathBuf],
941 archive_format: ArchiveFormat,
942 parallel_divisions: usize,
943) -> Result<UnarchivedSnapshot>
944where
945 P: AsRef<Path>,
946 Q: AsRef<Path>,
947{
948 let unpack_dir = tempfile::Builder::new()
949 .prefix(unpacked_snapshots_dir_prefix)
950 .tempdir_in(bank_snapshots_dir)?;
951 let unpacked_snapshots_dir = unpack_dir.path().join("snapshots");
952
953 let mut measure_untar = Measure::start(measure_name);
954 let unpacked_append_vec_map = untar_snapshot_in(
955 snapshot_archive_path,
956 unpack_dir.path(),
957 account_paths,
958 archive_format,
959 parallel_divisions,
960 )?;
961 measure_untar.stop();
962 info!("{}", measure_untar);
963
964 let unpacked_version_file = unpack_dir.path().join("version");
965 let snapshot_version = {
966 let mut snapshot_version = String::new();
967 File::open(unpacked_version_file)
968 .and_then(|mut f| f.read_to_string(&mut snapshot_version))?;
969 snapshot_version.trim().to_string()
970 };
971
972 Ok(UnarchivedSnapshot {
973 unpack_dir,
974 unpacked_append_vec_map,
975 unpacked_snapshots_dir_and_version: UnpackedSnapshotsDirAndVersion {
976 unpacked_snapshots_dir,
977 snapshot_version,
978 },
979 measure_untar,
980 })
981}
982
983fn check_are_snapshots_compatible(
986 full_snapshot_archive_info: &FullSnapshotArchiveInfo,
987 incremental_snapshot_archive_info: Option<&IncrementalSnapshotArchiveInfo>,
988) -> Result<()> {
989 if incremental_snapshot_archive_info.is_none() {
990 return Ok(());
991 }
992
993 let incremental_snapshot_archive_info = incremental_snapshot_archive_info.unwrap();
994
995 (full_snapshot_archive_info.slot() == incremental_snapshot_archive_info.base_slot())
996 .then(|| ())
997 .ok_or_else(|| {
998 SnapshotError::MismatchedBaseSlot(
999 full_snapshot_archive_info.slot(),
1000 incremental_snapshot_archive_info.base_slot(),
1001 )
1002 })
1003}
1004
1005pub fn path_to_file_name_str(path: &Path) -> Result<&str> {
1007 path.file_name()
1008 .ok_or_else(|| SnapshotError::PathToFileNameError(path.to_path_buf()))?
1009 .to_str()
1010 .ok_or_else(|| SnapshotError::FileNameToStrError(path.to_path_buf()))
1011}
1012
1013pub fn build_full_snapshot_archive_path(
1016 snapshot_archives_dir: PathBuf,
1017 slot: Slot,
1018 hash: &Hash,
1019 archive_format: ArchiveFormat,
1020) -> PathBuf {
1021 snapshot_archives_dir.join(format!(
1022 "snapshot-{}-{}.{}",
1023 slot,
1024 hash,
1025 get_archive_ext(archive_format),
1026 ))
1027}
1028
1029pub fn build_incremental_snapshot_archive_path(
1033 snapshot_archives_dir: PathBuf,
1034 base_slot: Slot,
1035 slot: Slot,
1036 hash: &Hash,
1037 archive_format: ArchiveFormat,
1038) -> PathBuf {
1039 snapshot_archives_dir.join(format!(
1040 "incremental-snapshot-{}-{}-{}.{}",
1041 base_slot,
1042 slot,
1043 hash,
1044 get_archive_ext(archive_format),
1045 ))
1046}
1047
1048fn archive_format_from_str(archive_format: &str) -> Option<ArchiveFormat> {
1049 match archive_format {
1050 "tar.bz2" => Some(ArchiveFormat::TarBzip2),
1051 "tar.gz" => Some(ArchiveFormat::TarGzip),
1052 "tar.zst" => Some(ArchiveFormat::TarZstd),
1053 "tar" => Some(ArchiveFormat::Tar),
1054 _ => None,
1055 }
1056}
1057
1058pub fn parse_full_snapshot_archive_filename(
1060 archive_filename: &str,
1061) -> Result<(Slot, Hash, ArchiveFormat)> {
1062 lazy_static! {
1063 static ref RE: Regex = Regex::new(FULL_SNAPSHOT_ARCHIVE_FILENAME_REGEX).unwrap();
1064 }
1065
1066 let do_parse = || {
1067 RE.captures(archive_filename).and_then(|captures| {
1068 let slot = captures
1069 .name("slot")
1070 .map(|x| x.as_str().parse::<Slot>())?
1071 .ok()?;
1072 let hash = captures
1073 .name("hash")
1074 .map(|x| x.as_str().parse::<Hash>())?
1075 .ok()?;
1076 let archive_format = captures
1077 .name("ext")
1078 .map(|x| archive_format_from_str(x.as_str()))??;
1079
1080 Some((slot, hash, archive_format))
1081 })
1082 };
1083
1084 do_parse().ok_or_else(|| {
1085 SnapshotError::ParseSnapshotArchiveFileNameError(archive_filename.to_string())
1086 })
1087}
1088
1089pub fn parse_incremental_snapshot_archive_filename(
1091 archive_filename: &str,
1092) -> Result<(Slot, Slot, Hash, ArchiveFormat)> {
1093 lazy_static! {
1094 static ref RE: Regex = Regex::new(INCREMENTAL_SNAPSHOT_ARCHIVE_FILENAME_REGEX).unwrap();
1095 }
1096
1097 let do_parse = || {
1098 RE.captures(archive_filename).and_then(|captures| {
1099 let base_slot = captures
1100 .name("base")
1101 .map(|x| x.as_str().parse::<Slot>())?
1102 .ok()?;
1103 let slot = captures
1104 .name("slot")
1105 .map(|x| x.as_str().parse::<Slot>())?
1106 .ok()?;
1107 let hash = captures
1108 .name("hash")
1109 .map(|x| x.as_str().parse::<Hash>())?
1110 .ok()?;
1111 let archive_format = captures
1112 .name("ext")
1113 .map(|x| archive_format_from_str(x.as_str()))??;
1114
1115 Some((base_slot, slot, hash, archive_format))
1116 })
1117 };
1118
1119 do_parse().ok_or_else(|| {
1120 SnapshotError::ParseSnapshotArchiveFileNameError(archive_filename.to_string())
1121 })
1122}
1123
1124pub fn get_full_snapshot_archives<P>(snapshot_archives_dir: P) -> Vec<FullSnapshotArchiveInfo>
1126where
1127 P: AsRef<Path>,
1128{
1129 match fs::read_dir(&snapshot_archives_dir) {
1130 Err(err) => {
1131 info!(
1132 "Unable to read snapshot archives directory: err: {}, path: {}",
1133 err,
1134 snapshot_archives_dir.as_ref().display()
1135 );
1136 vec![]
1137 }
1138 Ok(files) => files
1139 .filter_map(|entry| {
1140 entry.map_or(None, |entry| {
1141 FullSnapshotArchiveInfo::new_from_path(entry.path()).ok()
1142 })
1143 })
1144 .collect(),
1145 }
1146}
1147
1148fn get_incremental_snapshot_archives<P>(
1150 snapshot_archives_dir: P,
1151) -> Vec<IncrementalSnapshotArchiveInfo>
1152where
1153 P: AsRef<Path>,
1154{
1155 match fs::read_dir(&snapshot_archives_dir) {
1156 Err(err) => {
1157 info!(
1158 "Unable to read snapshot archives directory: err: {}, path: {}",
1159 err,
1160 snapshot_archives_dir.as_ref().display()
1161 );
1162 vec![]
1163 }
1164 Ok(files) => files
1165 .filter_map(|entry| {
1166 entry.map_or(None, |entry| {
1167 IncrementalSnapshotArchiveInfo::new_from_path(entry.path()).ok()
1168 })
1169 })
1170 .collect(),
1171 }
1172}
1173
1174pub fn get_highest_full_snapshot_archive_slot<P>(snapshot_archives_dir: P) -> Option<Slot>
1176where
1177 P: AsRef<Path>,
1178{
1179 get_highest_full_snapshot_archive_info(snapshot_archives_dir)
1180 .map(|full_snapshot_archive_info| full_snapshot_archive_info.slot())
1181}
1182
1183pub fn get_highest_incremental_snapshot_archive_slot<P: AsRef<Path>>(
1186 snapshot_archives_dir: P,
1187 full_snapshot_slot: Slot,
1188) -> Option<Slot> {
1189 get_highest_incremental_snapshot_archive_info(snapshot_archives_dir, full_snapshot_slot)
1190 .map(|incremental_snapshot_archive_info| incremental_snapshot_archive_info.slot())
1191}
1192
1193pub fn get_highest_full_snapshot_archive_info<P>(
1195 snapshot_archives_dir: P,
1196) -> Option<FullSnapshotArchiveInfo>
1197where
1198 P: AsRef<Path>,
1199{
1200 let mut full_snapshot_archives = get_full_snapshot_archives(snapshot_archives_dir);
1201 full_snapshot_archives.sort_unstable();
1202 full_snapshot_archives.into_iter().rev().next()
1203}
1204
1205pub fn get_highest_incremental_snapshot_archive_info<P>(
1208 snapshot_archives_dir: P,
1209 full_snapshot_slot: Slot,
1210) -> Option<IncrementalSnapshotArchiveInfo>
1211where
1212 P: AsRef<Path>,
1213{
1214 let mut incremental_snapshot_archives =
1218 get_incremental_snapshot_archives(snapshot_archives_dir)
1219 .into_iter()
1220 .filter(|incremental_snapshot_archive_info| {
1221 incremental_snapshot_archive_info.base_slot() == full_snapshot_slot
1222 })
1223 .collect::<Vec<_>>();
1224 incremental_snapshot_archives.sort_unstable();
1225 incremental_snapshot_archives.into_iter().rev().next()
1226}
1227
1228pub fn purge_old_snapshot_archives<P>(
1229 snapshot_archives_dir: P,
1230 maximum_full_snapshot_archives_to_retain: usize,
1231 maximum_incremental_snapshot_archives_to_retain: usize,
1232) where
1233 P: AsRef<Path>,
1234{
1235 info!(
1236 "Purging old snapshot archives in {}, retaining {} full snapshots and {} incremental snapshots",
1237 snapshot_archives_dir.as_ref().display(),
1238 maximum_full_snapshot_archives_to_retain,
1239 maximum_incremental_snapshot_archives_to_retain
1240 );
1241 let mut snapshot_archives = get_full_snapshot_archives(&snapshot_archives_dir);
1242 snapshot_archives.sort_unstable();
1243 snapshot_archives.reverse();
1244 snapshot_archives.pop();
1246 let max_snaps = max(1, maximum_full_snapshot_archives_to_retain);
1247 for old_archive in snapshot_archives.into_iter().skip(max_snaps) {
1248 trace!(
1249 "Purging old full snapshot archive: {}",
1250 old_archive.path().display()
1251 );
1252 fs::remove_file(old_archive.path())
1253 .unwrap_or_else(|err| info!("Failed to remove old full snapshot archive: {}", err));
1254 }
1255
1256 let highest_full_snapshot_slot = get_highest_full_snapshot_archive_slot(&snapshot_archives_dir);
1272 let mut incremental_snapshot_archives_with_same_base_slot = vec![];
1273 let mut incremental_snapshot_archives_with_different_base_slot = vec![];
1274 get_incremental_snapshot_archives(&snapshot_archives_dir)
1275 .drain(..)
1276 .for_each(|incremental_snapshot_archive| {
1277 if Some(incremental_snapshot_archive.base_slot()) == highest_full_snapshot_slot {
1278 incremental_snapshot_archives_with_same_base_slot
1279 .push(incremental_snapshot_archive);
1280 } else {
1281 incremental_snapshot_archives_with_different_base_slot
1282 .push(incremental_snapshot_archive);
1283 }
1284 });
1285
1286 incremental_snapshot_archives_with_same_base_slot.sort_unstable();
1287
1288 incremental_snapshot_archives_with_different_base_slot
1289 .iter()
1290 .chain(
1291 incremental_snapshot_archives_with_same_base_slot
1292 .iter()
1293 .rev()
1294 .skip(maximum_incremental_snapshot_archives_to_retain),
1295 )
1296 .for_each(|incremental_snapshot_archive| {
1297 trace!(
1298 "Purging old incremental snapshot archive: {}",
1299 incremental_snapshot_archive.path().display()
1300 );
1301 fs::remove_file(incremental_snapshot_archive.path()).unwrap_or_else(|err| {
1302 info!("Failed to remove old incremental snapshot archive: {}", err)
1303 })
1304 });
1305}
1306
1307fn unpack_snapshot_local<T: 'static + Read + std::marker::Send, F: Fn() -> T>(
1308 reader: F,
1309 ledger_dir: &Path,
1310 account_paths: &[PathBuf],
1311 parallel_archivers: usize,
1312) -> Result<UnpackedAppendVecMap> {
1313 assert!(parallel_archivers > 0);
1314 let shared_buffer = SharedBuffer::new(reader());
1316
1317 let readers = (0..parallel_archivers)
1319 .into_iter()
1320 .map(|_| SharedBufferReader::new(&shared_buffer))
1321 .collect::<Vec<_>>();
1322
1323 let all_unpacked_append_vec_map = readers
1325 .into_par_iter()
1326 .enumerate()
1327 .map(|(index, reader)| {
1328 let parallel_selector = Some(ParallelSelector {
1329 index,
1330 divisions: parallel_archivers,
1331 });
1332 let mut archive = Archive::new(reader);
1333 unpack_snapshot(&mut archive, ledger_dir, account_paths, parallel_selector)
1334 })
1335 .collect::<Vec<_>>();
1336 let mut unpacked_append_vec_map = UnpackedAppendVecMap::new();
1337 for h in all_unpacked_append_vec_map {
1338 unpacked_append_vec_map.extend(h?);
1339 }
1340
1341 Ok(unpacked_append_vec_map)
1342}
1343
1344fn untar_snapshot_in<P: AsRef<Path>>(
1345 snapshot_tar: P,
1346 unpack_dir: &Path,
1347 account_paths: &[PathBuf],
1348 archive_format: ArchiveFormat,
1349 parallel_divisions: usize,
1350) -> Result<UnpackedAppendVecMap> {
1351 let open_file = || File::open(&snapshot_tar).unwrap();
1352 let account_paths_map = match archive_format {
1353 ArchiveFormat::TarBzip2 => unpack_snapshot_local(
1354 || BzDecoder::new(BufReader::new(open_file())),
1355 unpack_dir,
1356 account_paths,
1357 parallel_divisions,
1358 )?,
1359 ArchiveFormat::TarGzip => unpack_snapshot_local(
1360 || GzDecoder::new(BufReader::new(open_file())),
1361 unpack_dir,
1362 account_paths,
1363 parallel_divisions,
1364 )?,
1365 ArchiveFormat::TarZstd => unpack_snapshot_local(
1366 || zstd::stream::read::Decoder::new(BufReader::new(open_file())).unwrap(),
1367 unpack_dir,
1368 account_paths,
1369 parallel_divisions,
1370 )?,
1371 ArchiveFormat::Tar => unpack_snapshot_local(
1372 || BufReader::new(open_file()),
1373 unpack_dir,
1374 account_paths,
1375 parallel_divisions,
1376 )?,
1377 };
1378 Ok(account_paths_map)
1379}
1380
1381fn verify_unpacked_snapshots_dir_and_version(
1382 unpacked_snapshots_dir_and_version: &UnpackedSnapshotsDirAndVersion,
1383) -> Result<(SnapshotVersion, BankSnapshotInfo)> {
1384 info!(
1385 "snapshot version: {}",
1386 &unpacked_snapshots_dir_and_version.snapshot_version
1387 );
1388
1389 let snapshot_version =
1390 SnapshotVersion::maybe_from_string(&unpacked_snapshots_dir_and_version.snapshot_version)
1391 .ok_or_else(|| {
1392 get_io_error(&format!(
1393 "unsupported snapshot version: {}",
1394 &unpacked_snapshots_dir_and_version.snapshot_version,
1395 ))
1396 })?;
1397 let mut bank_snapshot_infos =
1398 get_bank_snapshots(&unpacked_snapshots_dir_and_version.unpacked_snapshots_dir);
1399 if bank_snapshot_infos.len() > 1 {
1400 return Err(get_io_error("invalid snapshot format"));
1401 }
1402 bank_snapshot_infos.sort_unstable();
1403 let root_paths = bank_snapshot_infos
1404 .pop()
1405 .ok_or_else(|| get_io_error("No snapshots found in snapshots directory"))?;
1406 Ok((snapshot_version, root_paths))
1407}
1408
1409#[allow(clippy::too_many_arguments)]
1410fn rebuild_bank_from_snapshots(
1411 full_snapshot_unpacked_snapshots_dir_and_version: &UnpackedSnapshotsDirAndVersion,
1412 incremental_snapshot_unpacked_snapshots_dir_and_version: Option<
1413 &UnpackedSnapshotsDirAndVersion,
1414 >,
1415 frozen_account_pubkeys: &[Pubkey],
1416 account_paths: &[PathBuf],
1417 unpacked_append_vec_map: UnpackedAppendVecMap,
1418 genesis_config: &GenesisConfig,
1419 debug_keys: Option<Arc<HashSet<Pubkey>>>,
1420 additional_builtins: Option<&Builtins>,
1421 account_secondary_indexes: AccountSecondaryIndexes,
1422 accounts_db_caching_enabled: bool,
1423 limit_load_slot_count_from_snapshot: Option<usize>,
1424 shrink_ratio: AccountShrinkThreshold,
1425 verify_index: bool,
1426 accounts_db_config: Option<AccountsDbConfig>,
1427) -> Result<Bank> {
1428 let (full_snapshot_version, full_snapshot_root_paths) =
1429 verify_unpacked_snapshots_dir_and_version(
1430 full_snapshot_unpacked_snapshots_dir_and_version,
1431 )?;
1432 let (incremental_snapshot_version, incremental_snapshot_root_paths) =
1433 if let Some(snapshot_unpacked_snapshots_dir_and_version) =
1434 incremental_snapshot_unpacked_snapshots_dir_and_version
1435 {
1436 let (snapshot_version, bank_snapshot_info) = verify_unpacked_snapshots_dir_and_version(
1437 snapshot_unpacked_snapshots_dir_and_version,
1438 )?;
1439 (Some(snapshot_version), Some(bank_snapshot_info))
1440 } else {
1441 (None, None)
1442 };
1443 info!(
1444 "Loading bank from full snapshot {} and incremental snapshot {:?}",
1445 full_snapshot_root_paths.snapshot_path.display(),
1446 incremental_snapshot_root_paths
1447 .as_ref()
1448 .map(|paths| paths.snapshot_path.display()),
1449 );
1450
1451 let snapshot_root_paths = SnapshotRootPaths {
1452 full_snapshot_root_file_path: full_snapshot_root_paths.snapshot_path,
1453 incremental_snapshot_root_file_path: incremental_snapshot_root_paths
1454 .map(|root_paths| root_paths.snapshot_path),
1455 };
1456
1457 let bank = deserialize_snapshot_data_files(&snapshot_root_paths, |mut snapshot_streams| {
1458 Ok(
1459 match incremental_snapshot_version.unwrap_or(full_snapshot_version) {
1460 SnapshotVersion::V1_2_0 => bank_from_streams(
1461 SerdeStyle::Newer,
1462 &mut snapshot_streams,
1463 account_paths,
1464 unpacked_append_vec_map,
1465 genesis_config,
1466 frozen_account_pubkeys,
1467 debug_keys,
1468 additional_builtins,
1469 account_secondary_indexes,
1470 accounts_db_caching_enabled,
1471 limit_load_slot_count_from_snapshot,
1472 shrink_ratio,
1473 verify_index,
1474 accounts_db_config,
1475 ),
1476 }?,
1477 )
1478 })?;
1479
1480 let status_cache_path = incremental_snapshot_unpacked_snapshots_dir_and_version
1483 .map_or_else(
1484 || {
1485 full_snapshot_unpacked_snapshots_dir_and_version
1486 .unpacked_snapshots_dir
1487 .as_path()
1488 },
1489 |unpacked_snapshots_dir_and_version| {
1490 unpacked_snapshots_dir_and_version
1491 .unpacked_snapshots_dir
1492 .as_path()
1493 },
1494 )
1495 .join(SNAPSHOT_STATUS_CACHE_FILE_NAME);
1496 let slot_deltas = deserialize_snapshot_data_file(&status_cache_path, |stream| {
1497 info!(
1498 "Rebuilding status cache from {}",
1499 status_cache_path.display()
1500 );
1501 let slot_deltas: Vec<BankSlotDelta> = bincode::options()
1502 .with_limit(MAX_SNAPSHOT_DATA_FILE_SIZE)
1503 .with_fixint_encoding()
1504 .allow_trailing_bytes()
1505 .deserialize_from(stream)?;
1506 Ok(slot_deltas)
1507 })?;
1508
1509 bank.src.append(&slot_deltas);
1510
1511 info!("Loaded bank for slot: {}", bank.slot());
1512 Ok(bank)
1513}
1514
1515fn get_snapshot_file_name(slot: Slot) -> String {
1516 slot.to_string()
1517}
1518
1519fn get_bank_snapshots_dir<P: AsRef<Path>>(path: P, slot: Slot) -> PathBuf {
1520 path.as_ref().join(slot.to_string())
1521}
1522
1523fn get_io_error(error: &str) -> SnapshotError {
1524 warn!("Snapshot Error: {:?}", error);
1525 SnapshotError::Io(IoError::new(ErrorKind::Other, error))
1526}
1527
1528pub fn verify_snapshot_archive<P, Q, R>(
1529 snapshot_archive: P,
1530 snapshots_to_verify: Q,
1531 storages_to_verify: R,
1532 archive_format: ArchiveFormat,
1533) where
1534 P: AsRef<Path>,
1535 Q: AsRef<Path>,
1536 R: AsRef<Path>,
1537{
1538 let temp_dir = tempfile::TempDir::new().unwrap();
1539 let unpack_dir = temp_dir.path();
1540 untar_snapshot_in(
1541 snapshot_archive,
1542 unpack_dir,
1543 &[unpack_dir.to_path_buf()],
1544 archive_format,
1545 1,
1546 )
1547 .unwrap();
1548
1549 let unpacked_snapshots = unpack_dir.join("snapshots");
1551 assert!(!dir_diff::is_different(&snapshots_to_verify, unpacked_snapshots).unwrap());
1552
1553 let unpacked_accounts = unpack_dir.join("accounts");
1555 assert!(!dir_diff::is_different(&storages_to_verify, unpacked_accounts).unwrap());
1556}
1557
1558pub fn purge_old_bank_snapshots<P>(bank_snapshots_dir: P)
1560where
1561 P: AsRef<Path>,
1562{
1563 let mut bank_snapshot_infos = get_bank_snapshots(&bank_snapshots_dir);
1564 bank_snapshot_infos.sort_unstable();
1565 bank_snapshot_infos
1566 .into_iter()
1567 .rev()
1568 .skip(MAX_BANK_SNAPSHOTS_TO_RETAIN)
1569 .for_each(|bank_snapshot_info| {
1570 let r = remove_bank_snapshot(bank_snapshot_info.slot, &bank_snapshots_dir);
1571 if r.is_err() {
1572 warn!(
1573 "Couldn't remove bank snapshot at: {}",
1574 bank_snapshot_info.snapshot_path.display()
1575 );
1576 }
1577 })
1578}
1579
1580pub fn snapshot_bank(
1587 root_bank: &Bank,
1588 status_cache_slot_deltas: Vec<BankSlotDelta>,
1589 accounts_package_sender: &AccountsPackageSender,
1590 bank_snapshots_dir: impl AsRef<Path>,
1591 snapshot_archives_dir: impl AsRef<Path>,
1592 snapshot_version: SnapshotVersion,
1593 archive_format: ArchiveFormat,
1594 hash_for_testing: Option<Hash>,
1595 snapshot_type: Option<SnapshotType>,
1596) -> Result<()> {
1597 let snapshot_storages = snapshot_type.map_or_else(SnapshotStorages::default, |snapshot_type| {
1598 let incremental_snapshot_base_slot = match snapshot_type {
1599 SnapshotType::IncrementalSnapshot(incremental_snapshot_base_slot) => {
1600 Some(incremental_snapshot_base_slot)
1601 }
1602 _ => None,
1603 };
1604 root_bank.get_snapshot_storages(incremental_snapshot_base_slot)
1605 });
1606 let mut add_snapshot_time = Measure::start("add-snapshot-ms");
1607 let bank_snapshot_info = add_bank_snapshot(
1608 &bank_snapshots_dir,
1609 root_bank,
1610 &snapshot_storages,
1611 snapshot_version,
1612 )?;
1613 add_snapshot_time.stop();
1614 inc_new_counter_info!("add-snapshot-ms", add_snapshot_time.as_ms() as usize);
1615
1616 let accounts_package = AccountsPackage::new(
1617 root_bank,
1618 &bank_snapshot_info,
1619 bank_snapshots_dir,
1620 status_cache_slot_deltas,
1621 snapshot_archives_dir,
1622 snapshot_storages,
1623 archive_format,
1624 snapshot_version,
1625 hash_for_testing,
1626 snapshot_type,
1627 )
1628 .expect("failed to hard link bank snapshot into a tmpdir");
1629
1630 accounts_package_sender.send(accounts_package)?;
1631
1632 Ok(())
1633}
1634
1635pub fn bank_to_full_snapshot_archive(
1641 bank_snapshots_dir: impl AsRef<Path>,
1642 bank: &Bank,
1643 snapshot_version: Option<SnapshotVersion>,
1644 snapshot_archives_dir: impl AsRef<Path>,
1645 archive_format: ArchiveFormat,
1646 maximum_full_snapshot_archives_to_retain: usize,
1647 maximum_incremental_snapshot_archives_to_retain: usize,
1648) -> Result<FullSnapshotArchiveInfo> {
1649 let snapshot_version = snapshot_version.unwrap_or_default();
1650
1651 assert!(bank.is_complete());
1652 bank.squash(); bank.force_flush_accounts_cache();
1654 bank.clean_accounts(true, false, Some(bank.slot()));
1655 bank.update_accounts_hash();
1656 bank.rehash(); let temp_dir = tempfile::tempdir_in(bank_snapshots_dir)?;
1659 let snapshot_storages = bank.get_snapshot_storages(None);
1660 let bank_snapshot_info =
1661 add_bank_snapshot(&temp_dir, bank, &snapshot_storages, snapshot_version)?;
1662
1663 package_and_archive_full_snapshot(
1664 bank,
1665 &bank_snapshot_info,
1666 &temp_dir,
1667 snapshot_archives_dir,
1668 snapshot_storages,
1669 archive_format,
1670 snapshot_version,
1671 maximum_full_snapshot_archives_to_retain,
1672 maximum_incremental_snapshot_archives_to_retain,
1673 )
1674}
1675
1676pub fn bank_to_incremental_snapshot_archive(
1683 bank_snapshots_dir: impl AsRef<Path>,
1684 bank: &Bank,
1685 full_snapshot_slot: Slot,
1686 snapshot_version: Option<SnapshotVersion>,
1687 snapshot_archives_dir: impl AsRef<Path>,
1688 archive_format: ArchiveFormat,
1689 maximum_full_snapshot_archives_to_retain: usize,
1690 maximum_incremental_snapshot_archives_to_retain: usize,
1691) -> Result<IncrementalSnapshotArchiveInfo> {
1692 let snapshot_version = snapshot_version.unwrap_or_default();
1693
1694 assert!(bank.is_complete());
1695 assert!(bank.slot() > full_snapshot_slot);
1696 bank.squash(); bank.force_flush_accounts_cache();
1698 bank.clean_accounts(true, false, Some(full_snapshot_slot));
1699 bank.update_accounts_hash();
1700 bank.rehash(); let temp_dir = tempfile::tempdir_in(bank_snapshots_dir)?;
1703 let snapshot_storages = bank.get_snapshot_storages(Some(full_snapshot_slot));
1704 let bank_snapshot_info =
1705 add_bank_snapshot(&temp_dir, bank, &snapshot_storages, snapshot_version)?;
1706
1707 package_and_archive_incremental_snapshot(
1708 bank,
1709 full_snapshot_slot,
1710 &bank_snapshot_info,
1711 &temp_dir,
1712 snapshot_archives_dir,
1713 snapshot_storages,
1714 archive_format,
1715 snapshot_version,
1716 maximum_full_snapshot_archives_to_retain,
1717 maximum_incremental_snapshot_archives_to_retain,
1718 )
1719}
1720
1721pub fn package_and_archive_full_snapshot(
1723 bank: &Bank,
1724 bank_snapshot_info: &BankSnapshotInfo,
1725 bank_snapshots_dir: impl AsRef<Path>,
1726 snapshot_archives_dir: impl AsRef<Path>,
1727 snapshot_storages: SnapshotStorages,
1728 archive_format: ArchiveFormat,
1729 snapshot_version: SnapshotVersion,
1730 maximum_full_snapshot_archives_to_retain: usize,
1731 maximum_incremental_snapshot_archives_to_retain: usize,
1732) -> Result<FullSnapshotArchiveInfo> {
1733 let accounts_package = AccountsPackage::new(
1734 bank,
1735 bank_snapshot_info,
1736 bank_snapshots_dir,
1737 bank.src.slot_deltas(&bank.src.roots()),
1738 snapshot_archives_dir,
1739 snapshot_storages,
1740 archive_format,
1741 snapshot_version,
1742 None,
1743 Some(SnapshotType::FullSnapshot),
1744 )?;
1745
1746 let snapshot_package = SnapshotPackage::from(accounts_package);
1747 archive_snapshot_package(
1748 &snapshot_package,
1749 maximum_full_snapshot_archives_to_retain,
1750 maximum_incremental_snapshot_archives_to_retain,
1751 )?;
1752
1753 Ok(FullSnapshotArchiveInfo::new(
1754 snapshot_package.snapshot_archive_info,
1755 ))
1756}
1757
1758#[allow(clippy::too_many_arguments)]
1760pub fn package_and_archive_incremental_snapshot(
1761 bank: &Bank,
1762 incremental_snapshot_base_slot: Slot,
1763 bank_snapshot_info: &BankSnapshotInfo,
1764 bank_snapshots_dir: impl AsRef<Path>,
1765 snapshot_archives_dir: impl AsRef<Path>,
1766 snapshot_storages: SnapshotStorages,
1767 archive_format: ArchiveFormat,
1768 snapshot_version: SnapshotVersion,
1769 maximum_full_snapshot_archives_to_retain: usize,
1770 maximum_incremental_snapshot_archives_to_retain: usize,
1771) -> Result<IncrementalSnapshotArchiveInfo> {
1772 let accounts_package = AccountsPackage::new(
1773 bank,
1774 bank_snapshot_info,
1775 bank_snapshots_dir,
1776 bank.src.slot_deltas(&bank.src.roots()),
1777 snapshot_archives_dir,
1778 snapshot_storages,
1779 archive_format,
1780 snapshot_version,
1781 None,
1782 Some(SnapshotType::IncrementalSnapshot(
1783 incremental_snapshot_base_slot,
1784 )),
1785 )?;
1786
1787 let snapshot_package = SnapshotPackage::from(accounts_package);
1788 archive_snapshot_package(
1789 &snapshot_package,
1790 maximum_full_snapshot_archives_to_retain,
1791 maximum_incremental_snapshot_archives_to_retain,
1792 )?;
1793
1794 Ok(IncrementalSnapshotArchiveInfo::new(
1795 incremental_snapshot_base_slot,
1796 snapshot_package.snapshot_archive_info,
1797 ))
1798}
1799
1800pub fn should_take_full_snapshot(
1801 block_height: Slot,
1802 full_snapshot_archive_interval_slots: Slot,
1803) -> bool {
1804 block_height % full_snapshot_archive_interval_slots == 0
1805}
1806
1807pub fn should_take_incremental_snapshot(
1808 block_height: Slot,
1809 incremental_snapshot_archive_interval_slots: Slot,
1810 last_full_snapshot_slot: Option<Slot>,
1811) -> bool {
1812 block_height % incremental_snapshot_archive_interval_slots == 0
1813 && last_full_snapshot_slot.is_some()
1814}
1815
1816#[cfg(test)]
1817mod tests {
1818 use super::*;
1819 use crate::accounts_db::ACCOUNTS_DB_CONFIG_FOR_TESTING;
1820 use assert_matches::assert_matches;
1821 use bincode::{deserialize_from, serialize_into};
1822 use gemachain_sdk::{
1823 genesis_config::create_genesis_config,
1824 signature::{Keypair, Signer},
1825 system_transaction,
1826 transaction::SanitizedTransaction,
1827 };
1828 use std::{convert::TryFrom, mem::size_of};
1829
1830 #[test]
1831 fn test_serialize_snapshot_data_file_under_limit() {
1832 let temp_dir = tempfile::TempDir::new().unwrap();
1833 let expected_consumed_size = size_of::<u32>() as u64;
1834 let consumed_size = serialize_snapshot_data_file_capped(
1835 &temp_dir.path().join("data-file"),
1836 expected_consumed_size,
1837 |stream| {
1838 serialize_into(stream, &2323_u32)?;
1839 Ok(())
1840 },
1841 )
1842 .unwrap();
1843 assert_eq!(consumed_size, expected_consumed_size);
1844 }
1845
1846 #[test]
1847 fn test_serialize_snapshot_data_file_over_limit() {
1848 let temp_dir = tempfile::TempDir::new().unwrap();
1849 let expected_consumed_size = size_of::<u32>() as u64;
1850 let result = serialize_snapshot_data_file_capped(
1851 &temp_dir.path().join("data-file"),
1852 expected_consumed_size - 1,
1853 |stream| {
1854 serialize_into(stream, &2323_u32)?;
1855 Ok(())
1856 },
1857 );
1858 assert_matches!(result, Err(SnapshotError::Io(ref message)) if message.to_string().starts_with("too large snapshot data file to serialize"));
1859 }
1860
1861 #[test]
1862 fn test_deserialize_snapshot_data_file_under_limit() {
1863 let expected_data = 2323_u32;
1864 let expected_consumed_size = size_of::<u32>() as u64;
1865
1866 let temp_dir = tempfile::TempDir::new().unwrap();
1867 serialize_snapshot_data_file_capped(
1868 &temp_dir.path().join("data-file"),
1869 expected_consumed_size,
1870 |stream| {
1871 serialize_into(stream, &expected_data)?;
1872 Ok(())
1873 },
1874 )
1875 .unwrap();
1876
1877 let snapshot_root_paths = SnapshotRootPaths {
1878 full_snapshot_root_file_path: temp_dir.path().join("data-file"),
1879 incremental_snapshot_root_file_path: None,
1880 };
1881
1882 let actual_data = deserialize_snapshot_data_files_capped(
1883 &snapshot_root_paths,
1884 expected_consumed_size,
1885 |stream| {
1886 Ok(deserialize_from::<_, u32>(
1887 &mut stream.full_snapshot_stream,
1888 )?)
1889 },
1890 )
1891 .unwrap();
1892 assert_eq!(actual_data, expected_data);
1893 }
1894
1895 #[test]
1896 fn test_deserialize_snapshot_data_file_over_limit() {
1897 let expected_data = 2323_u32;
1898 let expected_consumed_size = size_of::<u32>() as u64;
1899
1900 let temp_dir = tempfile::TempDir::new().unwrap();
1901 serialize_snapshot_data_file_capped(
1902 &temp_dir.path().join("data-file"),
1903 expected_consumed_size,
1904 |stream| {
1905 serialize_into(stream, &expected_data)?;
1906 Ok(())
1907 },
1908 )
1909 .unwrap();
1910
1911 let snapshot_root_paths = SnapshotRootPaths {
1912 full_snapshot_root_file_path: temp_dir.path().join("data-file"),
1913 incremental_snapshot_root_file_path: None,
1914 };
1915
1916 let result = deserialize_snapshot_data_files_capped(
1917 &snapshot_root_paths,
1918 expected_consumed_size - 1,
1919 |stream| {
1920 Ok(deserialize_from::<_, u32>(
1921 &mut stream.full_snapshot_stream,
1922 )?)
1923 },
1924 );
1925 assert_matches!(result, Err(SnapshotError::Io(ref message)) if message.to_string().starts_with("too large snapshot data file to deserialize"));
1926 }
1927
1928 #[test]
1929 fn test_deserialize_snapshot_data_file_extra_data() {
1930 let expected_data = 2323_u32;
1931 let expected_consumed_size = size_of::<u32>() as u64;
1932
1933 let temp_dir = tempfile::TempDir::new().unwrap();
1934 serialize_snapshot_data_file_capped(
1935 &temp_dir.path().join("data-file"),
1936 expected_consumed_size * 2,
1937 |stream| {
1938 serialize_into(stream.by_ref(), &expected_data)?;
1939 serialize_into(stream.by_ref(), &expected_data)?;
1940 Ok(())
1941 },
1942 )
1943 .unwrap();
1944
1945 let snapshot_root_paths = SnapshotRootPaths {
1946 full_snapshot_root_file_path: temp_dir.path().join("data-file"),
1947 incremental_snapshot_root_file_path: None,
1948 };
1949
1950 let result = deserialize_snapshot_data_files_capped(
1951 &snapshot_root_paths,
1952 expected_consumed_size * 2,
1953 |stream| {
1954 Ok(deserialize_from::<_, u32>(
1955 &mut stream.full_snapshot_stream,
1956 )?)
1957 },
1958 );
1959 assert_matches!(result, Err(SnapshotError::Io(ref message)) if message.to_string().starts_with("invalid snapshot data file"));
1960 }
1961
1962 #[test]
1963 fn test_parse_full_snapshot_archive_filename() {
1964 assert_eq!(
1965 parse_full_snapshot_archive_filename(&format!(
1966 "snapshot-42-{}.tar.bz2",
1967 Hash::default()
1968 ))
1969 .unwrap(),
1970 (42, Hash::default(), ArchiveFormat::TarBzip2)
1971 );
1972 assert_eq!(
1973 parse_full_snapshot_archive_filename(&format!(
1974 "snapshot-43-{}.tar.zst",
1975 Hash::default()
1976 ))
1977 .unwrap(),
1978 (43, Hash::default(), ArchiveFormat::TarZstd)
1979 );
1980 assert_eq!(
1981 parse_full_snapshot_archive_filename(&format!("snapshot-44-{}.tar", Hash::default()))
1982 .unwrap(),
1983 (44, Hash::default(), ArchiveFormat::Tar)
1984 );
1985
1986 assert!(parse_full_snapshot_archive_filename("invalid").is_err());
1987 assert!(
1988 parse_full_snapshot_archive_filename("snapshot-bad!slot-bad!hash.bad!ext").is_err()
1989 );
1990
1991 assert!(
1992 parse_full_snapshot_archive_filename("snapshot-12345678-bad!hash.bad!ext").is_err()
1993 );
1994 assert!(parse_full_snapshot_archive_filename(&format!(
1995 "snapshot-12345678-{}.bad!ext",
1996 Hash::new_unique()
1997 ))
1998 .is_err());
1999 assert!(parse_full_snapshot_archive_filename("snapshot-12345678-bad!hash.tar").is_err());
2000
2001 assert!(parse_full_snapshot_archive_filename(&format!(
2002 "snapshot-bad!slot-{}.bad!ext",
2003 Hash::new_unique()
2004 ))
2005 .is_err());
2006 assert!(parse_full_snapshot_archive_filename(&format!(
2007 "snapshot-12345678-{}.bad!ext",
2008 Hash::new_unique()
2009 ))
2010 .is_err());
2011 assert!(parse_full_snapshot_archive_filename(&format!(
2012 "snapshot-bad!slot-{}.tar",
2013 Hash::new_unique()
2014 ))
2015 .is_err());
2016
2017 assert!(parse_full_snapshot_archive_filename("snapshot-bad!slot-bad!hash.tar").is_err());
2018 assert!(parse_full_snapshot_archive_filename("snapshot-12345678-bad!hash.tar").is_err());
2019 assert!(parse_full_snapshot_archive_filename(&format!(
2020 "snapshot-bad!slot-{}.tar",
2021 Hash::new_unique()
2022 ))
2023 .is_err());
2024 }
2025
2026 #[test]
2027 fn test_parse_incremental_snapshot_archive_filename() {
2028 gemachain_logger::setup();
2029 assert_eq!(
2030 parse_incremental_snapshot_archive_filename(&format!(
2031 "incremental-snapshot-42-123-{}.tar.bz2",
2032 Hash::default()
2033 ))
2034 .unwrap(),
2035 (42, 123, Hash::default(), ArchiveFormat::TarBzip2)
2036 );
2037 assert_eq!(
2038 parse_incremental_snapshot_archive_filename(&format!(
2039 "incremental-snapshot-43-234-{}.tar.zst",
2040 Hash::default()
2041 ))
2042 .unwrap(),
2043 (43, 234, Hash::default(), ArchiveFormat::TarZstd)
2044 );
2045 assert_eq!(
2046 parse_incremental_snapshot_archive_filename(&format!(
2047 "incremental-snapshot-44-345-{}.tar",
2048 Hash::default()
2049 ))
2050 .unwrap(),
2051 (44, 345, Hash::default(), ArchiveFormat::Tar)
2052 );
2053
2054 assert!(parse_incremental_snapshot_archive_filename("invalid").is_err());
2055 assert!(parse_incremental_snapshot_archive_filename(&format!(
2056 "snapshot-42-{}.tar",
2057 Hash::new_unique()
2058 ))
2059 .is_err());
2060 assert!(parse_incremental_snapshot_archive_filename(
2061 "incremental-snapshot-bad!slot-bad!slot-bad!hash.bad!ext"
2062 )
2063 .is_err());
2064
2065 assert!(parse_incremental_snapshot_archive_filename(&format!(
2066 "incremental-snapshot-bad!slot-56785678-{}.tar",
2067 Hash::new_unique()
2068 ))
2069 .is_err());
2070
2071 assert!(parse_incremental_snapshot_archive_filename(&format!(
2072 "incremental-snapshot-12345678-bad!slot-{}.tar",
2073 Hash::new_unique()
2074 ))
2075 .is_err());
2076
2077 assert!(parse_incremental_snapshot_archive_filename(
2078 "incremental-snapshot-12341234-56785678-bad!HASH.tar"
2079 )
2080 .is_err());
2081
2082 assert!(parse_incremental_snapshot_archive_filename(&format!(
2083 "incremental-snapshot-12341234-56785678-{}.bad!ext",
2084 Hash::new_unique()
2085 ))
2086 .is_err());
2087 }
2088
2089 #[test]
2090 fn test_check_are_snapshots_compatible() {
2091 gemachain_logger::setup();
2092 let slot1: Slot = 1234;
2093 let slot2: Slot = 5678;
2094 let slot3: Slot = 999_999;
2095
2096 let full_snapshot_archive_info = FullSnapshotArchiveInfo::new_from_path(PathBuf::from(
2097 format!("/dir/snapshot-{}-{}.tar", slot1, Hash::new_unique()),
2098 ))
2099 .unwrap();
2100
2101 assert!(check_are_snapshots_compatible(&full_snapshot_archive_info, None,).is_ok());
2102
2103 let incremental_snapshot_archive_info =
2104 IncrementalSnapshotArchiveInfo::new_from_path(PathBuf::from(format!(
2105 "/dir/incremental-snapshot-{}-{}-{}.tar",
2106 slot1,
2107 slot2,
2108 Hash::new_unique()
2109 )))
2110 .unwrap();
2111
2112 assert!(check_are_snapshots_compatible(
2113 &full_snapshot_archive_info,
2114 Some(&incremental_snapshot_archive_info)
2115 )
2116 .is_ok());
2117
2118 let incremental_snapshot_archive_info =
2119 IncrementalSnapshotArchiveInfo::new_from_path(PathBuf::from(format!(
2120 "/dir/incremental-snapshot-{}-{}-{}.tar",
2121 slot2,
2122 slot3,
2123 Hash::new_unique()
2124 )))
2125 .unwrap();
2126
2127 assert!(check_are_snapshots_compatible(
2128 &full_snapshot_archive_info,
2129 Some(&incremental_snapshot_archive_info)
2130 )
2131 .is_err());
2132 }
2133
2134 fn common_create_bank_snapshot_files(
2136 bank_snapshots_dir: &Path,
2137 min_slot: Slot,
2138 max_slot: Slot,
2139 ) {
2140 for slot in min_slot..max_slot {
2141 let snapshot_dir = get_bank_snapshots_dir(bank_snapshots_dir, slot);
2142 fs::create_dir_all(&snapshot_dir).unwrap();
2143
2144 let snapshot_filename = get_snapshot_file_name(slot);
2145 let snapshot_path = snapshot_dir.join(snapshot_filename);
2146 File::create(snapshot_path).unwrap();
2147 }
2148 }
2149
2150 #[test]
2151 fn test_get_bank_snapshot_infos() {
2152 gemachain_logger::setup();
2153 let temp_snapshots_dir = tempfile::TempDir::new().unwrap();
2154 let min_slot = 10;
2155 let max_slot = 20;
2156 common_create_bank_snapshot_files(temp_snapshots_dir.path(), min_slot, max_slot);
2157
2158 let bank_snapshot_infos = get_bank_snapshots(temp_snapshots_dir.path());
2159 assert_eq!(bank_snapshot_infos.len() as Slot, max_slot - min_slot);
2160 }
2161
2162 #[test]
2163 fn test_get_highest_bank_snapshot_info() {
2164 gemachain_logger::setup();
2165 let temp_snapshots_dir = tempfile::TempDir::new().unwrap();
2166 let min_slot = 99;
2167 let max_slot = 123;
2168 common_create_bank_snapshot_files(temp_snapshots_dir.path(), min_slot, max_slot);
2169
2170 let highest_bank_snapshot_info = get_highest_bank_snapshot_info(temp_snapshots_dir.path());
2171 assert!(highest_bank_snapshot_info.is_some());
2172 assert_eq!(highest_bank_snapshot_info.unwrap().slot, max_slot - 1);
2173 }
2174
2175 fn common_create_snapshot_archive_files(
2181 snapshot_archives_dir: &Path,
2182 min_full_snapshot_slot: Slot,
2183 max_full_snapshot_slot: Slot,
2184 min_incremental_snapshot_slot: Slot,
2185 max_incremental_snapshot_slot: Slot,
2186 ) {
2187 for full_snapshot_slot in min_full_snapshot_slot..max_full_snapshot_slot {
2188 for incremental_snapshot_slot in
2189 min_incremental_snapshot_slot..max_incremental_snapshot_slot
2190 {
2191 let snapshot_filename = format!(
2192 "incremental-snapshot-{}-{}-{}.tar",
2193 full_snapshot_slot,
2194 incremental_snapshot_slot,
2195 Hash::default()
2196 );
2197 let snapshot_filepath = snapshot_archives_dir.join(snapshot_filename);
2198 File::create(snapshot_filepath).unwrap();
2199 }
2200
2201 let snapshot_filename =
2202 format!("snapshot-{}-{}.tar", full_snapshot_slot, Hash::default());
2203 let snapshot_filepath = snapshot_archives_dir.join(snapshot_filename);
2204 File::create(snapshot_filepath).unwrap();
2205
2206 let bad_filename = format!(
2208 "incremental-snapshot-{}-{}-bad!hash.tar",
2209 full_snapshot_slot,
2210 max_incremental_snapshot_slot + 1,
2211 );
2212 let bad_filepath = snapshot_archives_dir.join(bad_filename);
2213 File::create(bad_filepath).unwrap();
2214 }
2215
2216 let bad_filename = format!("snapshot-{}-bad!hash.tar", max_full_snapshot_slot + 1);
2219 let bad_filepath = snapshot_archives_dir.join(bad_filename);
2220 File::create(bad_filepath).unwrap();
2221 }
2222
2223 #[test]
2224 fn test_get_full_snapshot_archives() {
2225 gemachain_logger::setup();
2226 let temp_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2227 let min_slot = 123;
2228 let max_slot = 456;
2229 common_create_snapshot_archive_files(
2230 temp_snapshot_archives_dir.path(),
2231 min_slot,
2232 max_slot,
2233 0,
2234 0,
2235 );
2236
2237 let snapshot_archives = get_full_snapshot_archives(temp_snapshot_archives_dir);
2238 assert_eq!(snapshot_archives.len() as Slot, max_slot - min_slot);
2239 }
2240
2241 #[test]
2242 fn test_get_incremental_snapshot_archives() {
2243 gemachain_logger::setup();
2244 let temp_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2245 let min_full_snapshot_slot = 12;
2246 let max_full_snapshot_slot = 23;
2247 let min_incremental_snapshot_slot = 34;
2248 let max_incremental_snapshot_slot = 45;
2249 common_create_snapshot_archive_files(
2250 temp_snapshot_archives_dir.path(),
2251 min_full_snapshot_slot,
2252 max_full_snapshot_slot,
2253 min_incremental_snapshot_slot,
2254 max_incremental_snapshot_slot,
2255 );
2256
2257 let incremental_snapshot_archives =
2258 get_incremental_snapshot_archives(temp_snapshot_archives_dir);
2259 assert_eq!(
2260 incremental_snapshot_archives.len() as Slot,
2261 (max_full_snapshot_slot - min_full_snapshot_slot)
2262 * (max_incremental_snapshot_slot - min_incremental_snapshot_slot)
2263 );
2264 }
2265
2266 #[test]
2267 fn test_get_highest_full_snapshot_archive_slot() {
2268 gemachain_logger::setup();
2269 let temp_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2270 let min_slot = 123;
2271 let max_slot = 456;
2272 common_create_snapshot_archive_files(
2273 temp_snapshot_archives_dir.path(),
2274 min_slot,
2275 max_slot,
2276 0,
2277 0,
2278 );
2279
2280 assert_eq!(
2281 get_highest_full_snapshot_archive_slot(temp_snapshot_archives_dir.path()),
2282 Some(max_slot - 1)
2283 );
2284 }
2285
2286 #[test]
2287 fn test_get_highest_incremental_snapshot_slot() {
2288 gemachain_logger::setup();
2289 let temp_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2290 let min_full_snapshot_slot = 12;
2291 let max_full_snapshot_slot = 23;
2292 let min_incremental_snapshot_slot = 34;
2293 let max_incremental_snapshot_slot = 45;
2294 common_create_snapshot_archive_files(
2295 temp_snapshot_archives_dir.path(),
2296 min_full_snapshot_slot,
2297 max_full_snapshot_slot,
2298 min_incremental_snapshot_slot,
2299 max_incremental_snapshot_slot,
2300 );
2301
2302 for full_snapshot_slot in min_full_snapshot_slot..max_full_snapshot_slot {
2303 assert_eq!(
2304 get_highest_incremental_snapshot_archive_slot(
2305 temp_snapshot_archives_dir.path(),
2306 full_snapshot_slot
2307 ),
2308 Some(max_incremental_snapshot_slot - 1)
2309 );
2310 }
2311
2312 assert_eq!(
2313 get_highest_incremental_snapshot_archive_slot(
2314 temp_snapshot_archives_dir.path(),
2315 max_full_snapshot_slot
2316 ),
2317 None
2318 );
2319 }
2320
2321 fn common_test_purge_old_snapshot_archives(
2322 snapshot_names: &[&String],
2323 maximum_full_snapshot_archives_to_retain: usize,
2324 maximum_incremental_snapshot_archives_to_retain: usize,
2325 expected_snapshots: &[&String],
2326 ) {
2327 let temp_snap_dir = tempfile::TempDir::new().unwrap();
2328
2329 for snap_name in snapshot_names {
2330 let snap_path = temp_snap_dir.path().join(&snap_name);
2331 let mut _snap_file = File::create(snap_path);
2332 }
2333 purge_old_snapshot_archives(
2334 temp_snap_dir.path(),
2335 maximum_full_snapshot_archives_to_retain,
2336 maximum_incremental_snapshot_archives_to_retain,
2337 );
2338
2339 let mut retained_snaps = HashSet::new();
2340 for entry in fs::read_dir(temp_snap_dir.path()).unwrap() {
2341 let entry_path_buf = entry.unwrap().path();
2342 let entry_path = entry_path_buf.as_path();
2343 let snapshot_name = entry_path
2344 .file_name()
2345 .unwrap()
2346 .to_str()
2347 .unwrap()
2348 .to_string();
2349 retained_snaps.insert(snapshot_name);
2350 }
2351
2352 for snap_name in expected_snapshots {
2353 assert!(retained_snaps.contains(snap_name.as_str()));
2354 }
2355 assert!(retained_snaps.len() == expected_snapshots.len());
2356 }
2357
2358 #[test]
2359 fn test_purge_old_full_snapshot_archives() {
2360 let snap1_name = format!("snapshot-1-{}.tar.zst", Hash::default());
2363 let snap2_name = format!("snapshot-3-{}.tar.zst", Hash::default());
2364 let snap3_name = format!("snapshot-50-{}.tar.zst", Hash::default());
2365 let snapshot_names = vec![&snap1_name, &snap2_name, &snap3_name];
2366 let expected_snapshots = vec![&snap1_name, &snap3_name];
2367 common_test_purge_old_snapshot_archives(
2368 &snapshot_names,
2369 1,
2370 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2371 &expected_snapshots,
2372 );
2373
2374 common_test_purge_old_snapshot_archives(
2376 &snapshot_names,
2377 0,
2378 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2379 &expected_snapshots,
2380 );
2381
2382 let expected_snapshots = vec![&snap1_name, &snap2_name, &snap3_name];
2384 common_test_purge_old_snapshot_archives(
2385 &snapshot_names,
2386 2,
2387 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2388 &expected_snapshots,
2389 );
2390 }
2391
2392 #[test]
2396 fn test_purge_old_full_snapshot_archives_in_the_loop() {
2397 let snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2398 let maximum_snapshots_to_retain = 5;
2399 let starting_slot: Slot = 42;
2400
2401 for slot in (starting_slot..).take(100) {
2402 let full_snapshot_archive_file_name =
2403 format!("snapshot-{}-{}.tar", slot, Hash::default());
2404 let full_snapshot_archive_path = snapshot_archives_dir
2405 .as_ref()
2406 .join(full_snapshot_archive_file_name);
2407 File::create(full_snapshot_archive_path).unwrap();
2408
2409 if slot < starting_slot + maximum_snapshots_to_retain as Slot {
2411 continue;
2412 }
2413
2414 if slot % (maximum_snapshots_to_retain as Slot * 2) != 0 {
2416 continue;
2417 }
2418
2419 purge_old_snapshot_archives(
2420 &snapshot_archives_dir,
2421 maximum_snapshots_to_retain,
2422 usize::MAX,
2423 );
2424 let mut full_snapshot_archives = get_full_snapshot_archives(&snapshot_archives_dir);
2425 full_snapshot_archives.sort_unstable();
2426 assert_eq!(
2427 full_snapshot_archives.len(),
2428 maximum_snapshots_to_retain + 1
2429 );
2430 assert_eq!(
2431 full_snapshot_archives.first().unwrap().slot(),
2432 starting_slot
2433 );
2434 assert_eq!(full_snapshot_archives.last().unwrap().slot(), slot);
2435 for (i, full_snapshot_archive) in
2436 full_snapshot_archives.iter().skip(1).rev().enumerate()
2437 {
2438 assert_eq!(full_snapshot_archive.slot(), slot - i as Slot);
2439 }
2440 }
2441 }
2442
2443 #[test]
2444 fn test_purge_old_incremental_snapshot_archives() {
2445 let snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2446 let starting_slot = 100_000;
2447
2448 let maximum_incremental_snapshot_archives_to_retain =
2449 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN;
2450 let maximum_full_snapshot_archives_to_retain = DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN;
2451
2452 let incremental_snapshot_interval = 100;
2453 let num_incremental_snapshots_per_full_snapshot =
2454 maximum_incremental_snapshot_archives_to_retain * 2;
2455 let full_snapshot_interval =
2456 incremental_snapshot_interval * num_incremental_snapshots_per_full_snapshot;
2457
2458 let mut snapshot_filenames = vec![];
2459 (starting_slot..)
2460 .step_by(full_snapshot_interval)
2461 .take(maximum_full_snapshot_archives_to_retain * 2)
2462 .for_each(|full_snapshot_slot| {
2463 let snapshot_filename =
2464 format!("snapshot-{}-{}.tar", full_snapshot_slot, Hash::default());
2465 let snapshot_path = snapshot_archives_dir.path().join(&snapshot_filename);
2466 File::create(snapshot_path).unwrap();
2467 snapshot_filenames.push(snapshot_filename);
2468
2469 (full_snapshot_slot..)
2470 .step_by(incremental_snapshot_interval)
2471 .take(num_incremental_snapshots_per_full_snapshot)
2472 .skip(1)
2473 .for_each(|incremental_snapshot_slot| {
2474 let snapshot_filename = format!(
2475 "incremental-snapshot-{}-{}-{}.tar",
2476 full_snapshot_slot,
2477 incremental_snapshot_slot,
2478 Hash::default()
2479 );
2480 let snapshot_path = snapshot_archives_dir.path().join(&snapshot_filename);
2481 File::create(snapshot_path).unwrap();
2482 snapshot_filenames.push(snapshot_filename);
2483 });
2484 });
2485
2486 purge_old_snapshot_archives(
2487 snapshot_archives_dir.path(),
2488 maximum_full_snapshot_archives_to_retain,
2489 maximum_incremental_snapshot_archives_to_retain,
2490 );
2491
2492 let mut remaining_full_snapshot_archives =
2495 get_full_snapshot_archives(snapshot_archives_dir.path());
2496 assert_eq!(
2497 remaining_full_snapshot_archives.len(),
2498 maximum_full_snapshot_archives_to_retain + 1,
2499 );
2500 remaining_full_snapshot_archives.sort_unstable();
2501
2502 let mut remaining_incremental_snapshot_archives =
2504 get_incremental_snapshot_archives(snapshot_archives_dir.path());
2505 assert_eq!(
2506 remaining_incremental_snapshot_archives.len(),
2507 maximum_incremental_snapshot_archives_to_retain
2508 );
2509 remaining_incremental_snapshot_archives.sort_unstable();
2510
2511 let latest_full_snapshot_archive_slot =
2513 remaining_full_snapshot_archives.last().unwrap().slot();
2514 for incremental_snapshot_archive in &remaining_incremental_snapshot_archives {
2515 assert_eq!(
2516 incremental_snapshot_archive.base_slot(),
2517 latest_full_snapshot_archive_slot
2518 );
2519 }
2520
2521 let expected_remaing_incremental_snapshot_archive_slots =
2523 (latest_full_snapshot_archive_slot..)
2524 .step_by(incremental_snapshot_interval)
2525 .take(num_incremental_snapshots_per_full_snapshot)
2526 .skip(
2527 num_incremental_snapshots_per_full_snapshot
2528 - maximum_incremental_snapshot_archives_to_retain,
2529 )
2530 .collect::<Vec<_>>();
2531
2532 let actual_remaining_incremental_snapshot_archive_slots =
2533 remaining_incremental_snapshot_archives
2534 .iter()
2535 .map(|snapshot| snapshot.slot())
2536 .collect::<Vec<_>>();
2537 assert_eq!(
2538 actual_remaining_incremental_snapshot_archive_slots,
2539 expected_remaing_incremental_snapshot_archive_slots
2540 );
2541 }
2542
2543 #[test]
2544 fn test_purge_all_incremental_snapshot_archives_when_no_full_snapshot_archives() {
2545 let snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2546
2547 for snapshot_filenames in [
2548 format!("incremental-snapshot-100-120-{}.tar", Hash::default()),
2549 format!("incremental-snapshot-100-140-{}.tar", Hash::default()),
2550 format!("incremental-snapshot-100-160-{}.tar", Hash::default()),
2551 format!("incremental-snapshot-100-180-{}.tar", Hash::default()),
2552 format!("incremental-snapshot-200-220-{}.tar", Hash::default()),
2553 format!("incremental-snapshot-200-240-{}.tar", Hash::default()),
2554 format!("incremental-snapshot-200-260-{}.tar", Hash::default()),
2555 format!("incremental-snapshot-200-280-{}.tar", Hash::default()),
2556 ] {
2557 let snapshot_path = snapshot_archives_dir.path().join(&snapshot_filenames);
2558 File::create(snapshot_path).unwrap();
2559 }
2560
2561 purge_old_snapshot_archives(snapshot_archives_dir.path(), usize::MAX, usize::MAX);
2562
2563 let remaining_incremental_snapshot_archives =
2564 get_incremental_snapshot_archives(snapshot_archives_dir.path());
2565 assert!(remaining_incremental_snapshot_archives.is_empty());
2566 }
2567
2568 #[test]
2571 fn test_roundtrip_bank_to_and_from_full_snapshot_simple() {
2572 gemachain_logger::setup();
2573 let genesis_config = GenesisConfig::default();
2574 let original_bank = Bank::new_for_tests(&genesis_config);
2575
2576 while !original_bank.is_complete() {
2577 original_bank.register_tick(&Hash::new_unique());
2578 }
2579
2580 let accounts_dir = tempfile::TempDir::new().unwrap();
2581 let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
2582 let snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2583 let snapshot_archive_format = ArchiveFormat::Tar;
2584
2585 let snapshot_archive_info = bank_to_full_snapshot_archive(
2586 &bank_snapshots_dir,
2587 &original_bank,
2588 None,
2589 snapshot_archives_dir.path(),
2590 snapshot_archive_format,
2591 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2592 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2593 )
2594 .unwrap();
2595
2596 let (roundtrip_bank, _) = bank_from_snapshot_archives(
2597 &[PathBuf::from(accounts_dir.path())],
2598 &[],
2599 bank_snapshots_dir.path(),
2600 &snapshot_archive_info,
2601 None,
2602 &genesis_config,
2603 None,
2604 None,
2605 AccountSecondaryIndexes::default(),
2606 false,
2607 None,
2608 AccountShrinkThreshold::default(),
2609 false,
2610 false,
2611 false,
2612 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
2613 )
2614 .unwrap();
2615
2616 assert_eq!(original_bank, roundtrip_bank);
2617 }
2618
2619 #[test]
2623 fn test_roundtrip_bank_to_and_from_snapshot_complex() {
2624 gemachain_logger::setup();
2625 let collector = Pubkey::new_unique();
2626 let key1 = Keypair::new();
2627 let key2 = Keypair::new();
2628 let key3 = Keypair::new();
2629 let key4 = Keypair::new();
2630 let key5 = Keypair::new();
2631
2632 let (genesis_config, mint_keypair) = create_genesis_config(1_000_000);
2633 let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
2634 bank0.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
2635 bank0.transfer(2, &mint_keypair, &key2.pubkey()).unwrap();
2636 bank0.transfer(3, &mint_keypair, &key3.pubkey()).unwrap();
2637 while !bank0.is_complete() {
2638 bank0.register_tick(&Hash::new_unique());
2639 }
2640
2641 let slot = 1;
2642 let bank1 = Arc::new(Bank::new_from_parent(&bank0, &collector, slot));
2643 bank1.transfer(3, &mint_keypair, &key3.pubkey()).unwrap();
2644 bank1.transfer(4, &mint_keypair, &key4.pubkey()).unwrap();
2645 bank1.transfer(5, &mint_keypair, &key5.pubkey()).unwrap();
2646 while !bank1.is_complete() {
2647 bank1.register_tick(&Hash::new_unique());
2648 }
2649
2650 let slot = slot + 1;
2651 let bank2 = Arc::new(Bank::new_from_parent(&bank1, &collector, slot));
2652 bank2.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
2653 while !bank2.is_complete() {
2654 bank2.register_tick(&Hash::new_unique());
2655 }
2656
2657 let slot = slot + 1;
2658 let bank3 = Arc::new(Bank::new_from_parent(&bank2, &collector, slot));
2659 bank3.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
2660 while !bank3.is_complete() {
2661 bank3.register_tick(&Hash::new_unique());
2662 }
2663
2664 let slot = slot + 1;
2665 let bank4 = Arc::new(Bank::new_from_parent(&bank3, &collector, slot));
2666 bank4.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
2667 while !bank4.is_complete() {
2668 bank4.register_tick(&Hash::new_unique());
2669 }
2670
2671 let accounts_dir = tempfile::TempDir::new().unwrap();
2672 let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
2673 let snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2674 let snapshot_archive_format = ArchiveFormat::TarGzip;
2675
2676 let full_snapshot_archive_info = bank_to_full_snapshot_archive(
2677 bank_snapshots_dir.path(),
2678 &bank4,
2679 None,
2680 snapshot_archives_dir.path(),
2681 snapshot_archive_format,
2682 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2683 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2684 )
2685 .unwrap();
2686
2687 let (roundtrip_bank, _) = bank_from_snapshot_archives(
2688 &[PathBuf::from(accounts_dir.path())],
2689 &[],
2690 bank_snapshots_dir.path(),
2691 &full_snapshot_archive_info,
2692 None,
2693 &genesis_config,
2694 None,
2695 None,
2696 AccountSecondaryIndexes::default(),
2697 false,
2698 None,
2699 AccountShrinkThreshold::default(),
2700 false,
2701 false,
2702 false,
2703 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
2704 )
2705 .unwrap();
2706
2707 assert_eq!(*bank4, roundtrip_bank);
2708 }
2709
2710 #[test]
2720 fn test_roundtrip_bank_to_and_from_incremental_snapshot() {
2721 gemachain_logger::setup();
2722 let collector = Pubkey::new_unique();
2723 let key1 = Keypair::new();
2724 let key2 = Keypair::new();
2725 let key3 = Keypair::new();
2726 let key4 = Keypair::new();
2727 let key5 = Keypair::new();
2728
2729 let (genesis_config, mint_keypair) = create_genesis_config(1_000_000);
2730 let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
2731 bank0.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
2732 bank0.transfer(2, &mint_keypair, &key2.pubkey()).unwrap();
2733 bank0.transfer(3, &mint_keypair, &key3.pubkey()).unwrap();
2734 while !bank0.is_complete() {
2735 bank0.register_tick(&Hash::new_unique());
2736 }
2737
2738 let slot = 1;
2739 let bank1 = Arc::new(Bank::new_from_parent(&bank0, &collector, slot));
2740 bank1.transfer(3, &mint_keypair, &key3.pubkey()).unwrap();
2741 bank1.transfer(4, &mint_keypair, &key4.pubkey()).unwrap();
2742 bank1.transfer(5, &mint_keypair, &key5.pubkey()).unwrap();
2743 while !bank1.is_complete() {
2744 bank1.register_tick(&Hash::new_unique());
2745 }
2746
2747 let accounts_dir = tempfile::TempDir::new().unwrap();
2748 let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
2749 let snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2750 let snapshot_archive_format = ArchiveFormat::TarZstd;
2751
2752 let full_snapshot_slot = slot;
2753 let full_snapshot_archive_info = bank_to_full_snapshot_archive(
2754 bank_snapshots_dir.path(),
2755 &bank1,
2756 None,
2757 snapshot_archives_dir.path(),
2758 snapshot_archive_format,
2759 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2760 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2761 )
2762 .unwrap();
2763
2764 let slot = slot + 1;
2765 let bank2 = Arc::new(Bank::new_from_parent(&bank1, &collector, slot));
2766 bank2.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
2767 while !bank2.is_complete() {
2768 bank2.register_tick(&Hash::new_unique());
2769 }
2770
2771 let slot = slot + 1;
2772 let bank3 = Arc::new(Bank::new_from_parent(&bank2, &collector, slot));
2773 bank3.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
2774 while !bank3.is_complete() {
2775 bank3.register_tick(&Hash::new_unique());
2776 }
2777
2778 let slot = slot + 1;
2779 let bank4 = Arc::new(Bank::new_from_parent(&bank3, &collector, slot));
2780 bank4.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
2781 while !bank4.is_complete() {
2782 bank4.register_tick(&Hash::new_unique());
2783 }
2784
2785 let incremental_snapshot_archive_info = bank_to_incremental_snapshot_archive(
2786 bank_snapshots_dir.path(),
2787 &bank4,
2788 full_snapshot_slot,
2789 None,
2790 snapshot_archives_dir.path(),
2791 snapshot_archive_format,
2792 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2793 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2794 )
2795 .unwrap();
2796
2797 let (roundtrip_bank, _) = bank_from_snapshot_archives(
2798 &[PathBuf::from(accounts_dir.path())],
2799 &[],
2800 bank_snapshots_dir.path(),
2801 &full_snapshot_archive_info,
2802 Some(&incremental_snapshot_archive_info),
2803 &genesis_config,
2804 None,
2805 None,
2806 AccountSecondaryIndexes::default(),
2807 false,
2808 None,
2809 AccountShrinkThreshold::default(),
2810 false,
2811 false,
2812 false,
2813 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
2814 )
2815 .unwrap();
2816
2817 assert_eq!(*bank4, roundtrip_bank);
2818 }
2819
2820 #[test]
2822 fn test_bank_from_latest_snapshot_archives() {
2823 gemachain_logger::setup();
2824 let collector = Pubkey::new_unique();
2825 let key1 = Keypair::new();
2826 let key2 = Keypair::new();
2827 let key3 = Keypair::new();
2828
2829 let (genesis_config, mint_keypair) = create_genesis_config(1_000_000);
2830 let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
2831 bank0.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
2832 bank0.transfer(2, &mint_keypair, &key2.pubkey()).unwrap();
2833 bank0.transfer(3, &mint_keypair, &key3.pubkey()).unwrap();
2834 while !bank0.is_complete() {
2835 bank0.register_tick(&Hash::new_unique());
2836 }
2837
2838 let slot = 1;
2839 let bank1 = Arc::new(Bank::new_from_parent(&bank0, &collector, slot));
2840 bank1.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
2841 bank1.transfer(2, &mint_keypair, &key2.pubkey()).unwrap();
2842 bank1.transfer(3, &mint_keypair, &key3.pubkey()).unwrap();
2843 while !bank1.is_complete() {
2844 bank1.register_tick(&Hash::new_unique());
2845 }
2846
2847 let accounts_dir = tempfile::TempDir::new().unwrap();
2848 let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
2849 let snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2850 let snapshot_archive_format = ArchiveFormat::Tar;
2851
2852 let full_snapshot_slot = slot;
2853 bank_to_full_snapshot_archive(
2854 &bank_snapshots_dir,
2855 &bank1,
2856 None,
2857 &snapshot_archives_dir,
2858 snapshot_archive_format,
2859 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2860 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2861 )
2862 .unwrap();
2863
2864 let slot = slot + 1;
2865 let bank2 = Arc::new(Bank::new_from_parent(&bank1, &collector, slot));
2866 bank2.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
2867 while !bank2.is_complete() {
2868 bank2.register_tick(&Hash::new_unique());
2869 }
2870
2871 let slot = slot + 1;
2872 let bank3 = Arc::new(Bank::new_from_parent(&bank2, &collector, slot));
2873 bank3.transfer(2, &mint_keypair, &key2.pubkey()).unwrap();
2874 while !bank3.is_complete() {
2875 bank3.register_tick(&Hash::new_unique());
2876 }
2877
2878 let slot = slot + 1;
2879 let bank4 = Arc::new(Bank::new_from_parent(&bank3, &collector, slot));
2880 bank4.transfer(3, &mint_keypair, &key3.pubkey()).unwrap();
2881 while !bank4.is_complete() {
2882 bank4.register_tick(&Hash::new_unique());
2883 }
2884
2885 bank_to_incremental_snapshot_archive(
2886 &bank_snapshots_dir,
2887 &bank4,
2888 full_snapshot_slot,
2889 None,
2890 &snapshot_archives_dir,
2891 snapshot_archive_format,
2892 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2893 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2894 )
2895 .unwrap();
2896
2897 let (deserialized_bank, ..) = bank_from_latest_snapshot_archives(
2898 &bank_snapshots_dir,
2899 &snapshot_archives_dir,
2900 &[accounts_dir.as_ref().to_path_buf()],
2901 &[],
2902 &genesis_config,
2903 None,
2904 None,
2905 AccountSecondaryIndexes::default(),
2906 false,
2907 None,
2908 AccountShrinkThreshold::default(),
2909 false,
2910 false,
2911 false,
2912 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
2913 )
2914 .unwrap();
2915
2916 assert_eq!(deserialized_bank, *bank4);
2917 }
2918
2919 #[test]
2942 fn test_incremental_snapshots_handle_zero_carat_accounts() {
2943 gemachain_logger::setup();
2944
2945 let collector = Pubkey::new_unique();
2946 let key1 = Keypair::new();
2947 let key2 = Keypair::new();
2948
2949 let accounts_dir = tempfile::TempDir::new().unwrap();
2950 let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
2951 let snapshot_archives_dir = tempfile::TempDir::new().unwrap();
2952 let snapshot_archive_format = ArchiveFormat::Tar;
2953
2954 let (genesis_config, mint_keypair) = create_genesis_config(1_000_000);
2955
2956 let carats_to_transfer = 123_456;
2957 let bank0 = Arc::new(Bank::new_with_paths_for_tests(
2958 &genesis_config,
2959 vec![accounts_dir.path().to_path_buf()],
2960 &[],
2961 None,
2962 None,
2963 AccountSecondaryIndexes::default(),
2964 false,
2965 AccountShrinkThreshold::default(),
2966 false,
2967 ));
2968 bank0
2969 .transfer(carats_to_transfer, &mint_keypair, &key2.pubkey())
2970 .unwrap();
2971 while !bank0.is_complete() {
2972 bank0.register_tick(&Hash::new_unique());
2973 }
2974
2975 let slot = 1;
2976 let bank1 = Arc::new(Bank::new_from_parent(&bank0, &collector, slot));
2977 bank1
2978 .transfer(carats_to_transfer, &key2, &key1.pubkey())
2979 .unwrap();
2980 while !bank1.is_complete() {
2981 bank1.register_tick(&Hash::new_unique());
2982 }
2983
2984 let full_snapshot_slot = slot;
2985 let full_snapshot_archive_info = bank_to_full_snapshot_archive(
2986 bank_snapshots_dir.path(),
2987 &bank1,
2988 None,
2989 snapshot_archives_dir.path(),
2990 snapshot_archive_format,
2991 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2992 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
2993 )
2994 .unwrap();
2995
2996 let slot = slot + 1;
2997 let bank2 = Arc::new(Bank::new_from_parent(&bank1, &collector, slot));
2998 let tx = SanitizedTransaction::try_from(system_transaction::transfer(
2999 &key1,
3000 &key2.pubkey(),
3001 carats_to_transfer,
3002 bank2.last_blockhash(),
3003 ))
3004 .unwrap();
3005 let fee = bank2
3006 .get_fee_for_message(&bank2.last_blockhash(), tx.message())
3007 .unwrap();
3008 let tx = system_transaction::transfer(
3009 &key1,
3010 &key2.pubkey(),
3011 carats_to_transfer - fee,
3012 bank2.last_blockhash(),
3013 );
3014 bank2.process_transaction(&tx).unwrap();
3015 assert_eq!(
3016 bank2.get_balance(&key1.pubkey()),
3017 0,
3018 "Ensure Account1's balance is zero"
3019 );
3020 while !bank2.is_complete() {
3021 bank2.register_tick(&Hash::new_unique());
3022 }
3023
3024 let incremental_snapshot_archive_info = bank_to_incremental_snapshot_archive(
3027 bank_snapshots_dir.path(),
3028 &bank2,
3029 full_snapshot_slot,
3030 None,
3031 snapshot_archives_dir.path(),
3032 snapshot_archive_format,
3033 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3034 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3035 )
3036 .unwrap();
3037 let (deserialized_bank, _) = bank_from_snapshot_archives(
3038 &[accounts_dir.path().to_path_buf()],
3039 &[],
3040 bank_snapshots_dir.path(),
3041 &full_snapshot_archive_info,
3042 Some(&incremental_snapshot_archive_info),
3043 &genesis_config,
3044 None,
3045 None,
3046 AccountSecondaryIndexes::default(),
3047 false,
3048 None,
3049 AccountShrinkThreshold::default(),
3050 false,
3051 false,
3052 false,
3053 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
3054 )
3055 .unwrap();
3056 assert_eq!(
3057 deserialized_bank, *bank2,
3058 "Ensure rebuilding from an incremental snapshot works"
3059 );
3060
3061 let slot = slot + 1;
3062 let bank3 = Arc::new(Bank::new_from_parent(&bank2, &collector, slot));
3063 bank3
3065 .transfer(carats_to_transfer, &mint_keypair, &key2.pubkey())
3066 .unwrap();
3067 while !bank3.is_complete() {
3068 bank3.register_tick(&Hash::new_unique());
3069 }
3070
3071 let slot = slot + 1;
3072 let bank4 = Arc::new(Bank::new_from_parent(&bank3, &collector, slot));
3073 while !bank4.is_complete() {
3074 bank4.register_tick(&Hash::new_unique());
3075 }
3076
3077 bank4.squash();
3079 bank4.clean_accounts(true, false, Some(full_snapshot_slot));
3080 assert!(
3081 bank4.get_account_modified_slot(&key1.pubkey()).is_none(),
3082 "Ensure Account1 has been cleaned and purged from AccountsDb"
3083 );
3084
3085 let incremental_snapshot_archive_info = bank_to_incremental_snapshot_archive(
3088 bank_snapshots_dir.path(),
3089 &bank4,
3090 full_snapshot_slot,
3091 None,
3092 snapshot_archives_dir.path(),
3093 snapshot_archive_format,
3094 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3095 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
3096 )
3097 .unwrap();
3098
3099 let (deserialized_bank, _) = bank_from_snapshot_archives(
3100 &[accounts_dir.path().to_path_buf()],
3101 &[],
3102 bank_snapshots_dir.path(),
3103 &full_snapshot_archive_info,
3104 Some(&incremental_snapshot_archive_info),
3105 &genesis_config,
3106 None,
3107 None,
3108 AccountSecondaryIndexes::default(),
3109 false,
3110 None,
3111 AccountShrinkThreshold::default(),
3112 false,
3113 false,
3114 false,
3115 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
3116 )
3117 .unwrap();
3118 assert_eq!(
3119 deserialized_bank, *bank4,
3120 "Ensure rebuilding from an incremental snapshot works",
3121 );
3122 assert!(
3123 deserialized_bank
3124 .get_account_modified_slot(&key1.pubkey())
3125 .is_none(),
3126 "Ensure Account1 has not been brought back from the dead"
3127 );
3128 }
3129}