add check_hash to non-index hash calculation (#17558)
This commit is contained in:
parent
7cf6e66ddd
commit
55c22d3b76
|
@ -4241,17 +4241,18 @@ impl AccountsDb {
|
||||||
use_index: bool,
|
use_index: bool,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
ancestors: &Ancestors,
|
ancestors: &Ancestors,
|
||||||
) -> (Hash, u64) {
|
check_hash: bool,
|
||||||
|
) -> Result<(Hash, u64), BankHashVerificationError> {
|
||||||
if !use_index {
|
if !use_index {
|
||||||
let combined_maps = self.get_snapshot_storages(slot);
|
let combined_maps = self.get_snapshot_storages(slot);
|
||||||
|
|
||||||
Self::calculate_accounts_hash_without_index(
|
Self::calculate_accounts_hash_without_index(
|
||||||
&combined_maps,
|
&combined_maps,
|
||||||
Some(&self.thread_pool_clean),
|
Some(&self.thread_pool_clean),
|
||||||
|
check_hash,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
self.calculate_accounts_hash(slot, ancestors, false)
|
self.calculate_accounts_hash(slot, ancestors, check_hash)
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4263,12 +4264,15 @@ impl AccountsDb {
|
||||||
ancestors: &Ancestors,
|
ancestors: &Ancestors,
|
||||||
expected_capitalization: Option<u64>,
|
expected_capitalization: Option<u64>,
|
||||||
) -> (Hash, u64) {
|
) -> (Hash, u64) {
|
||||||
let (hash, total_lamports) =
|
let check_hash = false;
|
||||||
self.calculate_accounts_hash_helper(use_index, slot, ancestors);
|
let (hash, total_lamports) = self
|
||||||
|
.calculate_accounts_hash_helper(use_index, slot, ancestors, check_hash)
|
||||||
|
.unwrap(); // unwrap here will never fail since check_hash = false
|
||||||
if debug_verify {
|
if debug_verify {
|
||||||
// calculate the other way (store or non-store) and verify results match.
|
// calculate the other way (store or non-store) and verify results match.
|
||||||
let (hash_other, total_lamports_other) =
|
let (hash_other, total_lamports_other) = self
|
||||||
self.calculate_accounts_hash_helper(!use_index, slot, ancestors);
|
.calculate_accounts_hash_helper(!use_index, slot, ancestors, check_hash)
|
||||||
|
.unwrap(); // unwrap here will never fail since check_hash = false
|
||||||
|
|
||||||
let success = hash == hash_other
|
let success = hash == hash_other
|
||||||
&& total_lamports == total_lamports_other
|
&& total_lamports == total_lamports_other
|
||||||
|
@ -4286,12 +4290,15 @@ impl AccountsDb {
|
||||||
mut stats: &mut crate::accounts_hash::HashStats,
|
mut stats: &mut crate::accounts_hash::HashStats,
|
||||||
bins: usize,
|
bins: usize,
|
||||||
bin_range: &Range<usize>,
|
bin_range: &Range<usize>,
|
||||||
) -> Vec<Vec<Vec<CalculateHashIntermediate>>> {
|
check_hash: bool,
|
||||||
|
) -> Result<Vec<Vec<Vec<CalculateHashIntermediate>>>, BankHashVerificationError> {
|
||||||
let max_plus_1 = std::u8::MAX as usize + 1;
|
let max_plus_1 = std::u8::MAX as usize + 1;
|
||||||
assert!(bins <= max_plus_1 && bins > 0);
|
assert!(bins <= max_plus_1 && bins > 0);
|
||||||
assert!(bin_range.start < bins && bin_range.end <= bins && bin_range.start < bin_range.end);
|
assert!(bin_range.start < bins && bin_range.end <= bins && bin_range.start < bin_range.end);
|
||||||
let mut time = Measure::start("scan all accounts");
|
let mut time = Measure::start("scan all accounts");
|
||||||
stats.num_snapshot_storage = storage.len();
|
stats.num_snapshot_storage = storage.len();
|
||||||
|
let mismatch_found = AtomicU64::new(0);
|
||||||
|
|
||||||
let result: Vec<Vec<Vec<CalculateHashIntermediate>>> = Self::scan_account_storage_no_bank(
|
let result: Vec<Vec<Vec<CalculateHashIntermediate>>> = Self::scan_account_storage_no_bank(
|
||||||
&storage,
|
&storage,
|
||||||
|loaded_account: LoadedAccount,
|
|loaded_account: LoadedAccount,
|
||||||
|
@ -4319,6 +4326,18 @@ impl AccountsDb {
|
||||||
slot,
|
slot,
|
||||||
pubkey,
|
pubkey,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if check_hash {
|
||||||
|
let computed_hash = loaded_account.compute_hash(slot, &pubkey);
|
||||||
|
if computed_hash != source_item.hash {
|
||||||
|
info!(
|
||||||
|
"hash mismatch found: computed: {}, loaded: {}, pubkey: {}",
|
||||||
|
computed_hash, source_item.hash, pubkey
|
||||||
|
);
|
||||||
|
mismatch_found.fetch_add(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let max = accum.len();
|
let max = accum.len();
|
||||||
if max == 0 {
|
if max == 0 {
|
||||||
accum.extend(vec![Vec::new(); bins]);
|
accum.extend(vec![Vec::new(); bins]);
|
||||||
|
@ -4326,9 +4345,19 @@ impl AccountsDb {
|
||||||
accum[pubkey_to_bin_index].push(source_item);
|
accum[pubkey_to_bin_index].push(source_item);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if check_hash && mismatch_found.load(Ordering::Relaxed) > 0 {
|
||||||
|
warn!(
|
||||||
|
"{} mismatched account hash(es) found",
|
||||||
|
mismatch_found.load(Ordering::Relaxed)
|
||||||
|
);
|
||||||
|
return Err(BankHashVerificationError::MismatchedAccountHash);
|
||||||
|
}
|
||||||
|
|
||||||
time.stop();
|
time.stop();
|
||||||
stats.scan_time_total_us += time.as_us();
|
stats.scan_time_total_us += time.as_us();
|
||||||
result
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// modeled after get_accounts_delta_hash
|
// modeled after get_accounts_delta_hash
|
||||||
|
@ -4336,7 +4365,8 @@ impl AccountsDb {
|
||||||
pub fn calculate_accounts_hash_without_index(
|
pub fn calculate_accounts_hash_without_index(
|
||||||
storages: &[SnapshotStorage],
|
storages: &[SnapshotStorage],
|
||||||
thread_pool: Option<&ThreadPool>,
|
thread_pool: Option<&ThreadPool>,
|
||||||
) -> (Hash, u64) {
|
check_hash: bool,
|
||||||
|
) -> Result<(Hash, u64), BankHashVerificationError> {
|
||||||
let scan_and_hash = || {
|
let scan_and_hash = || {
|
||||||
let mut stats = HashStats::default();
|
let mut stats = HashStats::default();
|
||||||
// When calculating hashes, it is helpful to break the pubkeys found into bins based on the pubkey value.
|
// When calculating hashes, it is helpful to break the pubkeys found into bins based on the pubkey value.
|
||||||
|
@ -4368,7 +4398,8 @@ impl AccountsDb {
|
||||||
&mut stats,
|
&mut stats,
|
||||||
PUBKEY_BINS_FOR_CALCULATING_HASHES,
|
PUBKEY_BINS_FOR_CALCULATING_HASHES,
|
||||||
&bounds,
|
&bounds,
|
||||||
);
|
check_hash,
|
||||||
|
)?;
|
||||||
|
|
||||||
let (hash, lamports, for_next_pass) = AccountsHash::rest_of_hash_calculation(
|
let (hash, lamports, for_next_pass) = AccountsHash::rest_of_hash_calculation(
|
||||||
result,
|
result,
|
||||||
|
@ -4379,7 +4410,7 @@ impl AccountsDb {
|
||||||
previous_pass = for_next_pass;
|
previous_pass = for_next_pass;
|
||||||
final_result = (hash, lamports);
|
final_result = (hash, lamports);
|
||||||
}
|
}
|
||||||
final_result
|
Ok(final_result)
|
||||||
};
|
};
|
||||||
if let Some(thread_pool) = thread_pool {
|
if let Some(thread_pool) = thread_pool {
|
||||||
thread_pool.install(scan_and_hash)
|
thread_pool.install(scan_and_hash)
|
||||||
|
@ -4397,7 +4428,7 @@ impl AccountsDb {
|
||||||
use BankHashVerificationError::*;
|
use BankHashVerificationError::*;
|
||||||
|
|
||||||
let (calculated_hash, calculated_lamports) =
|
let (calculated_hash, calculated_lamports) =
|
||||||
self.calculate_accounts_hash(slot, ancestors, true)?;
|
self.calculate_accounts_hash_helper(true, slot, ancestors, true)?;
|
||||||
|
|
||||||
if calculated_lamports != total_lamports {
|
if calculated_lamports != total_lamports {
|
||||||
warn!(
|
warn!(
|
||||||
|
@ -5574,7 +5605,7 @@ pub mod tests {
|
||||||
let mut stats = HashStats::default();
|
let mut stats = HashStats::default();
|
||||||
let bounds = Range { start: 0, end: 0 };
|
let bounds = Range { start: 0, end: 0 };
|
||||||
|
|
||||||
AccountsDb::scan_snapshot_stores(&[], &mut stats, 257, &bounds);
|
AccountsDb::scan_snapshot_stores(&[], &mut stats, 257, &bounds, false).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "assertion failed: bins <= max_plus_1 && bins > 0")]
|
#[should_panic(expected = "assertion failed: bins <= max_plus_1 && bins > 0")]
|
||||||
|
@ -5582,7 +5613,7 @@ pub mod tests {
|
||||||
let mut stats = HashStats::default();
|
let mut stats = HashStats::default();
|
||||||
let bounds = Range { start: 0, end: 0 };
|
let bounds = Range { start: 0, end: 0 };
|
||||||
|
|
||||||
AccountsDb::scan_snapshot_stores(&[], &mut stats, 0, &bounds);
|
AccountsDb::scan_snapshot_stores(&[], &mut stats, 0, &bounds, false).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -5593,7 +5624,7 @@ pub mod tests {
|
||||||
let mut stats = HashStats::default();
|
let mut stats = HashStats::default();
|
||||||
let bounds = Range { start: 2, end: 2 };
|
let bounds = Range { start: 2, end: 2 };
|
||||||
|
|
||||||
AccountsDb::scan_snapshot_stores(&[], &mut stats, 2, &bounds);
|
AccountsDb::scan_snapshot_stores(&[], &mut stats, 2, &bounds, false).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(
|
#[should_panic(
|
||||||
|
@ -5603,7 +5634,7 @@ pub mod tests {
|
||||||
let mut stats = HashStats::default();
|
let mut stats = HashStats::default();
|
||||||
let bounds = Range { start: 1, end: 3 };
|
let bounds = Range { start: 1, end: 3 };
|
||||||
|
|
||||||
AccountsDb::scan_snapshot_stores(&[], &mut stats, 2, &bounds);
|
AccountsDb::scan_snapshot_stores(&[], &mut stats, 2, &bounds, false).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -5614,7 +5645,7 @@ pub mod tests {
|
||||||
let mut stats = HashStats::default();
|
let mut stats = HashStats::default();
|
||||||
let bounds = Range { start: 1, end: 0 };
|
let bounds = Range { start: 1, end: 0 };
|
||||||
|
|
||||||
AccountsDb::scan_snapshot_stores(&[], &mut stats, 2, &bounds);
|
AccountsDb::scan_snapshot_stores(&[], &mut stats, 2, &bounds, false).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_storages_and_accounts() -> (SnapshotStorages, Vec<CalculateHashIntermediate>) {
|
fn sample_storages_and_accounts() -> (SnapshotStorages, Vec<CalculateHashIntermediate>) {
|
||||||
|
@ -5711,7 +5742,9 @@ pub mod tests {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: bins,
|
end: bins,
|
||||||
},
|
},
|
||||||
);
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result, vec![vec![raw_expected.clone()]]);
|
assert_eq!(result, vec![vec![raw_expected.clone()]]);
|
||||||
|
|
||||||
let bins = 2;
|
let bins = 2;
|
||||||
|
@ -5723,7 +5756,9 @@ pub mod tests {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: bins,
|
end: bins,
|
||||||
},
|
},
|
||||||
);
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let mut expected = vec![Vec::new(); bins];
|
let mut expected = vec![Vec::new(); bins];
|
||||||
expected[0].push(raw_expected[0].clone());
|
expected[0].push(raw_expected[0].clone());
|
||||||
expected[0].push(raw_expected[1].clone());
|
expected[0].push(raw_expected[1].clone());
|
||||||
|
@ -5740,7 +5775,9 @@ pub mod tests {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: bins,
|
end: bins,
|
||||||
},
|
},
|
||||||
);
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let mut expected = vec![Vec::new(); bins];
|
let mut expected = vec![Vec::new(); bins];
|
||||||
expected[0].push(raw_expected[0].clone());
|
expected[0].push(raw_expected[0].clone());
|
||||||
expected[1].push(raw_expected[1].clone());
|
expected[1].push(raw_expected[1].clone());
|
||||||
|
@ -5757,7 +5794,9 @@ pub mod tests {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: bins,
|
end: bins,
|
||||||
},
|
},
|
||||||
);
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let mut expected = vec![Vec::new(); bins];
|
let mut expected = vec![Vec::new(); bins];
|
||||||
expected[0].push(raw_expected[0].clone());
|
expected[0].push(raw_expected[0].clone());
|
||||||
expected[127].push(raw_expected[1].clone());
|
expected[127].push(raw_expected[1].clone());
|
||||||
|
@ -5779,7 +5818,9 @@ pub mod tests {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: bins,
|
end: bins,
|
||||||
},
|
},
|
||||||
);
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result.len(), 2); // 2 chunks
|
assert_eq!(result.len(), 2); // 2 chunks
|
||||||
assert_eq!(result[0].len(), 0); // nothing found in first slots
|
assert_eq!(result[0].len(), 0); // nothing found in first slots
|
||||||
assert_eq!(result[1].len(), bins);
|
assert_eq!(result[1].len(), bins);
|
||||||
|
@ -5801,7 +5842,9 @@ pub mod tests {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: bins / 2,
|
end: bins / 2,
|
||||||
},
|
},
|
||||||
);
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let mut expected = vec![Vec::new(); bins];
|
let mut expected = vec![Vec::new(); bins];
|
||||||
expected[0].push(raw_expected[0].clone());
|
expected[0].push(raw_expected[0].clone());
|
||||||
expected[0].push(raw_expected[1].clone());
|
expected[0].push(raw_expected[1].clone());
|
||||||
|
@ -5816,7 +5859,9 @@ pub mod tests {
|
||||||
start: 1,
|
start: 1,
|
||||||
end: bins,
|
end: bins,
|
||||||
},
|
},
|
||||||
);
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut expected = vec![Vec::new(); bins];
|
let mut expected = vec![Vec::new(); bins];
|
||||||
expected[bins - 1].push(raw_expected[2].clone());
|
expected[bins - 1].push(raw_expected[2].clone());
|
||||||
|
@ -5834,7 +5879,9 @@ pub mod tests {
|
||||||
start: bin,
|
start: bin,
|
||||||
end: bin + 1,
|
end: bin + 1,
|
||||||
},
|
},
|
||||||
);
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let mut expected = vec![Vec::new(); bins];
|
let mut expected = vec![Vec::new(); bins];
|
||||||
expected[bin].push(raw_expected[bin].clone());
|
expected[bin].push(raw_expected[bin].clone());
|
||||||
assert_eq!(result, vec![expected]);
|
assert_eq!(result, vec![expected]);
|
||||||
|
@ -5851,7 +5898,9 @@ pub mod tests {
|
||||||
start: bin,
|
start: bin,
|
||||||
end: bin + 1,
|
end: bin + 1,
|
||||||
},
|
},
|
||||||
);
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let mut expected = vec![];
|
let mut expected = vec![];
|
||||||
if let Some(index) = bin_locations.iter().position(|&r| r == bin) {
|
if let Some(index) = bin_locations.iter().position(|&r| r == bin) {
|
||||||
expected = vec![Vec::new(); bins];
|
expected = vec![Vec::new(); bins];
|
||||||
|
@ -5875,7 +5924,9 @@ pub mod tests {
|
||||||
start: 127,
|
start: 127,
|
||||||
end: 128,
|
end: 128,
|
||||||
},
|
},
|
||||||
);
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result.len(), 2); // 2 chunks
|
assert_eq!(result.len(), 2); // 2 chunks
|
||||||
assert_eq!(result[0].len(), 0); // nothing found in first slots
|
assert_eq!(result[0].len(), 0); // nothing found in first slots
|
||||||
let mut expected = vec![Vec::new(); bins];
|
let mut expected = vec![Vec::new(); bins];
|
||||||
|
@ -5889,7 +5940,8 @@ pub mod tests {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
||||||
let (storages, _size, _slot_expected) = sample_storage();
|
let (storages, _size, _slot_expected) = sample_storage();
|
||||||
let result = AccountsDb::calculate_accounts_hash_without_index(&storages, None);
|
let result =
|
||||||
|
AccountsDb::calculate_accounts_hash_without_index(&storages, None, false).unwrap();
|
||||||
let expected_hash = Hash::from_str("GKot5hBsd81kMupNCXHaqbhv3huEbxAFMLnpcX2hniwn").unwrap();
|
let expected_hash = Hash::from_str("GKot5hBsd81kMupNCXHaqbhv3huEbxAFMLnpcX2hniwn").unwrap();
|
||||||
assert_eq!(result, (expected_hash, 0));
|
assert_eq!(result, (expected_hash, 0));
|
||||||
}
|
}
|
||||||
|
@ -5904,7 +5956,8 @@ pub mod tests {
|
||||||
item.hash
|
item.hash
|
||||||
});
|
});
|
||||||
let sum = raw_expected.iter().map(|item| item.lamports).sum();
|
let sum = raw_expected.iter().map(|item| item.lamports).sum();
|
||||||
let result = AccountsDb::calculate_accounts_hash_without_index(&storages, None);
|
let result =
|
||||||
|
AccountsDb::calculate_accounts_hash_without_index(&storages, None, false).unwrap();
|
||||||
|
|
||||||
assert_eq!(result, (expected_hash, sum));
|
assert_eq!(result, (expected_hash, sum));
|
||||||
}
|
}
|
||||||
|
@ -7845,6 +7898,58 @@ pub mod tests {
|
||||||
assert_eq!(bank_hash.stats.num_executable_accounts, 1);
|
assert_eq!(bank_hash.stats.num_executable_accounts, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calculate_accounts_hash_check_hash_mismatch() {
|
||||||
|
solana_logger::setup();
|
||||||
|
let db = AccountsDb::new(Vec::new(), &ClusterType::Development);
|
||||||
|
|
||||||
|
let key = solana_sdk::pubkey::new_rand();
|
||||||
|
let some_data_len = 0;
|
||||||
|
let some_slot: Slot = 0;
|
||||||
|
let account = AccountSharedData::new(1, some_data_len, &key);
|
||||||
|
|
||||||
|
let ancestors = vec![(some_slot, 0)].into_iter().collect();
|
||||||
|
|
||||||
|
// put wrong hash value in store so we get a mismatch
|
||||||
|
db.store_accounts_unfrozen(
|
||||||
|
some_slot,
|
||||||
|
&[(&key, &account)],
|
||||||
|
Some(&[&Hash::default()]),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
db.add_root(some_slot);
|
||||||
|
let check_hash = true;
|
||||||
|
assert!(db
|
||||||
|
.calculate_accounts_hash_helper(false, some_slot, &ancestors, check_hash)
|
||||||
|
.is_err());
|
||||||
|
assert!(db
|
||||||
|
.calculate_accounts_hash_helper(true, some_slot, &ancestors, check_hash)
|
||||||
|
.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calculate_accounts_hash_check_hash() {
|
||||||
|
solana_logger::setup();
|
||||||
|
let db = AccountsDb::new(Vec::new(), &ClusterType::Development);
|
||||||
|
|
||||||
|
let key = solana_sdk::pubkey::new_rand();
|
||||||
|
let some_data_len = 0;
|
||||||
|
let some_slot: Slot = 0;
|
||||||
|
let account = AccountSharedData::new(1, some_data_len, &key);
|
||||||
|
|
||||||
|
let ancestors = vec![(some_slot, 0)].into_iter().collect();
|
||||||
|
|
||||||
|
db.store_uncached(some_slot, &[(&key, &account)]);
|
||||||
|
db.add_root(some_slot);
|
||||||
|
let check_hash = true;
|
||||||
|
assert_eq!(
|
||||||
|
db.calculate_accounts_hash_helper(false, some_slot, &ancestors, check_hash)
|
||||||
|
.unwrap(),
|
||||||
|
db.calculate_accounts_hash_helper(true, some_slot, &ancestors, check_hash)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_bank_hash() {
|
fn test_verify_bank_hash() {
|
||||||
use BankHashVerificationError::*;
|
use BankHashVerificationError::*;
|
||||||
|
|
|
@ -1002,7 +1002,9 @@ pub fn process_accounts_package_pre(
|
||||||
let (hash, lamports) = AccountsDb::calculate_accounts_hash_without_index(
|
let (hash, lamports) = AccountsDb::calculate_accounts_hash_without_index(
|
||||||
&accounts_package.storages,
|
&accounts_package.storages,
|
||||||
thread_pool,
|
thread_pool,
|
||||||
);
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(accounts_package.expected_capitalization, lamports);
|
assert_eq!(accounts_package.expected_capitalization, lamports);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue