* verify bank hash on startup with ledger tool option (#17939)
(cherry picked from commit f558b9b6bf
)
# Conflicts:
# core/tests/snapshots.rs
# ledger/src/bank_forks_utils.rs
# runtime/src/snapshot_utils.rs
* fix merge errors
Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
This commit is contained in:
@ -145,6 +145,7 @@ mod tests {
|
|||||||
|
|
||||||
let old_last_bank = old_bank_forks.get(old_last_slot).unwrap();
|
let old_last_bank = old_bank_forks.get(old_last_slot).unwrap();
|
||||||
|
|
||||||
|
let check_hash_calculation = false;
|
||||||
let (deserialized_bank, _timing) = snapshot_utils::bank_from_archive(
|
let (deserialized_bank, _timing) = snapshot_utils::bank_from_archive(
|
||||||
&account_paths,
|
&account_paths,
|
||||||
&[],
|
&[],
|
||||||
@ -165,6 +166,7 @@ mod tests {
|
|||||||
AccountSecondaryIndexes::default(),
|
AccountSecondaryIndexes::default(),
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
|
check_hash_calculation,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -142,6 +142,7 @@ fn load_from_snapshot(
|
|||||||
process_options.account_indexes.clone(),
|
process_options.account_indexes.clone(),
|
||||||
process_options.accounts_db_caching_enabled,
|
process_options.accounts_db_caching_enabled,
|
||||||
process_options.limit_load_slot_count_from_snapshot,
|
process_options.limit_load_slot_count_from_snapshot,
|
||||||
|
process_options.accounts_db_test_hash_calculation,
|
||||||
)
|
)
|
||||||
.expect("Load from snapshot failed");
|
.expect("Load from snapshot failed");
|
||||||
if let Some(shrink_paths) = shrink_paths {
|
if let Some(shrink_paths) = shrink_paths {
|
||||||
|
@ -110,7 +110,15 @@ fn test_accounts_hash_bank_hash(bencher: &mut Bencher) {
|
|||||||
create_test_accounts(&accounts, &mut pubkeys, num_accounts, slot);
|
create_test_accounts(&accounts, &mut pubkeys, num_accounts, slot);
|
||||||
let ancestors = Ancestors::from(vec![0]);
|
let ancestors = Ancestors::from(vec![0]);
|
||||||
let (_, total_lamports) = accounts.accounts_db.update_accounts_hash(0, &ancestors);
|
let (_, total_lamports) = accounts.accounts_db.update_accounts_hash(0, &ancestors);
|
||||||
bencher.iter(|| assert!(accounts.verify_bank_hash_and_lamports(0, &ancestors, total_lamports)));
|
let test_hash_calculation = false;
|
||||||
|
bencher.iter(|| {
|
||||||
|
assert!(accounts.verify_bank_hash_and_lamports(
|
||||||
|
0,
|
||||||
|
&ancestors,
|
||||||
|
total_lamports,
|
||||||
|
test_hash_calculation
|
||||||
|
))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
|
@ -645,11 +645,14 @@ impl Accounts {
|
|||||||
slot: Slot,
|
slot: Slot,
|
||||||
ancestors: &Ancestors,
|
ancestors: &Ancestors,
|
||||||
total_lamports: u64,
|
total_lamports: u64,
|
||||||
|
test_hash_calculation: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if let Err(err) =
|
if let Err(err) = self.accounts_db.verify_bank_hash_and_lamports(
|
||||||
self.accounts_db
|
slot,
|
||||||
.verify_bank_hash_and_lamports(slot, ancestors, total_lamports)
|
ancestors,
|
||||||
{
|
total_lamports,
|
||||||
|
test_hash_calculation,
|
||||||
|
) {
|
||||||
warn!("verify_bank_hash failed: {:?}", err);
|
warn!("verify_bank_hash failed: {:?}", err);
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
|
@ -4690,18 +4690,22 @@ impl AccountsDb {
|
|||||||
slot: Slot,
|
slot: Slot,
|
||||||
ancestors: &Ancestors,
|
ancestors: &Ancestors,
|
||||||
total_lamports: u64,
|
total_lamports: u64,
|
||||||
|
test_hash_calculation: bool,
|
||||||
) -> Result<(), BankHashVerificationError> {
|
) -> Result<(), BankHashVerificationError> {
|
||||||
use BankHashVerificationError::*;
|
use BankHashVerificationError::*;
|
||||||
|
|
||||||
let use_index = true;
|
let use_index = true;
|
||||||
let check_hash = true;
|
let check_hash = true;
|
||||||
let can_cached_slot_be_unflushed = false;
|
let can_cached_slot_be_unflushed = false;
|
||||||
let (calculated_hash, calculated_lamports) = self.calculate_accounts_hash_helper(
|
let (calculated_hash, calculated_lamports) = self
|
||||||
|
.calculate_accounts_hash_helper_with_verify(
|
||||||
use_index,
|
use_index,
|
||||||
|
test_hash_calculation,
|
||||||
slot,
|
slot,
|
||||||
ancestors,
|
ancestors,
|
||||||
check_hash,
|
None,
|
||||||
can_cached_slot_be_unflushed,
|
can_cached_slot_be_unflushed,
|
||||||
|
check_hash,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if calculated_lamports != total_lamports {
|
if calculated_lamports != total_lamports {
|
||||||
@ -7955,7 +7959,7 @@ pub mod tests {
|
|||||||
assert_load_account(&accounts, current_slot, dummy_pubkey, dummy_lamport);
|
assert_load_account(&accounts, current_slot, dummy_pubkey, dummy_lamport);
|
||||||
|
|
||||||
accounts
|
accounts
|
||||||
.verify_bank_hash_and_lamports(4, &Ancestors::default(), 1222)
|
.verify_bank_hash_and_lamports(4, &Ancestors::default(), 1222, true)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8430,13 +8434,13 @@ pub mod tests {
|
|||||||
db.add_root(some_slot);
|
db.add_root(some_slot);
|
||||||
db.update_accounts_hash_test(some_slot, &ancestors);
|
db.update_accounts_hash_test(some_slot, &ancestors);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1),
|
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1, true),
|
||||||
Ok(_)
|
Ok(_)
|
||||||
);
|
);
|
||||||
|
|
||||||
db.bank_hashes.write().unwrap().remove(&some_slot).unwrap();
|
db.bank_hashes.write().unwrap().remove(&some_slot).unwrap();
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1),
|
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1, true),
|
||||||
Err(MissingBankHash)
|
Err(MissingBankHash)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -8451,7 +8455,7 @@ pub mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(some_slot, bank_hash_info);
|
.insert(some_slot, bank_hash_info);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1),
|
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1, true),
|
||||||
Err(MismatchedBankHash)
|
Err(MismatchedBankHash)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -8472,7 +8476,7 @@ pub mod tests {
|
|||||||
db.add_root(some_slot);
|
db.add_root(some_slot);
|
||||||
db.update_accounts_hash_test(some_slot, &ancestors);
|
db.update_accounts_hash_test(some_slot, &ancestors);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1),
|
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1, true),
|
||||||
Ok(_)
|
Ok(_)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -8486,12 +8490,12 @@ pub mod tests {
|
|||||||
);
|
);
|
||||||
db.update_accounts_hash_test(some_slot, &ancestors);
|
db.update_accounts_hash_test(some_slot, &ancestors);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 2),
|
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 2, true),
|
||||||
Ok(_)
|
Ok(_)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 10),
|
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 10, true),
|
||||||
Err(MismatchedTotalLamports(expected, actual)) if expected == 2 && actual == 10
|
Err(MismatchedTotalLamports(expected, actual)) if expected == 2 && actual == 10
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -8511,7 +8515,7 @@ pub mod tests {
|
|||||||
db.add_root(some_slot);
|
db.add_root(some_slot);
|
||||||
db.update_accounts_hash_test(some_slot, &ancestors);
|
db.update_accounts_hash_test(some_slot, &ancestors);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 0),
|
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 0, true),
|
||||||
Ok(_)
|
Ok(_)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -8541,7 +8545,7 @@ pub mod tests {
|
|||||||
db.store_accounts_unfrozen(some_slot, accounts, Some(&[&some_hash]), false);
|
db.store_accounts_unfrozen(some_slot, accounts, Some(&[&some_hash]), false);
|
||||||
db.add_root(some_slot);
|
db.add_root(some_slot);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1),
|
db.verify_bank_hash_and_lamports(some_slot, &ancestors, 1, true),
|
||||||
Err(MismatchedAccountHash)
|
Err(MismatchedAccountHash)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -9110,12 +9114,12 @@ pub mod tests {
|
|||||||
let no_ancestors = Ancestors::default();
|
let no_ancestors = Ancestors::default();
|
||||||
accounts.update_accounts_hash(current_slot, &no_ancestors);
|
accounts.update_accounts_hash(current_slot, &no_ancestors);
|
||||||
accounts
|
accounts
|
||||||
.verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300)
|
.verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300, true)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let accounts = reconstruct_accounts_db_via_serialization(&accounts, current_slot);
|
let accounts = reconstruct_accounts_db_via_serialization(&accounts, current_slot);
|
||||||
accounts
|
accounts
|
||||||
.verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300)
|
.verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300, true)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// repeating should be no-op
|
// repeating should be no-op
|
||||||
|
@ -4530,11 +4530,12 @@ impl Bank {
|
|||||||
/// Recalculate the hash_internal_state from the account stores. Would be used to verify a
|
/// Recalculate the hash_internal_state from the account stores. Would be used to verify a
|
||||||
/// snapshot.
|
/// snapshot.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn verify_bank_hash(&self) -> bool {
|
fn verify_bank_hash(&self, test_hash_calculation: bool) -> bool {
|
||||||
self.rc.accounts.verify_bank_hash_and_lamports(
|
self.rc.accounts.verify_bank_hash_and_lamports(
|
||||||
self.slot(),
|
self.slot(),
|
||||||
&self.ancestors,
|
&self.ancestors,
|
||||||
self.capitalization(),
|
self.capitalization(),
|
||||||
|
test_hash_calculation,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4644,7 +4645,7 @@ impl Bank {
|
|||||||
|
|
||||||
/// A snapshot bank should be purged of 0 lamport accounts which are not part of the hash
|
/// A snapshot bank should be purged of 0 lamport accounts which are not part of the hash
|
||||||
/// calculation and could shield other real accounts.
|
/// calculation and could shield other real accounts.
|
||||||
pub fn verify_snapshot_bank(&self) -> bool {
|
pub fn verify_snapshot_bank(&self, test_hash_calculation: bool) -> bool {
|
||||||
info!("cleaning..");
|
info!("cleaning..");
|
||||||
let mut clean_time = Measure::start("clean");
|
let mut clean_time = Measure::start("clean");
|
||||||
if self.slot() > 0 {
|
if self.slot() > 0 {
|
||||||
@ -4661,7 +4662,7 @@ impl Bank {
|
|||||||
|
|
||||||
info!("verify_bank_hash..");
|
info!("verify_bank_hash..");
|
||||||
let mut verify_time = Measure::start("verify_bank_hash");
|
let mut verify_time = Measure::start("verify_bank_hash");
|
||||||
let mut verify = self.verify_bank_hash();
|
let mut verify = self.verify_bank_hash(test_hash_calculation);
|
||||||
verify_time.stop();
|
verify_time.stop();
|
||||||
|
|
||||||
info!("verify_hash..");
|
info!("verify_hash..");
|
||||||
@ -7483,17 +7484,17 @@ pub(crate) mod tests {
|
|||||||
assert_eq!(bank0.get_account(&keypair.pubkey()).unwrap().lamports(), 10);
|
assert_eq!(bank0.get_account(&keypair.pubkey()).unwrap().lamports(), 10);
|
||||||
assert_eq!(bank1.get_account(&keypair.pubkey()), None);
|
assert_eq!(bank1.get_account(&keypair.pubkey()), None);
|
||||||
|
|
||||||
assert!(bank0.verify_bank_hash());
|
assert!(bank0.verify_bank_hash(true));
|
||||||
|
|
||||||
// Squash and then verify hash_internal value
|
// Squash and then verify hash_internal value
|
||||||
bank0.freeze();
|
bank0.freeze();
|
||||||
bank0.squash();
|
bank0.squash();
|
||||||
assert!(bank0.verify_bank_hash());
|
assert!(bank0.verify_bank_hash(true));
|
||||||
|
|
||||||
bank1.freeze();
|
bank1.freeze();
|
||||||
bank1.squash();
|
bank1.squash();
|
||||||
bank1.update_accounts_hash();
|
bank1.update_accounts_hash();
|
||||||
assert!(bank1.verify_bank_hash());
|
assert!(bank1.verify_bank_hash(true));
|
||||||
|
|
||||||
// keypair should have 0 tokens on both forks
|
// keypair should have 0 tokens on both forks
|
||||||
assert_eq!(bank0.get_account(&keypair.pubkey()), None);
|
assert_eq!(bank0.get_account(&keypair.pubkey()), None);
|
||||||
@ -7501,7 +7502,7 @@ pub(crate) mod tests {
|
|||||||
bank1.force_flush_accounts_cache();
|
bank1.force_flush_accounts_cache();
|
||||||
bank1.clean_accounts(false, false);
|
bank1.clean_accounts(false, false);
|
||||||
|
|
||||||
assert!(bank1.verify_bank_hash());
|
assert!(bank1.verify_bank_hash(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -8307,7 +8308,7 @@ pub(crate) mod tests {
|
|||||||
info!("transfer 2 {}", pubkey2);
|
info!("transfer 2 {}", pubkey2);
|
||||||
bank2.transfer(10, &mint_keypair, &pubkey2).unwrap();
|
bank2.transfer(10, &mint_keypair, &pubkey2).unwrap();
|
||||||
bank2.update_accounts_hash();
|
bank2.update_accounts_hash();
|
||||||
assert!(bank2.verify_bank_hash());
|
assert!(bank2.verify_bank_hash(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -8331,19 +8332,19 @@ pub(crate) mod tests {
|
|||||||
// Checkpointing should never modify the checkpoint's state once frozen
|
// Checkpointing should never modify the checkpoint's state once frozen
|
||||||
let bank0_state = bank0.hash_internal_state();
|
let bank0_state = bank0.hash_internal_state();
|
||||||
bank2.update_accounts_hash();
|
bank2.update_accounts_hash();
|
||||||
assert!(bank2.verify_bank_hash());
|
assert!(bank2.verify_bank_hash(true));
|
||||||
let bank3 = Bank::new_from_parent(&bank0, &solana_sdk::pubkey::new_rand(), 2);
|
let bank3 = Bank::new_from_parent(&bank0, &solana_sdk::pubkey::new_rand(), 2);
|
||||||
assert_eq!(bank0_state, bank0.hash_internal_state());
|
assert_eq!(bank0_state, bank0.hash_internal_state());
|
||||||
assert!(bank2.verify_bank_hash());
|
assert!(bank2.verify_bank_hash(true));
|
||||||
bank3.update_accounts_hash();
|
bank3.update_accounts_hash();
|
||||||
assert!(bank3.verify_bank_hash());
|
assert!(bank3.verify_bank_hash(true));
|
||||||
|
|
||||||
let pubkey2 = solana_sdk::pubkey::new_rand();
|
let pubkey2 = solana_sdk::pubkey::new_rand();
|
||||||
info!("transfer 2 {}", pubkey2);
|
info!("transfer 2 {}", pubkey2);
|
||||||
bank2.transfer(10, &mint_keypair, &pubkey2).unwrap();
|
bank2.transfer(10, &mint_keypair, &pubkey2).unwrap();
|
||||||
bank2.update_accounts_hash();
|
bank2.update_accounts_hash();
|
||||||
assert!(bank2.verify_bank_hash());
|
assert!(bank2.verify_bank_hash(true));
|
||||||
assert!(bank3.verify_bank_hash());
|
assert!(bank3.verify_bank_hash(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -8363,11 +8364,11 @@ pub(crate) mod tests {
|
|||||||
bank.transfer(1_000, &mint_keypair, &pubkey).unwrap();
|
bank.transfer(1_000, &mint_keypair, &pubkey).unwrap();
|
||||||
bank.freeze();
|
bank.freeze();
|
||||||
bank.update_accounts_hash();
|
bank.update_accounts_hash();
|
||||||
assert!(bank.verify_snapshot_bank());
|
assert!(bank.verify_snapshot_bank(true));
|
||||||
|
|
||||||
// tamper the bank after freeze!
|
// tamper the bank after freeze!
|
||||||
bank.increment_signature_count(1);
|
bank.increment_signature_count(1);
|
||||||
assert!(!bank.verify_snapshot_bank());
|
assert!(!bank.verify_snapshot_bank(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that two bank forks with the same accounts should not hash to the same value.
|
// Test that two bank forks with the same accounts should not hash to the same value.
|
||||||
|
@ -612,6 +612,7 @@ pub fn bank_from_archive<P: AsRef<Path>>(
|
|||||||
account_indexes: AccountSecondaryIndexes,
|
account_indexes: AccountSecondaryIndexes,
|
||||||
accounts_db_caching_enabled: bool,
|
accounts_db_caching_enabled: bool,
|
||||||
limit_load_slot_count_from_snapshot: Option<usize>,
|
limit_load_slot_count_from_snapshot: Option<usize>,
|
||||||
|
test_hash_calculation: bool,
|
||||||
) -> Result<(Bank, BankFromArchiveTimings)> {
|
) -> Result<(Bank, BankFromArchiveTimings)> {
|
||||||
let unpack_dir = tempfile::Builder::new()
|
let unpack_dir = tempfile::Builder::new()
|
||||||
.prefix(TMP_SNAPSHOT_PREFIX)
|
.prefix(TMP_SNAPSHOT_PREFIX)
|
||||||
@ -649,7 +650,7 @@ pub fn bank_from_archive<P: AsRef<Path>>(
|
|||||||
measure.stop();
|
measure.stop();
|
||||||
|
|
||||||
let mut verify = Measure::start("verify");
|
let mut verify = Measure::start("verify");
|
||||||
if !bank.verify_snapshot_bank() {
|
if !bank.verify_snapshot_bank(test_hash_calculation) {
|
||||||
panic!("Snapshot bank for slot {} failed to verify", bank.slot());
|
panic!("Snapshot bank for slot {} failed to verify", bank.slot());
|
||||||
}
|
}
|
||||||
verify.stop();
|
verify.stop();
|
||||||
|
Reference in New Issue
Block a user