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