2021-11-12 10:57:55 -08:00
|
|
|
// Service to verify accounts hashes with other known validator nodes.
|
2020-03-16 08:37:31 -07:00
|
|
|
//
|
2022-04-05 13:55:47 -07:00
|
|
|
// Each interval, publish the snapshot hash which is the full accounts state
|
2021-08-17 21:17:46 -07:00
|
|
|
// hash on gossip. Monitor gossip for messages from validators in the `--known-validator`s
|
2020-03-16 08:37:31 -07:00
|
|
|
// set and halt the node if a mismatch is detected.
|
|
|
|
|
2021-12-03 09:00:31 -08:00
|
|
|
use {
|
2022-10-13 09:47:36 -07:00
|
|
|
crossbeam_channel::{Receiver, Sender},
|
2023-04-10 07:44:40 -07:00
|
|
|
solana_gossip::cluster_info::{ClusterInfo, MAX_ACCOUNTS_HASHES},
|
2023-03-22 07:20:16 -07:00
|
|
|
solana_measure::measure_us,
|
2021-12-03 09:00:31 -08:00
|
|
|
solana_runtime::{
|
2023-03-14 09:41:44 -07:00
|
|
|
accounts_db::CalcAccountsHashFlavor,
|
|
|
|
accounts_hash::{
|
2023-03-22 07:20:16 -07:00
|
|
|
AccountsHash, AccountsHashEnum, CalcAccountsHashConfig, HashStats,
|
|
|
|
IncrementalAccountsHash,
|
2023-03-14 09:41:44 -07:00
|
|
|
},
|
2023-04-18 11:18:17 -07:00
|
|
|
serde_snapshot::BankIncrementalSnapshotPersistence,
|
2021-12-03 09:00:31 -08:00
|
|
|
snapshot_config::SnapshotConfig,
|
|
|
|
snapshot_package::{
|
2023-02-21 19:36:29 -08:00
|
|
|
self, retain_max_n_elements, AccountsPackage, AccountsPackageType, SnapshotPackage,
|
2023-03-06 08:40:46 -08:00
|
|
|
SnapshotType,
|
2021-12-03 09:00:31 -08:00
|
|
|
},
|
2023-05-12 14:02:43 -07:00
|
|
|
snapshot_utils,
|
2021-12-03 09:00:31 -08:00
|
|
|
sorted_storages::SortedStorages,
|
2021-08-17 11:01:59 -07:00
|
|
|
},
|
2022-04-06 03:47:19 -07:00
|
|
|
solana_sdk::{
|
2022-12-16 08:05:09 -08:00
|
|
|
clock::{Slot, DEFAULT_MS_PER_SLOT},
|
2022-04-06 03:47:19 -07:00
|
|
|
hash::Hash,
|
|
|
|
pubkey::Pubkey,
|
|
|
|
},
|
2021-12-03 09:00:31 -08:00
|
|
|
std::{
|
|
|
|
collections::{HashMap, HashSet},
|
|
|
|
sync::{
|
|
|
|
atomic::{AtomicBool, Ordering},
|
|
|
|
Arc,
|
|
|
|
},
|
|
|
|
thread::{self, Builder, JoinHandle},
|
|
|
|
time::Duration,
|
2020-03-16 08:37:31 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2023-04-07 15:50:21 -07:00
|
|
|
pub type AccountsHashFaultInjector = fn(&Hash, Slot) -> Option<Hash>;
|
|
|
|
|
2020-03-16 08:37:31 -07:00
|
|
|
pub struct AccountsHashVerifier {
|
|
|
|
t_accounts_hash_verifier: JoinHandle<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AccountsHashVerifier {
|
|
|
|
pub fn new(
|
2022-10-13 09:47:36 -07:00
|
|
|
accounts_package_sender: Sender<AccountsPackage>,
|
|
|
|
accounts_package_receiver: Receiver<AccountsPackage>,
|
2023-02-21 19:36:29 -08:00
|
|
|
snapshot_package_sender: Option<Sender<SnapshotPackage>>,
|
2023-04-19 08:10:08 -07:00
|
|
|
exit: Arc<AtomicBool>,
|
|
|
|
cluster_info: Arc<ClusterInfo>,
|
2021-11-12 10:57:55 -08:00
|
|
|
known_validators: Option<HashSet<Pubkey>>,
|
|
|
|
halt_on_known_validators_accounts_hash_mismatch: bool,
|
2023-04-07 15:50:21 -07:00
|
|
|
accounts_hash_fault_injector: Option<AccountsHashFaultInjector>,
|
2022-12-06 14:47:55 -08:00
|
|
|
snapshot_config: SnapshotConfig,
|
2020-03-16 08:37:31 -07:00
|
|
|
) -> Self {
|
2022-10-13 09:47:36 -07:00
|
|
|
// If there are no accounts packages to process, limit how often we re-check
|
2022-12-16 08:05:09 -08:00
|
|
|
const LOOP_LIMITER: Duration = Duration::from_millis(DEFAULT_MS_PER_SLOT);
|
2020-03-16 08:37:31 -07:00
|
|
|
let t_accounts_hash_verifier = Builder::new()
|
2022-08-17 08:40:23 -07:00
|
|
|
.name("solAcctHashVer".to_string())
|
2020-03-16 08:37:31 -07:00
|
|
|
.spawn(move || {
|
2023-03-28 05:32:18 -07:00
|
|
|
info!("AccountsHashVerifier has started");
|
2020-03-16 08:37:31 -07:00
|
|
|
let mut hashes = vec![];
|
2023-04-14 14:38:44 -07:00
|
|
|
// To support fastboot, we must ensure the storages used in the latest POST snapshot are
|
|
|
|
// not recycled nor removed early. Hold an Arc of their AppendVecs to prevent them from
|
|
|
|
// expiring.
|
|
|
|
let mut last_snapshot_storages = None;
|
2020-03-16 08:37:31 -07:00
|
|
|
loop {
|
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-03-03 17:59:29 -08:00
|
|
|
let Some((
|
2022-04-06 03:47:19 -07:00
|
|
|
accounts_package,
|
2022-10-13 09:47:36 -07:00
|
|
|
num_outstanding_accounts_packages,
|
|
|
|
num_re_enqueued_accounts_packages,
|
|
|
|
)) = Self::get_next_accounts_package(
|
|
|
|
&accounts_package_sender,
|
|
|
|
&accounts_package_receiver,
|
2023-03-03 17:59:29 -08:00
|
|
|
) else {
|
|
|
|
std::thread::sleep(LOOP_LIMITER);
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
info!("handling accounts package: {accounts_package:?}");
|
|
|
|
let enqueued_time = accounts_package.enqueued.elapsed();
|
2022-10-13 09:47:36 -07:00
|
|
|
|
2023-04-14 14:38:44 -07:00
|
|
|
let snapshot_storages = accounts_package.snapshot_storages.clone();
|
|
|
|
|
2023-03-03 17:59:29 -08:00
|
|
|
let (_, handling_time_us) = measure_us!(Self::process_accounts_package(
|
|
|
|
accounts_package,
|
|
|
|
&cluster_info,
|
|
|
|
known_validators.as_ref(),
|
|
|
|
halt_on_known_validators_accounts_hash_mismatch,
|
|
|
|
snapshot_package_sender.as_ref(),
|
|
|
|
&mut hashes,
|
|
|
|
&exit,
|
|
|
|
&snapshot_config,
|
2023-04-07 15:50:21 -07:00
|
|
|
accounts_hash_fault_injector,
|
2023-03-03 17:59:29 -08:00
|
|
|
));
|
2022-10-13 09:47:36 -07:00
|
|
|
|
2023-04-14 14:38:44 -07:00
|
|
|
// Done processing the current snapshot, so the current snapshot dir
|
|
|
|
// has been converted to POST state. It is the time to update
|
|
|
|
// last_snapshot_storages to release the reference counts for the
|
|
|
|
// previous POST snapshot dir, and save the new ones for the new
|
|
|
|
// POST snapshot dir.
|
|
|
|
last_snapshot_storages = Some(snapshot_storages);
|
|
|
|
debug!(
|
|
|
|
"Number of snapshot storages kept alive for fastboot: {}",
|
|
|
|
last_snapshot_storages
|
|
|
|
.as_ref()
|
|
|
|
.map(|storages| storages.len())
|
|
|
|
.unwrap_or(0)
|
|
|
|
);
|
|
|
|
|
2023-03-03 17:59:29 -08:00
|
|
|
datapoint_info!(
|
|
|
|
"accounts_hash_verifier",
|
|
|
|
(
|
|
|
|
"num-outstanding-accounts-packages",
|
|
|
|
num_outstanding_accounts_packages,
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"num-re-enqueued-accounts-packages",
|
|
|
|
num_re_enqueued_accounts_packages,
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
("enqueued-time-us", enqueued_time.as_micros(), i64),
|
|
|
|
("handling-time-us", handling_time_us, i64),
|
|
|
|
);
|
2020-03-16 08:37:31 -07:00
|
|
|
}
|
2023-04-14 14:38:44 -07:00
|
|
|
debug!(
|
|
|
|
"Number of snapshot storages kept alive for fastboot: {}",
|
|
|
|
last_snapshot_storages
|
|
|
|
.as_ref()
|
|
|
|
.map(|storages| storages.len())
|
|
|
|
.unwrap_or(0)
|
|
|
|
);
|
2023-03-28 05:32:18 -07:00
|
|
|
info!("AccountsHashVerifier has stopped");
|
2020-03-16 08:37:31 -07:00
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
Self {
|
|
|
|
t_accounts_hash_verifier,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-13 09:47:36 -07:00
|
|
|
/// Get the next accounts package to handle
|
|
|
|
///
|
|
|
|
/// Look through the accounts package channel to find the highest priority one to handle next.
|
|
|
|
/// If there are no accounts packages in the channel, return None. Otherwise return the
|
|
|
|
/// highest priority one. Unhandled accounts packages with slots GREATER-THAN the handled one
|
|
|
|
/// will be re-enqueued. The remaining will be dropped.
|
|
|
|
///
|
|
|
|
/// Also return the number of accounts packages initially in the channel, and the number of
|
|
|
|
/// ones re-enqueued.
|
|
|
|
fn get_next_accounts_package(
|
|
|
|
accounts_package_sender: &Sender<AccountsPackage>,
|
|
|
|
accounts_package_receiver: &Receiver<AccountsPackage>,
|
|
|
|
) -> Option<(
|
|
|
|
AccountsPackage,
|
|
|
|
/*num outstanding accounts packages*/ usize,
|
|
|
|
/*num re-enqueued accounts packages*/ usize,
|
|
|
|
)> {
|
|
|
|
let mut accounts_packages: Vec<_> = accounts_package_receiver.try_iter().collect();
|
|
|
|
// `select_nth()` panics if the slice is empty, so continue if that's the case
|
|
|
|
if accounts_packages.is_empty() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let accounts_packages_len = accounts_packages.len();
|
|
|
|
debug!("outstanding accounts packages ({accounts_packages_len}): {accounts_packages:?}");
|
|
|
|
let num_eah_packages = accounts_packages
|
|
|
|
.iter()
|
|
|
|
.filter(|account_package| {
|
|
|
|
account_package.package_type == AccountsPackageType::EpochAccountsHash
|
|
|
|
})
|
|
|
|
.count();
|
|
|
|
assert!(
|
|
|
|
num_eah_packages <= 1,
|
|
|
|
"Only a single EAH accounts package is allowed at a time! count: {num_eah_packages}"
|
|
|
|
);
|
|
|
|
|
|
|
|
accounts_packages.select_nth_unstable_by(
|
|
|
|
accounts_packages_len - 1,
|
|
|
|
snapshot_package::cmp_accounts_packages_by_priority,
|
|
|
|
);
|
|
|
|
// SAFETY: We know `accounts_packages` is not empty, so its len is >= 1,
|
|
|
|
// therefore there is always an element to pop.
|
|
|
|
let accounts_package = accounts_packages.pop().unwrap();
|
|
|
|
let handled_accounts_package_slot = accounts_package.slot;
|
|
|
|
// re-enqueue any remaining accounts packages for slots GREATER-THAN the accounts package
|
|
|
|
// that will be handled
|
|
|
|
let num_re_enqueued_accounts_packages = accounts_packages
|
|
|
|
.into_iter()
|
|
|
|
.filter(|accounts_package| accounts_package.slot > handled_accounts_package_slot)
|
|
|
|
.map(|accounts_package| {
|
|
|
|
accounts_package_sender
|
|
|
|
.try_send(accounts_package)
|
|
|
|
.expect("re-enqueue accounts package")
|
|
|
|
})
|
|
|
|
.count();
|
|
|
|
|
|
|
|
Some((
|
|
|
|
accounts_package,
|
|
|
|
accounts_packages_len,
|
|
|
|
num_re_enqueued_accounts_packages,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2021-02-05 11:48:55 -08:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2021-08-13 14:08:09 -07:00
|
|
|
fn process_accounts_package(
|
|
|
|
accounts_package: AccountsPackage,
|
2021-02-04 07:00:33 -08:00
|
|
|
cluster_info: &ClusterInfo,
|
2021-11-12 10:57:55 -08:00
|
|
|
known_validators: Option<&HashSet<Pubkey>>,
|
|
|
|
halt_on_known_validator_accounts_hash_mismatch: bool,
|
2023-02-21 19:36:29 -08:00
|
|
|
snapshot_package_sender: Option<&Sender<SnapshotPackage>>,
|
2021-02-04 07:00:33 -08:00
|
|
|
hashes: &mut Vec<(Slot, Hash)>,
|
2023-04-19 08:10:08 -07:00
|
|
|
exit: &AtomicBool,
|
2022-12-06 14:47:55 -08:00
|
|
|
snapshot_config: &SnapshotConfig,
|
2023-04-07 15:50:21 -07:00
|
|
|
accounts_hash_fault_injector: Option<AccountsHashFaultInjector>,
|
2021-02-04 07:00:33 -08:00
|
|
|
) {
|
2023-05-12 14:02:43 -07:00
|
|
|
let accounts_hash =
|
|
|
|
Self::calculate_and_verify_accounts_hash(&accounts_package, snapshot_config);
|
2021-08-31 16:33:27 -07:00
|
|
|
|
2022-09-07 13:41:40 -07:00
|
|
|
Self::save_epoch_accounts_hash(&accounts_package, accounts_hash);
|
|
|
|
|
2021-08-31 16:33:27 -07:00
|
|
|
Self::push_accounts_hashes_to_cluster(
|
|
|
|
&accounts_package,
|
2021-02-04 07:00:33 -08:00
|
|
|
cluster_info,
|
2021-11-12 10:57:55 -08:00
|
|
|
known_validators,
|
|
|
|
halt_on_known_validator_accounts_hash_mismatch,
|
2021-02-04 07:00:33 -08:00
|
|
|
hashes,
|
|
|
|
exit,
|
2022-04-08 08:42:03 -07:00
|
|
|
accounts_hash,
|
2023-04-07 15:50:21 -07:00
|
|
|
accounts_hash_fault_injector,
|
2021-02-04 07:00:33 -08:00
|
|
|
);
|
2021-08-31 16:33:27 -07:00
|
|
|
|
2022-04-08 08:42:03 -07:00
|
|
|
Self::submit_for_packaging(
|
|
|
|
accounts_package,
|
2023-02-21 19:36:29 -08:00
|
|
|
snapshot_package_sender,
|
2022-04-08 08:42:03 -07:00
|
|
|
snapshot_config,
|
|
|
|
accounts_hash,
|
|
|
|
);
|
2021-02-04 07:00:33 -08:00
|
|
|
}
|
|
|
|
|
2022-04-08 08:42:03 -07:00
|
|
|
/// returns calculated accounts hash
|
2023-05-12 14:02:43 -07:00
|
|
|
fn calculate_and_verify_accounts_hash(
|
|
|
|
accounts_package: &AccountsPackage,
|
|
|
|
snapshot_config: &SnapshotConfig,
|
|
|
|
) -> AccountsHashEnum {
|
2023-03-14 09:41:44 -07:00
|
|
|
let accounts_hash_calculation_flavor = match accounts_package.package_type {
|
|
|
|
AccountsPackageType::AccountsHashVerifier => CalcAccountsHashFlavor::Full,
|
|
|
|
AccountsPackageType::EpochAccountsHash => CalcAccountsHashFlavor::Full,
|
|
|
|
AccountsPackageType::Snapshot(snapshot_type) => match snapshot_type {
|
|
|
|
SnapshotType::FullSnapshot => CalcAccountsHashFlavor::Full,
|
|
|
|
SnapshotType::IncrementalSnapshot(_) => {
|
|
|
|
if accounts_package.is_incremental_accounts_hash_feature_enabled {
|
|
|
|
CalcAccountsHashFlavor::Incremental
|
|
|
|
} else {
|
|
|
|
CalcAccountsHashFlavor::Full
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2023-03-22 07:20:16 -07:00
|
|
|
let (
|
|
|
|
accounts_hash_enum,
|
|
|
|
accounts_hash_for_reserialize,
|
|
|
|
bank_incremental_snapshot_persistence,
|
|
|
|
) = match accounts_hash_calculation_flavor {
|
|
|
|
CalcAccountsHashFlavor::Full => {
|
|
|
|
let (accounts_hash, _capitalization) =
|
|
|
|
Self::_calculate_full_accounts_hash(accounts_package);
|
|
|
|
(accounts_hash.into(), accounts_hash, None)
|
|
|
|
}
|
|
|
|
CalcAccountsHashFlavor::Incremental => {
|
|
|
|
let AccountsPackageType::Snapshot(SnapshotType::IncrementalSnapshot(base_slot)) = accounts_package.package_type else {
|
|
|
|
panic!("Calculating incremental accounts hash requires a base slot");
|
|
|
|
};
|
|
|
|
let (base_accounts_hash, base_capitalization) = accounts_package
|
|
|
|
.accounts
|
|
|
|
.accounts_db
|
|
|
|
.get_accounts_hash(base_slot)
|
|
|
|
.expect("incremental snapshot requires accounts hash and capitalization from the full snapshot it is based on");
|
|
|
|
let (incremental_accounts_hash, incremental_capitalization) =
|
|
|
|
Self::_calculate_incremental_accounts_hash(accounts_package, base_slot);
|
|
|
|
let bank_incremental_snapshot_persistence = BankIncrementalSnapshotPersistence {
|
|
|
|
full_slot: base_slot,
|
|
|
|
full_hash: base_accounts_hash.into(),
|
|
|
|
full_capitalization: base_capitalization,
|
|
|
|
incremental_hash: incremental_accounts_hash.into(),
|
|
|
|
incremental_capitalization,
|
|
|
|
};
|
|
|
|
(
|
|
|
|
incremental_accounts_hash.into(),
|
|
|
|
AccountsHash(Hash::default()), // value does not matter; not used for incremental snapshots
|
|
|
|
Some(bank_incremental_snapshot_persistence),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(snapshot_info) = &accounts_package.snapshot_info {
|
|
|
|
solana_runtime::serde_snapshot::reserialize_bank_with_new_accounts_hash(
|
2023-05-01 11:24:59 -07:00
|
|
|
&snapshot_info.bank_snapshot_dir,
|
2023-03-22 07:20:16 -07:00
|
|
|
accounts_package.slot,
|
|
|
|
&accounts_hash_for_reserialize,
|
|
|
|
bank_incremental_snapshot_persistence.as_ref(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if accounts_package.package_type
|
|
|
|
== AccountsPackageType::Snapshot(SnapshotType::FullSnapshot)
|
|
|
|
{
|
|
|
|
accounts_package
|
|
|
|
.accounts
|
|
|
|
.accounts_db
|
|
|
|
.purge_old_accounts_hashes(accounts_package.slot);
|
|
|
|
}
|
2023-05-12 14:02:43 -07:00
|
|
|
|
|
|
|
// After an accounts package has had its accounts hash calculated and
|
|
|
|
// has been reserialized to become a BankSnapshotPost, it is now safe
|
|
|
|
// to clean up some older bank snapshots.
|
|
|
|
//
|
|
|
|
// If we are generating snapshots, then this accounts package will be sent
|
|
|
|
// to SnapshotPackagerService to be archived. SPS needs the bank snapshots
|
|
|
|
// to make the archives, so we cannot purge any bank snapshots that SPS
|
|
|
|
// may still be using. Therefore, we defer purging to SPS.
|
|
|
|
//
|
|
|
|
// If we are *not* generating snapshots, then purge old bank snapshots here.
|
|
|
|
if !snapshot_config.should_generate_snapshots() {
|
|
|
|
snapshot_utils::purge_bank_snapshots_older_than_slot(
|
|
|
|
&snapshot_config.bank_snapshots_dir,
|
|
|
|
accounts_package.slot,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-03-22 07:20:16 -07:00
|
|
|
accounts_hash_enum
|
|
|
|
}
|
|
|
|
|
|
|
|
fn _calculate_full_accounts_hash(
|
|
|
|
accounts_package: &AccountsPackage,
|
|
|
|
) -> (AccountsHash, /*capitalization*/ u64) {
|
|
|
|
let (sorted_storages, storage_sort_us) =
|
|
|
|
measure_us!(SortedStorages::new(&accounts_package.snapshot_storages));
|
2022-03-24 15:49:48 -07:00
|
|
|
|
2022-04-08 08:42:03 -07:00
|
|
|
let mut timings = HashStats {
|
2023-03-22 07:20:16 -07:00
|
|
|
storage_sort_us,
|
2022-04-08 08:42:03 -07:00
|
|
|
..HashStats::default()
|
|
|
|
};
|
|
|
|
timings.calc_storage_size_quartiles(&accounts_package.snapshot_storages);
|
|
|
|
|
2023-03-13 12:39:28 -07:00
|
|
|
let calculate_accounts_hash_config = CalcAccountsHashConfig {
|
|
|
|
use_bg_thread_pool: true,
|
|
|
|
check_hash: false,
|
|
|
|
ancestors: None,
|
|
|
|
epoch_schedule: &accounts_package.epoch_schedule,
|
|
|
|
rent_collector: &accounts_package.rent_collector,
|
|
|
|
store_detailed_debug_info_on_failure: false,
|
2023-05-11 13:23:29 -07:00
|
|
|
include_slot_in_hash: accounts_package.include_slot_in_hash,
|
2023-03-13 12:39:28 -07:00
|
|
|
};
|
|
|
|
|
2023-03-22 07:20:16 -07:00
|
|
|
let ((accounts_hash, lamports), measure_hash_us) = measure_us!(accounts_package
|
2022-04-08 08:42:03 -07:00
|
|
|
.accounts
|
|
|
|
.accounts_db
|
2022-10-24 18:07:00 -07:00
|
|
|
.calculate_accounts_hash_from_storages(
|
2023-03-13 12:39:28 -07:00
|
|
|
&calculate_accounts_hash_config,
|
2022-04-08 08:42:03 -07:00
|
|
|
&sorted_storages,
|
|
|
|
timings,
|
|
|
|
)
|
2023-03-22 07:20:16 -07:00
|
|
|
.unwrap()); // unwrap here will never fail since check_hash = false
|
2023-03-13 09:41:24 -07:00
|
|
|
|
2023-03-22 07:20:16 -07:00
|
|
|
let slot = accounts_package.slot;
|
|
|
|
let old_accounts_hash = accounts_package
|
|
|
|
.accounts
|
|
|
|
.accounts_db
|
|
|
|
.set_accounts_hash(slot, (accounts_hash, lamports));
|
|
|
|
if let Some(old_accounts_hash) = old_accounts_hash {
|
|
|
|
warn!("Accounts hash was already set for slot {slot}! old: {old_accounts_hash:?}, new: {accounts_hash:?}");
|
2023-03-13 09:41:24 -07:00
|
|
|
}
|
2022-04-08 08:42:03 -07:00
|
|
|
|
2022-07-10 21:10:22 -07:00
|
|
|
if accounts_package.expected_capitalization != lamports {
|
2022-07-05 16:01:02 -07:00
|
|
|
// before we assert, run the hash calc again. This helps track down whether it could have been a failure in a race condition possibly with shrink.
|
|
|
|
// We could add diagnostics to the hash calc here to produce a per bin cap or something to help narrow down how many pubkeys are different.
|
2023-03-13 12:39:28 -07:00
|
|
|
let calculate_accounts_hash_config = CalcAccountsHashConfig {
|
|
|
|
// since we're going to assert, use the fg thread pool to go faster
|
|
|
|
use_bg_thread_pool: false,
|
|
|
|
..calculate_accounts_hash_config
|
|
|
|
};
|
2022-08-22 08:58:04 -07:00
|
|
|
let result_with_index = accounts_package
|
|
|
|
.accounts
|
|
|
|
.accounts_db
|
2023-03-14 09:41:44 -07:00
|
|
|
.calculate_accounts_hash_from_index(slot, &calculate_accounts_hash_config);
|
|
|
|
info!("hash calc with index: {slot}, {result_with_index:?}",);
|
2023-03-13 12:39:28 -07:00
|
|
|
let calculate_accounts_hash_config = CalcAccountsHashConfig {
|
|
|
|
// now that we've failed, store off the failing contents that produced a bad capitalization
|
|
|
|
store_detailed_debug_info_on_failure: true,
|
|
|
|
..calculate_accounts_hash_config
|
|
|
|
};
|
|
|
|
_ = accounts_package
|
2022-07-10 21:10:22 -07:00
|
|
|
.accounts
|
|
|
|
.accounts_db
|
2022-10-24 18:07:00 -07:00
|
|
|
.calculate_accounts_hash_from_storages(
|
2023-03-13 12:39:28 -07:00
|
|
|
&calculate_accounts_hash_config,
|
2022-07-10 21:10:22 -07:00
|
|
|
&sorted_storages,
|
|
|
|
HashStats::default(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-08-24 12:11:53 -07:00
|
|
|
assert_eq!(
|
|
|
|
accounts_package.expected_capitalization, lamports,
|
|
|
|
"accounts hash capitalization mismatch"
|
|
|
|
);
|
2022-07-10 21:10:22 -07:00
|
|
|
if let Some(expected_hash) = accounts_package.accounts_hash_for_testing {
|
2022-07-05 16:01:02 -07:00
|
|
|
assert_eq!(expected_hash, accounts_hash);
|
|
|
|
};
|
|
|
|
|
2023-03-22 07:20:16 -07:00
|
|
|
datapoint_info!(
|
|
|
|
"accounts_hash_verifier",
|
|
|
|
("calculate_hash", measure_hash_us, i64),
|
|
|
|
);
|
2023-03-13 14:44:34 -07:00
|
|
|
|
2023-03-22 07:20:16 -07:00
|
|
|
(accounts_hash, lamports)
|
|
|
|
}
|
2023-03-06 08:40:46 -08:00
|
|
|
|
2023-03-22 07:20:16 -07:00
|
|
|
fn _calculate_incremental_accounts_hash(
|
|
|
|
accounts_package: &AccountsPackage,
|
|
|
|
base_slot: Slot,
|
|
|
|
) -> (IncrementalAccountsHash, /*capitalization*/ u64) {
|
|
|
|
let incremental_storages =
|
|
|
|
accounts_package
|
|
|
|
.snapshot_storages
|
|
|
|
.iter()
|
|
|
|
.filter_map(|storage| {
|
|
|
|
let storage_slot = storage.slot();
|
|
|
|
(storage_slot > base_slot).then_some((storage, storage_slot))
|
|
|
|
});
|
|
|
|
let sorted_storages = SortedStorages::new_with_slots(incremental_storages, None, None);
|
|
|
|
|
|
|
|
let calculate_accounts_hash_config = CalcAccountsHashConfig {
|
|
|
|
use_bg_thread_pool: true,
|
|
|
|
check_hash: false,
|
|
|
|
ancestors: None,
|
|
|
|
epoch_schedule: &accounts_package.epoch_schedule,
|
|
|
|
rent_collector: &accounts_package.rent_collector,
|
|
|
|
store_detailed_debug_info_on_failure: false,
|
2023-05-11 13:23:29 -07:00
|
|
|
include_slot_in_hash: accounts_package.include_slot_in_hash,
|
2023-03-22 07:20:16 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
let (incremental_accounts_hash, measure_hash_us) = measure_us!(
|
2023-03-13 08:12:11 -07:00
|
|
|
accounts_package
|
|
|
|
.accounts
|
|
|
|
.accounts_db
|
2023-03-22 07:20:16 -07:00
|
|
|
.update_incremental_accounts_hash(
|
|
|
|
&calculate_accounts_hash_config,
|
|
|
|
&sorted_storages,
|
|
|
|
accounts_package.slot,
|
|
|
|
HashStats::default(),
|
|
|
|
)
|
|
|
|
.unwrap() // unwrap here will never fail since check_hash = false
|
|
|
|
);
|
2023-03-06 08:40:46 -08:00
|
|
|
|
2021-08-31 16:33:27 -07:00
|
|
|
datapoint_info!(
|
|
|
|
"accounts_hash_verifier",
|
2023-03-24 14:39:20 -07:00
|
|
|
(
|
|
|
|
"calculate_incremental_accounts_hash_us",
|
|
|
|
measure_hash_us,
|
|
|
|
i64
|
|
|
|
),
|
2021-08-31 16:33:27 -07:00
|
|
|
);
|
2023-03-22 07:20:16 -07:00
|
|
|
|
|
|
|
incremental_accounts_hash
|
2021-08-31 16:33:27 -07:00
|
|
|
}
|
|
|
|
|
2023-02-24 14:17:54 -08:00
|
|
|
fn save_epoch_accounts_hash(
|
|
|
|
accounts_package: &AccountsPackage,
|
|
|
|
accounts_hash: AccountsHashEnum,
|
|
|
|
) {
|
2022-09-07 13:41:40 -07:00
|
|
|
if accounts_package.package_type == AccountsPackageType::EpochAccountsHash {
|
2023-02-24 14:17:54 -08:00
|
|
|
let AccountsHashEnum::Full(accounts_hash) = accounts_hash else {
|
|
|
|
panic!("EAH requires a full accounts hash!");
|
|
|
|
};
|
2022-10-05 14:44:35 -07:00
|
|
|
info!(
|
2022-09-23 11:04:48 -07:00
|
|
|
"saving epoch accounts hash, slot: {}, hash: {}",
|
2022-11-28 07:09:47 -08:00
|
|
|
accounts_package.slot, accounts_hash.0,
|
2022-09-23 11:04:48 -07:00
|
|
|
);
|
2022-10-05 14:44:35 -07:00
|
|
|
accounts_package
|
2022-09-07 13:41:40 -07:00
|
|
|
.accounts
|
|
|
|
.accounts_db
|
2022-10-05 14:44:35 -07:00
|
|
|
.epoch_accounts_hash_manager
|
2023-02-24 14:17:54 -08:00
|
|
|
.set_valid(accounts_hash.into(), accounts_package.slot);
|
2022-09-07 13:41:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-31 16:33:27 -07:00
|
|
|
fn push_accounts_hashes_to_cluster(
|
|
|
|
accounts_package: &AccountsPackage,
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info: &ClusterInfo,
|
2021-11-12 10:57:55 -08:00
|
|
|
known_validators: Option<&HashSet<Pubkey>>,
|
|
|
|
halt_on_known_validator_accounts_hash_mismatch: bool,
|
2020-03-16 08:37:31 -07:00
|
|
|
hashes: &mut Vec<(Slot, Hash)>,
|
2023-04-19 08:10:08 -07:00
|
|
|
exit: &AtomicBool,
|
2023-02-24 14:17:54 -08:00
|
|
|
accounts_hash: AccountsHashEnum,
|
2023-04-07 15:50:21 -07:00
|
|
|
accounts_hash_fault_injector: Option<AccountsHashFaultInjector>,
|
2020-03-16 08:37:31 -07:00
|
|
|
) {
|
2023-04-07 15:50:21 -07:00
|
|
|
let hash = accounts_hash_fault_injector
|
|
|
|
.and_then(|f| f(accounts_hash.as_hash(), accounts_package.slot))
|
|
|
|
.or(Some(*accounts_hash.as_hash()));
|
|
|
|
hashes.push((accounts_package.slot, hash.unwrap()));
|
2020-03-16 08:37:31 -07:00
|
|
|
|
2023-04-10 07:44:40 -07:00
|
|
|
retain_max_n_elements(hashes, MAX_ACCOUNTS_HASHES);
|
2020-03-31 21:39:48 -07:00
|
|
|
|
2021-11-12 10:57:55 -08:00
|
|
|
if halt_on_known_validator_accounts_hash_mismatch {
|
2020-03-16 08:37:31 -07:00
|
|
|
let mut slot_to_hash = HashMap::new();
|
|
|
|
for (slot, hash) in hashes.iter() {
|
|
|
|
slot_to_hash.insert(*slot, *hash);
|
|
|
|
}
|
2021-11-12 10:57:55 -08:00
|
|
|
if Self::should_halt(cluster_info, known_validators, &mut slot_to_hash) {
|
2020-03-16 08:37:31 -07:00
|
|
|
exit.store(true, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
}
|
2020-04-16 15:12:20 -07:00
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info.push_accounts_hashes(hashes.clone());
|
2020-03-16 08:37:31 -07:00
|
|
|
}
|
|
|
|
|
2021-08-31 16:33:27 -07:00
|
|
|
fn submit_for_packaging(
|
|
|
|
accounts_package: AccountsPackage,
|
2023-02-21 19:36:29 -08:00
|
|
|
snapshot_package_sender: Option<&Sender<SnapshotPackage>>,
|
2022-12-06 14:47:55 -08:00
|
|
|
snapshot_config: &SnapshotConfig,
|
2023-02-24 14:17:54 -08:00
|
|
|
accounts_hash: AccountsHashEnum,
|
2021-08-31 16:33:27 -07:00
|
|
|
) {
|
2023-02-21 19:36:29 -08:00
|
|
|
if !snapshot_config.should_generate_snapshots()
|
2022-09-07 13:41:40 -07:00
|
|
|
|| !matches!(
|
|
|
|
accounts_package.package_type,
|
|
|
|
AccountsPackageType::Snapshot(_)
|
|
|
|
)
|
2021-08-31 16:33:27 -07:00
|
|
|
{
|
|
|
|
return;
|
2022-09-07 13:41:40 -07:00
|
|
|
}
|
2023-02-21 19:36:29 -08:00
|
|
|
let Some(snapshot_package_sender) = snapshot_package_sender else {
|
|
|
|
return;
|
2021-08-31 16:33:27 -07:00
|
|
|
};
|
|
|
|
|
2023-02-24 14:17:54 -08:00
|
|
|
let snapshot_package = SnapshotPackage::new(accounts_package, accounts_hash);
|
2023-02-21 19:36:29 -08:00
|
|
|
snapshot_package_sender
|
|
|
|
.send(snapshot_package)
|
|
|
|
.expect("send snapshot package");
|
2021-08-31 16:33:27 -07:00
|
|
|
}
|
|
|
|
|
2020-03-16 08:37:31 -07:00
|
|
|
fn should_halt(
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info: &ClusterInfo,
|
2021-11-12 10:57:55 -08:00
|
|
|
known_validators: Option<&HashSet<Pubkey>>,
|
2020-03-16 08:37:31 -07:00
|
|
|
slot_to_hash: &mut HashMap<Slot, Hash>,
|
|
|
|
) -> bool {
|
2020-03-18 08:39:09 -07:00
|
|
|
let mut verified_count = 0;
|
2020-03-31 21:39:48 -07:00
|
|
|
let mut highest_slot = 0;
|
2021-11-12 10:57:55 -08:00
|
|
|
if let Some(known_validators) = known_validators {
|
|
|
|
for known_validator in known_validators {
|
|
|
|
let is_conflicting = cluster_info.get_accounts_hash_for_node(known_validator, |accounts_hashes|
|
2020-03-16 08:37:31 -07:00
|
|
|
{
|
2020-04-21 12:54:45 -07:00
|
|
|
accounts_hashes.iter().any(|(slot, hash)| {
|
2020-03-16 08:37:31 -07:00
|
|
|
if let Some(reference_hash) = slot_to_hash.get(slot) {
|
|
|
|
if *hash != *reference_hash {
|
2022-08-25 11:56:56 -07:00
|
|
|
error!("Fatal! Exiting! Known validator {} produced conflicting hashes for slot: {} ({} != {})",
|
2021-11-12 10:57:55 -08:00
|
|
|
known_validator,
|
2020-03-16 08:37:31 -07:00
|
|
|
slot,
|
|
|
|
hash,
|
|
|
|
reference_hash,
|
|
|
|
);
|
2020-04-21 12:54:45 -07:00
|
|
|
true
|
2020-03-18 08:39:09 -07:00
|
|
|
} else {
|
|
|
|
verified_count += 1;
|
2020-04-21 12:54:45 -07:00
|
|
|
false
|
2020-03-16 08:37:31 -07:00
|
|
|
}
|
|
|
|
} else {
|
2020-03-31 21:39:48 -07:00
|
|
|
highest_slot = std::cmp::max(*slot, highest_slot);
|
2020-03-16 08:37:31 -07:00
|
|
|
slot_to_hash.insert(*slot, *hash);
|
2020-04-21 12:54:45 -07:00
|
|
|
false
|
2020-03-16 08:37:31 -07:00
|
|
|
}
|
2020-04-21 12:54:45 -07:00
|
|
|
})
|
|
|
|
}).unwrap_or(false);
|
|
|
|
|
|
|
|
if is_conflicting {
|
|
|
|
return true;
|
2020-03-16 08:37:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-31 21:39:48 -07:00
|
|
|
datapoint_info!(
|
|
|
|
"accounts_hash_verifier",
|
|
|
|
("highest_slot_verified", highest_slot, i64),
|
2023-04-30 18:39:44 -07:00
|
|
|
("num_verified", verified_count, i64),
|
2020-03-31 21:39:48 -07:00
|
|
|
);
|
2020-03-16 08:37:31 -07:00
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn join(self) -> thread::Result<()> {
|
|
|
|
self.t_accounts_hash_verifier.join()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-12-03 09:00:31 -08:00
|
|
|
use {
|
|
|
|
super::*,
|
2022-10-13 09:47:36 -07:00
|
|
|
rand::seq::SliceRandom,
|
2023-02-10 12:07:45 -08:00
|
|
|
solana_gossip::{cluster_info::make_accounts_hashes_message, contact_info::ContactInfo},
|
2023-02-21 19:36:29 -08:00
|
|
|
solana_runtime::snapshot_package::SnapshotType,
|
2021-12-03 09:00:31 -08:00
|
|
|
solana_sdk::{
|
|
|
|
hash::hash,
|
|
|
|
signature::{Keypair, Signer},
|
2023-01-24 08:57:55 -08:00
|
|
|
timing::timestamp,
|
2021-12-03 09:00:31 -08:00
|
|
|
},
|
|
|
|
solana_streamer::socket::SocketAddrSpace,
|
2022-11-02 11:13:35 -07:00
|
|
|
std::str::FromStr,
|
2020-03-16 08:37:31 -07:00
|
|
|
};
|
2021-07-23 08:25:03 -07:00
|
|
|
|
2023-01-24 08:57:55 -08:00
|
|
|
fn new_test_cluster_info() -> ClusterInfo {
|
|
|
|
let keypair = Arc::new(Keypair::new());
|
|
|
|
let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), timestamp());
|
|
|
|
ClusterInfo::new(contact_info, keypair, SocketAddrSpace::Unspecified)
|
2021-07-23 08:25:03 -07:00
|
|
|
}
|
2020-03-16 08:37:31 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_should_halt() {
|
2023-01-24 08:57:55 -08:00
|
|
|
let cluster_info = new_test_cluster_info();
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = Arc::new(cluster_info);
|
2020-03-16 08:37:31 -07:00
|
|
|
|
2021-11-12 10:57:55 -08:00
|
|
|
let mut known_validators = HashSet::new();
|
2020-03-16 08:37:31 -07:00
|
|
|
let mut slot_to_hash = HashMap::new();
|
|
|
|
assert!(!AccountsHashVerifier::should_halt(
|
|
|
|
&cluster_info,
|
2021-11-12 10:57:55 -08:00
|
|
|
Some(&known_validators),
|
2020-03-16 08:37:31 -07:00
|
|
|
&mut slot_to_hash,
|
|
|
|
));
|
|
|
|
|
|
|
|
let validator1 = Keypair::new();
|
|
|
|
let hash1 = hash(&[1]);
|
|
|
|
let hash2 = hash(&[2]);
|
|
|
|
{
|
|
|
|
let message = make_accounts_hashes_message(&validator1, vec![(0, hash1)]).unwrap();
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info.push_message(message);
|
2020-10-13 18:10:25 -07:00
|
|
|
cluster_info.flush_push_queue();
|
2020-03-16 08:37:31 -07:00
|
|
|
}
|
|
|
|
slot_to_hash.insert(0, hash2);
|
2021-11-12 10:57:55 -08:00
|
|
|
known_validators.insert(validator1.pubkey());
|
2020-03-16 08:37:31 -07:00
|
|
|
assert!(AccountsHashVerifier::should_halt(
|
|
|
|
&cluster_info,
|
2021-11-12 10:57:55 -08:00
|
|
|
Some(&known_validators),
|
2020-03-16 08:37:31 -07:00
|
|
|
&mut slot_to_hash,
|
|
|
|
));
|
|
|
|
}
|
2020-03-31 21:39:48 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_max_hashes() {
|
|
|
|
solana_logger::setup();
|
2023-01-24 08:57:55 -08:00
|
|
|
let cluster_info = new_test_cluster_info();
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = Arc::new(cluster_info);
|
2020-03-31 21:39:48 -07:00
|
|
|
|
2021-11-12 10:57:55 -08:00
|
|
|
let known_validators = HashSet::new();
|
2020-03-31 21:39:48 -07:00
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
|
|
|
let mut hashes = vec![];
|
2021-08-10 12:02:34 -07:00
|
|
|
let full_snapshot_archive_interval_slots = 100;
|
|
|
|
let snapshot_config = SnapshotConfig {
|
|
|
|
full_snapshot_archive_interval_slots,
|
|
|
|
incremental_snapshot_archive_interval_slots: Slot::MAX,
|
2021-09-12 11:44:27 -07:00
|
|
|
..SnapshotConfig::default()
|
2021-08-10 12:02:34 -07:00
|
|
|
};
|
2022-04-08 08:42:03 -07:00
|
|
|
let expected_hash = Hash::from_str("GKot5hBsd81kMupNCXHaqbhv3huEbxAFMLnpcX2hniwn").unwrap();
|
2023-04-10 07:44:40 -07:00
|
|
|
for i in 0..MAX_ACCOUNTS_HASHES + 1 {
|
2022-11-06 18:32:33 -08:00
|
|
|
let slot = full_snapshot_archive_interval_slots + i as u64;
|
2021-08-31 16:33:27 -07:00
|
|
|
let accounts_package = AccountsPackage {
|
2022-11-06 18:32:33 -08:00
|
|
|
slot,
|
|
|
|
block_height: slot,
|
2022-11-02 11:13:35 -07:00
|
|
|
..AccountsPackage::default_for_tests()
|
2021-08-31 16:33:27 -07:00
|
|
|
};
|
2020-03-31 21:39:48 -07:00
|
|
|
|
2021-08-31 16:33:27 -07:00
|
|
|
AccountsHashVerifier::process_accounts_package(
|
|
|
|
accounts_package,
|
2020-03-31 21:39:48 -07:00
|
|
|
&cluster_info,
|
2021-11-12 10:57:55 -08:00
|
|
|
Some(&known_validators),
|
2020-03-31 21:39:48 -07:00
|
|
|
false,
|
2021-08-10 12:02:34 -07:00
|
|
|
None,
|
2020-03-31 21:39:48 -07:00
|
|
|
&mut hashes,
|
|
|
|
&exit,
|
2022-12-06 14:47:55 -08:00
|
|
|
&snapshot_config,
|
2023-04-07 15:50:21 -07:00
|
|
|
None,
|
2020-03-31 21:39:48 -07:00
|
|
|
);
|
2021-08-31 16:33:27 -07:00
|
|
|
|
2022-11-06 18:32:33 -08:00
|
|
|
// sleep for 1ms to create a newer timestamp for gossip entry
|
2020-12-17 15:12:18 -08:00
|
|
|
// otherwise the timestamp won't be newer.
|
|
|
|
std::thread::sleep(Duration::from_millis(1));
|
2020-03-31 21:39:48 -07:00
|
|
|
}
|
2020-10-13 18:10:25 -07:00
|
|
|
cluster_info.flush_push_queue();
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_hashes = cluster_info
|
2023-01-24 08:57:55 -08:00
|
|
|
.get_accounts_hash_for_node(&cluster_info.id(), |c| c.clone())
|
2020-03-31 21:39:48 -07:00
|
|
|
.unwrap();
|
|
|
|
info!("{:?}", cluster_hashes);
|
2023-04-10 07:44:40 -07:00
|
|
|
assert_eq!(hashes.len(), MAX_ACCOUNTS_HASHES);
|
|
|
|
assert_eq!(cluster_hashes.len(), MAX_ACCOUNTS_HASHES);
|
2021-08-10 12:02:34 -07:00
|
|
|
assert_eq!(
|
|
|
|
cluster_hashes[0],
|
2022-04-08 08:42:03 -07:00
|
|
|
(full_snapshot_archive_interval_slots + 1, expected_hash)
|
2021-08-10 12:02:34 -07:00
|
|
|
);
|
2020-03-31 21:39:48 -07:00
|
|
|
assert_eq!(
|
2023-04-10 07:44:40 -07:00
|
|
|
cluster_hashes[MAX_ACCOUNTS_HASHES - 1],
|
2020-03-31 21:39:48 -07:00
|
|
|
(
|
2023-04-10 07:44:40 -07:00
|
|
|
full_snapshot_archive_interval_slots + MAX_ACCOUNTS_HASHES as u64,
|
2022-04-08 08:42:03 -07:00
|
|
|
expected_hash
|
2020-03-31 21:39:48 -07:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2022-10-13 09:47:36 -07:00
|
|
|
|
|
|
|
/// Ensure that unhandled accounts packages are properly re-enqueued or dropped
|
|
|
|
///
|
|
|
|
/// The accounts package handler should re-enqueue unhandled accounts packages, if those
|
|
|
|
/// unhandled accounts packages are for slots GREATER-THAN the last handled accounts package.
|
|
|
|
/// Otherwise, they should be dropped.
|
|
|
|
#[test]
|
|
|
|
fn test_get_next_accounts_package() {
|
|
|
|
fn new(package_type: AccountsPackageType, slot: Slot) -> AccountsPackage {
|
|
|
|
AccountsPackage {
|
|
|
|
package_type,
|
|
|
|
slot,
|
|
|
|
block_height: slot,
|
|
|
|
..AccountsPackage::default_for_tests()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn new_eah(slot: Slot) -> AccountsPackage {
|
|
|
|
new(AccountsPackageType::EpochAccountsHash, slot)
|
|
|
|
}
|
|
|
|
fn new_fss(slot: Slot) -> AccountsPackage {
|
|
|
|
new(
|
|
|
|
AccountsPackageType::Snapshot(SnapshotType::FullSnapshot),
|
|
|
|
slot,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
fn new_iss(slot: Slot, base: Slot) -> AccountsPackage {
|
|
|
|
new(
|
|
|
|
AccountsPackageType::Snapshot(SnapshotType::IncrementalSnapshot(base)),
|
|
|
|
slot,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
fn new_ahv(slot: Slot) -> AccountsPackage {
|
|
|
|
new(AccountsPackageType::AccountsHashVerifier, slot)
|
|
|
|
}
|
|
|
|
|
|
|
|
let (accounts_package_sender, accounts_package_receiver) = crossbeam_channel::unbounded();
|
|
|
|
|
|
|
|
// Populate the channel so that re-enqueueing and dropping will be tested
|
|
|
|
let mut accounts_packages = [
|
|
|
|
new_ahv(99),
|
|
|
|
new_fss(100),
|
|
|
|
new_ahv(101),
|
|
|
|
new_iss(110, 100),
|
|
|
|
new_ahv(111),
|
|
|
|
new_eah(200), // <-- handle 1st
|
|
|
|
new_ahv(201),
|
|
|
|
new_iss(210, 100),
|
|
|
|
new_ahv(211),
|
|
|
|
new_fss(300),
|
|
|
|
new_ahv(301),
|
|
|
|
new_iss(310, 300),
|
|
|
|
new_ahv(311),
|
|
|
|
new_fss(400), // <-- handle 2nd
|
|
|
|
new_ahv(401),
|
|
|
|
new_iss(410, 400),
|
|
|
|
new_ahv(411),
|
|
|
|
new_iss(420, 400), // <-- handle 3rd
|
|
|
|
new_ahv(421),
|
|
|
|
new_ahv(422),
|
|
|
|
new_ahv(423), // <-- handle 4th
|
|
|
|
];
|
|
|
|
// Shuffle the accounts packages to simulate receiving new accounts packages from ABS
|
|
|
|
// simultaneously as AHV is processing them.
|
|
|
|
accounts_packages.shuffle(&mut rand::thread_rng());
|
|
|
|
accounts_packages
|
|
|
|
.into_iter()
|
|
|
|
.for_each(|accounts_package| accounts_package_sender.send(accounts_package).unwrap());
|
|
|
|
|
|
|
|
// The EAH is handled 1st
|
|
|
|
let (
|
|
|
|
account_package,
|
|
|
|
_num_outstanding_accounts_packages,
|
|
|
|
num_re_enqueued_accounts_packages,
|
|
|
|
) = AccountsHashVerifier::get_next_accounts_package(
|
|
|
|
&accounts_package_sender,
|
|
|
|
&accounts_package_receiver,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
account_package.package_type,
|
|
|
|
AccountsPackageType::EpochAccountsHash
|
|
|
|
);
|
|
|
|
assert_eq!(account_package.slot, 200);
|
|
|
|
assert_eq!(num_re_enqueued_accounts_packages, 15);
|
|
|
|
|
|
|
|
// The Full Snapshot from slot 400 is handled 2nd
|
|
|
|
// (the older full snapshot from slot 300 is skipped and dropped)
|
|
|
|
let (
|
|
|
|
account_package,
|
|
|
|
_num_outstanding_accounts_packages,
|
|
|
|
num_re_enqueued_accounts_packages,
|
|
|
|
) = AccountsHashVerifier::get_next_accounts_package(
|
|
|
|
&accounts_package_sender,
|
|
|
|
&accounts_package_receiver,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
account_package.package_type,
|
|
|
|
AccountsPackageType::Snapshot(SnapshotType::FullSnapshot)
|
|
|
|
);
|
|
|
|
assert_eq!(account_package.slot, 400);
|
|
|
|
assert_eq!(num_re_enqueued_accounts_packages, 7);
|
|
|
|
|
|
|
|
// The Incremental Snapshot from slot 420 is handled 3rd
|
|
|
|
// (the older incremental snapshot from slot 410 is skipped and dropped)
|
|
|
|
let (
|
|
|
|
account_package,
|
|
|
|
_num_outstanding_accounts_packages,
|
|
|
|
num_re_enqueued_accounts_packages,
|
|
|
|
) = AccountsHashVerifier::get_next_accounts_package(
|
|
|
|
&accounts_package_sender,
|
|
|
|
&accounts_package_receiver,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
account_package.package_type,
|
|
|
|
AccountsPackageType::Snapshot(SnapshotType::IncrementalSnapshot(400))
|
|
|
|
);
|
|
|
|
assert_eq!(account_package.slot, 420);
|
|
|
|
assert_eq!(num_re_enqueued_accounts_packages, 3);
|
|
|
|
|
|
|
|
// The Accounts Have Verifier from slot 423 is handled 4th
|
|
|
|
// (the older accounts have verifiers from slot 421 and 422 are skipped and dropped)
|
|
|
|
let (
|
|
|
|
account_package,
|
|
|
|
_num_outstanding_accounts_packages,
|
|
|
|
num_re_enqueued_accounts_packages,
|
|
|
|
) = AccountsHashVerifier::get_next_accounts_package(
|
|
|
|
&accounts_package_sender,
|
|
|
|
&accounts_package_receiver,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
account_package.package_type,
|
|
|
|
AccountsPackageType::AccountsHashVerifier
|
|
|
|
);
|
|
|
|
assert_eq!(account_package.slot, 423);
|
|
|
|
assert_eq!(num_re_enqueued_accounts_packages, 0);
|
|
|
|
|
|
|
|
// And now the accounts package channel is empty!
|
|
|
|
assert!(AccountsHashVerifier::get_next_accounts_package(
|
|
|
|
&accounts_package_sender,
|
|
|
|
&accounts_package_receiver
|
|
|
|
)
|
|
|
|
.is_none());
|
|
|
|
}
|
2020-03-16 08:37:31 -07:00
|
|
|
}
|