Requires EAH state cannot be Invalid (#28817)

This commit is contained in:
Brooks Prumo 2022-11-17 11:01:01 -05:00 committed by GitHub
parent ae0bb44401
commit 2bafb0cb12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 70 additions and 53 deletions

View File

@ -20,6 +20,7 @@ use {
accounts_index::AccountSecondaryIndexes, accounts_index::AccountSecondaryIndexes,
bank::{Bank, BankSlotDelta}, bank::{Bank, BankSlotDelta},
bank_forks::BankForks, bank_forks::BankForks,
epoch_accounts_hash::EpochAccountsHash,
genesis_utils::{create_genesis_config_with_leader, GenesisConfigInfo}, genesis_utils::{create_genesis_config_with_leader, GenesisConfigInfo},
runtime_config::RuntimeConfig, runtime_config::RuntimeConfig,
snapshot_archive_info::FullSnapshotArchiveInfo, snapshot_archive_info::FullSnapshotArchiveInfo,
@ -616,13 +617,21 @@ fn test_slots_to_snapshot(snapshot_version: SnapshotVersion, cluster_type: Clust
// Since the accounts background services are not runnning, EpochAccountsHash // Since the accounts background services are not runnning, EpochAccountsHash
// calculation requests will not be handled. To prevent banks from hanging during // calculation requests will not be handled. To prevent banks from hanging during
// Bank::freeze() due to waiting for EAH to complete, just set the EAH to Invalid. // Bank::freeze() due to waiting for EAH to complete, just set the EAH to Valid.
current_bank let epoch_accounts_hash_manager = &current_bank
.rc .rc
.accounts .accounts
.accounts_db .accounts_db
.epoch_accounts_hash_manager .epoch_accounts_hash_manager;
.set_invalid_for_tests(); if epoch_accounts_hash_manager
.try_get_epoch_accounts_hash()
.is_none()
{
epoch_accounts_hash_manager.set_valid(
EpochAccountsHash::new(Hash::new_unique()),
current_bank.slot(),
)
}
} }
let num_old_slots = num_set_roots * *add_root_interval - MAX_CACHE_ENTRIES + 1; let num_old_slots = num_set_roots * *add_root_interval - MAX_CACHE_ENTRIES + 1;

View File

@ -31,6 +31,7 @@ use {
block_cost_limits::*, block_cost_limits::*,
commitment::VOTE_THRESHOLD_SIZE, commitment::VOTE_THRESHOLD_SIZE,
cost_model::CostModel, cost_model::CostModel,
epoch_accounts_hash::EpochAccountsHash,
prioritization_fee_cache::PrioritizationFeeCache, prioritization_fee_cache::PrioritizationFeeCache,
runtime_config::RuntimeConfig, runtime_config::RuntimeConfig,
transaction_batch::TransactionBatch, transaction_batch::TransactionBatch,
@ -730,9 +731,9 @@ pub fn test_process_blockstore(
opts: &ProcessOptions, opts: &ProcessOptions,
exit: &Arc<AtomicBool>, exit: &Arc<AtomicBool>,
) -> (Arc<RwLock<BankForks>>, LeaderScheduleCache) { ) -> (Arc<RwLock<BankForks>>, LeaderScheduleCache) {
// Spin up a thread to be a fake Accounts Background Service. Need to intercept and handle // Spin up a thread to be a fake Accounts Background Service. Need to intercept and handle all
// (i.e. skip/make invalid) all EpochAccountsHash requests so future rooted banks do not hang // EpochAccountsHash requests so future rooted banks do not hang in Bank::freeze() waiting for
// in Bank::freeze() waiting for an in-flight EAH calculation to complete. // an in-flight EAH calculation to complete.
let (snapshot_request_sender, snapshot_request_receiver) = crossbeam_channel::unbounded(); let (snapshot_request_sender, snapshot_request_receiver) = crossbeam_channel::unbounded();
let abs_request_sender = AbsRequestSender::new(snapshot_request_sender); let abs_request_sender = AbsRequestSender::new(snapshot_request_sender);
let bg_exit = Arc::new(AtomicBool::new(false)); let bg_exit = Arc::new(AtomicBool::new(false));
@ -752,7 +753,10 @@ pub fn test_process_blockstore(
.accounts .accounts
.accounts_db .accounts_db
.epoch_accounts_hash_manager .epoch_accounts_hash_manager
.set_invalid_for_tests(); .set_valid(
EpochAccountsHash::new(Hash::new_unique()),
snapshot_request.snapshot_root_bank.slot(),
)
}); });
std::thread::sleep(Duration::from_millis(100)); std::thread::sleep(Duration::from_millis(100));
} }

View File

@ -20,6 +20,7 @@ use {
bank_forks::BankForks, bank_forks::BankForks,
builtins::Builtin, builtins::Builtin,
commitment::BlockCommitmentCache, commitment::BlockCommitmentCache,
epoch_accounts_hash::EpochAccountsHash,
genesis_utils::{create_genesis_config_with_leader_ex, GenesisConfigInfo}, genesis_utils::{create_genesis_config_with_leader_ex, GenesisConfigInfo},
runtime_config::RuntimeConfig, runtime_config::RuntimeConfig,
}, },
@ -1138,8 +1139,8 @@ impl ProgramTestContext {
bank_forks.set_root(pre_warp_slot, &abs_request_sender, Some(pre_warp_slot)); bank_forks.set_root(pre_warp_slot, &abs_request_sender, Some(pre_warp_slot));
// The call to `set_root()` above will send an EAH request. Need to intercept and handle // The call to `set_root()` above will send an EAH request. Need to intercept and handle
// (i.e. skip/make invalid) all EpochAccountsHash requests so future rooted banks do not // all EpochAccountsHash requests so future rooted banks do not hang in Bank::freeze()
// hang in Bank::freeze() waiting for an in-flight EAH calculation to complete. // waiting for an in-flight EAH calculation to complete.
snapshot_request_receiver snapshot_request_receiver
.try_iter() .try_iter()
.filter(|snapshot_request| { .filter(|snapshot_request| {
@ -1152,7 +1153,10 @@ impl ProgramTestContext {
.accounts .accounts
.accounts_db .accounts_db
.epoch_accounts_hash_manager .epoch_accounts_hash_manager
.set_invalid_for_tests(); .set_valid(
EpochAccountsHash::new(Hash::new_unique()),
snapshot_request.snapshot_root_bank.slot(),
)
}); });
// warp_bank is frozen so go forward to get unfrozen bank at warp_slot // warp_bank is frozen so go forward to get unfrozen bank at warp_slot

View File

@ -781,7 +781,10 @@ fn cmp_requests_by_priority(
mod test { mod test {
use { use {
super::*, super::*,
crate::{epoch_accounts_hash, genesis_utils::create_genesis_config}, crate::{
epoch_accounts_hash::{self, EpochAccountsHash},
genesis_utils::create_genesis_config,
},
crossbeam_channel::unbounded, crossbeam_channel::unbounded,
solana_sdk::{account::AccountSharedData, epoch_schedule::EpochSchedule, pubkey::Pubkey}, solana_sdk::{account::AccountSharedData, epoch_schedule::EpochSchedule, pubkey::Pubkey},
}; };
@ -859,6 +862,13 @@ mod test {
EpochSchedule::custom(SLOTS_PER_EPOCH, SLOTS_PER_EPOCH, false); EpochSchedule::custom(SLOTS_PER_EPOCH, SLOTS_PER_EPOCH, false);
let bank = Arc::new(Bank::new_for_tests(&genesis_config_info.genesis_config)); let bank = Arc::new(Bank::new_for_tests(&genesis_config_info.genesis_config));
bank.set_startup_verification_complete(); bank.set_startup_verification_complete();
// Need to set the EAH to Valid so that `Bank::new_from_parent()` doesn't panic during
// freeze when parent is in the EAH calculation window.
bank.rc
.accounts
.accounts_db
.epoch_accounts_hash_manager
.set_valid(EpochAccountsHash::new(Hash::new_unique()), 0);
// Create new banks and send snapshot requests so that the following requests will be in // Create new banks and send snapshot requests so that the following requests will be in
// the channel before handling the requests: // the channel before handling the requests:

View File

@ -635,6 +635,7 @@ mod tests {
super::*, super::*,
crate::{ crate::{
bank::tests::update_vote_account_timestamp, bank::tests::update_vote_account_timestamp,
epoch_accounts_hash::EpochAccountsHash,
genesis_utils::{ genesis_utils::{
create_genesis_config, create_genesis_config_with_leader, GenesisConfigInfo, create_genesis_config, create_genesis_config_with_leader, GenesisConfigInfo,
}, },
@ -746,8 +747,8 @@ mod tests {
genesis_config.epoch_schedule = EpochSchedule::new(slots_in_epoch); genesis_config.epoch_schedule = EpochSchedule::new(slots_in_epoch);
// Spin up a thread to be a fake Accounts Background Service. Need to intercept and handle // Spin up a thread to be a fake Accounts Background Service. Need to intercept and handle
// (i.e. skip/make invalid) all EpochAccountsHash requests so future rooted banks do not hang // all EpochAccountsHash requests so future rooted banks do not hang in Bank::freeze()
// in Bank::freeze() waiting for an in-flight EAH calculation to complete. // waiting for an in-flight EAH calculation to complete.
let (snapshot_request_sender, snapshot_request_receiver) = crossbeam_channel::unbounded(); let (snapshot_request_sender, snapshot_request_receiver) = crossbeam_channel::unbounded();
let abs_request_sender = AbsRequestSender::new(snapshot_request_sender); let abs_request_sender = AbsRequestSender::new(snapshot_request_sender);
let bg_exit = Arc::new(AtomicBool::new(false)); let bg_exit = Arc::new(AtomicBool::new(false));
@ -767,7 +768,10 @@ mod tests {
.accounts .accounts
.accounts_db .accounts_db
.epoch_accounts_hash_manager .epoch_accounts_hash_manager
.set_invalid_for_tests(); .set_valid(
EpochAccountsHash::new(Hash::new_unique()),
snapshot_request.snapshot_root_bank.slot(),
)
}); });
std::thread::sleep(Duration::from_millis(100)); std::thread::sleep(Duration::from_millis(100));
} }

View File

@ -1,6 +1,6 @@
use { use {
super::EpochAccountsHash, super::EpochAccountsHash,
solana_sdk::{clock::Slot, hash::Hash}, solana_sdk::clock::Slot,
std::sync::{Condvar, Mutex}, std::sync::{Condvar, Mutex},
}; };
@ -68,8 +68,8 @@ impl Manager {
loop { loop {
match &*state { match &*state {
State::Valid(epoch_accounts_hash, _slot) => break *epoch_accounts_hash, State::Valid(epoch_accounts_hash, _slot) => break *epoch_accounts_hash,
State::Invalid => break SENTINEL_EPOCH_ACCOUNTS_HASH,
State::InFlight(_slot) => state = self.cvar.wait(state).unwrap(), State::InFlight(_slot) => state = self.cvar.wait(state).unwrap(),
State::Invalid => panic!("The epoch accounts hash cannot be awaited when Invalid!"),
} }
} }
} }
@ -84,23 +84,15 @@ impl Manager {
_ => None, _ => None,
} }
} }
/// **FOR TESTS ONLY**
/// Set the state to Invalid
/// This is needed by tests that do not fully startup all the accounts background services.
/// **FOR TESTS ONLY**
pub fn set_invalid_for_tests(&self) {
*self.state.lock().unwrap() = State::Invalid;
}
} }
/// The EpochAccountsHash is calculated in the background via AccountsBackgroundService. This enum /// The EpochAccountsHash is calculated in the background via AccountsBackgroundService. This enum
/// is used to track the state of that calculation, and queried when saving the EAH into a Bank. /// is used to track the state of that calculation, and queried when saving the EAH into a Bank or
/// Snapshot.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State { enum State {
/// On startup from genesis/slot0, the initial state of the EAH is invalid since one has not /// The initial state of the EAH. This can occur when loading from a snapshot that does not
/// yet been requested. This state should only really occur for tests and new clusters; not /// include an EAH, or when starting from genesis (before an EAH calculation is requested).
/// for established running clusters.
Invalid, Invalid,
/// An EAH calculation has been requested (for `Slot`) and is in flight. The Bank that should /// An EAH calculation has been requested (for `Slot`) and is in flight. The Bank that should
/// save the EAH must wait until the calculation has completed. /// save the EAH must wait until the calculation has completed.
@ -109,19 +101,9 @@ enum State {
Valid(EpochAccountsHash, Slot), Valid(EpochAccountsHash, Slot),
} }
/// Sentinel epoch accounts hash value; used when getting an Invalid EAH
///
/// Displays as "Sentine1EpochAccountsHash111111111111111111"
const SENTINEL_EPOCH_ACCOUNTS_HASH: EpochAccountsHash =
EpochAccountsHash::new(Hash::new_from_array([
0x06, 0x92, 0x40, 0x3b, 0xee, 0xea, 0x7e, 0xe2, 0x7d, 0xf4, 0x90, 0x7f, 0xbd, 0x9e, 0xd0,
0xd2, 0x1c, 0x2b, 0x66, 0x9a, 0xc4, 0xda, 0xce, 0xd7, 0x23, 0x41, 0x69, 0xab, 0xb7, 0x80,
0x00, 0x00,
]));
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use {super::*, std::time::Duration}; use {super::*, solana_sdk::hash::Hash, std::time::Duration};
#[test] #[test]
fn test_new_valid() { fn test_new_valid() {
@ -138,10 +120,6 @@ mod tests {
fn test_new_invalid() { fn test_new_invalid() {
let manager = Manager::new_invalid(); let manager = Manager::new_invalid();
assert!(manager.try_get_epoch_accounts_hash().is_none()); assert!(manager.try_get_epoch_accounts_hash().is_none());
assert_eq!(
manager.wait_get_epoch_accounts_hash(),
SENTINEL_EPOCH_ACCOUNTS_HASH,
);
} }
#[test] #[test]
@ -163,15 +141,6 @@ mod tests {
#[test] #[test]
fn test_wait_epoch_accounts_hash() { fn test_wait_epoch_accounts_hash() {
// Test: State is Invalid, no need to wait
{
let manager = Manager::new_invalid();
assert_eq!(
manager.wait_get_epoch_accounts_hash(),
SENTINEL_EPOCH_ACCOUNTS_HASH,
);
}
// Test: State is Valid, no need to wait // Test: State is Valid, no need to wait
{ {
let epoch_accounts_hash = EpochAccountsHash::new(Hash::new_unique()); let epoch_accounts_hash = EpochAccountsHash::new(Hash::new_unique());
@ -196,4 +165,12 @@ mod tests {
}); });
} }
} }
#[test]
#[should_panic]
fn test_wait_epoch_accounts_hash_invalid() {
// Test: State is Invalid, should panic
let manager = Manager::new_invalid();
let _epoch_accounts_hash = manager.wait_get_epoch_accounts_hash();
}
} }

View File

@ -3,12 +3,14 @@ use {
solana_runtime::{ solana_runtime::{
bank::Bank, bank::Bank,
bank_client::BankClient, bank_client::BankClient,
epoch_accounts_hash::EpochAccountsHash,
genesis_utils::{create_genesis_config_with_leader, GenesisConfigInfo}, genesis_utils::{create_genesis_config_with_leader, GenesisConfigInfo},
}, },
solana_sdk::{ solana_sdk::{
account::from_account, account::from_account,
account_utils::StateMut, account_utils::StateMut,
client::SyncClient, client::SyncClient,
hash::Hash,
message::Message, message::Message,
pubkey::Pubkey, pubkey::Pubkey,
rent::Rent, rent::Rent,
@ -282,6 +284,13 @@ fn test_stake_account_lifetime() {
let bank = Bank::new_for_tests(&genesis_config); let bank = Bank::new_for_tests(&genesis_config);
let mint_pubkey = mint_keypair.pubkey(); let mint_pubkey = mint_keypair.pubkey();
let mut bank = Arc::new(bank); let mut bank = Arc::new(bank);
// Need to set the EAH to Valid so that `Bank::new_from_parent()` doesn't panic during freeze
// when parent is in the EAH calculation window.
bank.rc
.accounts
.accounts_db
.epoch_accounts_hash_manager
.set_valid(EpochAccountsHash::new(Hash::new_unique()), bank.slot());
let bank_client = BankClient::new_shared(&bank); let bank_client = BankClient::new_shared(&bank);
let (vote_balance, stake_rent_exempt_reserve, stake_minimum_delegation) = { let (vote_balance, stake_rent_exempt_reserve, stake_minimum_delegation) = {