atlas_runtime/
serde_snapshot.rs

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, // obsolete, formerly write_version
76    Slot,
77    BankHashInfo,
78    /// all slots that were roots within the last epoch
79    #[serde(deserialize_with = "default_on_eof")]
80    Vec<Slot>,
81    /// slots that were roots within the last epoch for which we care about the hash value
82    #[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// Deserializable version of Bank which need not be serializable,
114// because it's handled by SerializableVersionedBank.
115// So, sync fields with it!
116#[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        // This serves as a canary for the LtHash.
156        // If it is not replaced during deserialization, it indicates a bug.
157        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(), // populated from ExtraFieldsToDeserialize
188            accounts_lt_hash: AccountsLtHash(LT_HASH_CANARY), // populated from ExtraFieldsToDeserialize
189            bank_hash_stats: BankHashStats::default(),        // populated from AccountsDbFields
190        }
191    }
192}
193
194// Serializable version of Bank, not Deserializable to avoid cloning by using refs.
195// Sync fields with DeserializableVersionedBank!
196#[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
275/// Helper type to wrap BufReader streams when deserializing and reconstructing from either just a
276/// full snapshot, or both a full and incremental snapshot
277pub 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/// Helper type to wrap BankFields when reconstructing Bank from either just a full
283/// snapshot, or both a full and incremental snapshot
284#[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    /// Collapse the SnapshotBankFields into a single (the latest) BankFieldsToDeserialize.
299    pub fn collapse_into(self) -> BankFieldsToDeserialize {
300        self.incremental.unwrap_or(self.full)
301    }
302}
303
304/// Helper type to wrap AccountsDbFields when reconstructing AccountsDb from either just a full
305/// snapshot, or both a full and incremental snapshot
306#[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    /// Collapse the SnapshotAccountsDbFields into a single AccountsDbFields.  If there is no
324    /// incremental snapshot, this returns the AccountsDbFields from the full snapshot.
325    /// Otherwise, use the AccountsDbFields from the incremental snapshot, and a combination
326    /// of the storages from both the full and incremental snapshots.
327    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                // filter out incremental snapshot storages with slot <= full snapshot slot
342                incremental_snapshot_storages.retain(|slot, _| *slot > full_snapshot_slot);
343
344                // There must not be any overlap in the slots of storages between the full snapshot and the incremental snapshot
345                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/// Extra fields that are deserialized from the end of snapshots.
395///
396/// Note that this struct's fields should stay synced with the fields in
397/// ExtraFieldsToSerialize with the exception that new "extra fields" should be
398/// added to this struct a minor release before they are added to the serialize
399/// struct.
400#[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/// Extra fields that are serialized at the end of snapshots.
417///
418/// Note that this struct's fields should stay synced with the fields in
419/// ExtraFieldsToDeserialize with the exception that new "extra fields" should
420/// be added to the deserialize struct a minor release before they are added to
421/// this one.
422#[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    // Process extra fields
456    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/// used by tests to compare contents of serialized bank fields
476/// serialized format is not deterministic - likely due to randomness in structs like hashmaps
477#[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
493/// Get snapshot storage lengths from accounts_db_fields
494pub(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/// This struct contains side-info while reconstructing the bank from streams
556#[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
622/// Serializes bank snapshot into `stream` with bincode
623pub 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
648/// Serializes bank snapshot with `serializer`
649pub 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        // (1st of 3 elements) write the list of account storage entry lists out as a map
768        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/// This struct contains side-info while reconstructing the bank from fields
812#[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    // if limit_load_slot_count_from_snapshot is set, then we need to side-step some correctness checks beneath this call
853    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
890// Remap the AppendVec ID to handle any duplicate IDs that may previously existed
891// due to full snapshots and incremental snapshots generated from different
892// nodes
893pub(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    // Break out of the loop in the following situations:
906    // 1. The new ID is the same as the original ID.  This means we do not need to
907    //    rename the file, since the ID is the "correct" one already.
908    // 2. There is not a file already at the new path.  This means it is safe to
909    //    rename the file to this new path.
910    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        // this can only happen in the first iteration of the loop
914        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            // On linux we use renameat2(NO_REPLACE) instead of IF metadata(path).is_err() THEN
926            // rename() in order to save a statx() syscall.
927            match rename_no_replace(&append_vec_path_cstr, &remapped_append_vec_path_cstr) {
928                // If the file was successfully renamed, break out of the loop
929                Ok(_) => break (remapped_append_vec_id, remapped_append_vec_path),
930                // If there's already a file at the new path, continue so we try
931                // the next ID
932                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        // If we made it this far, a file exists at the new path.  Record the collision
946        // and try again.
947        num_collisions.fetch_add(1, Ordering::Relaxed);
948    };
949
950    // Only rename the file if the new ID is actually different from the original. In the target_os
951    // = linux case, we have already renamed if necessary.
952    #[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/// This struct contains side-info while reconstructing the accounts DB from fields.
990#[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    // Ensure all account paths exist
1028    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    // Process deserialized data, set necessary fields in self
1051    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// Rename `src` to `dest` only if `dest` doesn't already exist.
1097#[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    // It is better to allocate here than use the stack. Jemalloc is going to give us a chunk of a
1118    // preallocated small arena anyway. Instead if we used the stack since PATH_MAX=4096 it would
1119    // result in LLVM inserting a stack probe, see
1120    // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html.
1121    CString::new(path.as_os_str().as_encoded_bytes())
1122        .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1123}