From 7a22e086e2b97d987fa10b2a10b431161b44ba71 Mon Sep 17 00:00:00 2001 From: sakridge Date: Tue, 14 Jan 2020 11:57:29 -0800 Subject: [PATCH] Add hash stats information to check hashes between validators (#7780) automerge (cherry picked from commit 3b78be83cf97ae90fc33b94844fe026456ea8e47) # Conflicts: # runtime/src/accounts.rs # runtime/src/accounts_db.rs --- runtime/src/accounts.rs | 16 ++++- runtime/src/accounts_db.rs | 123 ++++++++++++++++++++++++++++++++++++- runtime/src/bank.rs | 11 +++- 3 files changed, 143 insertions(+), 7 deletions(-) diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 78fa1a255b63ef..db3020711652d0 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -1,4 +1,6 @@ -use crate::accounts_db::{AccountInfo, AccountStorage, AccountsDB, AppendVecId, ErrorCounters}; +use crate::accounts_db::{ + AccountInfo, AccountStorage, AccountsDB, AppendVecId, BankHashInfo, ErrorCounters, +}; use crate::accounts_index::AccountsIndex; use crate::append_vec::StoredAccount; use crate::bank::{HashAgeKind, TransactionProcessResult}; @@ -489,10 +491,22 @@ impl Accounts { } pub fn bank_hash_at(&self, slot_id: Slot) -> BankHash { +<<<<<<< HEAD let slot_hashes = self.accounts_db.slot_hashes.read().unwrap(); *slot_hashes .get(&slot_id) .expect("No accounts hash was found for this bank, that should not be possible") +======= + self.bank_hash_info_at(slot_id).hash + } + + pub fn bank_hash_info_at(&self, slot_id: Slot) -> BankHashInfo { + let bank_hashes = self.accounts_db.bank_hashes.read().unwrap(); + bank_hashes + .get(&slot_id) + .expect("No bank hash was found for this bank, that should not be possible") + .clone() +>>>>>>> 3b78be83c... Add hash stats information to check hashes between validators (#7780) } /// This function will prevent multiple threads from modifying the same account state at the diff --git a/runtime/src/accounts_db.rs b/runtime/src/accounts_db.rs index fb2ccc2b6f4519..9f3fff09fb2759 100644 --- a/runtime/src/accounts_db.rs +++ b/runtime/src/accounts_db.rs @@ -355,6 +355,46 @@ impl<'a> Serialize for AccountsDBSerialize<'a> { } } +#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq)] +pub struct BankHashStats { + pub num_removed_accounts: u64, + pub num_added_accounts: u64, + pub num_lamports_stored: u64, + pub total_data_len: u64, + pub num_executable_accounts: u64, +} + +impl BankHashStats { + pub fn update(&mut self, account: &Account) { + if Hash::default() == account.hash { + self.num_added_accounts += 1; + } else { + self.num_removed_accounts += 1; + } + self.total_data_len = self.total_data_len.wrapping_add(account.data.len() as u64); + if account.executable { + self.num_executable_accounts += 1; + } + self.num_lamports_stored = self.num_lamports_stored.wrapping_add(account.lamports); + } + + pub fn merge(&mut self, other: &BankHashStats) { + self.num_removed_accounts += other.num_removed_accounts; + self.num_added_accounts += other.num_added_accounts; + self.total_data_len = self.total_data_len.wrapping_add(other.total_data_len); + self.num_lamports_stored = self + .num_lamports_stored + .wrapping_add(other.num_lamports_stored); + self.num_executable_accounts += other.num_executable_accounts; + } +} + +#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq)] +pub struct BankHashInfo { + pub hash: BankHash, + pub stats: BankHashStats, +} + // This structure handles the load/store of the accounts #[derive(Debug)] pub struct AccountsDB { @@ -384,7 +424,11 @@ pub struct AccountsDB { /// the accounts min_num_stores: usize, +<<<<<<< HEAD pub slot_hashes: RwLock>, +======= + pub bank_hashes: RwLock>, +>>>>>>> 3b78be83c... Add hash stats information to check hashes between validators (#7780) } impl Default for AccountsDB { @@ -521,12 +565,18 @@ impl AccountsDB { let version: u64 = deserialize_from(&mut stream) .map_err(|_| AccountsDB::get_io_error("write version deserialize error"))?; +<<<<<<< HEAD let slot_hash: (Slot, BankHash) = deserialize_from(&mut stream) .map_err(|_| AccountsDB::get_io_error("slot hashes deserialize error"))?; self.slot_hashes .write() .unwrap() .insert(slot_hash.0, slot_hash.1); +======= + let (slot, bank_hash): (Slot, BankHashInfo) = deserialize_from(&mut stream) + .map_err(|_| AccountsDB::get_io_error("bank hashes deserialize error"))?; + self.bank_hashes.write().unwrap().insert(slot, bank_hash); +>>>>>>> 3b78be83c... Add hash stats information to check hashes between validators (#7780) // Process deserialized data, set necessary fields in self *self.paths.write().unwrap() = local_account_paths.to_vec(); @@ -717,11 +767,24 @@ impl AccountsDB { } pub fn set_hash(&self, slot: Slot, parent_slot: Slot) { +<<<<<<< HEAD let mut slot_hashes = self.slot_hashes.write().unwrap(); let hash = *slot_hashes .get(&parent_slot) .expect("accounts_db::set_hash::no parent slot"); slot_hashes.insert(slot, hash); +======= + let mut bank_hashes = self.bank_hashes.write().unwrap(); + let hash_info = bank_hashes + .get(&parent_slot) + .expect("accounts_db::set_hash::no parent slot"); + let hash = hash_info.hash; + let new_hash_info = BankHashInfo { + hash, + stats: BankHashStats::default(), + }; + bank_hashes.insert(slot, new_hash_info); +>>>>>>> 3b78be83c... Add hash stats information to check hashes between validators (#7780) } pub fn load( @@ -1007,9 +1070,15 @@ impl AccountsDB { for hash in hashes { calculated_hash.xor(hash); } +<<<<<<< HEAD let slot_hashes = self.slot_hashes.read().unwrap(); if let Some(found_hash) = slot_hashes.get(&slot) { if calculated_hash == *found_hash { +======= + let bank_hashes = self.bank_hashes.read().unwrap(); + if let Some(found_hash_info) = bank_hashes.get(&slot) { + if calculated_hash == found_hash_info.hash { +>>>>>>> 3b78be83c... Add hash stats information to check hashes between validators (#7780) Ok(()) } else { Err(MismatchedBankHash) @@ -1019,10 +1088,21 @@ impl AccountsDB { } } +<<<<<<< HEAD pub fn xor_in_hash_state(&self, slot_id: Slot, hash: BankHash) { let mut slot_hashes = self.slot_hashes.write().unwrap(); let slot_hash_state = slot_hashes.entry(slot_id).or_insert_with(BankHash::default); slot_hash_state.xor(hash); +======= + pub fn xor_in_hash_state(&self, slot_id: Slot, hash: BankHash, stats: &BankHashStats) { + let mut bank_hashes = self.bank_hashes.write().unwrap(); + let bank_hash = bank_hashes + .entry(slot_id) + .or_insert_with(BankHashInfo::default); + bank_hash.hash.xor(hash); + + bank_hash.stats.merge(stats); +>>>>>>> 3b78be83c... Add hash stats information to check hashes between validators (#7780) } fn update_index( @@ -1111,11 +1191,13 @@ impl AccountsDB { fn hash_accounts(&self, slot_id: Slot, accounts: &[(&Pubkey, &Account)]) -> Vec { let mut hash_state = BankHash::default(); let mut had_account = false; + let mut stats = BankHashStats::default(); let hashes: Vec<_> = accounts .iter() .map(|(pubkey, account)| { if !sysvar::check_id(&account.owner) { let hash = BankHash::from_hash(&account.hash); + stats.update(account); let new_hash = Self::hash_account(slot_id, account, pubkey); let new_bank_hash = BankHash::from_hash(&new_hash); debug!( @@ -1137,7 +1219,7 @@ impl AccountsDB { .collect(); if had_account { - self.xor_in_hash_state(slot_id, hash_state); + self.xor_in_hash_state(slot_id, hash_state, &stats); } hashes } @@ -2226,6 +2308,33 @@ pub mod tests { ); } + #[test] + fn test_bank_hash_stats() { + solana_logger::setup(); + let db = AccountsDB::new(Vec::new()); + + let key = Pubkey::default(); + let some_data_len = 5; + let some_slot: Slot = 0; + let account = Account::new(1, some_data_len, &key); + let ancestors = vec![(some_slot, 0)].into_iter().collect(); + + db.store(some_slot, &[(&key, &account)]); + let mut account = db.load_slow(&ancestors, &key).unwrap().0; + account.lamports += 1; + account.executable = true; + db.store(some_slot, &[(&key, &account)]); + db.add_root(some_slot); + + let bank_hashes = db.bank_hashes.read().unwrap(); + let bank_hash = bank_hashes.get(&some_slot).unwrap(); + assert_eq!(bank_hash.stats.num_removed_accounts, 1); + assert_eq!(bank_hash.stats.num_added_accounts, 1); + assert_eq!(bank_hash.stats.num_lamports_stored, 3); + assert_eq!(bank_hash.stats.total_data_len, 2 * some_data_len as u64); + assert_eq!(bank_hash.stats.num_executable_accounts, 1); + } + #[test] fn test_verify_bank_hash() { use BankHashVerificatonError::*; @@ -2249,10 +2358,18 @@ pub mod tests { ); let some_bank_hash = BankHash::from_hash(&Hash::new(&[0xca; HASH_BYTES])); +<<<<<<< HEAD db.slot_hashes +======= + let bank_hash_info = BankHashInfo { + hash: some_bank_hash, + stats: BankHashStats::default(), + }; + db.bank_hashes +>>>>>>> 3b78be83c... Add hash stats information to check hashes between validators (#7780) .write() .unwrap() - .insert(some_slot, some_bank_hash); + .insert(some_slot, bank_hash_info); assert_matches!( db.verify_bank_hash(some_slot, &ancestors), Err(MismatchedBankHash) @@ -2270,7 +2387,7 @@ pub mod tests { db.slot_hashes .write() .unwrap() - .insert(some_slot, BankHash::default()); + .insert(some_slot, BankHashInfo::default()); db.add_root(some_slot); assert_matches!(db.verify_bank_hash(some_slot, &ancestors), Ok(_)); } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index ca3871d86e3d4e..ba06c526f8db36 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1631,12 +1631,12 @@ impl Bank { /// of the delta of the ledger since the last vote and up to now fn hash_internal_state(&self) -> Hash { // If there are no accounts, return the hash of the previous state and the latest blockhash - let accounts_delta_hash = self.rc.accounts.bank_hash_at(self.slot()); + let accounts_delta_hash = self.rc.accounts.bank_hash_info_at(self.slot()); let mut signature_count_buf = [0u8; 8]; LittleEndian::write_u64(&mut signature_count_buf[..], self.signature_count() as u64); let hash = hashv(&[ self.parent_hash.as_ref(), - accounts_delta_hash.as_ref(), + accounts_delta_hash.hash.as_ref(), &signature_count_buf, self.last_blockhash().as_ref(), ]); @@ -1644,10 +1644,15 @@ impl Bank { "bank frozen: {} hash: {} accounts_delta: {} signature_count: {} last_blockhash: {}", self.slot(), hash, - accounts_delta_hash, + accounts_delta_hash.hash, self.signature_count(), self.last_blockhash() ); + info!( + "accounts hash slot: {} stats: {:?}", + self.slot(), + accounts_delta_hash.stats, + ); hash }