1#[cfg(all(target_os = "linux", target_env = "gnu"))]
2use std::ffi::{CStr, CString};
3use {
4 crate::{
5 bank::{Bank, BankFieldsToDeserialize, BankFieldsToSerialize, BankHashStats, BankRc},
6 epoch_stakes::VersionedEpochStakes,
7 rent_collector::RentCollector,
8 runtime_config::RuntimeConfig,
9 snapshot_utils::StorageAndNextAccountsFileId,
10 stake_account::StakeAccount,
11 stakes::{serialize_stake_accounts_to_delegation_format, Stakes},
12 },
13 agave_snapshots::error::SnapshotError,
14 bincode::{self, config::Options, Error},
15 log::*,
16 serde::{de::DeserializeOwned, Deserialize, Serialize},
17 solana_accounts_db::{
18 accounts::Accounts,
19 accounts_db::{
20 AccountStorageEntry, AccountsDb, AccountsDbConfig, AccountsFileId,
21 AtomicAccountsFileId, IndexGenerationInfo,
22 },
23 accounts_file::{AccountsFile, StorageAccess},
24 accounts_hash::AccountsLtHash,
25 accounts_update_notifier_interface::AccountsUpdateNotifier,
26 ancestors::AncestorsForSerialization,
27 blockhash_queue::BlockhashQueue,
28 ObsoleteAccounts,
29 },
30 solana_clock::{Epoch, Slot, UnixTimestamp},
31 solana_epoch_schedule::EpochSchedule,
32 solana_fee_calculator::{FeeCalculator, FeeRateGovernor},
33 solana_genesis_config::GenesisConfig,
34 solana_hard_forks::HardForks,
35 solana_hash::Hash,
36 solana_inflation::Inflation,
37 solana_lattice_hash::lt_hash::LtHash,
38 solana_measure::measure::Measure,
39 solana_pubkey::Pubkey,
40 solana_serde::default_on_eof,
41 solana_stake_interface::state::Delegation,
42 std::{
43 cell::RefCell,
44 collections::{HashMap, HashSet},
45 io::{self, BufReader, BufWriter, Read, Write},
46 path::{Path, PathBuf},
47 result::Result,
48 sync::{
49 atomic::{AtomicBool, AtomicUsize, Ordering},
50 Arc,
51 },
52 time::Instant,
53 },
54 storage::SerializableStorage,
55 types::SerdeAccountsLtHash,
56};
57
58mod obsolete_accounts;
59mod status_cache;
60mod storage;
61mod tests;
62mod types;
63mod utils;
64
65pub(crate) use {
66 obsolete_accounts::SerdeObsoleteAccountsMap,
67 status_cache::{deserialize_status_cache, serialize_status_cache},
68 storage::{SerializableAccountStorageEntry, SerializedAccountsFileId},
69};
70
71const MAX_STREAM_SIZE: u64 = 32 * 1024 * 1024 * 1024;
72
73#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
74#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
75pub struct AccountsDbFields<T>(
76 HashMap<Slot, Vec<T>>,
77 u64, Slot,
79 BankHashInfo,
80 #[serde(deserialize_with = "default_on_eof")]
82 Vec<Slot>,
83 #[serde(deserialize_with = "default_on_eof")]
85 Vec<(Slot, Hash)>,
86);
87
88#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
89#[cfg_attr(feature = "dev-context-only-utils", derive(Default, PartialEq))]
90#[derive(Serialize, Deserialize, Clone, Debug)]
91pub struct ObsoleteIncrementalSnapshotPersistence {
92 pub full_slot: u64,
93 pub full_hash: [u8; 32],
94 pub full_capitalization: u64,
95 pub incremental_hash: [u8; 32],
96 pub incremental_capitalization: u64,
97}
98
99#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
100#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq)]
101struct BankHashInfo {
102 obsolete_accounts_delta_hash: [u8; 32],
103 obsolete_accounts_hash: [u8; 32],
104 stats: BankHashStats,
105}
106
107#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
108#[derive(Default, Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
109struct UnusedAccounts {
110 unused1: HashSet<Pubkey>,
111 unused2: HashSet<Pubkey>,
112 unused3: HashMap<Pubkey, u64>,
113}
114
115#[derive(Clone, Deserialize)]
119struct DeserializableVersionedBank {
120 blockhash_queue: BlockhashQueue,
121 ancestors: AncestorsForSerialization,
122 hash: Hash,
123 parent_hash: Hash,
124 parent_slot: Slot,
125 hard_forks: HardForks,
126 transaction_count: u64,
127 tick_height: u64,
128 signature_count: u64,
129 capitalization: u64,
130 max_tick_height: u64,
131 hashes_per_tick: Option<u64>,
132 ticks_per_slot: u64,
133 ns_per_slot: u128,
134 genesis_creation_time: UnixTimestamp,
135 slots_per_year: f64,
136 accounts_data_len: u64,
137 slot: Slot,
138 epoch: Epoch,
139 block_height: u64,
140 collector_id: Pubkey,
141 collector_fees: u64,
142 _fee_calculator: FeeCalculator,
143 fee_rate_governor: FeeRateGovernor,
144 _collected_rent: u64,
145 rent_collector: RentCollector,
146 epoch_schedule: EpochSchedule,
147 inflation: Inflation,
148 stakes: Stakes<Delegation>,
149 #[allow(dead_code)]
150 unused_accounts: UnusedAccounts,
151 unused_epoch_stakes: HashMap<Epoch, ()>,
152 is_delta: bool,
153}
154
155impl From<DeserializableVersionedBank> for BankFieldsToDeserialize {
156 fn from(dvb: DeserializableVersionedBank) -> Self {
157 const LT_HASH_CANARY: LtHash = LtHash([0xCAFE; LtHash::NUM_ELEMENTS]);
160 BankFieldsToDeserialize {
161 blockhash_queue: dvb.blockhash_queue,
162 ancestors: dvb.ancestors,
163 hash: dvb.hash,
164 parent_hash: dvb.parent_hash,
165 parent_slot: dvb.parent_slot,
166 hard_forks: dvb.hard_forks,
167 transaction_count: dvb.transaction_count,
168 tick_height: dvb.tick_height,
169 signature_count: dvb.signature_count,
170 capitalization: dvb.capitalization,
171 max_tick_height: dvb.max_tick_height,
172 hashes_per_tick: dvb.hashes_per_tick,
173 ticks_per_slot: dvb.ticks_per_slot,
174 ns_per_slot: dvb.ns_per_slot,
175 genesis_creation_time: dvb.genesis_creation_time,
176 slots_per_year: dvb.slots_per_year,
177 accounts_data_len: dvb.accounts_data_len,
178 slot: dvb.slot,
179 epoch: dvb.epoch,
180 block_height: dvb.block_height,
181 collector_id: dvb.collector_id,
182 collector_fees: dvb.collector_fees,
183 fee_rate_governor: dvb.fee_rate_governor,
184 rent_collector: dvb.rent_collector,
185 epoch_schedule: dvb.epoch_schedule,
186 inflation: dvb.inflation,
187 stakes: dvb.stakes,
188 is_delta: dvb.is_delta,
189 versioned_epoch_stakes: HashMap::default(), accounts_lt_hash: AccountsLtHash(LT_HASH_CANARY), bank_hash_stats: BankHashStats::default(), }
193 }
194}
195
196#[derive(Serialize)]
199struct SerializableVersionedBank {
200 blockhash_queue: BlockhashQueue,
201 ancestors: AncestorsForSerialization,
202 hash: Hash,
203 parent_hash: Hash,
204 parent_slot: Slot,
205 hard_forks: HardForks,
206 transaction_count: u64,
207 tick_height: u64,
208 signature_count: u64,
209 capitalization: u64,
210 max_tick_height: u64,
211 hashes_per_tick: Option<u64>,
212 ticks_per_slot: u64,
213 ns_per_slot: u128,
214 genesis_creation_time: UnixTimestamp,
215 slots_per_year: f64,
216 accounts_data_len: u64,
217 slot: Slot,
218 epoch: Epoch,
219 block_height: u64,
220 collector_id: Pubkey,
221 collector_fees: u64,
222 fee_calculator: FeeCalculator,
223 fee_rate_governor: FeeRateGovernor,
224 collected_rent: u64,
225 rent_collector: RentCollector,
226 epoch_schedule: EpochSchedule,
227 inflation: Inflation,
228 #[serde(serialize_with = "serialize_stake_accounts_to_delegation_format")]
229 stakes: Stakes<StakeAccount<Delegation>>,
230 unused_accounts: UnusedAccounts,
231 unused_epoch_stakes: HashMap<Epoch, ()>,
232 is_delta: bool,
233}
234
235impl From<BankFieldsToSerialize> for SerializableVersionedBank {
236 fn from(rhs: BankFieldsToSerialize) -> Self {
237 Self {
238 blockhash_queue: rhs.blockhash_queue,
239 ancestors: rhs.ancestors,
240 hash: rhs.hash,
241 parent_hash: rhs.parent_hash,
242 parent_slot: rhs.parent_slot,
243 hard_forks: rhs.hard_forks,
244 transaction_count: rhs.transaction_count,
245 tick_height: rhs.tick_height,
246 signature_count: rhs.signature_count,
247 capitalization: rhs.capitalization,
248 max_tick_height: rhs.max_tick_height,
249 hashes_per_tick: rhs.hashes_per_tick,
250 ticks_per_slot: rhs.ticks_per_slot,
251 ns_per_slot: rhs.ns_per_slot,
252 genesis_creation_time: rhs.genesis_creation_time,
253 slots_per_year: rhs.slots_per_year,
254 accounts_data_len: rhs.accounts_data_len,
255 slot: rhs.slot,
256 epoch: rhs.epoch,
257 block_height: rhs.block_height,
258 collector_id: rhs.collector_id,
259 collector_fees: rhs.collector_fees,
260 fee_calculator: FeeCalculator::default(),
261 fee_rate_governor: rhs.fee_rate_governor,
262 collected_rent: u64::default(),
263 rent_collector: rhs.rent_collector,
264 epoch_schedule: rhs.epoch_schedule,
265 inflation: rhs.inflation,
266 stakes: rhs.stakes,
267 unused_accounts: UnusedAccounts::default(),
268 unused_epoch_stakes: HashMap::default(),
269 is_delta: rhs.is_delta,
270 }
271 }
272}
273
274#[cfg(feature = "frozen-abi")]
275impl solana_frozen_abi::abi_example::TransparentAsHelper for SerializableVersionedBank {}
276
277pub struct SnapshotStreams<'a, R> {
280 pub full_snapshot_stream: &'a mut BufReader<R>,
281 pub incremental_snapshot_stream: Option<&'a mut BufReader<R>>,
282}
283
284#[derive(Debug)]
287pub struct SnapshotBankFields {
288 full: BankFieldsToDeserialize,
289 incremental: Option<BankFieldsToDeserialize>,
290}
291
292impl SnapshotBankFields {
293 pub fn new(
294 full: BankFieldsToDeserialize,
295 incremental: Option<BankFieldsToDeserialize>,
296 ) -> Self {
297 Self { full, incremental }
298 }
299
300 pub fn collapse_into(self) -> BankFieldsToDeserialize {
302 self.incremental.unwrap_or(self.full)
303 }
304}
305
306#[derive(Debug)]
309pub struct SnapshotAccountsDbFields<T> {
310 full_snapshot_accounts_db_fields: AccountsDbFields<T>,
311 incremental_snapshot_accounts_db_fields: Option<AccountsDbFields<T>>,
312}
313
314impl<T> SnapshotAccountsDbFields<T> {
315 pub fn new(
316 full_snapshot_accounts_db_fields: AccountsDbFields<T>,
317 incremental_snapshot_accounts_db_fields: Option<AccountsDbFields<T>>,
318 ) -> Self {
319 Self {
320 full_snapshot_accounts_db_fields,
321 incremental_snapshot_accounts_db_fields,
322 }
323 }
324
325 pub fn collapse_into(self) -> Result<AccountsDbFields<T>, Error> {
330 match self.incremental_snapshot_accounts_db_fields {
331 None => Ok(self.full_snapshot_accounts_db_fields),
332 Some(AccountsDbFields(
333 mut incremental_snapshot_storages,
334 incremental_snapshot_version,
335 incremental_snapshot_slot,
336 incremental_snapshot_bank_hash_info,
337 incremental_snapshot_historical_roots,
338 incremental_snapshot_historical_roots_with_hash,
339 )) => {
340 let full_snapshot_storages = self.full_snapshot_accounts_db_fields.0;
341 let full_snapshot_slot = self.full_snapshot_accounts_db_fields.2;
342
343 incremental_snapshot_storages.retain(|slot, _| *slot > full_snapshot_slot);
345
346 incremental_snapshot_storages
348 .iter()
349 .all(|storage_entry| !full_snapshot_storages.contains_key(storage_entry.0))
350 .then_some(())
351 .ok_or_else(|| {
352 io::Error::new(
353 io::ErrorKind::InvalidData,
354 "Snapshots are incompatible: There are storages for the same slot in \
355 both the full snapshot and the incremental snapshot!",
356 )
357 })?;
358
359 let mut combined_storages = full_snapshot_storages;
360 combined_storages.extend(incremental_snapshot_storages);
361
362 Ok(AccountsDbFields(
363 combined_storages,
364 incremental_snapshot_version,
365 incremental_snapshot_slot,
366 incremental_snapshot_bank_hash_info,
367 incremental_snapshot_historical_roots,
368 incremental_snapshot_historical_roots_with_hash,
369 ))
370 }
371 }
372 }
373}
374
375pub(crate) fn serialize_into<W, T>(writer: W, value: &T) -> bincode::Result<()>
376where
377 W: Write,
378 T: Serialize,
379{
380 bincode::options()
381 .with_fixint_encoding()
382 .with_limit(MAX_STREAM_SIZE)
383 .serialize_into(writer, value)
384}
385
386pub(crate) fn deserialize_from<R, T>(reader: R) -> bincode::Result<T>
387where
388 R: Read,
389 T: DeserializeOwned,
390{
391 bincode::options()
392 .with_limit(MAX_STREAM_SIZE)
393 .with_fixint_encoding()
394 .allow_trailing_bytes()
395 .deserialize_from::<R, T>(reader)
396}
397
398fn deserialize_accounts_db_fields<R>(
399 stream: &mut BufReader<R>,
400) -> Result<AccountsDbFields<SerializableAccountStorageEntry>, Error>
401where
402 R: Read,
403{
404 deserialize_from::<_, _>(stream)
405}
406
407#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
414#[cfg_attr(feature = "dev-context-only-utils", derive(PartialEq))]
415#[derive(Clone, Debug, Deserialize)]
416struct ExtraFieldsToDeserialize {
417 #[serde(deserialize_with = "default_on_eof")]
418 lamports_per_signature: u64,
419 #[serde(deserialize_with = "default_on_eof")]
420 _obsolete_incremental_snapshot_persistence: Option<ObsoleteIncrementalSnapshotPersistence>,
421 #[serde(deserialize_with = "default_on_eof")]
422 _obsolete_epoch_accounts_hash: Option<Hash>,
423 #[serde(deserialize_with = "default_on_eof")]
424 versioned_epoch_stakes: HashMap<u64, VersionedEpochStakes>,
425 #[serde(deserialize_with = "default_on_eof")]
426 accounts_lt_hash: Option<SerdeAccountsLtHash>,
427}
428
429#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
436#[cfg_attr(feature = "dev-context-only-utils", derive(Default, PartialEq))]
437#[derive(Debug, Serialize)]
438pub struct ExtraFieldsToSerialize {
439 pub lamports_per_signature: u64,
440 pub obsolete_incremental_snapshot_persistence: Option<ObsoleteIncrementalSnapshotPersistence>,
441 pub obsolete_epoch_accounts_hash: Option<Hash>,
442 pub versioned_epoch_stakes: HashMap<u64, VersionedEpochStakes>,
443 pub accounts_lt_hash: Option<SerdeAccountsLtHash>,
444}
445
446fn deserialize_bank_fields<R>(
447 mut stream: &mut BufReader<R>,
448) -> Result<
449 (
450 BankFieldsToDeserialize,
451 AccountsDbFields<SerializableAccountStorageEntry>,
452 ),
453 Error,
454>
455where
456 R: Read,
457{
458 let deserializable_bank = deserialize_from::<_, DeserializableVersionedBank>(&mut stream)?;
459 if !deserializable_bank.unused_epoch_stakes.is_empty() {
460 return Err(Box::new(bincode::ErrorKind::Custom(
461 "Expected deserialized bank's unused_epoch_stakes field to be empty".to_string(),
462 )));
463 }
464 let mut bank_fields = BankFieldsToDeserialize::from(deserializable_bank);
465 let accounts_db_fields = deserialize_accounts_db_fields(stream)?;
466 let extra_fields = deserialize_from(stream)?;
467
468 let ExtraFieldsToDeserialize {
470 lamports_per_signature,
471 _obsolete_incremental_snapshot_persistence,
472 _obsolete_epoch_accounts_hash,
473 versioned_epoch_stakes,
474 accounts_lt_hash,
475 } = extra_fields;
476
477 bank_fields.fee_rate_governor = bank_fields
478 .fee_rate_governor
479 .clone_with_lamports_per_signature(lamports_per_signature);
480 bank_fields.versioned_epoch_stakes = versioned_epoch_stakes;
481 bank_fields.accounts_lt_hash = accounts_lt_hash
482 .expect("snapshot must have accounts_lt_hash")
483 .into();
484
485 Ok((bank_fields, accounts_db_fields))
486}
487
488pub(crate) fn snapshot_storage_lengths_from_fields(
490 accounts_db_fields: &AccountsDbFields<SerializableAccountStorageEntry>,
491) -> HashMap<Slot, HashMap<SerializedAccountsFileId, usize>> {
492 let AccountsDbFields(snapshot_storage, ..) = &accounts_db_fields;
493 snapshot_storage
494 .iter()
495 .map(|(slot, slot_storage)| {
496 (
497 *slot,
498 slot_storage
499 .iter()
500 .map(|storage_entry| (storage_entry.id(), storage_entry.current_len()))
501 .collect(),
502 )
503 })
504 .collect()
505}
506
507pub(crate) fn fields_from_stream<R: Read>(
508 snapshot_stream: &mut BufReader<R>,
509) -> std::result::Result<
510 (
511 BankFieldsToDeserialize,
512 AccountsDbFields<SerializableAccountStorageEntry>,
513 ),
514 Error,
515> {
516 deserialize_bank_fields(snapshot_stream)
517}
518
519#[cfg(feature = "dev-context-only-utils")]
520pub(crate) fn fields_from_streams(
521 snapshot_streams: &mut SnapshotStreams<impl Read>,
522) -> std::result::Result<
523 (
524 SnapshotBankFields,
525 SnapshotAccountsDbFields<SerializableAccountStorageEntry>,
526 ),
527 Error,
528> {
529 let (full_snapshot_bank_fields, full_snapshot_accounts_db_fields) =
530 fields_from_stream(snapshot_streams.full_snapshot_stream)?;
531 let (incremental_snapshot_bank_fields, incremental_snapshot_accounts_db_fields) =
532 snapshot_streams
533 .incremental_snapshot_stream
534 .as_mut()
535 .map(|stream| fields_from_stream(stream))
536 .transpose()?
537 .unzip();
538
539 let snapshot_bank_fields = SnapshotBankFields {
540 full: full_snapshot_bank_fields,
541 incremental: incremental_snapshot_bank_fields,
542 };
543 let snapshot_accounts_db_fields = SnapshotAccountsDbFields {
544 full_snapshot_accounts_db_fields,
545 incremental_snapshot_accounts_db_fields,
546 };
547 Ok((snapshot_bank_fields, snapshot_accounts_db_fields))
548}
549
550#[derive(Debug)]
552pub struct BankFromStreamsInfo {
553 pub calculated_accounts_lt_hash: AccountsLtHash,
556}
557
558#[allow(clippy::too_many_arguments)]
559#[cfg(test)]
560pub(crate) fn bank_from_streams<R>(
561 snapshot_streams: &mut SnapshotStreams<R>,
562 account_paths: &[PathBuf],
563 storage_and_next_append_vec_id: StorageAndNextAccountsFileId,
564 genesis_config: &GenesisConfig,
565 runtime_config: &RuntimeConfig,
566 debug_keys: Option<Arc<HashSet<Pubkey>>>,
567 limit_load_slot_count_from_snapshot: Option<usize>,
568 verify_index: bool,
569 accounts_db_config: AccountsDbConfig,
570 accounts_update_notifier: Option<AccountsUpdateNotifier>,
571 exit: Arc<AtomicBool>,
572) -> std::result::Result<(Bank, BankFromStreamsInfo), Error>
573where
574 R: Read,
575{
576 let (bank_fields, accounts_db_fields) = fields_from_streams(snapshot_streams)?;
577 let (bank, info) = reconstruct_bank_from_fields(
578 bank_fields,
579 accounts_db_fields,
580 genesis_config,
581 runtime_config,
582 account_paths,
583 storage_and_next_append_vec_id,
584 debug_keys,
585 limit_load_slot_count_from_snapshot,
586 verify_index,
587 accounts_db_config,
588 accounts_update_notifier,
589 exit,
590 )?;
591 Ok((
592 bank,
593 BankFromStreamsInfo {
594 calculated_accounts_lt_hash: info.calculated_accounts_lt_hash,
595 },
596 ))
597}
598
599#[cfg(test)]
600pub(crate) fn bank_to_stream<W>(
601 stream: &mut BufWriter<W>,
602 bank: &Bank,
603 snapshot_storages: &[Vec<Arc<AccountStorageEntry>>],
604) -> Result<(), Error>
605where
606 W: Write,
607{
608 bincode::serialize_into(
609 stream,
610 &SerializableBankAndStorage {
611 bank,
612 snapshot_storages,
613 },
614 )
615}
616
617pub fn serialize_bank_snapshot_into<W>(
619 stream: &mut BufWriter<W>,
620 bank_fields: BankFieldsToSerialize,
621 bank_hash_stats: BankHashStats,
622 account_storage_entries: &[Vec<Arc<AccountStorageEntry>>],
623 extra_fields: ExtraFieldsToSerialize,
624 write_version: u64,
625) -> Result<(), Error>
626where
627 W: Write,
628{
629 let mut serializer = bincode::Serializer::new(
630 stream,
631 bincode::DefaultOptions::new().with_fixint_encoding(),
632 );
633 serialize_bank_snapshot_with(
634 &mut serializer,
635 bank_fields,
636 bank_hash_stats,
637 account_storage_entries,
638 extra_fields,
639 write_version,
640 )
641}
642
643pub fn serialize_bank_snapshot_with<S>(
645 serializer: S,
646 bank_fields: BankFieldsToSerialize,
647 bank_hash_stats: BankHashStats,
648 account_storage_entries: &[Vec<Arc<AccountStorageEntry>>],
649 extra_fields: ExtraFieldsToSerialize,
650 write_version: u64,
651) -> Result<S::Ok, S::Error>
652where
653 S: serde::Serializer,
654{
655 let slot = bank_fields.slot;
656 let serializable_bank = SerializableVersionedBank::from(bank_fields);
657 let serializable_accounts_db = SerializableAccountsDb::<'_> {
658 slot,
659 account_storage_entries,
660 bank_hash_stats,
661 write_version,
662 };
663 (serializable_bank, serializable_accounts_db, extra_fields).serialize(serializer)
664}
665
666#[cfg(test)]
667struct SerializableBankAndStorage<'a> {
668 bank: &'a Bank,
669 snapshot_storages: &'a [Vec<Arc<AccountStorageEntry>>],
670}
671
672#[cfg(test)]
673impl Serialize for SerializableBankAndStorage<'_> {
674 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
675 where
676 S: serde::ser::Serializer,
677 {
678 let slot = self.bank.slot();
679 let mut bank_fields = self.bank.get_fields_to_serialize();
680 let accounts_db = &self.bank.rc.accounts.accounts_db;
681 let bank_hash_stats = self.bank.get_bank_hash_stats();
682 let write_version = accounts_db.write_version.load(Ordering::Acquire);
683 let lamports_per_signature = bank_fields.fee_rate_governor.lamports_per_signature;
684 let versioned_epoch_stakes = std::mem::take(&mut bank_fields.versioned_epoch_stakes);
685 let accounts_lt_hash = Some(bank_fields.accounts_lt_hash.clone().into());
686 let bank_fields_to_serialize = (
687 SerializableVersionedBank::from(bank_fields),
688 SerializableAccountsDb::<'_> {
689 slot,
690 account_storage_entries: self.snapshot_storages,
691 bank_hash_stats,
692 write_version,
693 },
694 ExtraFieldsToSerialize {
695 lamports_per_signature,
696 obsolete_incremental_snapshot_persistence: None,
697 obsolete_epoch_accounts_hash: None,
698 versioned_epoch_stakes,
699 accounts_lt_hash,
700 },
701 );
702 bank_fields_to_serialize.serialize(serializer)
703 }
704}
705
706#[cfg(test)]
707struct SerializableBankAndStorageNoExtra<'a> {
708 bank: &'a Bank,
709 snapshot_storages: &'a [Vec<Arc<AccountStorageEntry>>],
710}
711
712#[cfg(test)]
713impl Serialize for SerializableBankAndStorageNoExtra<'_> {
714 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
715 where
716 S: serde::ser::Serializer,
717 {
718 let slot = self.bank.slot();
719 let bank_fields = self.bank.get_fields_to_serialize();
720 let accounts_db = &self.bank.rc.accounts.accounts_db;
721 let bank_hash_stats = self.bank.get_bank_hash_stats();
722 let write_version = accounts_db.write_version.load(Ordering::Acquire);
723 (
724 SerializableVersionedBank::from(bank_fields),
725 SerializableAccountsDb::<'_> {
726 slot,
727 account_storage_entries: self.snapshot_storages,
728 bank_hash_stats,
729 write_version,
730 },
731 )
732 .serialize(serializer)
733 }
734}
735
736#[cfg(test)]
737impl<'a> From<SerializableBankAndStorageNoExtra<'a>> for SerializableBankAndStorage<'a> {
738 fn from(s: SerializableBankAndStorageNoExtra<'a>) -> SerializableBankAndStorage<'a> {
739 let SerializableBankAndStorageNoExtra {
740 bank,
741 snapshot_storages,
742 } = s;
743 SerializableBankAndStorage {
744 bank,
745 snapshot_storages,
746 }
747 }
748}
749
750struct SerializableAccountsDb<'a> {
751 slot: Slot,
752 account_storage_entries: &'a [Vec<Arc<AccountStorageEntry>>],
753 bank_hash_stats: BankHashStats,
754 write_version: u64,
755}
756
757impl Serialize for SerializableAccountsDb<'_> {
758 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
759 where
760 S: serde::ser::Serializer,
761 {
762 let entry_count = RefCell::<usize>::new(0);
764 let entries = utils::serialize_iter_as_map(self.account_storage_entries.iter().map(|x| {
765 *entry_count.borrow_mut() += x.len();
766 (
767 x.first().unwrap().slot(),
768 utils::serialize_iter_as_seq(
769 x.iter()
770 .map(|x| SerializableAccountStorageEntry::new(x.as_ref(), self.slot)),
771 ),
772 )
773 }));
774 let bank_hash_info = BankHashInfo {
775 obsolete_accounts_delta_hash: [0; 32],
776 obsolete_accounts_hash: [0; 32],
777 stats: self.bank_hash_stats.clone(),
778 };
779
780 let historical_roots = Vec::<Slot>::default();
781 let historical_roots_with_hash = Vec::<(Slot, Hash)>::default();
782
783 let mut serialize_account_storage_timer = Measure::start("serialize_account_storage_ms");
784 let result = (
785 entries,
786 self.write_version,
787 self.slot,
788 bank_hash_info,
789 historical_roots,
790 historical_roots_with_hash,
791 )
792 .serialize(serializer);
793 serialize_account_storage_timer.stop();
794 datapoint_info!(
795 "serialize_account_storage_ms",
796 ("duration", serialize_account_storage_timer.as_ms(), i64),
797 ("num_entries", *entry_count.borrow(), i64),
798 );
799 result
800 }
801}
802
803#[cfg(feature = "frozen-abi")]
804impl solana_frozen_abi::abi_example::TransparentAsHelper for SerializableAccountsDb<'_> {}
805
806#[derive(Debug)]
808pub(crate) struct ReconstructedBankInfo {
809 pub(crate) calculated_accounts_lt_hash: AccountsLtHash,
812}
813
814#[allow(clippy::too_many_arguments)]
815pub(crate) fn reconstruct_bank_from_fields<E>(
816 bank_fields: SnapshotBankFields,
817 snapshot_accounts_db_fields: SnapshotAccountsDbFields<E>,
818 genesis_config: &GenesisConfig,
819 runtime_config: &RuntimeConfig,
820 account_paths: &[PathBuf],
821 storage_and_next_append_vec_id: StorageAndNextAccountsFileId,
822 debug_keys: Option<Arc<HashSet<Pubkey>>>,
823 limit_load_slot_count_from_snapshot: Option<usize>,
824 verify_index: bool,
825 accounts_db_config: AccountsDbConfig,
826 accounts_update_notifier: Option<AccountsUpdateNotifier>,
827 exit: Arc<AtomicBool>,
828) -> Result<(Bank, ReconstructedBankInfo), Error>
829where
830 E: SerializableStorage + std::marker::Sync,
831{
832 let mut bank_fields = bank_fields.collapse_into();
833 let (accounts_db, reconstructed_accounts_db_info) = reconstruct_accountsdb_from_fields(
834 snapshot_accounts_db_fields,
835 account_paths,
836 storage_and_next_append_vec_id,
837 limit_load_slot_count_from_snapshot,
838 verify_index,
839 accounts_db_config,
840 accounts_update_notifier,
841 exit,
842 )?;
843 bank_fields.bank_hash_stats = reconstructed_accounts_db_info.bank_hash_stats;
844
845 let bank_rc = BankRc::new(Accounts::new(Arc::new(accounts_db)));
846 let runtime_config = Arc::new(runtime_config.clone());
847
848 let bank = Bank::new_from_snapshot(
849 bank_rc,
850 genesis_config,
851 runtime_config,
852 bank_fields,
853 debug_keys,
854 reconstructed_accounts_db_info.accounts_data_len,
855 );
856
857 info!("rent_collector: {:?}", bank.rent_collector());
858 Ok((
859 bank,
860 ReconstructedBankInfo {
861 calculated_accounts_lt_hash: reconstructed_accounts_db_info.calculated_accounts_lt_hash,
862 },
863 ))
864}
865
866pub(crate) fn reconstruct_single_storage(
867 slot: &Slot,
868 append_vec_path: &Path,
869 current_len: usize,
870 id: AccountsFileId,
871 storage_access: StorageAccess,
872 obsolete_accounts: Option<(ObsoleteAccounts, AccountsFileId, usize)>,
873) -> Result<Arc<AccountStorageEntry>, SnapshotError> {
874 let (current_len, obsolete_accounts) = if let Some(obsolete_accounts) = obsolete_accounts {
878 let updated_len = current_len + obsolete_accounts.2;
879 if obsolete_accounts.1 != id {
880 return Err(SnapshotError::MismatchedAccountsFileId(
881 id,
882 obsolete_accounts.1,
883 ));
884 }
885
886 (updated_len, obsolete_accounts.0)
887 } else {
888 (current_len, ObsoleteAccounts::default())
889 };
890
891 let accounts_file =
892 AccountsFile::new_for_startup(append_vec_path, current_len, storage_access)?;
893 Ok(Arc::new(AccountStorageEntry::new_existing(
894 *slot,
895 id,
896 accounts_file,
897 obsolete_accounts,
898 )))
899}
900
901pub(crate) fn remap_append_vec_file(
905 slot: Slot,
906 old_append_vec_id: SerializedAccountsFileId,
907 append_vec_path: &Path,
908 next_append_vec_id: &AtomicAccountsFileId,
909 num_collisions: &AtomicUsize,
910) -> io::Result<(AccountsFileId, PathBuf)> {
911 #[cfg(all(target_os = "linux", target_env = "gnu"))]
912 let append_vec_path_cstr = cstring_from_path(append_vec_path)?;
913
914 let mut remapped_append_vec_path = append_vec_path.to_path_buf();
915
916 let (remapped_append_vec_id, remapped_append_vec_path) = loop {
922 let remapped_append_vec_id = next_append_vec_id.fetch_add(1, Ordering::AcqRel);
923
924 if old_append_vec_id == remapped_append_vec_id as SerializedAccountsFileId {
926 break (remapped_append_vec_id, remapped_append_vec_path);
927 }
928
929 let remapped_file_name = AccountsFile::file_name(slot, remapped_append_vec_id);
930 remapped_append_vec_path = append_vec_path.parent().unwrap().join(remapped_file_name);
931
932 #[cfg(all(target_os = "linux", target_env = "gnu"))]
933 {
934 let remapped_append_vec_path_cstr = cstring_from_path(&remapped_append_vec_path)?;
935
936 match rename_no_replace(&append_vec_path_cstr, &remapped_append_vec_path_cstr) {
939 Ok(_) => break (remapped_append_vec_id, remapped_append_vec_path),
941 Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {}
944 Err(e) => return Err(e),
945 }
946 }
947
948 #[cfg(any(
949 not(target_os = "linux"),
950 all(target_os = "linux", not(target_env = "gnu"))
951 ))]
952 if std::fs::metadata(&remapped_append_vec_path).is_err() {
953 break (remapped_append_vec_id, remapped_append_vec_path);
954 }
955
956 num_collisions.fetch_add(1, Ordering::Relaxed);
959 };
960
961 #[cfg(any(
964 not(target_os = "linux"),
965 all(target_os = "linux", not(target_env = "gnu"))
966 ))]
967 if old_append_vec_id != remapped_append_vec_id as SerializedAccountsFileId {
968 std::fs::rename(append_vec_path, &remapped_append_vec_path)?;
969 }
970
971 Ok((remapped_append_vec_id, remapped_append_vec_path))
972}
973
974pub(crate) fn remap_and_reconstruct_single_storage(
975 slot: Slot,
976 old_append_vec_id: SerializedAccountsFileId,
977 current_len: usize,
978 append_vec_path: &Path,
979 next_append_vec_id: &AtomicAccountsFileId,
980 num_collisions: &AtomicUsize,
981 storage_access: StorageAccess,
982) -> Result<Arc<AccountStorageEntry>, SnapshotError> {
983 let (remapped_append_vec_id, remapped_append_vec_path) = remap_append_vec_file(
984 slot,
985 old_append_vec_id,
986 append_vec_path,
987 next_append_vec_id,
988 num_collisions,
989 )?;
990 let storage = reconstruct_single_storage(
991 &slot,
992 &remapped_append_vec_path,
993 current_len,
994 remapped_append_vec_id,
995 storage_access,
996 None,
997 )?;
998 Ok(storage)
999}
1000
1001#[derive(Debug)]
1003pub struct ReconstructedAccountsDbInfo {
1004 pub accounts_data_len: u64,
1005 pub calculated_accounts_lt_hash: AccountsLtHash,
1008 pub bank_hash_stats: BankHashStats,
1009}
1010
1011#[allow(clippy::too_many_arguments)]
1012fn reconstruct_accountsdb_from_fields<E>(
1013 snapshot_accounts_db_fields: SnapshotAccountsDbFields<E>,
1014 account_paths: &[PathBuf],
1015 storage_and_next_append_vec_id: StorageAndNextAccountsFileId,
1016 limit_load_slot_count_from_snapshot: Option<usize>,
1017 verify_index: bool,
1018 accounts_db_config: AccountsDbConfig,
1019 accounts_update_notifier: Option<AccountsUpdateNotifier>,
1020 exit: Arc<AtomicBool>,
1021) -> Result<(AccountsDb, ReconstructedAccountsDbInfo), Error>
1022where
1023 E: SerializableStorage + std::marker::Sync,
1024{
1025 let mut accounts_db = AccountsDb::new_with_config(
1026 account_paths.to_vec(),
1027 accounts_db_config,
1028 accounts_update_notifier,
1029 exit,
1030 );
1031
1032 let AccountsDbFields(
1033 _snapshot_storages,
1034 snapshot_version,
1035 _snapshot_slot,
1036 snapshot_bank_hash_info,
1037 _snapshot_historical_roots,
1038 _snapshot_historical_roots_with_hash,
1039 ) = snapshot_accounts_db_fields.collapse_into()?;
1040
1041 for path in &accounts_db.paths {
1043 std::fs::create_dir_all(path)
1044 .unwrap_or_else(|err| panic!("Failed to create directory {}: {}", path.display(), err));
1045 }
1046
1047 let StorageAndNextAccountsFileId {
1048 storage,
1049 next_append_vec_id,
1050 } = storage_and_next_append_vec_id;
1051
1052 assert!(
1053 !storage.is_empty(),
1054 "At least one storage entry must exist from deserializing stream"
1055 );
1056
1057 let next_append_vec_id = next_append_vec_id.load(Ordering::Acquire);
1058 let max_append_vec_id = next_append_vec_id - 1;
1059 assert!(
1060 max_append_vec_id <= AccountsFileId::MAX / 2,
1061 "Storage id {max_append_vec_id} larger than allowed max"
1062 );
1063
1064 accounts_db.storage.initialize(storage);
1066 accounts_db
1067 .next_id
1068 .store(next_append_vec_id, Ordering::Release);
1069 accounts_db
1070 .write_version
1071 .fetch_add(snapshot_version, Ordering::Release);
1072
1073 info!("Building accounts index...");
1074 let start = Instant::now();
1075 let IndexGenerationInfo {
1076 accounts_data_len,
1077 calculated_accounts_lt_hash,
1078 } = accounts_db.generate_index(limit_load_slot_count_from_snapshot, verify_index);
1079 info!("Building accounts index... Done in {:?}", start.elapsed());
1080
1081 Ok((
1082 accounts_db,
1083 ReconstructedAccountsDbInfo {
1084 accounts_data_len,
1085 calculated_accounts_lt_hash,
1086 bank_hash_stats: snapshot_bank_hash_info.stats,
1087 },
1088 ))
1089}
1090
1091#[cfg(all(target_os = "linux", target_env = "gnu"))]
1093fn rename_no_replace(src: &CStr, dest: &CStr) -> io::Result<()> {
1094 let ret = unsafe {
1095 libc::renameat2(
1096 libc::AT_FDCWD,
1097 src.as_ptr() as *const _,
1098 libc::AT_FDCWD,
1099 dest.as_ptr() as *const _,
1100 libc::RENAME_NOREPLACE,
1101 )
1102 };
1103 if ret == -1 {
1104 return Err(io::Error::last_os_error());
1105 }
1106
1107 Ok(())
1108}
1109
1110#[cfg(all(target_os = "linux", target_env = "gnu"))]
1111fn cstring_from_path(path: &Path) -> io::Result<CString> {
1112 CString::new(path.as_os_str().as_encoded_bytes())
1117 .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1118}