add stake_accounts to banks' caches (#4267)

This commit is contained in:
Rob Walker 2019-05-13 12:33:23 -07:00 committed by GitHub
parent 1393d26f63
commit 23c696706b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 76 additions and 40 deletions

View File

@ -129,6 +129,16 @@ impl EpochSchedule {
} }
} }
/// cache of staking information
#[derive(Default, Clone)]
pub struct Stakes {
/// stakes
vote_accounts: HashMap<Pubkey, Account>,
/// stake_accounts
stake_accounts: HashMap<Pubkey, Account>,
}
type BankStatusCache = StatusCache<Result<()>>; type BankStatusCache = StatusCache<Result<()>>;
/// Manager for the state of all accounts and programs after processing its entries. /// Manager for the state of all accounts and programs after processing its entries.
@ -182,12 +192,12 @@ pub struct Bank {
/// initialized from genesis /// initialized from genesis
epoch_schedule: EpochSchedule, epoch_schedule: EpochSchedule,
/// cache of vote_account state for this fork /// cache of vote_account and stake_account state for this fork
vote_accounts: RwLock<HashMap<Pubkey, Account>>, stakes: RwLock<Stakes>,
/// staked nodes on epoch boundaries, saved off when a bank.slot() is at /// staked nodes on epoch boundaries, saved off when a bank.slot() is at
/// a leader schedule boundary /// a leader schedule calculation boundary
epoch_vote_accounts: HashMap<u64, HashMap<Pubkey, Account>>, epoch_stakes: HashMap<u64, Stakes>,
/// A boolean reflecting whether any entries were recorded into the PoH /// A boolean reflecting whether any entries were recorded into the PoH
/// stream for the slot == self.slot /// stream for the slot == self.slot
@ -215,9 +225,9 @@ impl Bank {
bank.process_genesis_block(genesis_block); bank.process_genesis_block(genesis_block);
// genesis needs stakes for all epochs up to the epoch implied by // genesis needs stakes for all epochs up to the epoch implied by
// slot = 0 and genesis configuration // slot = 0 and genesis configuration
let vote_accounts = bank.vote_accounts(); let stakes = bank.stakes();
for i in 0..=bank.get_stakers_epoch(bank.slot) { for i in 0..=bank.get_stakers_epoch(bank.slot) {
bank.epoch_vote_accounts.insert(i, vote_accounts.clone()); bank.epoch_stakes.insert(i, stakes.clone());
} }
bank bank
} }
@ -235,7 +245,7 @@ impl Bank {
bank.transaction_count bank.transaction_count
.store(parent.transaction_count() as usize, Ordering::Relaxed); .store(parent.transaction_count() as usize, Ordering::Relaxed);
bank.vote_accounts = RwLock::new(parent.vote_accounts()); bank.stakes = RwLock::new(parent.stakes());
bank.tick_height bank.tick_height
.store(parent.tick_height.load(Ordering::SeqCst), Ordering::SeqCst); .store(parent.tick_height.load(Ordering::SeqCst), Ordering::SeqCst);
@ -257,16 +267,16 @@ impl Bank {
bank.accounts = Arc::new(Accounts::new_from_parent(&parent.accounts)); bank.accounts = Arc::new(Accounts::new_from_parent(&parent.accounts));
bank.epoch_vote_accounts = { bank.epoch_stakes = {
let mut epoch_vote_accounts = parent.epoch_vote_accounts.clone(); let mut epoch_stakes = parent.epoch_stakes.clone();
let epoch = bank.get_stakers_epoch(bank.slot); let epoch = bank.get_stakers_epoch(bank.slot);
// update epoch_vote_states cache // update epoch_vote_states cache
// if my parent didn't populate for this epoch, we've // if my parent didn't populate for this epoch, we've
// crossed a boundary // crossed a boundary
if epoch_vote_accounts.get(&epoch).is_none() { if epoch_stakes.get(&epoch).is_none() {
epoch_vote_accounts.insert(epoch, bank.vote_accounts()); epoch_stakes.insert(epoch, bank.stakes());
} }
epoch_vote_accounts epoch_stakes
}; };
bank.ancestors.insert(bank.slot(), 0); bank.ancestors.insert(bank.slot(), 0);
bank.parents().iter().enumerate().for_each(|(i, p)| { bank.parents().iter().enumerate().for_each(|(i, p)| {
@ -798,7 +808,7 @@ impl Bank {
self.accounts self.accounts
.store_accounts(self.slot(), txs, executed, loaded_accounts); .store_accounts(self.slot(), txs, executed, loaded_accounts);
self.store_vote_accounts(txs, executed, loaded_accounts); self.store_stakes(txs, executed, loaded_accounts);
// once committed there is no way to unroll // once committed there is no way to unroll
let write_elapsed = now.elapsed(); let write_elapsed = now.elapsed();
@ -862,15 +872,33 @@ impl Bank {
parents parents
} }
fn update_stakes_accounts(
accounts: &mut HashMap<Pubkey, Account>,
pubkey: &Pubkey,
account: &Account,
) {
if account.lamports != 0 {
accounts.insert(*pubkey, account.clone());
} else {
accounts.remove(pubkey);
}
}
fn store(&self, pubkey: &Pubkey, account: &Account) { fn store(&self, pubkey: &Pubkey, account: &Account) {
self.accounts.store_slow(self.slot(), pubkey, account); self.accounts.store_slow(self.slot(), pubkey, account);
if solana_vote_api::check_id(&account.owner) { if solana_vote_api::check_id(&account.owner) {
let mut vote_accounts = self.vote_accounts.write().unwrap(); Self::update_stakes_accounts(
if account.lamports != 0 { &mut self.stakes.write().unwrap().vote_accounts,
vote_accounts.insert(*pubkey, account.clone()); pubkey,
} else { account,
vote_accounts.remove(pubkey); );
} } else if solana_stake_api::check_id(&account.owner) {
Self::update_stakes_accounts(
&mut self.stakes.write().unwrap().stake_accounts,
pubkey,
account,
);
} }
} }
@ -982,13 +1010,13 @@ impl Bank {
} }
/// a bank-level cache of vote accounts /// a bank-level cache of vote accounts
fn store_vote_accounts( fn store_stakes(
&self, &self,
txs: &[Transaction], txs: &[Transaction],
res: &[Result<()>], res: &[Result<()>],
loaded: &[Result<(InstructionAccounts, InstructionLoaders)>], loaded: &[Result<(InstructionAccounts, InstructionLoaders)>],
) { ) {
let mut vote_accounts = self.vote_accounts.write().unwrap(); let mut stakes = self.stakes.write().unwrap();
for (i, raccs) in loaded.iter().enumerate() { for (i, raccs) in loaded.iter().enumerate() {
if res[i].is_err() || raccs.is_err() { if res[i].is_err() || raccs.is_err() {
@ -997,29 +1025,37 @@ impl Bank {
let message = &txs[i].message(); let message = &txs[i].message();
let acc = raccs.as_ref().unwrap(); let acc = raccs.as_ref().unwrap();
for (key, account) in message
.account_keys for (pubkey, account) in message.account_keys.iter().zip(acc.0.iter()) {
.iter() if solana_vote_api::check_id(&account.owner) {
.zip(acc.0.iter()) Self::update_stakes_accounts(&mut stakes.vote_accounts, pubkey, account);
.filter(|(_, account)| solana_vote_api::check_id(&account.owner)) } else if solana_stake_api::check_id(&account.owner) {
{ Self::update_stakes_accounts(&mut stakes.stake_accounts, pubkey, account);
if account.lamports != 0 {
vote_accounts.insert(*key, account.clone());
} else {
vote_accounts.remove(key);
} }
} }
} }
} }
/// current stakes for this bank
pub fn stakes(&self) -> Stakes {
self.stakes.read().unwrap().clone()
}
/// current vote accounts for this bank /// current vote accounts for this bank
pub fn vote_accounts(&self) -> HashMap<Pubkey, Account> { pub fn vote_accounts(&self) -> HashMap<Pubkey, Account> {
self.vote_accounts.read().unwrap().clone() self.stakes.read().unwrap().vote_accounts.clone()
}
/// stakes for the specific epoch
pub fn epoch_stakes(&self, epoch: u64) -> Option<&Stakes> {
self.epoch_stakes.get(&epoch)
} }
/// vote accounts for the specific epoch /// vote accounts for the specific epoch
pub fn epoch_vote_accounts(&self, epoch: u64) -> Option<&HashMap<Pubkey, Account>> { pub fn epoch_vote_accounts(&self, epoch: u64) -> Option<&HashMap<Pubkey, Account>> {
self.epoch_vote_accounts.get(&epoch) self.epoch_stakes
.get(&epoch)
.map(|stakes| &stakes.vote_accounts)
} }
/// given a slot, return the epoch and offset into the epoch this slot falls /// given a slot, return the epoch and offset into the epoch this slot falls
@ -1646,7 +1682,7 @@ mod tests {
} }
#[test] #[test]
fn test_bank_epoch_vote_accounts() { fn test_bank_epoch_stakes() {
let leader_id = Pubkey::new_rand(); let leader_id = Pubkey::new_rand();
let leader_lamports = 3; let leader_lamports = 3;
let mut genesis_block = create_genesis_block_with_leader(5, &leader_id, leader_lamports).0; let mut genesis_block = create_genesis_block_with_leader(5, &leader_id, leader_lamports).0;
@ -1661,7 +1697,7 @@ mod tests {
let parent = Arc::new(Bank::new(&genesis_block)); let parent = Arc::new(Bank::new(&genesis_block));
let vote_accounts0: Option<HashMap<_, _>> = parent.epoch_vote_accounts(0).map(|accounts| { let stakes0: Option<HashMap<_, _>> = parent.epoch_vote_accounts(0).map(|accounts| {
accounts accounts
.iter() .iter()
.filter_map(|(pubkey, account)| { .filter_map(|(pubkey, account)| {
@ -1677,15 +1713,15 @@ mod tests {
}) })
.collect() .collect()
}); });
assert!(vote_accounts0.is_some()); assert!(stakes0.is_some());
assert!(vote_accounts0.iter().len() != 0); assert!(stakes0.iter().len() != 0);
let mut i = 1; let mut i = 1;
loop { loop {
if i > STAKERS_SLOT_OFFSET / SLOTS_PER_EPOCH { if i > STAKERS_SLOT_OFFSET / SLOTS_PER_EPOCH {
break; break;
} }
assert!(parent.epoch_vote_accounts(i).is_some()); assert!(parent.epoch_stakes(i).is_some());
i += 1; i += 1;
} }
@ -1696,7 +1732,7 @@ mod tests {
SLOTS_PER_EPOCH - (STAKERS_SLOT_OFFSET % SLOTS_PER_EPOCH), SLOTS_PER_EPOCH - (STAKERS_SLOT_OFFSET % SLOTS_PER_EPOCH),
); );
assert!(child.epoch_vote_accounts(i).is_some()); assert!(child.epoch_stakes(i).is_some());
// child crosses epoch boundary but isn't the first slot in the epoch // child crosses epoch boundary but isn't the first slot in the epoch
let child = Bank::new_from_parent( let child = Bank::new_from_parent(
@ -1704,7 +1740,7 @@ mod tests {
&leader_id, &leader_id,
SLOTS_PER_EPOCH - (STAKERS_SLOT_OFFSET % SLOTS_PER_EPOCH) + 1, SLOTS_PER_EPOCH - (STAKERS_SLOT_OFFSET % SLOTS_PER_EPOCH) + 1,
); );
assert!(child.epoch_vote_accounts(i).is_some()); assert!(child.epoch_stakes(i).is_some());
} }
#[test] #[test]