Adds AccountHash newtype (#33597)

This commit is contained in:
Brooks 2023-10-09 16:00:52 -04:00 committed by GitHub
parent 052677595c
commit fc73813db2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 50 deletions

View File

@ -1,10 +1,12 @@
use {
crate::accounts_db::IncludeSlotInHash,
crate::{
accounts_db::{AccountsDb, IncludeSlotInHash},
accounts_hash::AccountHash,
},
dashmap::DashMap,
solana_sdk::{
account::{AccountSharedData, ReadableAccount},
clock::Slot,
hash::Hash,
pubkey::Pubkey,
},
std::{
@ -141,7 +143,7 @@ pub type CachedAccount = Arc<CachedAccountInner>;
#[derive(Debug)]
pub struct CachedAccountInner {
pub account: AccountSharedData,
hash: RwLock<Option<Hash>>,
hash: RwLock<Option<AccountHash>>,
slot: Slot,
pubkey: Pubkey,
/// temporarily here during feature activation
@ -150,13 +152,13 @@ pub struct CachedAccountInner {
}
impl CachedAccountInner {
pub fn hash(&self) -> Hash {
pub fn hash(&self) -> AccountHash {
let hash = self.hash.read().unwrap();
match *hash {
Some(hash) => hash,
None => {
drop(hash);
let hash = crate::accounts_db::AccountsDb::hash_account(
let hash = AccountsDb::hash_account(
self.slot,
&self.account,
&self.pubkey,

View File

@ -33,7 +33,7 @@ use {
accounts_cache::{AccountsCache, CachedAccount, SlotCache},
accounts_file::{AccountsFile, AccountsFileError},
accounts_hash::{
AccountsDeltaHash, AccountsHash, AccountsHashKind, AccountsHasher,
AccountHash, AccountsDeltaHash, AccountsHash, AccountsHashKind, AccountsHasher,
CalcAccountsHashConfig, CalculateHashIntermediate, HashStats, IncrementalAccountsHash,
SerdeAccountsDeltaHash, SerdeAccountsHash, SerdeIncrementalAccountsHash,
ZeroLamportAccounts,
@ -894,9 +894,9 @@ pub enum LoadedAccount<'a> {
}
impl<'a> LoadedAccount<'a> {
pub fn loaded_hash(&self) -> Hash {
pub fn loaded_hash(&self) -> AccountHash {
match self {
LoadedAccount::Stored(stored_account_meta) => *stored_account_meta.hash(),
LoadedAccount::Stored(stored_account_meta) => AccountHash(*stored_account_meta.hash()),
LoadedAccount::Cached(cached_account) => cached_account.hash(),
}
}
@ -913,7 +913,7 @@ impl<'a> LoadedAccount<'a> {
slot: Slot,
pubkey: &Pubkey,
include_slot: IncludeSlotInHash,
) -> Hash {
) -> AccountHash {
match self {
LoadedAccount::Stored(stored_account_meta) => AccountsDb::hash_account(
slot,
@ -2392,7 +2392,7 @@ impl<'a> AppendVecScan for ScanState<'a> {
let balance = loaded_account.lamports();
let mut loaded_hash = loaded_account.loaded_hash();
let hash_is_missing = loaded_hash == Hash::default();
let hash_is_missing = loaded_hash == AccountHash(Hash::default());
if (self.config.check_hash || hash_is_missing)
&& !AccountsDb::is_filler_account_helper(pubkey, self.filler_account_suffix)
{
@ -2406,13 +2406,13 @@ impl<'a> AppendVecScan for ScanState<'a> {
} else if self.config.check_hash && computed_hash != loaded_hash {
info!(
"hash mismatch found: computed: {}, loaded: {}, pubkey: {}",
computed_hash, loaded_hash, pubkey
computed_hash.0, loaded_hash.0, pubkey
);
self.mismatch_found.fetch_add(1, Ordering::Relaxed);
}
}
let source_item = CalculateHashIntermediate {
hash: loaded_hash,
hash: loaded_hash.0,
lamports: balance,
pubkey: *pubkey,
};
@ -2429,7 +2429,7 @@ impl<'a> AppendVecScan for ScanState<'a> {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PubkeyHashAccount {
pub pubkey: Pubkey,
pub hash: Hash,
pub hash: AccountHash,
pub account: AccountSharedData,
}
@ -5567,7 +5567,7 @@ impl AccountsDb {
pubkey: &Pubkey,
max_root: Option<Slot>,
load_hint: LoadHint,
) -> Option<Hash> {
) -> Option<AccountHash> {
let (slot, storage_location, _maybe_account_accesor) =
self.read_index_for_accessor_or_load_slow(ancestors, pubkey, max_root, false)?;
// Notice the subtle `?` at previous line, we bail out pretty early if missing.
@ -6185,7 +6185,7 @@ impl AccountsDb {
account: &T,
pubkey: &Pubkey,
include_slot: IncludeSlotInHash,
) -> Hash {
) -> AccountHash {
Self::hash_account_data(
slot,
account.lamports(),
@ -6207,9 +6207,9 @@ impl AccountsDb {
data: &[u8],
pubkey: &Pubkey,
include_slot: IncludeSlotInHash,
) -> Hash {
) -> AccountHash {
if lamports == 0 {
return Hash::default();
return AccountHash(Hash::default());
}
let mut hasher = blake3::Hasher::new();
@ -6239,7 +6239,7 @@ impl AccountsDb {
hasher.update(owner.as_ref());
hasher.update(pubkey.as_ref());
Hash::new_from_array(hasher.finalize().into())
AccountHash(Hash::new_from_array(hasher.finalize().into()))
}
fn bulk_assign_write_version(&self, count: usize) -> StoredMetaWriteVersion {
@ -6560,7 +6560,7 @@ impl AccountsDb {
}
}
let (accounts, hashes): (Vec<(&Pubkey, &AccountSharedData)>, Vec<Hash>) = iter_items
let (accounts, hashes): (Vec<(&Pubkey, &AccountSharedData)>, Vec<AccountHash>) = iter_items
.iter()
.filter_map(|iter_item| {
let key = iter_item.key();
@ -7025,7 +7025,7 @@ impl AccountsDb {
|loaded_account| {
let mut loaded_hash = loaded_account.loaded_hash();
let balance = loaded_account.lamports();
let hash_is_missing = loaded_hash == Hash::default();
let hash_is_missing = loaded_hash == AccountHash(Hash::default());
if (config.check_hash || hash_is_missing) && !self.is_filler_account(pubkey) {
let computed_hash =
loaded_account.compute_hash(*slot, pubkey, config.include_slot_in_hash);
@ -7033,7 +7033,7 @@ impl AccountsDb {
loaded_hash = computed_hash;
}
else if config.check_hash && computed_hash != loaded_hash {
info!("hash mismatch found: computed: {}, loaded: {}, pubkey: {}", computed_hash, loaded_hash, pubkey);
info!("hash mismatch found: computed: {}, loaded: {}, pubkey: {}", computed_hash.0, loaded_hash.0, pubkey);
mismatch_found
.fetch_add(1, Ordering::Relaxed);
return None;
@ -7041,7 +7041,7 @@ impl AccountsDb {
}
sum += balance as u128;
Some(loaded_hash)
Some(loaded_hash.0)
},
)
} else {
@ -7916,17 +7916,20 @@ impl AccountsDb {
/// 1. pubkey, hash pairs for the slot
/// 2. us spent scanning
/// 3. Measure started when we began accumulating
pub fn get_pubkey_hash_for_slot(&self, slot: Slot) -> (Vec<(Pubkey, Hash)>, u64, Measure) {
pub fn get_pubkey_hash_for_slot(
&self,
slot: Slot,
) -> (Vec<(Pubkey, AccountHash)>, u64, Measure) {
let mut scan = Measure::start("scan");
let scan_result: ScanStorageResult<(Pubkey, Hash), DashMap<Pubkey, Hash>> = self
.scan_account_storage(
let scan_result: ScanStorageResult<(Pubkey, AccountHash), DashMap<Pubkey, AccountHash>> =
self.scan_account_storage(
slot,
|loaded_account: LoadedAccount| {
// Cache only has one version per key, don't need to worry about versioning
Some((*loaded_account.pubkey(), loaded_account.loaded_hash()))
},
|accum: &DashMap<Pubkey, Hash>, loaded_account: LoadedAccount| {
|accum: &DashMap<Pubkey, AccountHash>, loaded_account: LoadedAccount| {
let loaded_hash = loaded_account.loaded_hash();
accum.insert(*loaded_account.pubkey(), loaded_hash);
},
@ -7944,7 +7947,7 @@ impl AccountsDb {
/// Return all of the accounts for a given slot
pub fn get_pubkey_hash_account_for_slot(&self, slot: Slot) -> Vec<PubkeyHashAccount> {
type ScanResult =
ScanStorageResult<PubkeyHashAccount, DashMap<Pubkey, (Hash, AccountSharedData)>>;
ScanStorageResult<PubkeyHashAccount, DashMap<Pubkey, (AccountHash, AccountSharedData)>>;
let scan_result: ScanResult = self.scan_account_storage(
slot,
|loaded_account: LoadedAccount| {
@ -7955,7 +7958,8 @@ impl AccountsDb {
account: loaded_account.take_account(),
})
},
|accum: &DashMap<Pubkey, (Hash, AccountSharedData)>, loaded_account: LoadedAccount| {
|accum: &DashMap<Pubkey, (AccountHash, AccountSharedData)>,
loaded_account: LoadedAccount| {
// Storage may have duplicates so only keep the latest version for each key
accum.insert(
*loaded_account.pubkey(),
@ -10482,10 +10486,10 @@ pub mod tests {
];
let expected_hashes = [
Hash::from_str("5K3NW73xFHwgTWVe4LyCg4QfQda8f88uZj2ypDx2kmmH").unwrap(),
Hash::from_str("84ozw83MZ8oeSF4hRAg7SeW1Tqs9LMXagX1BrDRjtZEx").unwrap(),
Hash::from_str("5XqtnEJ41CG2JWNp7MAg9nxkRUAnyjLxfsKsdrLxQUbC").unwrap(),
Hash::from_str("DpvwJcznzwULYh19Zu5CuAA4AT6WTBe4H6n15prATmqj").unwrap(),
AccountHash(Hash::from_str("5K3NW73xFHwgTWVe4LyCg4QfQda8f88uZj2ypDx2kmmH").unwrap()),
AccountHash(Hash::from_str("84ozw83MZ8oeSF4hRAg7SeW1Tqs9LMXagX1BrDRjtZEx").unwrap()),
AccountHash(Hash::from_str("5XqtnEJ41CG2JWNp7MAg9nxkRUAnyjLxfsKsdrLxQUbC").unwrap()),
AccountHash(Hash::from_str("DpvwJcznzwULYh19Zu5CuAA4AT6WTBe4H6n15prATmqj").unwrap()),
];
let mut raw_accounts = Vec::default();
@ -10505,7 +10509,7 @@ pub mod tests {
if slot == 1 && matches!(include_slot_in_hash, IncludeSlotInHash::IncludeSlot) {
assert_eq!(hash, expected_hashes[i]);
}
raw_expected[i].hash = hash;
raw_expected[i].hash = hash.0;
}
let to_store = raw_accounts
@ -12696,7 +12700,7 @@ pub mod tests {
let account = stored_account.to_account_shared_data();
let expected_account_hash =
Hash::from_str("6VeAL4x4PVkECKL1hD1avwPE1uMCRoWiZJzVMvVNYhTq").unwrap();
AccountHash(Hash::from_str("6VeAL4x4PVkECKL1hD1avwPE1uMCRoWiZJzVMvVNYhTq").unwrap());
assert_eq!(
AccountsDb::hash_account(
@ -18161,7 +18165,7 @@ pub mod tests {
// Ensure the zero-lamport accounts are NOT included in the full accounts hash.
let full_account_hashes = [(2, 0), (3, 0), (4, 1)].into_iter().map(|(index, slot)| {
let (pubkey, account) = &accounts[index];
AccountsDb::hash_account(slot, account, pubkey, INCLUDE_SLOT_IN_HASH_TESTS)
AccountsDb::hash_account(slot, account, pubkey, INCLUDE_SLOT_IN_HASH_TESTS).0
});
let expected_accounts_hash = AccountsHash(compute_merkle_root(full_account_hashes));
assert_eq!(full_accounts_hash.0, expected_accounts_hash);
@ -18243,6 +18247,7 @@ pub mod tests {
Hash::new_from_array(hash.into())
} else {
AccountsDb::hash_account(slot, account, pubkey, INCLUDE_SLOT_IN_HASH_TESTS)
.0
}
});
let expected_accounts_hash =

View File

@ -740,14 +740,9 @@ impl<'a> AccountsHasher<'a> {
)
}
pub fn accumulate_account_hashes(mut hashes: Vec<(Pubkey, Hash)>) -> Hash {
Self::sort_hashes_by_pubkey(&mut hashes);
Self::compute_merkle_root_loop(hashes, MERKLE_FANOUT, |i| &i.1)
}
pub fn sort_hashes_by_pubkey(hashes: &mut Vec<(Pubkey, Hash)>) {
pub fn accumulate_account_hashes(mut hashes: Vec<(Pubkey, AccountHash)>) -> Hash {
hashes.par_sort_unstable_by(|a, b| a.0.cmp(&b.0));
Self::compute_merkle_root_loop(hashes, MERKLE_FANOUT, |i| &i.1 .0)
}
pub fn compare_two_hash_entries(
@ -1206,6 +1201,21 @@ pub enum ZeroLamportAccounts {
Included,
}
/// Hash of an account
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Pod, Zeroable)]
pub struct AccountHash(pub Hash);
// Ensure the newtype wrapper never changes size from the underlying Hash
// This also ensures there are no padding bytes, which is requried to safely implement Pod
const _: () = assert!(std::mem::size_of::<AccountHash>() == std::mem::size_of::<Hash>());
impl Borrow<Hash> for AccountHash {
fn borrow(&self) -> &Hash {
&self.0
}
}
/// Hash of accounts
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum AccountsHashKind {
@ -2320,14 +2330,18 @@ mod tests {
.collect();
let result = if pass == 0 {
test_hashing_larger(input.clone(), fanout)
test_hashing_larger(input, fanout)
} else {
// this sorts inside
let early_result = AccountsHasher::accumulate_account_hashes(
input.iter().map(|i| (i.0, i.1)).collect::<Vec<_>>(),
input
.iter()
.map(|i| (i.0, AccountHash(i.1)))
.collect::<Vec<_>>(),
);
AccountsHasher::sort_hashes_by_pubkey(&mut input);
let result = AccountsHasher::compute_merkle_root(input.clone(), fanout);
input.par_sort_unstable_by(|a, b| a.0.cmp(&b.0));
let result = AccountsHasher::compute_merkle_root(input, fanout);
assert_eq!(early_result, result);
result
};

View File

@ -8,7 +8,10 @@ use {
de::{self, Deserialize, Deserializer},
ser::{Serialize, SerializeSeq, Serializer},
},
solana_accounts_db::{accounts_db::PubkeyHashAccount, accounts_hash::AccountsDeltaHash},
solana_accounts_db::{
accounts_db::PubkeyHashAccount,
accounts_hash::{AccountHash, AccountsDeltaHash},
},
solana_sdk::{
account::{Account, AccountSharedData, ReadableAccount},
clock::{Epoch, Slot},
@ -124,7 +127,7 @@ impl From<&PubkeyHashAccount> for SerdeAccount {
} = pubkey_hash_account;
Self {
pubkey: pubkey.to_string(),
hash: hash.to_string(),
hash: hash.0.to_string(),
owner: account.owner().to_string(),
lamports: account.lamports(),
rent_epoch: account.rent_epoch(),
@ -139,7 +142,7 @@ impl TryFrom<SerdeAccount> for PubkeyHashAccount {
fn try_from(temp_account: SerdeAccount) -> Result<Self, Self::Error> {
let pubkey = Pubkey::from_str(&temp_account.pubkey).map_err(|err| err.to_string())?;
let hash = Hash::from_str(&temp_account.hash).map_err(|err| err.to_string())?;
let hash = AccountHash(Hash::from_str(&temp_account.hash).map_err(|err| err.to_string())?);
let account = AccountSharedData::from(Account {
lamports: temp_account.lamports,
@ -244,7 +247,7 @@ pub mod tests {
rent_epoch: 123,
});
let account_pubkey = Pubkey::new_unique();
let account_hash = hash("account".as_bytes());
let account_hash = AccountHash(hash("account".as_bytes()));
let accounts = BankHashAccounts {
accounts: vec![PubkeyHashAccount {
pubkey: account_pubkey,