Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hash stats information to check hashes between validators (bp #7780) #7793

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion 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 @@ -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
Expand Down
123 changes: 120 additions & 3 deletions runtime/src/accounts_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -384,7 +424,11 @@ pub struct AccountsDB {
/// the accounts
min_num_stores: usize,

<<<<<<< HEAD
pub slot_hashes: RwLock<HashMap<Slot, BankHash>>,
=======
pub bank_hashes: RwLock<HashMap<Slot, BankHashInfo>>,
>>>>>>> 3b78be83c... Add hash stats information to check hashes between validators (#7780)
}

impl Default for AccountsDB {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)
Expand All @@ -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(
Expand Down Expand Up @@ -1111,11 +1191,13 @@ 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);
stats.update(account);
let new_hash = Self::hash_account(slot_id, account, pubkey);
let new_bank_hash = BankHash::from_hash(&new_hash);
debug!(
Expand All @@ -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
}
Expand Down Expand Up @@ -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::*;
Expand All @@ -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)
Expand All @@ -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(_));
}
Expand Down
11 changes: 8 additions & 3 deletions runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1631,23 +1631,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 slot: {} stats: {:?}",
self.slot(),
accounts_delta_hash.stats,
);
hash
}

Expand Down