#[cfg(test)]
mod tests {
use {
crate::{
bank::{Bank, test_utils as bank_test_utils},
epoch_stakes::{
EpochAuthorizedVoters, EpochStakes, NodeIdToVoteAccounts, VersionedEpochStakes,
},
genesis_utils::{
GenesisConfigInfo, activate_all_features, create_genesis_config_with_leader,
},
runtime_config::RuntimeConfig,
serde_snapshot::{self, ExtraFieldsToSerialize, SnapshotStreams},
snapshot_bank_utils,
snapshot_utils::{StorageAndNextAccountsFileId, create_tmp_accounts_dir_for_tests},
},
agave_snapshots::snapshot_config::SnapshotConfig,
solana_accounts_db::{
ObsoleteAccounts,
account_storage::AccountStorageMap,
account_storage_entry::AccountStorageEntry,
accounts_db::{
ACCOUNTS_DB_CONFIG_FOR_TESTING, AccountsDb, AtomicAccountsFileId,
get_temp_accounts_paths,
},
accounts_file::{AccountsFile, AccountsFileError, StorageAccess},
},
solana_epoch_schedule::EpochSchedule,
solana_hash::Hash,
solana_native_token::LAMPORTS_PER_SOL,
solana_pubkey::Pubkey,
std::{
io::{BufReader, BufWriter, Cursor},
mem,
ops::RangeFull,
path::Path,
sync::{Arc, OnceLock},
},
tempfile::TempDir,
test_case::{test_case, test_matrix},
};
fn copy_append_vecs<P: AsRef<Path>>(
accounts_db: &AccountsDb,
output_dir: P,
storage_access: StorageAccess,
) -> Result<StorageAndNextAccountsFileId, AccountsFileError> {
let storage_entries = accounts_db.get_storages(RangeFull).0;
let storage: AccountStorageMap = AccountStorageMap::with_capacity(storage_entries.len());
let mut next_append_vec_id = 0;
for storage_entry in storage_entries.into_iter() {
let storage_path = storage_entry.path();
let file_name = AccountsFile::file_name(storage_entry.slot(), storage_entry.id());
let output_path = output_dir.as_ref().join(file_name);
std::fs::copy(storage_path, &output_path)?;
let (accounts_file, _num_accounts) = AccountsFile::new_from_file(
output_path,
storage_entry.accounts.len(),
storage_access,
)?;
let new_storage_entry = AccountStorageEntry::new_existing(
storage_entry.slot(),
storage_entry.id(),
accounts_file,
ObsoleteAccounts::default(),
);
next_append_vec_id = next_append_vec_id.max(new_storage_entry.id());
storage.insert(new_storage_entry.slot(), Arc::new(new_storage_entry));
}
Ok(StorageAndNextAccountsFileId {
storage,
next_append_vec_id: AtomicAccountsFileId::new(next_append_vec_id + 1),
})
}
#[test_matrix(
[#[allow(deprecated)] StorageAccess::Mmap, StorageAccess::File]
)]
fn test_serialize_bank_snapshot(storage_access: StorageAccess) {
let leader_id = Pubkey::new_unique();
let GenesisConfigInfo {
mut genesis_config, ..
} = create_genesis_config_with_leader(500, &leader_id, LAMPORTS_PER_SOL);
genesis_config.epoch_schedule = EpochSchedule::custom(400, 400, false);
let (bank0, bank_forks) =
Bank::new_for_tests(&genesis_config).wrap_with_bank_forks_for_tests();
let deposit_amount = bank0.get_minimum_balance_for_rent_exemption(0);
let bank1 = Bank::new_from_parent_with_bank_forks(
bank_forks.as_ref(),
bank0.clone(),
*bank0.leader(),
1,
);
let key1 = Pubkey::new_unique();
bank_test_utils::deposit(&bank1, &key1, deposit_amount).unwrap();
let bank2_slot = 2;
let bank0_leader = *bank0.leader();
let bank2 = Bank::new_from_parent_with_bank_forks(
bank_forks.as_ref(),
bank0,
bank0_leader,
bank2_slot,
);
let key2 = Pubkey::new_unique();
bank_test_utils::deposit(&bank2, &key2, deposit_amount).unwrap();
assert_eq!(bank2.get_balance(&key2), deposit_amount);
let key3 = Pubkey::new_unique();
bank_test_utils::deposit(&bank2, &key3, 0).unwrap();
let accounts_db = &bank2.rc.accounts.accounts_db;
bank2.set_block_id(Some(Hash::default()));
bank2.squash();
bank2.force_flush_accounts_cache();
let expected_accounts_lt_hash = bank2.accounts_lt_hash.lock().unwrap().clone();
let mut buf = Vec::new();
let cursor = Cursor::new(&mut buf);
let mut writer = BufWriter::new(cursor);
{
let mut bank_fields = bank2.get_fields_to_serialize();
let versioned_epoch_stakes = mem::take(&mut bank_fields.versioned_epoch_stakes);
let accounts_lt_hash = Some(bank_fields.accounts_lt_hash.clone().into());
let block_id = Some(bank_fields.block_id);
serde_snapshot::serialize_bank_snapshot_into(
&mut writer,
bank_fields,
bank2.get_bank_hash_stats(),
&bank2.get_snapshot_storages(None),
ExtraFieldsToSerialize {
lamports_per_signature: bank2.fee_rate_governor.lamports_per_signature,
unused_incremental_snapshot_persistence: None,
unused_epoch_accounts_hash: None,
versioned_epoch_stakes,
accounts_lt_hash,
block_id,
},
)
.unwrap();
}
drop(writer);
let (_accounts_dir, dbank_paths) = get_temp_accounts_paths(4).unwrap();
let copied_accounts = TempDir::new().unwrap();
let storage_and_next_append_vec_id =
copy_append_vecs(accounts_db, copied_accounts.path(), storage_access).unwrap();
let cursor = Cursor::new(buf.as_slice());
let mut reader = BufReader::new(cursor);
let mut snapshot_streams = SnapshotStreams {
full_snapshot_stream: &mut reader,
incremental_snapshot_stream: None,
};
let (dbank, _) = serde_snapshot::bank_from_streams(
&mut snapshot_streams,
&dbank_paths,
storage_and_next_append_vec_id,
&genesis_config,
&RuntimeConfig::default(),
None,
None,
false,
ACCOUNTS_DB_CONFIG_FOR_TESTING,
None,
Arc::default(),
)
.unwrap();
assert_eq!(dbank.get_balance(&key1), 0);
assert_eq!(dbank.get_balance(&key2), deposit_amount);
assert_eq!(dbank.get_balance(&key3), 0);
assert_eq!(
dbank.accounts_lt_hash.lock().unwrap().clone(),
expected_accounts_lt_hash,
);
assert_eq!(dbank.get_bank_hash_stats(), bank2.get_bank_hash_stats());
assert_eq!(&dbank, bank2.as_ref());
}
fn add_root_and_flush_write_cache(bank: &Bank) {
bank.rc.accounts.add_root(bank.slot());
bank.force_flush_accounts_cache();
}
#[test_case(#[allow(deprecated)] StorageAccess::Mmap)]
#[test_case(StorageAccess::File)]
fn test_extra_fields_eof(storage_access: StorageAccess) {
agave_logger::setup();
let leader_id = Pubkey::new_unique();
let GenesisConfigInfo { genesis_config, .. } =
create_genesis_config_with_leader(500, &leader_id, LAMPORTS_PER_SOL);
let (bank0, _bank_forks) =
Bank::new_for_tests(&genesis_config).wrap_with_bank_forks_for_tests();
bank0.squash();
let mut bank = Bank::new_from_parent(bank0.clone(), *bank0.leader(), 1);
bank.set_block_id(Some(Hash::default()));
bank.freeze();
add_root_and_flush_write_cache(&bank0);
bank.fee_rate_governor.lamports_per_signature = 7000;
bank.epoch_stakes.insert(
42,
VersionedEpochStakes::Current {
stakes: EpochStakes::default(),
total_stake: 42,
node_id_to_vote_accounts: Arc::<NodeIdToVoteAccounts>::default(),
epoch_authorized_voters: Arc::<EpochAuthorizedVoters>::default(),
bls_pubkey_to_rank_map: OnceLock::new(),
},
);
assert_eq!(bank.epoch_stakes.len(), 3);
let snapshot_storages = bank.get_snapshot_storages(None);
let mut buf = vec![];
let mut writer = Cursor::new(&mut buf);
crate::serde_snapshot::bank_to_stream(
&mut std::io::BufWriter::new(&mut writer),
&bank,
&snapshot_storages,
)
.unwrap();
let rdr = Cursor::new(&buf[..]);
let mut reader = std::io::BufReader::new(&buf[rdr.position() as usize..]);
let mut snapshot_streams = SnapshotStreams {
full_snapshot_stream: &mut reader,
incremental_snapshot_stream: None,
};
let (_accounts_dir, dbank_paths) = get_temp_accounts_paths(4).unwrap();
let copied_accounts = TempDir::new().unwrap();
let storage_and_next_append_vec_id = copy_append_vecs(
&bank.rc.accounts.accounts_db,
copied_accounts.path(),
storage_access,
)
.unwrap();
let (dbank, _) = crate::serde_snapshot::bank_from_streams(
&mut snapshot_streams,
&dbank_paths,
storage_and_next_append_vec_id,
&genesis_config,
&RuntimeConfig::default(),
None,
None,
false,
ACCOUNTS_DB_CONFIG_FOR_TESTING,
None,
Arc::default(),
)
.unwrap();
assert_eq!(bank.epoch_stakes, dbank.epoch_stakes);
assert_eq!(
bank.fee_rate_governor.lamports_per_signature,
dbank.fee_rate_governor.lamports_per_signature
);
}
#[test]
fn test_extra_fields_full_snapshot_archive() {
agave_logger::setup();
let leader_id = Pubkey::new_unique();
let GenesisConfigInfo {
mut genesis_config, ..
} = create_genesis_config_with_leader(500, &leader_id, LAMPORTS_PER_SOL);
activate_all_features(&mut genesis_config);
let (bank0, _bank_forks) =
Bank::new_for_tests(&genesis_config).wrap_with_bank_forks_for_tests();
let bank0_leader = *bank0.leader();
let mut bank = Bank::new_from_parent(bank0, bank0_leader, 1);
while !bank.is_complete() {
bank.fill_bank_with_ticks_for_tests();
}
bank.set_block_id(Some(Hash::default()));
bank.fee_rate_governor.lamports_per_signature = 7000;
let (_tmp_dir, accounts_dir) = create_tmp_accounts_dir_for_tests();
let bank_snapshots_dir = TempDir::new().unwrap();
let full_snapshot_archives_dir = TempDir::new().unwrap();
let incremental_snapshot_archives_dir = TempDir::new().unwrap();
let snapshot_config = SnapshotConfig {
full_snapshot_archives_dir: full_snapshot_archives_dir.path().to_path_buf(),
incremental_snapshot_archives_dir: incremental_snapshot_archives_dir
.path()
.to_path_buf(),
bank_snapshots_dir: bank_snapshots_dir.path().to_path_buf(),
..SnapshotConfig::default()
};
let snapshot_archive_info =
snapshot_bank_utils::bank_to_full_snapshot_archive(&snapshot_config, &bank).unwrap();
let dbank = snapshot_bank_utils::bank_from_snapshot_archives(
&[accounts_dir],
&snapshot_archive_info,
None,
&snapshot_config,
&genesis_config,
&RuntimeConfig::default(),
None,
None, None,
false,
false,
false,
ACCOUNTS_DB_CONFIG_FOR_TESTING,
None,
Arc::default(),
)
.unwrap();
assert_eq!(
bank.fee_rate_governor.lamports_per_signature,
dbank.fee_rate_governor.lamports_per_signature
);
}
#[cfg(feature = "frozen-abi")]
mod test_bank_serialize {
use {
super::*,
crate::{bank::BankHashStats, serde_snapshot::UnusedIncrementalSnapshotPersistence},
solana_accounts_db::accounts_hash::AccountsLtHash,
solana_frozen_abi::abi_example::AbiExample,
solana_hash::Hash,
solana_lattice_hash::lt_hash::LtHash,
std::marker::PhantomData,
};
#[cfg_attr(
feature = "frozen-abi",
derive(AbiExample),
frozen_abi(digest = "5ESyZ9Aseo4v1XA2xnC8dENK8MueieQESYfeNcxByCmd")
)]
#[derive(serde::Serialize)]
pub struct BankAbiTestWrapper {
#[serde(serialize_with = "wrapper")]
bank: PhantomData<Bank>,
}
pub fn wrapper<S>(_bank: &PhantomData<Bank>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let bank = Bank::default_for_tests();
bank.set_block_id(Some(Hash::default()));
let snapshot_storages = AccountsDb::example().get_storages(0..1).0;
assert!(!snapshot_storages.is_empty());
let incremental_snapshot_persistence = UnusedIncrementalSnapshotPersistence {
full_slot: u64::default(),
full_hash: [1; 32],
full_capitalization: u64::default(),
incremental_hash: [2; 32],
incremental_capitalization: u64::default(),
};
let mut bank_fields = bank.get_fields_to_serialize();
let versioned_epoch_stakes = std::mem::take(&mut bank_fields.versioned_epoch_stakes);
serde_snapshot::serialize_bank_snapshot_with(
serializer,
bank_fields,
BankHashStats::default(),
&snapshot_storages,
ExtraFieldsToSerialize {
lamports_per_signature: bank.fee_rate_governor.lamports_per_signature,
unused_incremental_snapshot_persistence: Some(incremental_snapshot_persistence),
unused_epoch_accounts_hash: Some(Hash::new_unique()),
versioned_epoch_stakes,
accounts_lt_hash: Some(AccountsLtHash(LtHash::identity()).into()),
block_id: Some(Hash::new_unique()),
},
)
}
}
}