diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 0975a6dbccde23..0a4cb785f2c091 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -2869,11 +2869,34 @@ impl Bank { stake_weighted_timestamp } + /// Recalculates the bank hash + /// + /// This is used by ledger-tool when creating a snapshot, which + /// recalcuates the bank hash. + /// + /// Note that the account state is *not* allowed to change by rehashing. + /// If it does, this function will panic. + /// If modifying accounts in ledger-tool is needed, create a new bank. pub fn rehash(&self) { + let get_delta_hash = || { + self.rc + .accounts + .accounts_db + .get_accounts_delta_hash(self.slot()) + }; + let mut hash = self.hash.write().unwrap(); + let curr_accounts_delta_hash = get_delta_hash(); let new = self.hash_internal_state(); + if let Some(curr_accounts_delta_hash) = curr_accounts_delta_hash { + let new_accounts_delta_hash = get_delta_hash().unwrap(); + assert_eq!( + new_accounts_delta_hash, curr_accounts_delta_hash, + "rehashing is not allowed to change the account state", + ); + } if new != *hash { - warn!("Updating bank hash to {}", new); + warn!("Updating bank hash to {new}"); *hash = new; } } diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 454aa32215de11..9a8324d58a0165 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -13252,3 +13252,48 @@ fn test_bank_epoch_stakes() { ); } } + +#[test] +fn test_rehash_good() { + let ten_sol = 10 * LAMPORTS_PER_SOL; + let (genesis_config, _mint) = create_genesis_config(ten_sol); + let bank = Bank::new_for_tests(&genesis_config); + + let lamports = 123_456_789; + let account = AccountSharedData::new(lamports, 0, &Pubkey::default()); + let pubkey = Pubkey::new_unique(); + bank.store_account_and_update_capitalization(&pubkey, &account); + + // freeze the bank to trigger hash calculation + bank.freeze(); + + // ensure the bank hash is the same before and after rehashing + let prev_bank_hash = bank.hash(); + bank.rehash(); + let post_bank_hash = bank.hash(); + assert_eq!(post_bank_hash, prev_bank_hash); +} + +#[test] +#[should_panic(expected = "rehashing is not allowed to change the account state")] +fn test_rehash_bad() { + let ten_sol = 10 * LAMPORTS_PER_SOL; + let (genesis_config, _mint) = create_genesis_config(ten_sol); + let bank = Bank::new_for_tests(&genesis_config); + + let mut account = AccountSharedData::new(ten_sol, 0, &Pubkey::default()); + let pubkey = Pubkey::new_unique(); + bank.store_account_and_update_capitalization(&pubkey, &account); + + // freeze the bank to trigger hash calculation + bank.freeze(); + + // change an account, which will cause rehashing to panic + account.checked_add_lamports(ten_sol).unwrap(); + bank.rc + .accounts + .store_accounts_cached((bank.slot(), [(&pubkey, &account)].as_slice())); + + // let the show begin + bank.rehash(); +}