Skip to content

Commit

Permalink
Add hash stats information to check hashes between validators
Browse files Browse the repository at this point in the history
  • Loading branch information
sakridge committed Jan 14, 2020
1 parent f91ffbb commit 3e39b7d
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 17 deletions.
11 changes: 9 additions & 2 deletions runtime/src/accounts.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -487,10 +489,15 @@ impl Accounts {
}

pub fn bank_hash_at(&self, slot_id: Slot) -> BankHash {
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
bank_hashes
.get(&slot_id)
.expect("No bank hash was found for this bank, that should not be possible")
.clone()
}

/// This function will prevent multiple threads from modifying the same account state at the
Expand Down
75 changes: 63 additions & 12 deletions runtime/src/accounts_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,21 @@ 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,
}

#[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 {
Expand Down Expand Up @@ -385,7 +400,7 @@ pub struct AccountsDB {
/// the accounts
min_num_stores: usize,

pub bank_hashes: RwLock<HashMap<Slot, BankHash>>,
pub bank_hashes: RwLock<HashMap<Slot, BankHashInfo>>,
}

impl Default for AccountsDB {
Expand Down Expand Up @@ -522,7 +537,7 @@ impl AccountsDB {
let version: u64 = deserialize_from(&mut stream)
.map_err(|_| AccountsDB::get_io_error("write version deserialize error"))?;

let (slot, bank_hash): (Slot, BankHash) = deserialize_from(&mut stream)
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);

Expand Down Expand Up @@ -716,10 +731,15 @@ impl AccountsDB {

pub fn set_hash(&self, slot: Slot, parent_slot: Slot) {
let mut bank_hashes = self.bank_hashes.write().unwrap();
let hash = *bank_hashes
let hash_info = bank_hashes
.get(&parent_slot)
.expect("accounts_db::set_hash::no parent slot");
bank_hashes.insert(slot, hash);
let hash = hash_info.hash;
let new_hash_info = BankHashInfo {
hash,
stats: BankHashStats::default(),
};
bank_hashes.insert(slot, new_hash_info);
}

pub fn load(
Expand Down Expand Up @@ -1006,8 +1026,8 @@ impl AccountsDB {
calculated_hash.xor(hash);
}
let bank_hashes = self.bank_hashes.read().unwrap();
if let Some(found_hash) = bank_hashes.get(&slot) {
if calculated_hash == *found_hash {
if let Some(found_hash_info) = bank_hashes.get(&slot) {
if calculated_hash == found_hash_info.hash {
Ok(())
} else {
Err(MismatchedBankHash)
Expand All @@ -1017,10 +1037,24 @@ impl AccountsDB {
}
}

pub fn xor_in_hash_state(&self, slot_id: Slot, hash: BankHash) {
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(BankHash::default);
bank_hash.xor(hash);
let bank_hash = bank_hashes
.entry(slot_id)
.or_insert_with(BankHashInfo::default);
bank_hash.hash.xor(hash);

bank_hash.stats.num_removed_accounts += stats.num_removed_accounts;
bank_hash.stats.num_added_accounts += stats.num_added_accounts;
bank_hash.stats.total_data_len = bank_hash
.stats
.total_data_len
.wrapping_add(stats.total_data_len);
bank_hash.stats.num_lamports_stored = bank_hash
.stats
.num_lamports_stored
.wrapping_add(stats.num_lamports_stored);
bank_hash.stats.num_executable_accounts += stats.num_executable_accounts;
}

fn update_index(
Expand Down Expand Up @@ -1109,11 +1143,24 @@ impl AccountsDB {
fn hash_accounts(&self, slot_id: Slot, accounts: &[(&Pubkey, &Account)]) -> Vec<Hash> {
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);
if hash.is_default() {
stats.num_added_accounts += 1;
} else {
stats.num_removed_accounts += 1;
}
stats.total_data_len =
stats.total_data_len.wrapping_add(account.data.len() as u64);
if account.executable {
stats.num_executable_accounts += 1;
}
stats.num_lamports_stored =
stats.num_lamports_stored.wrapping_add(account.lamports);
let new_hash = Self::hash_account(slot_id, account, pubkey);
let new_bank_hash = BankHash::from_hash(&new_hash);
debug!(
Expand All @@ -1135,7 +1182,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
}
Expand Down Expand Up @@ -2247,10 +2294,14 @@ pub mod tests {
);

let some_bank_hash = BankHash::from_hash(&Hash::new(&[0xca; HASH_BYTES]));
let bank_hash_info = BankHashInfo {
hash: some_bank_hash,
stats: BankHashStats::default(),
};
db.bank_hashes
.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)
Expand All @@ -2268,7 +2319,7 @@ pub mod tests {
db.bank_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(_));
}
Expand Down
11 changes: 8 additions & 3 deletions runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1644,23 +1644,28 @@ 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(),
]);
info!(
"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 stats: {:?} slot: {}",
accounts_delta_hash.stats,
self.slot()
);
hash
}

Expand Down

0 comments on commit 3e39b7d

Please sign in to comment.