1use {
2 super::Bank,
3 rayon::prelude::*,
4 solana_account::{accounts_equal, AccountSharedData},
5 solana_accounts_db::accounts_db::AccountsDb,
6 solana_hash::Hash,
7 solana_lattice_hash::lt_hash::LtHash,
8 solana_measure::{meas_dur, measure::Measure},
9 solana_pubkey::Pubkey,
10 solana_svm_callback::AccountState,
11 std::{
12 ops::AddAssign,
13 sync::atomic::{AtomicU64, Ordering},
14 time::Duration,
15 },
16};
17
18impl Bank {
19 pub fn update_accounts_lt_hash(&self) {
28 let delta_lt_hash = self.calculate_delta_lt_hash();
29 let mut accounts_lt_hash = self.accounts_lt_hash.lock().unwrap();
30 accounts_lt_hash.0.mix_in(&delta_lt_hash);
31 }
32
33 fn calculate_delta_lt_hash(&self) -> LtHash {
43 let measure_total = Measure::start("");
44 let slot = self.slot();
45
46 let strictly_ancestors = {
50 let mut ancestors = self.ancestors.clone();
51 ancestors.remove(&self.slot());
52 ancestors
53 };
54
55 if slot == 0 {
56 assert!(strictly_ancestors.is_empty());
68 self.cache_for_accounts_lt_hash.clear();
69 }
70
71 let (accounts_curr, time_loading_accounts_curr) = meas_dur!({
75 self.rc
76 .accounts
77 .accounts_db
78 .get_pubkey_account_for_slot(slot)
79 });
80 let num_accounts_total = accounts_curr.len();
81
82 #[derive(Debug, Default)]
83 struct Stats {
84 num_cache_misses: usize,
85 num_accounts_unmodified: usize,
86 time_loading_accounts_prev: Duration,
87 time_comparing_accounts: Duration,
88 time_computing_hashes: Duration,
89 time_mixing_hashes: Duration,
90 }
91 impl AddAssign for Stats {
92 fn add_assign(&mut self, other: Self) {
93 self.num_cache_misses += other.num_cache_misses;
94 self.num_accounts_unmodified += other.num_accounts_unmodified;
95 self.time_loading_accounts_prev += other.time_loading_accounts_prev;
96 self.time_comparing_accounts += other.time_comparing_accounts;
97 self.time_computing_hashes += other.time_computing_hashes;
98 self.time_mixing_hashes += other.time_mixing_hashes;
99 }
100 }
101
102 let do_calculate_delta_lt_hash = || {
103 const CHUNK_SIZE: usize = 128;
108 accounts_curr
109 .par_iter()
110 .fold_chunks(
111 CHUNK_SIZE,
112 || (LtHash::identity(), Stats::default()),
113 |mut accum, (pubkey, curr_account)| {
114 let (initial_state_of_account, measure_load) = meas_dur!({
116 let cache_value = self
117 .cache_for_accounts_lt_hash
118 .get(pubkey)
119 .map(|entry| entry.value().clone());
120 match cache_value {
121 Some(CacheValue::InspectAccount(initial_state_of_account)) => {
122 initial_state_of_account
123 }
124 Some(CacheValue::BankNew) | None => {
125 accum.1.num_cache_misses += 1;
126 let account_slot = self
133 .rc
134 .accounts
135 .load_with_fixed_root_do_not_populate_read_cache(
136 &strictly_ancestors,
137 pubkey,
138 );
139 match account_slot {
140 Some((account, _slot)) => {
141 InitialStateOfAccount::Alive(account)
142 }
143 None => InitialStateOfAccount::Dead,
144 }
145 }
146 }
147 });
148 accum.1.time_loading_accounts_prev += measure_load;
149
150 match initial_state_of_account {
152 InitialStateOfAccount::Dead => {
153 }
155 InitialStateOfAccount::Alive(prev_account) => {
156 let (are_accounts_equal, measure_is_equal) =
157 meas_dur!(accounts_equal(curr_account, &prev_account));
158 accum.1.time_comparing_accounts += measure_is_equal;
159 if are_accounts_equal {
160 accum.1.num_accounts_unmodified += 1;
162 return accum;
163 }
164 let (prev_lt_hash, measure_hashing) =
165 meas_dur!(AccountsDb::lt_hash_account(&prev_account, pubkey));
166 let (_, measure_mixing) =
167 meas_dur!(accum.0.mix_out(&prev_lt_hash.0));
168 accum.1.time_computing_hashes += measure_hashing;
169 accum.1.time_mixing_hashes += measure_mixing;
170 }
171 }
172
173 let (curr_lt_hash, measure_hashing) =
175 meas_dur!(AccountsDb::lt_hash_account(curr_account, pubkey));
176 let (_, measure_mixing) = meas_dur!(accum.0.mix_in(&curr_lt_hash.0));
177 accum.1.time_computing_hashes += measure_hashing;
178 accum.1.time_mixing_hashes += measure_mixing;
179
180 accum
181 },
182 )
183 .reduce(
184 || (LtHash::identity(), Stats::default()),
185 |mut accum, elem| {
186 accum.0.mix_in(&elem.0);
187 accum.1 += elem.1;
188 accum
189 },
190 )
191 };
192 let (delta_lt_hash, stats) = self
193 .rc
194 .accounts
195 .accounts_db
196 .thread_pool_foreground
197 .install(do_calculate_delta_lt_hash);
198
199 let total_time = measure_total.end_as_duration();
200 let num_accounts_modified =
201 num_accounts_total.saturating_sub(stats.num_accounts_unmodified);
202 datapoint_info!(
203 "bank-accounts_lt_hash",
204 ("slot", slot, i64),
205 ("num_accounts_total", num_accounts_total, i64),
206 ("num_accounts_modified", num_accounts_modified, i64),
207 (
208 "num_accounts_unmodified",
209 stats.num_accounts_unmodified,
210 i64
211 ),
212 ("num_cache_misses", stats.num_cache_misses, i64),
213 ("total_us", total_time.as_micros(), i64),
214 (
215 "loading_accounts_curr_us",
216 time_loading_accounts_curr.as_micros(),
217 i64
218 ),
219 (
220 "par_loading_accounts_prev_us",
221 stats.time_loading_accounts_prev.as_micros(),
222 i64
223 ),
224 (
225 "par_comparing_accounts_us",
226 stats.time_comparing_accounts.as_micros(),
227 i64
228 ),
229 (
230 "par_computing_hashes_us",
231 stats.time_computing_hashes.as_micros(),
232 i64
233 ),
234 (
235 "par_mixing_hashes_us",
236 stats.time_mixing_hashes.as_micros(),
237 i64
238 ),
239 (
240 "num_inspect_account_hits",
241 self.stats_for_accounts_lt_hash
242 .num_inspect_account_hits
243 .load(Ordering::Relaxed),
244 i64
245 ),
246 (
247 "num_inspect_account_misses",
248 self.stats_for_accounts_lt_hash
249 .num_inspect_account_misses
250 .load(Ordering::Relaxed),
251 i64
252 ),
253 (
254 "num_inspect_account_after_frozen",
255 self.stats_for_accounts_lt_hash
256 .num_inspect_account_after_frozen
257 .load(Ordering::Relaxed),
258 i64
259 ),
260 (
261 "inspect_account_lookup_ns",
262 self.stats_for_accounts_lt_hash
263 .inspect_account_lookup_time_ns
264 .load(Ordering::Relaxed),
265 i64
266 ),
267 (
268 "inspect_account_insert_ns",
269 self.stats_for_accounts_lt_hash
270 .inspect_account_insert_time_ns
271 .load(Ordering::Relaxed),
272 i64
273 ),
274 );
275
276 delta_lt_hash
277 }
278
279 pub fn inspect_account_for_accounts_lt_hash(
285 &self,
286 address: &Pubkey,
287 account_state: &AccountState,
288 is_writable: bool,
289 ) {
290 if !is_writable {
291 return;
293 }
294
295 let (is_in_cache, lookup_time) =
298 meas_dur!(self.cache_for_accounts_lt_hash.contains_key(address));
299 if !is_in_cache {
300 let freeze_guard = self.freeze_lock();
303 let is_frozen = *freeze_guard != Hash::default();
304 if is_frozen {
305 self.stats_for_accounts_lt_hash
313 .num_inspect_account_after_frozen
314 .fetch_add(1, Ordering::Relaxed);
315 return;
316 }
317 let (_, insert_time) = meas_dur!({
318 self.cache_for_accounts_lt_hash
319 .entry(*address)
320 .or_insert_with(|| {
321 let initial_state_of_account = match account_state {
322 AccountState::Dead => InitialStateOfAccount::Dead,
323 AccountState::Alive(account) => {
324 InitialStateOfAccount::Alive((*account).clone())
325 }
326 };
327 CacheValue::InspectAccount(initial_state_of_account)
328 });
329 });
330 drop(freeze_guard);
331
332 self.stats_for_accounts_lt_hash
333 .num_inspect_account_misses
334 .fetch_add(1, Ordering::Relaxed);
335 self.stats_for_accounts_lt_hash
336 .inspect_account_insert_time_ns
337 .fetch_add(insert_time.as_nanos() as u64, Ordering::Relaxed);
339 } else {
340 self.stats_for_accounts_lt_hash
342 .num_inspect_account_hits
343 .fetch_add(1, Ordering::Relaxed);
344 }
345
346 self.stats_for_accounts_lt_hash
347 .inspect_account_lookup_time_ns
348 .fetch_add(lookup_time.as_nanos() as u64, Ordering::Relaxed);
350 }
351}
352
353#[derive(Debug, Default)]
355pub struct Stats {
356 num_inspect_account_hits: AtomicU64,
358 num_inspect_account_misses: AtomicU64,
360 num_inspect_account_after_frozen: AtomicU64,
362 inspect_account_lookup_time_ns: AtomicU64,
364 inspect_account_insert_time_ns: AtomicU64,
366}
367
368#[derive(Debug, Clone, PartialEq)]
370pub enum InitialStateOfAccount {
371 Dead,
373 Alive(AccountSharedData),
375}
376
377#[derive(Debug, Clone, PartialEq)]
379pub enum CacheValue {
380 InspectAccount(InitialStateOfAccount),
383 BankNew,
386}
387
388#[cfg(test)]
389mod tests {
390 use {
391 super::*,
392 crate::{
393 bank::tests::new_bank_from_parent_with_bank_forks, runtime_config::RuntimeConfig,
394 snapshot_bank_utils, snapshot_config::SnapshotConfig, snapshot_utils,
395 },
396 solana_account::{ReadableAccount as _, WritableAccount as _},
397 solana_accounts_db::{
398 accounts_db::{AccountsDbConfig, DuplicatesLtHash, ACCOUNTS_DB_CONFIG_FOR_TESTING},
399 accounts_index::{
400 AccountsIndexConfig, IndexLimitMb, ACCOUNTS_INDEX_CONFIG_FOR_TESTING,
401 },
402 },
403 solana_fee_calculator::FeeRateGovernor,
404 solana_genesis_config::{self, GenesisConfig},
405 solana_keypair::Keypair,
406 solana_native_token::LAMPORTS_PER_SOL,
407 solana_pubkey::{self as pubkey, Pubkey},
408 solana_signer::Signer as _,
409 std::{
410 cmp, collections::HashMap, iter, num::NonZeroUsize, ops::RangeFull, str::FromStr as _,
411 sync::Arc,
412 },
413 tempfile::TempDir,
414 test_case::{test_case, test_matrix},
415 };
416
417 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
419 enum Features {
420 None,
422 All,
424 }
425
426 fn genesis_config_with(features: Features) -> (GenesisConfig, Keypair) {
428 let mint_lamports = 123_456_789 * LAMPORTS_PER_SOL;
429 match features {
430 Features::None => solana_genesis_config::create_genesis_config(mint_lamports),
431 Features::All => {
432 let info = crate::genesis_utils::create_genesis_config(mint_lamports);
433 (info.genesis_config, info.mint_keypair)
434 }
435 }
436 }
437
438 #[test]
439 fn test_update_accounts_lt_hash() {
440 let keypair1 = Keypair::new();
451 let keypair2 = Keypair::new();
452 let keypair3 = Keypair::new();
453 let keypair4 = Keypair::new();
454 let keypair5 = Keypair::new();
455
456 let (mut genesis_config, mint_keypair) =
457 solana_genesis_config::create_genesis_config(123_456_789 * LAMPORTS_PER_SOL);
458 genesis_config.fee_rate_governor = FeeRateGovernor::new(0, 0);
459 let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
460
461 let amount = cmp::max(
462 bank.get_minimum_balance_for_rent_exemption(0),
463 LAMPORTS_PER_SOL,
464 );
465
466 bank.register_unique_recent_blockhash_for_test();
469 bank.transfer(amount, &mint_keypair, &keypair1.pubkey())
470 .unwrap();
471 bank.transfer(amount, &mint_keypair, &keypair2.pubkey())
472 .unwrap();
473 bank.transfer(amount, &mint_keypair, &keypair5.pubkey())
474 .unwrap();
475
476 bank.freeze();
478 let prev_accounts_lt_hash = bank.accounts_lt_hash.lock().unwrap().clone();
479
480 let prev_mint = bank.get_account_with_fixed_root(&mint_keypair.pubkey());
482 let prev_account1 = bank.get_account_with_fixed_root(&keypair1.pubkey());
483 let prev_account2 = bank.get_account_with_fixed_root(&keypair2.pubkey());
484 let prev_account3 = bank.get_account_with_fixed_root(&keypair3.pubkey());
485 let prev_account4 = bank.get_account_with_fixed_root(&keypair4.pubkey());
486 let prev_account5 = bank.get_account_with_fixed_root(&keypair5.pubkey());
487
488 assert!(prev_mint.is_some());
489 assert!(prev_account1.is_some());
490 assert!(prev_account2.is_some());
491 assert!(prev_account3.is_none());
492 assert!(prev_account4.is_none());
493 assert!(prev_account5.is_some());
494
495 let sysvars = [
499 Pubkey::from_str("SysvarS1otHashes111111111111111111111111111").unwrap(),
500 Pubkey::from_str("SysvarC1ock11111111111111111111111111111111").unwrap(),
501 Pubkey::from_str("SysvarRecentB1ockHashes11111111111111111111").unwrap(),
502 Pubkey::from_str("SysvarS1otHistory11111111111111111111111111").unwrap(),
503 ];
504 let prev_sysvar_accounts: Vec<_> = sysvars
505 .iter()
506 .map(|address| bank.get_account_with_fixed_root(address))
507 .collect();
508
509 let bank = {
510 let slot = bank.slot() + 1;
511 new_bank_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot)
512 };
513
514 bank.register_unique_recent_blockhash_for_test();
516 bank.transfer(amount, &keypair2, &keypair1.pubkey())
517 .unwrap();
518
519 bank.register_unique_recent_blockhash_for_test();
522 bank.transfer(amount, &mint_keypair, &keypair4.pubkey())
523 .unwrap();
524 bank.register_unique_recent_blockhash_for_test();
525 bank.transfer(amount, &keypair4, &keypair3.pubkey())
526 .unwrap();
527
528 bank.store_account(&keypair5.pubkey(), prev_account5.as_ref().unwrap());
530
531 bank.freeze();
533
534 let actual_delta_lt_hash = bank.calculate_delta_lt_hash();
535 let post_accounts_lt_hash = bank.accounts_lt_hash.lock().unwrap().clone();
536 let post_mint = bank.get_account_with_fixed_root(&mint_keypair.pubkey());
537 let post_account1 = bank.get_account_with_fixed_root(&keypair1.pubkey());
538 let post_account2 = bank.get_account_with_fixed_root(&keypair2.pubkey());
539 let post_account3 = bank.get_account_with_fixed_root(&keypair3.pubkey());
540 let post_account4 = bank.get_account_with_fixed_root(&keypair4.pubkey());
541 let post_account5 = bank.get_account_with_fixed_root(&keypair5.pubkey());
542
543 assert!(post_mint.is_some());
544 assert!(post_account1.is_some());
545 assert!(post_account2.is_none());
546 assert!(post_account3.is_some());
547 assert!(post_account4.is_none());
548 assert!(post_account5.is_some());
549
550 let post_sysvar_accounts: Vec<_> = sysvars
551 .iter()
552 .map(|address| bank.get_account_with_fixed_root(address))
553 .collect();
554
555 let mut expected_delta_lt_hash = LtHash::identity();
556 let mut expected_accounts_lt_hash = prev_accounts_lt_hash.clone();
557 let mut updater =
558 |address: &Pubkey, prev: Option<AccountSharedData>, post: Option<AccountSharedData>| {
559 if let Some(prev) = prev {
561 let prev_lt_hash = AccountsDb::lt_hash_account(&prev, address);
562 expected_delta_lt_hash.mix_out(&prev_lt_hash.0);
563 expected_accounts_lt_hash.0.mix_out(&prev_lt_hash.0);
564 }
565
566 let post = post.unwrap_or_default();
568 let post_lt_hash = AccountsDb::lt_hash_account(&post, address);
569 expected_delta_lt_hash.mix_in(&post_lt_hash.0);
570 expected_accounts_lt_hash.0.mix_in(&post_lt_hash.0);
571 };
572 updater(&mint_keypair.pubkey(), prev_mint, post_mint);
573 updater(&keypair1.pubkey(), prev_account1, post_account1);
574 updater(&keypair2.pubkey(), prev_account2, post_account2);
575 updater(&keypair3.pubkey(), prev_account3, post_account3);
576 updater(&keypair4.pubkey(), prev_account4, post_account4);
577 updater(&keypair5.pubkey(), prev_account5, post_account5);
578 for (i, sysvar) in sysvars.iter().enumerate() {
579 updater(
580 sysvar,
581 prev_sysvar_accounts[i].clone(),
582 post_sysvar_accounts[i].clone(),
583 );
584 }
585
586 let expected = expected_delta_lt_hash.checksum();
588 let actual = actual_delta_lt_hash.checksum();
589 assert_eq!(
590 expected, actual,
591 "delta_lt_hash, expected: {expected}, actual: {actual}",
592 );
593
594 let expected = expected_accounts_lt_hash.0.checksum();
596 let actual = post_accounts_lt_hash.0.checksum();
597 assert_eq!(
598 expected, actual,
599 "accounts_lt_hash, expected: {expected}, actual: {actual}",
600 );
601 }
602
603 #[test_case(Features::None; "no features")]
609 #[test_case(Features::All; "all features")]
610 fn test_slot0_accounts_lt_hash(features: Features) {
611 let (genesis_config, mint_keypair) = genesis_config_with(features);
612 let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
613
614 assert_eq!(bank.slot(), 0);
616
617 bank.transfer(LAMPORTS_PER_SOL, &mint_keypair, &Pubkey::new_unique())
619 .unwrap();
620
621 bank.freeze();
623 let actual_accounts_lt_hash = bank.accounts_lt_hash.lock().unwrap().clone();
624
625 let calculated_accounts_lt_hash = bank
627 .rc
628 .accounts
629 .accounts_db
630 .calculate_accounts_lt_hash_at_startup_from_index(&bank.ancestors, bank.slot());
631 assert_eq!(actual_accounts_lt_hash, calculated_accounts_lt_hash);
632 }
633
634 #[test_case(Features::None; "no features")]
635 #[test_case(Features::All; "all features")]
636 fn test_inspect_account_for_accounts_lt_hash(features: Features) {
637 let (genesis_config, _mint_keypair) = genesis_config_with(features);
638 let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
639
640 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 0);
642
643 bank.inspect_account_for_accounts_lt_hash(
645 &Pubkey::new_unique(),
646 &AccountState::Dead,
647 false,
648 );
649 bank.inspect_account_for_accounts_lt_hash(
650 &Pubkey::new_unique(),
651 &AccountState::Alive(&AccountSharedData::default()),
652 false,
653 );
654 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 0);
655
656 let address = Pubkey::new_unique();
658 bank.inspect_account_for_accounts_lt_hash(&address, &AccountState::Dead, true);
659 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 1);
660 assert!(bank.cache_for_accounts_lt_hash.contains_key(&address));
661
662 let address = Pubkey::new_unique();
664 let initial_lamports = 123;
665 let mut account = AccountSharedData::new(initial_lamports, 0, &Pubkey::default());
666 bank.inspect_account_for_accounts_lt_hash(&address, &AccountState::Alive(&account), true);
667 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 2);
668 if let CacheValue::InspectAccount(InitialStateOfAccount::Alive(cached_account)) = bank
669 .cache_for_accounts_lt_hash
670 .get(&address)
671 .unwrap()
672 .value()
673 {
674 assert_eq!(*cached_account, account);
675 } else {
676 panic!("wrong initial state for account");
677 };
678
679 let updated_lamports = account.lamports() + 1;
681 account.set_lamports(updated_lamports);
682 bank.inspect_account_for_accounts_lt_hash(&address, &AccountState::Alive(&account), true);
683 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 2);
684 if let CacheValue::InspectAccount(InitialStateOfAccount::Alive(cached_account)) = bank
685 .cache_for_accounts_lt_hash
686 .get(&address)
687 .unwrap()
688 .value()
689 {
690 assert_eq!(cached_account.lamports(), initial_lamports);
691 } else {
692 panic!("wrong initial state for account");
693 };
694
695 {
697 let address = Pubkey::new_unique();
698 bank.inspect_account_for_accounts_lt_hash(&address, &AccountState::Dead, true);
699 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 3);
700 match bank
701 .cache_for_accounts_lt_hash
702 .get(&address)
703 .unwrap()
704 .value()
705 {
706 CacheValue::InspectAccount(InitialStateOfAccount::Dead) => {
707 }
709 _ => panic!("wrong initial state for account"),
710 };
711
712 bank.inspect_account_for_accounts_lt_hash(
713 &address,
714 &AccountState::Alive(&AccountSharedData::default()),
715 true,
716 );
717 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 3);
718 match bank
719 .cache_for_accounts_lt_hash
720 .get(&address)
721 .unwrap()
722 .value()
723 {
724 CacheValue::InspectAccount(InitialStateOfAccount::Dead) => {
725 }
727 _ => panic!("wrong initial state for account"),
728 };
729 }
730
731 bank.freeze();
734 let address = Pubkey::new_unique();
735 let num_cache_entries_prev = bank.cache_for_accounts_lt_hash.len();
736 bank.inspect_account_for_accounts_lt_hash(&address, &AccountState::Dead, true);
737 let num_cache_entries_curr = bank.cache_for_accounts_lt_hash.len();
738 assert_eq!(num_cache_entries_curr, num_cache_entries_prev);
739 assert!(!bank.cache_for_accounts_lt_hash.contains_key(&address));
740 }
741
742 #[test_case(Features::None; "no features")]
743 #[test_case(Features::All; "all features")]
744 fn test_calculate_accounts_lt_hash_at_startup_from_index(features: Features) {
745 let (genesis_config, mint_keypair) = genesis_config_with(features);
746 let (mut bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
747
748 let amount = cmp::max(
749 bank.get_minimum_balance_for_rent_exemption(0),
750 LAMPORTS_PER_SOL,
751 );
752
753 for _ in 0..7 {
756 let slot = bank.slot() + 1;
757 bank =
758 new_bank_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot);
759 for _ in 0..13 {
760 bank.register_unique_recent_blockhash_for_test();
761 bank.transfer(amount, &mint_keypair, &pubkey::new_rand())
764 .unwrap();
765 }
766 bank.freeze();
767 }
768 let expected_accounts_lt_hash = bank.accounts_lt_hash.lock().unwrap().clone();
769
770 bank.squash();
773 bank.force_flush_accounts_cache();
774
775 let calculated_accounts_lt_hash = bank
777 .rc
778 .accounts
779 .accounts_db
780 .calculate_accounts_lt_hash_at_startup_from_index(&bank.ancestors, bank.slot());
781 assert_eq!(expected_accounts_lt_hash, calculated_accounts_lt_hash);
782 }
783
784 #[test_case(Features::None; "no features")]
785 #[test_case(Features::All; "all features")]
786 fn test_calculate_accounts_lt_hash_at_startup_from_storages(features: Features) {
787 let (genesis_config, mint_keypair) = genesis_config_with(features);
788 let (mut bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
789
790 let amount = cmp::max(
791 bank.get_minimum_balance_for_rent_exemption(0),
792 LAMPORTS_PER_SOL,
793 );
794
795 let duplicate_pubkey = pubkey::new_rand();
797
798 for _ in 0..7 {
801 let slot = bank.slot() + 1;
802 bank =
803 new_bank_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot);
804 for _ in 0..9 {
805 bank.register_unique_recent_blockhash_for_test();
806 bank.transfer(amount, &mint_keypair, &pubkey::new_rand())
810 .unwrap();
811
812 bank.register_unique_recent_blockhash_for_test();
813 bank.transfer(amount, &mint_keypair, &duplicate_pubkey)
814 .unwrap();
815 }
816
817 bank.squash();
819 bank.force_flush_accounts_cache();
820 }
821 let expected_accounts_lt_hash = bank.accounts_lt_hash.lock().unwrap().clone();
822
823 let (mut storages, _slots) = bank.rc.accounts.accounts_db.get_storages(RangeFull);
825 storages.sort_unstable_by_key(|storage| cmp::Reverse(storage.slot()));
828 let storages = storages.into_boxed_slice();
829
830 let mut stored_accounts_map = HashMap::<_, Vec<_>>::new();
832 AccountsDb::scan_accounts_from_storages(&storages, |_offset, account| {
833 let pubkey = account.pubkey();
834 let account_lt_hash = AccountsDb::lt_hash_account(&account, pubkey);
835 stored_accounts_map
836 .entry(*pubkey)
837 .or_default()
838 .push(account_lt_hash)
839 });
840
841 let duplicates_lt_hash = stored_accounts_map
844 .values()
845 .map(|lt_hashes| {
846 <_hashes[1..]
848 })
849 .fold(LtHash::identity(), |mut accum, duplicate_lt_hashes| {
850 for duplicate_lt_hash in duplicate_lt_hashes {
851 accum.mix_in(&duplicate_lt_hash.0);
852 }
853 accum
854 });
855 let duplicates_lt_hash = DuplicatesLtHash(duplicates_lt_hash);
856
857 let calculated_accounts_lt_hash_from_storages = bank
859 .rc
860 .accounts
861 .accounts_db
862 .calculate_accounts_lt_hash_at_startup_from_storages(
863 &storages,
864 &duplicates_lt_hash,
865 bank.slot(),
866 NonZeroUsize::new(2).unwrap(),
867 );
868 assert_eq!(
869 expected_accounts_lt_hash,
870 calculated_accounts_lt_hash_from_storages
871 );
872 }
873
874 #[test_matrix(
875 [Features::None, Features::All],
876 [IndexLimitMb::Minimal, IndexLimitMb::InMemOnly]
877 )]
878 fn test_verify_accounts_lt_hash_at_startup(
879 features: Features,
880 accounts_index_limit: IndexLimitMb,
881 ) {
882 let (mut genesis_config, mint_keypair) = genesis_config_with(features);
883 genesis_config.fee_rate_governor = FeeRateGovernor::new(0, 0);
885 let (mut bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
886
887 let amount = cmp::max(
888 bank.get_minimum_balance_for_rent_exemption(0),
889 LAMPORTS_PER_SOL,
890 );
891
892 let duplicate_pubkey = pubkey::new_rand();
894
895 for _ in 0..9 {
898 let slot = bank.slot() + 1;
899 bank =
900 new_bank_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot);
901 for _ in 0..3 {
902 bank.register_unique_recent_blockhash_for_test();
903 bank.transfer(amount, &mint_keypair, &pubkey::new_rand())
904 .unwrap();
905 bank.register_unique_recent_blockhash_for_test();
906 bank.transfer(amount, &mint_keypair, &duplicate_pubkey)
907 .unwrap();
908 }
909
910 bank.fill_bank_with_ticks_for_tests();
912 bank.squash();
913 bank.force_flush_accounts_cache();
914 }
915
916 let num_accounts = 2;
922 let accounts: Vec<_> = iter::repeat_with(Keypair::new).take(num_accounts).collect();
923 for i in 0..num_accounts {
924 let slot = bank.slot() + 1;
925 bank =
926 new_bank_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot);
927 bank.register_unique_recent_blockhash_for_test();
928
929 for account in &accounts {
931 bank.transfer(amount, &mint_keypair, &account.pubkey())
932 .unwrap();
933 assert_ne!(bank.get_balance(&account.pubkey()), 0);
934 }
935
936 bank.transfer(
938 bank.get_balance(&accounts[i].pubkey()),
939 &accounts[i],
940 &pubkey::new_rand(),
941 )
942 .unwrap();
943 assert_eq!(bank.get_balance(&accounts[i].pubkey()), 0);
944
945 bank.fill_bank_with_ticks_for_tests();
947 bank.squash();
948 bank.force_flush_accounts_cache();
949 }
950
951 let snapshot_config = SnapshotConfig::default();
953 let bank_snapshots_dir = TempDir::new().unwrap();
954 let snapshot_archives_dir = TempDir::new().unwrap();
955 let snapshot = snapshot_bank_utils::bank_to_full_snapshot_archive(
956 &bank_snapshots_dir,
957 &bank,
958 Some(snapshot_config.snapshot_version),
959 &snapshot_archives_dir,
960 &snapshot_archives_dir,
961 snapshot_config.archive_format,
962 )
963 .unwrap();
964 let (_accounts_tempdir, accounts_dir) = snapshot_utils::create_tmp_accounts_dir_for_tests();
965 let accounts_index_config = AccountsIndexConfig {
966 index_limit_mb: accounts_index_limit,
967 ..ACCOUNTS_INDEX_CONFIG_FOR_TESTING
968 };
969 let accounts_db_config = AccountsDbConfig {
970 index: Some(accounts_index_config),
971 ..ACCOUNTS_DB_CONFIG_FOR_TESTING
972 };
973 let (roundtrip_bank, _) = snapshot_bank_utils::bank_from_snapshot_archives(
974 &[accounts_dir],
975 &bank_snapshots_dir,
976 &snapshot,
977 None,
978 &genesis_config,
979 &RuntimeConfig::default(),
980 None,
981 None,
982 None,
983 false,
984 false,
985 false,
986 Some(accounts_db_config),
987 None,
988 Arc::default(),
989 )
990 .unwrap();
991
992 assert!(roundtrip_bank.is_frozen());
996
997 roundtrip_bank.wait_for_initial_accounts_hash_verification_completed_for_tests();
999 assert_eq!(roundtrip_bank, *bank);
1000 }
1001
1002 #[test_case(Features::None; "no features")]
1004 #[test_case(Features::All; "all features")]
1005 fn test_accounts_lt_hash_cache_values_from_bank_new(features: Features) {
1006 let (genesis_config, _mint_keypair) = genesis_config_with(features);
1007 let (mut bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
1008
1009 let slot = bank.slot() + 1;
1010 bank = new_bank_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot);
1011
1012 let expected_cache = &[
1016 (
1017 Pubkey::from_str_const("SysvarC1ock11111111111111111111111111111111"),
1018 CacheValue::BankNew,
1019 ),
1020 (
1021 Pubkey::from_str_const("SysvarS1otHashes111111111111111111111111111"),
1022 CacheValue::BankNew,
1023 ),
1024 ];
1025 let mut actual_cache: Vec<_> = bank
1026 .cache_for_accounts_lt_hash
1027 .iter()
1028 .map(|entry| (*entry.key(), entry.value().clone()))
1029 .collect();
1030 actual_cache.sort_unstable_by(|a, b| a.0.cmp(&b.0));
1031 assert_eq!(expected_cache, actual_cache.as_slice());
1032 }
1033
1034 #[test_case(Features::None; "no features")]
1036 #[test_case(Features::All; "all features")]
1037 fn test_snapshots(features: Features) {
1038 let (genesis_config, mint_keypair) = genesis_config_with(features);
1039 let (mut bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
1040
1041 let amount = cmp::max(
1042 bank.get_minimum_balance_for_rent_exemption(0),
1043 LAMPORTS_PER_SOL,
1044 );
1045
1046 for _ in 0..3 {
1049 let slot = bank.slot() + 1;
1050 bank =
1051 new_bank_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot);
1052 bank.register_unique_recent_blockhash_for_test();
1053 bank.transfer(amount, &mint_keypair, &pubkey::new_rand())
1054 .unwrap();
1055 bank.fill_bank_with_ticks_for_tests();
1056 bank.squash();
1057 bank.force_flush_accounts_cache();
1058 }
1059
1060 let snapshot_config = SnapshotConfig::default();
1061 let bank_snapshots_dir = TempDir::new().unwrap();
1062 let snapshot_archives_dir = TempDir::new().unwrap();
1063 let snapshot = snapshot_bank_utils::bank_to_full_snapshot_archive(
1064 &bank_snapshots_dir,
1065 &bank,
1066 Some(snapshot_config.snapshot_version),
1067 &snapshot_archives_dir,
1068 &snapshot_archives_dir,
1069 snapshot_config.archive_format,
1070 )
1071 .unwrap();
1072 let (_accounts_tempdir, accounts_dir) = snapshot_utils::create_tmp_accounts_dir_for_tests();
1073 let (roundtrip_bank, _) = snapshot_bank_utils::bank_from_snapshot_archives(
1074 &[accounts_dir],
1075 &bank_snapshots_dir,
1076 &snapshot,
1077 None,
1078 &genesis_config,
1079 &RuntimeConfig::default(),
1080 None,
1081 None,
1082 None,
1083 false,
1084 false,
1085 false,
1086 Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
1087 None,
1088 Arc::default(),
1089 )
1090 .unwrap();
1091
1092 roundtrip_bank.wait_for_initial_accounts_hash_verification_completed_for_tests();
1094 assert_eq!(roundtrip_bank, *bank);
1095 }
1096
1097 #[test]
1101 fn test_accounts_lt_hash_with_obsolete_accounts() {
1102 let key1 = Pubkey::new_unique();
1103 let key2 = Pubkey::new_unique();
1104 let key3 = Pubkey::new_unique();
1105
1106 let (genesis_config, mint_keypair) =
1108 solana_genesis_config::create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1109 let (bank, _forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
1110 bank.transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1)
1111 .unwrap();
1112 bank.transfer(2 * LAMPORTS_PER_SOL, &mint_keypair, &key2)
1113 .unwrap();
1114 bank.transfer(3 * LAMPORTS_PER_SOL, &mint_keypair, &key3)
1115 .unwrap();
1116 bank.fill_bank_with_ticks_for_tests();
1117
1118 bank.squash();
1120 bank.force_flush_accounts_cache();
1121
1122 let (storages, _slots) = bank.rc.accounts.accounts_db.get_storages(RangeFull);
1123
1124 let expected_accounts_lt_hash = bank.accounts_lt_hash.lock().unwrap().clone();
1126 assert_eq!(storages.len(), 1);
1128 let account_storage_entry = storages.first().unwrap();
1129 assert_eq!(account_storage_entry.slot(), bank.slot());
1130
1131 let accounts = bank
1133 .accounts()
1134 .accounts_db
1135 .get_unique_accounts_from_storage(account_storage_entry);
1136
1137 let offset = accounts
1139 .stored_accounts
1140 .iter()
1141 .find(|account| key1 == *account.pubkey())
1142 .map(|account| account.index_info.offset())
1143 .expect("Pubkey1 is present in Slot0");
1144
1145 account_storage_entry
1149 .mark_accounts_obsolete(vec![(offset, 0)].into_iter(), bank.slot() + 1);
1150
1151 let calculated_accounts_lt_hash = bank
1153 .accounts()
1154 .accounts_db
1155 .calculate_accounts_lt_hash_at_startup_from_storages(
1156 storages.as_slice(),
1157 &DuplicatesLtHash::default(),
1158 bank.slot(),
1159 NonZeroUsize::new(2).unwrap(),
1160 );
1161
1162 assert_eq!(calculated_accounts_lt_hash, expected_accounts_lt_hash);
1165
1166 let recalculated_accounts_lt_hash = bank
1168 .accounts()
1169 .accounts_db
1170 .calculate_accounts_lt_hash_at_startup_from_storages(
1171 storages.as_slice(),
1172 &DuplicatesLtHash::default(),
1173 bank.slot() + 1,
1174 NonZeroUsize::new(2).unwrap(),
1175 );
1176
1177 assert_ne!(recalculated_accounts_lt_hash, expected_accounts_lt_hash);
1179 }
1180}