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::{runtime_config::RuntimeConfig, snapshot_bank_utils, snapshot_utils},
393 agave_snapshots::snapshot_config::SnapshotConfig,
394 solana_account::{ReadableAccount as _, WritableAccount as _},
395 solana_accounts_db::{
396 accounts_db::{AccountsDbConfig, MarkObsoleteAccounts, ACCOUNTS_DB_CONFIG_FOR_TESTING},
397 accounts_index::{
398 AccountsIndexConfig, IndexLimitMb, ACCOUNTS_INDEX_CONFIG_FOR_TESTING,
399 },
400 },
401 solana_fee_calculator::FeeRateGovernor,
402 solana_genesis_config::{self, GenesisConfig},
403 solana_keypair::Keypair,
404 solana_native_token::LAMPORTS_PER_SOL,
405 solana_pubkey::{self as pubkey, Pubkey},
406 solana_signer::Signer as _,
407 std::{cmp, iter, str::FromStr as _, sync::Arc},
408 tempfile::TempDir,
409 test_case::{test_case, test_matrix},
410 };
411
412 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
414 enum Features {
415 None,
417 All,
419 }
420
421 fn genesis_config_with(features: Features) -> (GenesisConfig, Keypair) {
423 let mint_lamports = 123_456_789 * LAMPORTS_PER_SOL;
424 match features {
425 Features::None => solana_genesis_config::create_genesis_config(mint_lamports),
426 Features::All => {
427 let info = crate::genesis_utils::create_genesis_config(mint_lamports);
428 (info.genesis_config, info.mint_keypair)
429 }
430 }
431 }
432
433 #[test]
434 fn test_update_accounts_lt_hash() {
435 let keypair1 = Keypair::new();
446 let keypair2 = Keypair::new();
447 let keypair3 = Keypair::new();
448 let keypair4 = Keypair::new();
449 let keypair5 = Keypair::new();
450
451 let (mut genesis_config, mint_keypair) =
452 solana_genesis_config::create_genesis_config(123_456_789 * LAMPORTS_PER_SOL);
453 genesis_config.fee_rate_governor = FeeRateGovernor::new(0, 0);
454 let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
455
456 let amount = cmp::max(
457 bank.get_minimum_balance_for_rent_exemption(0),
458 LAMPORTS_PER_SOL,
459 );
460
461 bank.register_unique_recent_blockhash_for_test();
464 bank.transfer(amount, &mint_keypair, &keypair1.pubkey())
465 .unwrap();
466 bank.transfer(amount, &mint_keypair, &keypair2.pubkey())
467 .unwrap();
468 bank.transfer(amount, &mint_keypair, &keypair5.pubkey())
469 .unwrap();
470
471 bank.freeze();
473 let prev_accounts_lt_hash = bank.accounts_lt_hash.lock().unwrap().clone();
474
475 let prev_mint = bank.get_account_with_fixed_root(&mint_keypair.pubkey());
477 let prev_account1 = bank.get_account_with_fixed_root(&keypair1.pubkey());
478 let prev_account2 = bank.get_account_with_fixed_root(&keypair2.pubkey());
479 let prev_account3 = bank.get_account_with_fixed_root(&keypair3.pubkey());
480 let prev_account4 = bank.get_account_with_fixed_root(&keypair4.pubkey());
481 let prev_account5 = bank.get_account_with_fixed_root(&keypair5.pubkey());
482
483 assert!(prev_mint.is_some());
484 assert!(prev_account1.is_some());
485 assert!(prev_account2.is_some());
486 assert!(prev_account3.is_none());
487 assert!(prev_account4.is_none());
488 assert!(prev_account5.is_some());
489
490 let sysvars = [
494 Pubkey::from_str("SysvarS1otHashes111111111111111111111111111").unwrap(),
495 Pubkey::from_str("SysvarC1ock11111111111111111111111111111111").unwrap(),
496 Pubkey::from_str("SysvarRecentB1ockHashes11111111111111111111").unwrap(),
497 Pubkey::from_str("SysvarS1otHistory11111111111111111111111111").unwrap(),
498 ];
499 let prev_sysvar_accounts: Vec<_> = sysvars
500 .iter()
501 .map(|address| bank.get_account_with_fixed_root(address))
502 .collect();
503
504 let bank = {
505 let slot = bank.slot() + 1;
506 Bank::new_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot)
507 };
508
509 bank.register_unique_recent_blockhash_for_test();
511 bank.transfer(amount, &keypair2, &keypair1.pubkey())
512 .unwrap();
513
514 bank.register_unique_recent_blockhash_for_test();
517 bank.transfer(amount, &mint_keypair, &keypair4.pubkey())
518 .unwrap();
519 bank.register_unique_recent_blockhash_for_test();
520 bank.transfer(amount, &keypair4, &keypair3.pubkey())
521 .unwrap();
522
523 bank.store_account(&keypair5.pubkey(), prev_account5.as_ref().unwrap());
525
526 bank.freeze();
528
529 let actual_delta_lt_hash = bank.calculate_delta_lt_hash();
530 let post_accounts_lt_hash = bank.accounts_lt_hash.lock().unwrap().clone();
531 let post_mint = bank.get_account_with_fixed_root(&mint_keypair.pubkey());
532 let post_account1 = bank.get_account_with_fixed_root(&keypair1.pubkey());
533 let post_account2 = bank.get_account_with_fixed_root(&keypair2.pubkey());
534 let post_account3 = bank.get_account_with_fixed_root(&keypair3.pubkey());
535 let post_account4 = bank.get_account_with_fixed_root(&keypair4.pubkey());
536 let post_account5 = bank.get_account_with_fixed_root(&keypair5.pubkey());
537
538 assert!(post_mint.is_some());
539 assert!(post_account1.is_some());
540 assert!(post_account2.is_none());
541 assert!(post_account3.is_some());
542 assert!(post_account4.is_none());
543 assert!(post_account5.is_some());
544
545 let post_sysvar_accounts: Vec<_> = sysvars
546 .iter()
547 .map(|address| bank.get_account_with_fixed_root(address))
548 .collect();
549
550 let mut expected_delta_lt_hash = LtHash::identity();
551 let mut expected_accounts_lt_hash = prev_accounts_lt_hash.clone();
552 let mut updater =
553 |address: &Pubkey, prev: Option<AccountSharedData>, post: Option<AccountSharedData>| {
554 if let Some(prev) = prev {
556 let prev_lt_hash = AccountsDb::lt_hash_account(&prev, address);
557 expected_delta_lt_hash.mix_out(&prev_lt_hash.0);
558 expected_accounts_lt_hash.0.mix_out(&prev_lt_hash.0);
559 }
560
561 let post = post.unwrap_or_default();
563 let post_lt_hash = AccountsDb::lt_hash_account(&post, address);
564 expected_delta_lt_hash.mix_in(&post_lt_hash.0);
565 expected_accounts_lt_hash.0.mix_in(&post_lt_hash.0);
566 };
567 updater(&mint_keypair.pubkey(), prev_mint, post_mint);
568 updater(&keypair1.pubkey(), prev_account1, post_account1);
569 updater(&keypair2.pubkey(), prev_account2, post_account2);
570 updater(&keypair3.pubkey(), prev_account3, post_account3);
571 updater(&keypair4.pubkey(), prev_account4, post_account4);
572 updater(&keypair5.pubkey(), prev_account5, post_account5);
573 for (i, sysvar) in sysvars.iter().enumerate() {
574 updater(
575 sysvar,
576 prev_sysvar_accounts[i].clone(),
577 post_sysvar_accounts[i].clone(),
578 );
579 }
580
581 let expected = expected_delta_lt_hash.checksum();
583 let actual = actual_delta_lt_hash.checksum();
584 assert_eq!(
585 expected, actual,
586 "delta_lt_hash, expected: {expected}, actual: {actual}",
587 );
588
589 let expected = expected_accounts_lt_hash.0.checksum();
591 let actual = post_accounts_lt_hash.0.checksum();
592 assert_eq!(
593 expected, actual,
594 "accounts_lt_hash, expected: {expected}, actual: {actual}",
595 );
596 }
597
598 #[test_case(Features::None; "no features")]
604 #[test_case(Features::All; "all features")]
605 fn test_slot0_accounts_lt_hash(features: Features) {
606 let (genesis_config, mint_keypair) = genesis_config_with(features);
607 let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
608
609 assert_eq!(bank.slot(), 0);
611
612 bank.transfer(LAMPORTS_PER_SOL, &mint_keypair, &Pubkey::new_unique())
614 .unwrap();
615
616 bank.freeze();
618 let actual_accounts_lt_hash = bank.accounts_lt_hash.lock().unwrap().clone();
619
620 let calculated_accounts_lt_hash = bank
622 .rc
623 .accounts
624 .accounts_db
625 .calculate_accounts_lt_hash_at_startup_from_index(&bank.ancestors, bank.slot());
626 assert_eq!(actual_accounts_lt_hash, calculated_accounts_lt_hash);
627 }
628
629 #[test_case(Features::None; "no features")]
630 #[test_case(Features::All; "all features")]
631 fn test_inspect_account_for_accounts_lt_hash(features: Features) {
632 let (genesis_config, _mint_keypair) = genesis_config_with(features);
633 let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
634
635 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 0);
637
638 bank.inspect_account_for_accounts_lt_hash(
640 &Pubkey::new_unique(),
641 &AccountState::Dead,
642 false,
643 );
644 bank.inspect_account_for_accounts_lt_hash(
645 &Pubkey::new_unique(),
646 &AccountState::Alive(&AccountSharedData::default()),
647 false,
648 );
649 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 0);
650
651 let address = Pubkey::new_unique();
653 bank.inspect_account_for_accounts_lt_hash(&address, &AccountState::Dead, true);
654 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 1);
655 assert!(bank.cache_for_accounts_lt_hash.contains_key(&address));
656
657 let address = Pubkey::new_unique();
659 let initial_lamports = 123;
660 let mut account = AccountSharedData::new(initial_lamports, 0, &Pubkey::default());
661 bank.inspect_account_for_accounts_lt_hash(&address, &AccountState::Alive(&account), true);
662 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 2);
663 if let CacheValue::InspectAccount(InitialStateOfAccount::Alive(cached_account)) = bank
664 .cache_for_accounts_lt_hash
665 .get(&address)
666 .unwrap()
667 .value()
668 {
669 assert_eq!(*cached_account, account);
670 } else {
671 panic!("wrong initial state for account");
672 };
673
674 let updated_lamports = account.lamports() + 1;
676 account.set_lamports(updated_lamports);
677 bank.inspect_account_for_accounts_lt_hash(&address, &AccountState::Alive(&account), true);
678 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 2);
679 if let CacheValue::InspectAccount(InitialStateOfAccount::Alive(cached_account)) = bank
680 .cache_for_accounts_lt_hash
681 .get(&address)
682 .unwrap()
683 .value()
684 {
685 assert_eq!(cached_account.lamports(), initial_lamports);
686 } else {
687 panic!("wrong initial state for account");
688 };
689
690 {
692 let address = Pubkey::new_unique();
693 bank.inspect_account_for_accounts_lt_hash(&address, &AccountState::Dead, true);
694 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 3);
695 match bank
696 .cache_for_accounts_lt_hash
697 .get(&address)
698 .unwrap()
699 .value()
700 {
701 CacheValue::InspectAccount(InitialStateOfAccount::Dead) => {
702 }
704 _ => panic!("wrong initial state for account"),
705 };
706
707 bank.inspect_account_for_accounts_lt_hash(
708 &address,
709 &AccountState::Alive(&AccountSharedData::default()),
710 true,
711 );
712 assert_eq!(bank.cache_for_accounts_lt_hash.len(), 3);
713 match bank
714 .cache_for_accounts_lt_hash
715 .get(&address)
716 .unwrap()
717 .value()
718 {
719 CacheValue::InspectAccount(InitialStateOfAccount::Dead) => {
720 }
722 _ => panic!("wrong initial state for account"),
723 };
724 }
725
726 bank.freeze();
729 let address = Pubkey::new_unique();
730 let num_cache_entries_prev = bank.cache_for_accounts_lt_hash.len();
731 bank.inspect_account_for_accounts_lt_hash(&address, &AccountState::Dead, true);
732 let num_cache_entries_curr = bank.cache_for_accounts_lt_hash.len();
733 assert_eq!(num_cache_entries_curr, num_cache_entries_prev);
734 assert!(!bank.cache_for_accounts_lt_hash.contains_key(&address));
735 }
736
737 #[test_case(Features::None; "no features")]
738 #[test_case(Features::All; "all features")]
739 fn test_calculate_accounts_lt_hash_at_startup_from_index(features: Features) {
740 let (genesis_config, mint_keypair) = genesis_config_with(features);
741 let (mut bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
742
743 let amount = cmp::max(
744 bank.get_minimum_balance_for_rent_exemption(0),
745 LAMPORTS_PER_SOL,
746 );
747
748 for _ in 0..7 {
751 let slot = bank.slot() + 1;
752 bank =
753 Bank::new_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot);
754 for _ in 0..13 {
755 bank.register_unique_recent_blockhash_for_test();
756 bank.transfer(amount, &mint_keypair, &pubkey::new_rand())
759 .unwrap();
760 }
761 bank.freeze();
762 }
763 let expected_accounts_lt_hash = bank.accounts_lt_hash.lock().unwrap().clone();
764
765 bank.squash();
768 bank.force_flush_accounts_cache();
769
770 let calculated_accounts_lt_hash = bank
772 .rc
773 .accounts
774 .accounts_db
775 .calculate_accounts_lt_hash_at_startup_from_index(&bank.ancestors, bank.slot());
776 assert_eq!(expected_accounts_lt_hash, calculated_accounts_lt_hash);
777 }
778
779 #[test_matrix(
780 [Features::None, Features::All],
781 [IndexLimitMb::Minimal, IndexLimitMb::InMemOnly],
782 [MarkObsoleteAccounts::Disabled, MarkObsoleteAccounts::Enabled]
783 )]
784 fn test_verify_accounts_lt_hash_at_startup(
785 features: Features,
786 accounts_index_limit: IndexLimitMb,
787 mark_obsolete_accounts: MarkObsoleteAccounts,
788 ) {
789 let (mut genesis_config, mint_keypair) = genesis_config_with(features);
790 genesis_config.fee_rate_governor = FeeRateGovernor::new(0, 0);
792 let (mut bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
793
794 let amount = cmp::max(
795 bank.get_minimum_balance_for_rent_exemption(0),
796 LAMPORTS_PER_SOL,
797 );
798
799 let duplicate_pubkey = pubkey::new_rand();
801
802 for _ in 0..9 {
805 let slot = bank.slot() + 1;
806 bank =
807 Bank::new_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot);
808 for _ in 0..3 {
809 bank.register_unique_recent_blockhash_for_test();
810 bank.transfer(amount, &mint_keypair, &pubkey::new_rand())
811 .unwrap();
812 bank.register_unique_recent_blockhash_for_test();
813 bank.transfer(amount, &mint_keypair, &duplicate_pubkey)
814 .unwrap();
815 }
816
817 bank.fill_bank_with_ticks_for_tests();
819 bank.squash();
820 bank.force_flush_accounts_cache();
821 }
822
823 let num_accounts = 2;
829 let accounts: Vec<_> = iter::repeat_with(Keypair::new).take(num_accounts).collect();
830 for i in 0..num_accounts {
831 let slot = bank.slot() + 1;
832 bank =
833 Bank::new_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot);
834 bank.register_unique_recent_blockhash_for_test();
835
836 for account in &accounts {
838 bank.transfer(amount, &mint_keypair, &account.pubkey())
839 .unwrap();
840 assert_ne!(bank.get_balance(&account.pubkey()), 0);
841 }
842
843 bank.transfer(
845 bank.get_balance(&accounts[i].pubkey()),
846 &accounts[i],
847 &pubkey::new_rand(),
848 )
849 .unwrap();
850 assert_eq!(bank.get_balance(&accounts[i].pubkey()), 0);
851
852 bank.fill_bank_with_ticks_for_tests();
854 bank.squash();
855 bank.force_flush_accounts_cache();
856 }
857
858 let snapshot_config = SnapshotConfig::default();
860 let bank_snapshots_dir = TempDir::new().unwrap();
861 let snapshot_archives_dir = TempDir::new().unwrap();
862 let snapshot = snapshot_bank_utils::bank_to_full_snapshot_archive(
863 &bank_snapshots_dir,
864 &bank,
865 Some(snapshot_config.snapshot_version),
866 &snapshot_archives_dir,
867 &snapshot_archives_dir,
868 snapshot_config.archive_format,
869 )
870 .unwrap();
871 let (_accounts_tempdir, accounts_dir) = snapshot_utils::create_tmp_accounts_dir_for_tests();
872 let accounts_index_config = AccountsIndexConfig {
873 index_limit_mb: accounts_index_limit,
874 ..ACCOUNTS_INDEX_CONFIG_FOR_TESTING
875 };
876 let accounts_db_config = AccountsDbConfig {
877 index: Some(accounts_index_config),
878 mark_obsolete_accounts,
879 ..ACCOUNTS_DB_CONFIG_FOR_TESTING
880 };
881 let roundtrip_bank = snapshot_bank_utils::bank_from_snapshot_archives(
882 &[accounts_dir],
883 &bank_snapshots_dir,
884 &snapshot,
885 None,
886 &genesis_config,
887 &RuntimeConfig::default(),
888 None,
889 None,
890 false,
891 false,
892 false,
893 accounts_db_config,
894 None,
895 Arc::default(),
896 )
897 .unwrap();
898
899 assert!(roundtrip_bank.is_frozen());
903
904 assert_eq!(roundtrip_bank, *bank);
905 }
906
907 #[test_case(Features::None; "no features")]
909 #[test_case(Features::All; "all features")]
910 fn test_accounts_lt_hash_cache_values_from_bank_new(features: Features) {
911 let (genesis_config, _mint_keypair) = genesis_config_with(features);
912 let (mut bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
913
914 let slot = bank.slot() + 1;
915 bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot);
916
917 let expected_cache = &[
921 (
922 Pubkey::from_str_const("SysvarC1ock11111111111111111111111111111111"),
923 CacheValue::BankNew,
924 ),
925 (
926 Pubkey::from_str_const("SysvarS1otHashes111111111111111111111111111"),
927 CacheValue::BankNew,
928 ),
929 ];
930 let mut actual_cache: Vec<_> = bank
931 .cache_for_accounts_lt_hash
932 .iter()
933 .map(|entry| (*entry.key(), entry.value().clone()))
934 .collect();
935 actual_cache.sort_unstable_by(|a, b| a.0.cmp(&b.0));
936 assert_eq!(expected_cache, actual_cache.as_slice());
937 }
938
939 #[test_case(Features::None; "no features")]
941 #[test_case(Features::All; "all features")]
942 fn test_snapshots(features: Features) {
943 let (genesis_config, mint_keypair) = genesis_config_with(features);
944 let (mut bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
945
946 let amount = cmp::max(
947 bank.get_minimum_balance_for_rent_exemption(0),
948 LAMPORTS_PER_SOL,
949 );
950
951 for _ in 0..3 {
954 let slot = bank.slot() + 1;
955 bank =
956 Bank::new_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slot);
957 bank.register_unique_recent_blockhash_for_test();
958 bank.transfer(amount, &mint_keypair, &pubkey::new_rand())
959 .unwrap();
960 bank.fill_bank_with_ticks_for_tests();
961 bank.squash();
962 bank.force_flush_accounts_cache();
963 }
964
965 let snapshot_config = SnapshotConfig::default();
966 let bank_snapshots_dir = TempDir::new().unwrap();
967 let snapshot_archives_dir = TempDir::new().unwrap();
968 let snapshot = snapshot_bank_utils::bank_to_full_snapshot_archive(
969 &bank_snapshots_dir,
970 &bank,
971 Some(snapshot_config.snapshot_version),
972 &snapshot_archives_dir,
973 &snapshot_archives_dir,
974 snapshot_config.archive_format,
975 )
976 .unwrap();
977 let (_accounts_tempdir, accounts_dir) = snapshot_utils::create_tmp_accounts_dir_for_tests();
978 let roundtrip_bank = snapshot_bank_utils::bank_from_snapshot_archives(
979 &[accounts_dir],
980 &bank_snapshots_dir,
981 &snapshot,
982 None,
983 &genesis_config,
984 &RuntimeConfig::default(),
985 None,
986 None,
987 false,
988 false,
989 false,
990 ACCOUNTS_DB_CONFIG_FOR_TESTING,
991 None,
992 Arc::default(),
993 )
994 .unwrap();
995
996 assert_eq!(roundtrip_bank, *bank);
997 }
998}