Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.
Merged
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
4 changes: 2 additions & 2 deletions runtime/benches/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ fn test_accounts_squash(bencher: &mut Bencher) {
}

#[bench]
fn test_accounts_hash_internal_state(bencher: &mut Bencher) {
fn test_accounts_hash_bank_hash(bencher: &mut Bencher) {
let accounts = Accounts::new(vec![PathBuf::from("bench_accounts_hash_internal")]);
let mut pubkeys: Vec<Pubkey> = vec![];
create_test_accounts(&accounts, &mut pubkeys, 60000, 0);
let ancestors = vec![(0, 0)].into_iter().collect();
bencher.iter(|| {
accounts.verify_hash_internal_state(0, &ancestors);
accounts.verify_bank_hash(0, &ancestors);
});
}
19 changes: 8 additions & 11 deletions runtime/src/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,8 @@ impl Accounts {
})
}

pub fn verify_hash_internal_state(&self, slot: Slot, ancestors: &HashMap<Slot, usize>) -> bool {
self.accounts_db.verify_hash_internal_state(slot, ancestors)
pub fn verify_bank_hash(&self, slot: Slot, ancestors: &HashMap<Slot, usize>) -> bool {
self.accounts_db.verify_bank_hash(slot, ancestors).is_ok()
Copy link
Copy Markdown
Contributor Author

@ryoqun ryoqun Dec 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the very layer boundary of "Bank<=>AccountsDB", we discard detailed error and encapsulate the result into a bool.

}

pub fn load_by_program(
Expand Down Expand Up @@ -476,7 +476,7 @@ impl Accounts {
}
}

pub fn hash_internal_state(&self, slot_id: Slot) -> BankHash {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed from verb_object to noun_preposition style here, as it's rather a getter fn, not doing actual calculating here.

pub fn bank_hash_at(&self, slot_id: Slot) -> BankHash {
let slot_hashes = self.accounts_db.slot_hashes.read().unwrap();
*slot_hashes
.get(&slot_id)
Expand Down Expand Up @@ -1164,17 +1164,17 @@ mod tests {

#[test]
#[should_panic]
fn test_accounts_empty_hash_internal_state() {
fn test_accounts_empty_bank_hash() {
let accounts = Accounts::new(Vec::new());
accounts.hash_internal_state(0);
accounts.bank_hash_at(0);
}

#[test]
#[should_panic]
fn test_accounts_empty_account_hash_internal_state() {
fn test_accounts_empty_account_bank_hash() {
let accounts = Accounts::new(Vec::new());
accounts.store_slow(0, &Pubkey::default(), &Account::new(1, 0, &sysvar::id()));
accounts.hash_internal_state(0);
accounts.bank_hash_at(0);
}

fn check_accounts(accounts: &Accounts, pubkeys: &Vec<Pubkey>, num: usize) {
Expand Down Expand Up @@ -1221,10 +1221,7 @@ mod tests {
.accounts_from_stream(&mut reader, &daccounts_paths, copied_accounts.path())
.is_ok());
check_accounts(&daccounts, &pubkeys, 100);
assert_eq!(
accounts.hash_internal_state(0),
daccounts.hash_internal_state(0)
);
assert_eq!(accounts.bank_hash_at(0), daccounts.bank_hash_at(0));
}

#[test]
Expand Down
130 changes: 117 additions & 13 deletions runtime/src/accounts_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ pub enum AccountStorageStatus {
Candidate = 2,
}

#[derive(Debug)]
pub enum BankHashVerificatonError {
Copy link
Copy Markdown
Contributor Author

@ryoqun ryoqun Dec 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, the naming here is very clear after all the hustle of naming changes of this PR. :)

MismatchedAccountHash,
MismatchedBankHash,
MissingBankHash,
}

/// Persistent storage structure holding the accounts
#[derive(Debug, Serialize, Deserialize)]
pub struct AccountStorageEntry {
Expand Down Expand Up @@ -354,13 +361,11 @@ pub struct AccountsDB {
/// Keeps tracks of index into AppendVec on a per slot basis
pub accounts_index: RwLock<AccountsIndex<AccountInfo>>,

/// Account storage
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this as the same reason as this.

pub storage: RwLock<AccountStorage>,

/// distribute the accounts across storage lists
pub next_id: AtomicUsize,

/// write version
Copy link
Copy Markdown
Contributor Author

@ryoqun ryoqun Dec 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit aggressive here, but remove this comment as it adds no additional information to the commented code itself. use fewer comments and remove otherwise as it's too common for engineers to forget to maintain them up-to-date (including me!) as this is the case.

write_version: AtomicUsize,

/// Set of storage paths to pick from
Expand All @@ -379,7 +384,6 @@ pub struct AccountsDB {
/// the accounts
min_num_stores: usize,

/// slot to BankHash and a status flag to indicate if the hash has been initialized or not
Copy link
Copy Markdown
Contributor Author

@ryoqun ryoqun Dec 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit aggressive, but delete this comment as it's outdated and now adds no additional information by the comment.

pub slot_hashes: RwLock<HashMap<Slot, BankHash>>,
}

Expand Down Expand Up @@ -969,28 +973,49 @@ impl AccountsDB {
datapoint_info!("accounts_db-stores", ("total_count", total_count, i64));
}

pub fn verify_hash_internal_state(&self, slot: Slot, ancestors: &HashMap<Slot, usize>) -> bool {
let mut hash_state = BankHash::default();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to its use site and renamed.

let hashes: Vec<_> = self.scan_accounts(
pub fn verify_bank_hash(
&self,
slot: Slot,
ancestors: &HashMap<Slot, usize>,
) -> Result<(), BankHashVerificatonError> {
use BankHashVerificatonError::*;

let (hashes, mismatch_found) = self.scan_accounts(
ancestors,
|collector: &mut Vec<BankHash>, option: Option<(&Pubkey, Account, Slot)>| {
|(collector, mismatch_found): &mut (Vec<BankHash>, bool),
option: Option<(&Pubkey, Account, Slot)>| {
if let Some((pubkey, account, slot)) = option {
if !sysvar::check_id(&account.owner) {
let hash = BankHash::from_hash(&Self::hash_account(slot, &account, pubkey));
let hash = Self::hash_account(slot, &account, pubkey);
if hash != account.hash {
*mismatch_found = true;
Copy link
Copy Markdown
Contributor Author

@ryoqun ryoqun Dec 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally assigned false, but this didn't work because the Default::default() of bool is false... So I inverted the boolean value.

}
if *mismatch_found {
return;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added by me, bail out early as the PR description explains.

}
let hash = BankHash::from_hash(&hash);
debug!("xoring..{} key: {}", hash, pubkey);
collector.push(hash);
}
}
},
);
if mismatch_found {
return Err(MismatchedAccountHash);
}
let mut calculated_hash = BankHash::default();
for hash in hashes {
hash_state.xor(hash);
calculated_hash.xor(hash);
}
let slot_hashes = self.slot_hashes.read().unwrap();
if let Some(state) = slot_hashes.get(&slot) {
hash_state == *state
if let Some(found_hash) = slot_hashes.get(&slot) {
if calculated_hash == *found_hash {
Ok(())
} else {
Err(MismatchedBankHash)
}
} else {
false
Err(BankHashVerificatonError::MissingBankHash)
}
}

Expand Down Expand Up @@ -1120,9 +1145,12 @@ impl AccountsDB {
/// Store the account update.
pub fn store(&self, slot_id: Slot, accounts: &[(&Pubkey, &Account)]) {
let hashes = self.hash_accounts(slot_id, accounts);
self.store_with_hashes(slot_id, accounts, &hashes);
}

fn store_with_hashes(&self, slot_id: Slot, accounts: &[(&Pubkey, &Account)], hashes: &[Hash]) {
Copy link
Copy Markdown
Contributor Author

@ryoqun ryoqun Dec 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted into new fn purely for the purpose of unit-testing.

let mut store_accounts = Measure::start("store::store_accounts");
let infos = self.store_accounts(slot_id, accounts, &hashes);
let infos = self.store_accounts(slot_id, accounts, hashes);
store_accounts.stop();

let mut update_index = Measure::start("store::update_index");
Expand Down Expand Up @@ -1234,9 +1262,11 @@ pub mod tests {
// TODO: all the bank tests are bank specific, issue: 2194
use super::*;
use crate::append_vec::AccountMeta;
use assert_matches::assert_matches;
use bincode::serialize_into;
use rand::{thread_rng, Rng};
use solana_sdk::account::Account;
use solana_sdk::hash::HASH_BYTES;
use std::fs;
use std::str::FromStr;
use tempfile::TempDir;
Expand Down Expand Up @@ -2195,4 +2225,78 @@ pub mod tests {
"Account-based hashing must be consistent with StoredAccount-based one."
);
}

#[test]
fn test_verify_bank_hash() {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the coverage report from the CI will reveal, these tests does the 100% coverage. :p

use BankHashVerificatonError::*;
solana_logger::setup();
let db = AccountsDB::new(Vec::new());

let key = Pubkey::default();
let some_data_len = 0;
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)]);
db.add_root(some_slot);
assert_matches!(db.verify_bank_hash(some_slot, &ancestors), Ok(_));

db.slot_hashes.write().unwrap().remove(&some_slot).unwrap();
assert_matches!(
db.verify_bank_hash(some_slot, &ancestors),
Err(MissingBankHash)
);

let some_bank_hash = BankHash::from_hash(&Hash::new(&[0xca; HASH_BYTES]));
db.slot_hashes
.write()
.unwrap()
.insert(some_slot, some_bank_hash);
assert_matches!(
db.verify_bank_hash(some_slot, &ancestors),
Err(MismatchedBankHash)
);
}

#[test]
fn test_verify_bank_hash_no_account() {
solana_logger::setup();
let db = AccountsDB::new(Vec::new());

let some_slot: Slot = 0;
let ancestors = vec![(some_slot, 0)].into_iter().collect();

db.slot_hashes
.write()
.unwrap()
.insert(some_slot, BankHash::default());
db.add_root(some_slot);
assert_matches!(db.verify_bank_hash(some_slot, &ancestors), Ok(_));
}

#[test]
fn test_verify_bank_hash_bad_account_hash() {
use BankHashVerificatonError::*;
solana_logger::setup();
let db = AccountsDB::new(Vec::new());

let key = Pubkey::default();
let some_data_len = 0;
let some_slot: Slot = 0;
let account = Account::new(1, some_data_len, &key);
let ancestors = vec![(some_slot, 0)].into_iter().collect();

let accounts = &[(&key, &account)];
// update AccountsDB's bank hash but discard real account hashes
db.hash_accounts(some_slot, accounts);
// provide bogus account hashes
let some_hash = Hash::new(&[0xca; HASH_BYTES]);
db.store_with_hashes(some_slot, accounts, &vec![some_hash]);
db.add_root(some_slot);
assert_matches!(
db.verify_bank_hash(some_slot, &ancestors),
Err(MismatchedAccountHash)
);
}
}
10 changes: 5 additions & 5 deletions runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1595,7 +1595,7 @@ 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.hash_internal_state(self.slot());
let accounts_delta_hash = self.rc.accounts.bank_hash_at(self.slot());
let mut signature_count_buf = [0u8; 8];
LittleEndian::write_u64(&mut signature_count_buf[..], self.signature_count() as u64);
hashv(&[
Expand All @@ -1611,7 +1611,7 @@ impl Bank {
pub fn verify_hash_internal_state(&self) -> bool {
self.rc
.accounts
.verify_hash_internal_state(self.slot(), &self.ancestors)
.verify_bank_hash(self.slot(), &self.ancestors)
Copy link
Copy Markdown
Contributor Author

@ryoqun ryoqun Dec 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this place, we semantically translate verify-ing Bank's internal_state into AccountDB's bank_hash. So outwards rename propagation from AccountsDB ends here. :)

}

/// A snapshot bank should be purged of 0 lamport accounts which are not part of the hash
Expand All @@ -1620,7 +1620,7 @@ impl Bank {
self.purge_zero_lamport_accounts();
self.rc
.accounts
.verify_hash_internal_state(self.slot(), &self.ancestors)
.verify_bank_hash(self.slot(), &self.ancestors)
}

/// Return the number of hashes per tick
Expand Down Expand Up @@ -1806,8 +1806,8 @@ impl Bank {
let dsc = dbank.src.status_cache.read().unwrap();
assert_eq!(*sc, *dsc);
assert_eq!(
self.rc.accounts.hash_internal_state(self.slot),
dbank.rc.accounts.hash_internal_state(dbank.slot)
self.rc.accounts.bank_hash_at(self.slot),
dbank.rc.accounts.bank_hash_at(dbank.slot)
);
}

Expand Down
7 changes: 4 additions & 3 deletions sdk/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ use std::fmt;
use std::mem;
use std::str::FromStr;

pub const HASH_BYTES: usize = 32;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A tiny nicety. This aligns with BANK_HASH_BYTES in bank_hash.rs.

#[derive(Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Hash([u8; 32]);
pub struct Hash([u8; HASH_BYTES]);

#[derive(Clone, Default)]
pub struct Hasher {
Expand All @@ -28,7 +29,7 @@ impl Hasher {
pub fn result(self) -> Hash {
// At the time of this writing, the sha2 library is stuck on an old version
// of generic_array (0.9.0). Decouple ourselves with a clone to our version.
Hash(<[u8; 32]>::try_from(self.hasher.result().as_slice()).unwrap())
Hash(<[u8; HASH_BYTES]>::try_from(self.hasher.result().as_slice()).unwrap())
}
}

Expand Down Expand Up @@ -73,7 +74,7 @@ impl FromStr for Hash {

impl Hash {
pub fn new(hash_slice: &[u8]) -> Self {
Hash(<[u8; 32]>::try_from(hash_slice).unwrap())
Hash(<[u8; HASH_BYTES]>::try_from(hash_slice).unwrap())
}
}

Expand Down