solana_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::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, // obsolete, formerly write_version
78    Slot,
79    BankHashInfo,
80    /// all slots that were roots within the last epoch
81    #[serde(deserialize_with = "default_on_eof")]
82    Vec<Slot>,
83    /// slots that were roots within the last epoch for which we care about the hash value
84    #[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// Deserializable version of Bank which need not be serializable,
116// because it's handled by SerializableVersionedBank.
117// So, sync fields with it!
118#[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        // This serves as a canary for the LtHash.
158        // If it is not replaced during deserialization, it indicates a bug.
159        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(), // populated from ExtraFieldsToDeserialize
190            accounts_lt_hash: AccountsLtHash(LT_HASH_CANARY), // populated from ExtraFieldsToDeserialize
191            bank_hash_stats: BankHashStats::default(),        // populated from AccountsDbFields
192        }
193    }
194}
195
196// Serializable version of Bank, not Deserializable to avoid cloning by using refs.
197// Sync fields with DeserializableVersionedBank!
198#[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
277/// Helper type to wrap BufReader streams when deserializing and reconstructing from either just a
278/// full snapshot, or both a full and incremental snapshot
279pub 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/// Helper type to wrap BankFields when reconstructing Bank from either just a full
285/// snapshot, or both a full and incremental snapshot
286#[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    /// Collapse the SnapshotBankFields into a single (the latest) BankFieldsToDeserialize.
301    pub fn collapse_into(self) -> BankFieldsToDeserialize {
302        self.incremental.unwrap_or(self.full)
303    }
304}
305
306/// Helper type to wrap AccountsDbFields when reconstructing AccountsDb from either just a full
307/// snapshot, or both a full and incremental snapshot
308#[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    /// Collapse the SnapshotAccountsDbFields into a single AccountsDbFields.  If there is no
326    /// incremental snapshot, this returns the AccountsDbFields from the full snapshot.
327    /// Otherwise, use the AccountsDbFields from the incremental snapshot, and a combination
328    /// of the storages from both the full and incremental snapshots.
329    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                // filter out incremental snapshot storages with slot <= full snapshot slot
344                incremental_snapshot_storages.retain(|slot, _| *slot > full_snapshot_slot);
345
346                // There must not be any overlap in the slots of storages between the full snapshot and the incremental snapshot
347                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/// Extra fields that are deserialized from the end of snapshots.
408///
409/// Note that this struct's fields should stay synced with the fields in
410/// ExtraFieldsToSerialize with the exception that new "extra fields" should be
411/// added to this struct a minor release before they are added to the serialize
412/// struct.
413#[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/// Extra fields that are serialized at the end of snapshots.
430///
431/// Note that this struct's fields should stay synced with the fields in
432/// ExtraFieldsToDeserialize with the exception that new "extra fields" should
433/// be added to the deserialize struct a minor release before they are added to
434/// this one.
435#[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    // Process extra fields
469    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
488/// Get snapshot storage lengths from accounts_db_fields
489pub(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/// This struct contains side-info while reconstructing the bank from streams
551#[derive(Debug)]
552pub struct BankFromStreamsInfo {
553    /// The accounts lt hash calculated during index generation.
554    /// Will be used when verifying accounts, after rebuilding a Bank.
555    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
617/// Serializes bank snapshot into `stream` with bincode
618pub 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
643/// Serializes bank snapshot with `serializer`
644pub 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        // (1st of 3 elements) write the list of account storage entry lists out as a map
763        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/// This struct contains side-info while reconstructing the bank from fields
807#[derive(Debug)]
808pub(crate) struct ReconstructedBankInfo {
809    /// The accounts lt hash calculated during index generation.
810    /// Will be used when verifying accounts, after rebuilding a Bank.
811    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    // When restoring from an archive, obsolete accounts will always be `None`
875    // When restoring from fastboot, obsolete accounts will be 'Some' if the storage contained
876    // accounts marked obsolete at the time the snapshot was taken.
877    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
901// Remap the AppendVec ID to handle any duplicate IDs that may previously existed
902// due to full snapshots and incremental snapshots generated from different
903// nodes
904pub(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    // Break out of the loop in the following situations:
917    // 1. The new ID is the same as the original ID.  This means we do not need to
918    //    rename the file, since the ID is the "correct" one already.
919    // 2. There is not a file already at the new path.  This means it is safe to
920    //    rename the file to this new path.
921    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        // this can only happen in the first iteration of the loop
925        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            // On linux we use renameat2(NO_REPLACE) instead of IF metadata(path).is_err() THEN
937            // rename() in order to save a statx() syscall.
938            match rename_no_replace(&append_vec_path_cstr, &remapped_append_vec_path_cstr) {
939                // If the file was successfully renamed, break out of the loop
940                Ok(_) => break (remapped_append_vec_id, remapped_append_vec_path),
941                // If there's already a file at the new path, continue so we try
942                // the next ID
943                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        // If we made it this far, a file exists at the new path.  Record the collision
957        // and try again.
958        num_collisions.fetch_add(1, Ordering::Relaxed);
959    };
960
961    // Only rename the file if the new ID is actually different from the original. In the target_os
962    // = linux case, we have already renamed if necessary.
963    #[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/// This struct contains side-info while reconstructing the accounts DB from fields.
1002#[derive(Debug)]
1003pub struct ReconstructedAccountsDbInfo {
1004    pub accounts_data_len: u64,
1005    /// The accounts lt hash calculated during index generation.
1006    /// Will be used when verifying accounts, after rebuilding a Bank.
1007    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    // Ensure all account paths exist
1042    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    // Process deserialized data, set necessary fields in self
1065    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// Rename `src` to `dest` only if `dest` doesn't already exist.
1092#[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    // It is better to allocate here than use the stack. Jemalloc is going to give us a chunk of a
1113    // preallocated small arena anyway. Instead if we used the stack since PATH_MAX=4096 it would
1114    // result in LLVM inserting a stack probe, see
1115    // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html.
1116    CString::new(path.as_os_str().as_encoded_bytes())
1117        .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1118}