solana_accounts_db/
accounts_index.rs

1pub(crate) mod in_mem_accounts_index;
2use {
3    crate::{
4        accounts_index_storage::{AccountsIndexStorage, Startup},
5        accounts_partition::RentPayingAccountsByPartition,
6        ancestors::Ancestors,
7        bucket_map_holder::{Age, AtomicAge, BucketMapHolder},
8        contains::Contains,
9        pubkey_bins::PubkeyBinCalculator24,
10        rolling_bit_field::RollingBitField,
11        secondary_index::*,
12    },
13    in_mem_accounts_index::{InMemAccountsIndex, InsertNewEntryResults, StartupStats},
14    log::*,
15    rand::{thread_rng, Rng},
16    rayon::{
17        iter::{IntoParallelIterator, ParallelIterator},
18        ThreadPool,
19    },
20    solana_measure::measure::Measure,
21    solana_nohash_hasher::IntSet,
22    solana_sdk::{
23        account::ReadableAccount,
24        clock::{BankId, Slot},
25        pubkey::Pubkey,
26    },
27    std::{
28        collections::{btree_map::BTreeMap, HashSet},
29        fmt::Debug,
30        num::NonZeroUsize,
31        ops::{
32            Bound,
33            Bound::{Excluded, Included, Unbounded},
34            Range, RangeBounds,
35        },
36        path::PathBuf,
37        sync::{
38            atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
39            Arc, Mutex, OnceLock, RwLock,
40        },
41    },
42    thiserror::Error,
43};
44
45pub const ITER_BATCH_SIZE: usize = 1000;
46pub const BINS_DEFAULT: usize = 8192;
47pub const BINS_FOR_TESTING: usize = 2; // we want > 1, but each bin is a few disk files with a disk based index, so fewer is better
48pub const BINS_FOR_BENCHMARKS: usize = 8192;
49// The unsafe is safe because we're using a fixed, known non-zero value
50pub const FLUSH_THREADS_TESTING: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(1) };
51pub const ACCOUNTS_INDEX_CONFIG_FOR_TESTING: AccountsIndexConfig = AccountsIndexConfig {
52    bins: Some(BINS_FOR_TESTING),
53    num_flush_threads: Some(FLUSH_THREADS_TESTING),
54    drives: None,
55    index_limit_mb: IndexLimitMb::Unlimited,
56    ages_to_stay_in_cache: None,
57    scan_results_limit_bytes: None,
58    started_from_validator: false,
59};
60pub const ACCOUNTS_INDEX_CONFIG_FOR_BENCHMARKS: AccountsIndexConfig = AccountsIndexConfig {
61    bins: Some(BINS_FOR_BENCHMARKS),
62    num_flush_threads: Some(FLUSH_THREADS_TESTING),
63    drives: None,
64    index_limit_mb: IndexLimitMb::Unlimited,
65    ages_to_stay_in_cache: None,
66    scan_results_limit_bytes: None,
67    started_from_validator: false,
68};
69pub type ScanResult<T> = Result<T, ScanError>;
70pub type SlotList<T> = Vec<(Slot, T)>;
71pub type SlotSlice<'s, T> = &'s [(Slot, T)];
72pub type RefCount = u64;
73pub type AccountMap<T, U> = Arc<InMemAccountsIndex<T, U>>;
74
75#[derive(Default, Debug, PartialEq, Eq)]
76pub(crate) struct GenerateIndexResult<T: IndexValue> {
77    /// number of accounts inserted in the index
78    pub count: usize,
79    /// pubkeys which were present multiple times in the insertion request.
80    pub duplicates: Option<Vec<(Pubkey, (Slot, T))>>,
81}
82
83#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
84/// which accounts `scan` should load from disk
85pub enum ScanFilter {
86    /// Scan both in-memory and on-disk index
87    #[default]
88    All,
89
90    /// abnormal = ref_count != 1 or slot list.len() != 1
91    /// Scan only in-memory index and skip on-disk index
92    OnlyAbnormal,
93
94    /// Similar to `OnlyAbnormal but also check on-disk index to verify the
95    /// entry on-disk is indeed normal.
96    OnlyAbnormalWithVerify,
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100/// how accounts index 'upsert' should handle reclaims
101pub enum UpsertReclaim {
102    /// previous entry for this slot in the index is expected to be cached, so irrelevant to reclaims
103    PreviousSlotEntryWasCached,
104    /// previous entry for this slot in the index may need to be reclaimed, so return it.
105    /// reclaims is the only output of upsert, requiring a synchronous execution
106    PopulateReclaims,
107    /// overwrite existing data in the same slot and do not return in 'reclaims'
108    IgnoreReclaims,
109}
110
111#[derive(Debug)]
112pub struct ScanConfig {
113    /// checked by the scan. When true, abort scan.
114    pub abort: Option<Arc<AtomicBool>>,
115
116    /// true to allow return of all matching items and allow them to be unsorted.
117    /// This is more efficient.
118    pub collect_all_unsorted: bool,
119}
120
121impl Default for ScanConfig {
122    fn default() -> Self {
123        Self {
124            abort: None,
125            collect_all_unsorted: true,
126        }
127    }
128}
129
130impl ScanConfig {
131    pub fn new(collect_all_unsorted: bool) -> Self {
132        Self {
133            collect_all_unsorted,
134            ..Default::default()
135        }
136    }
137
138    /// mark the scan as aborted
139    pub fn abort(&self) {
140        if let Some(abort) = self.abort.as_ref() {
141            abort.store(true, Ordering::Relaxed)
142        }
143    }
144
145    /// use existing 'abort' if available, otherwise allocate one
146    pub fn recreate_with_abort(&self) -> Self {
147        ScanConfig {
148            abort: Some(self.abort.clone().unwrap_or_default()),
149            collect_all_unsorted: self.collect_all_unsorted,
150        }
151    }
152
153    /// true if scan should abort
154    pub fn is_aborted(&self) -> bool {
155        if let Some(abort) = self.abort.as_ref() {
156            abort.load(Ordering::Relaxed)
157        } else {
158            false
159        }
160    }
161}
162
163pub(crate) type AccountMapEntry<T> = Arc<AccountMapEntryInner<T>>;
164
165pub trait IsCached {
166    fn is_cached(&self) -> bool;
167}
168
169pub trait IndexValue: 'static + IsCached + ZeroLamport + DiskIndexValue {}
170
171pub trait DiskIndexValue:
172    'static + Clone + Debug + PartialEq + Copy + Default + Sync + Send
173{
174}
175
176#[derive(Error, Debug, PartialEq, Eq)]
177pub enum ScanError {
178    #[error("Node detected it replayed bad version of slot {slot:?} with id {bank_id:?}, thus the scan on said slot was aborted")]
179    SlotRemoved { slot: Slot, bank_id: BankId },
180    #[error("scan aborted: {0}")]
181    Aborted(String),
182}
183
184enum ScanTypes<R: RangeBounds<Pubkey>> {
185    Unindexed(Option<R>),
186    Indexed(IndexKey),
187}
188
189#[derive(Debug, Clone, Copy)]
190pub enum IndexKey {
191    ProgramId(Pubkey),
192    SplTokenMint(Pubkey),
193    SplTokenOwner(Pubkey),
194}
195
196#[derive(Debug, Clone, PartialEq, Eq, Hash)]
197pub enum AccountIndex {
198    ProgramId,
199    SplTokenMint,
200    SplTokenOwner,
201}
202
203#[derive(Debug, PartialEq, Eq, Clone)]
204pub struct AccountSecondaryIndexesIncludeExclude {
205    pub exclude: bool,
206    pub keys: HashSet<Pubkey>,
207}
208
209/// specification of how much memory in-mem portion of account index can use
210#[derive(Debug, Copy, Clone, Default)]
211pub enum IndexLimitMb {
212    /// use disk index while allowing to use as much memory as available for
213    /// in-memory index.
214    #[default]
215    Unlimited,
216    /// in-mem-only was specified, no disk index
217    InMemOnly,
218}
219
220#[derive(Debug, Default, Clone)]
221pub struct AccountsIndexConfig {
222    pub bins: Option<usize>,
223    pub num_flush_threads: Option<NonZeroUsize>,
224    pub drives: Option<Vec<PathBuf>>,
225    pub index_limit_mb: IndexLimitMb,
226    pub ages_to_stay_in_cache: Option<Age>,
227    pub scan_results_limit_bytes: Option<usize>,
228    /// true if the accounts index is being created as a result of being started as a validator (as opposed to test, etc.)
229    pub started_from_validator: bool,
230}
231
232pub fn default_num_flush_threads() -> NonZeroUsize {
233    NonZeroUsize::new(std::cmp::max(2, num_cpus::get() / 4)).expect("non-zero system threads")
234}
235
236#[derive(Debug, Default, Clone)]
237pub struct AccountSecondaryIndexes {
238    pub keys: Option<AccountSecondaryIndexesIncludeExclude>,
239    pub indexes: HashSet<AccountIndex>,
240}
241
242impl AccountSecondaryIndexes {
243    pub fn is_empty(&self) -> bool {
244        self.indexes.is_empty()
245    }
246    pub fn contains(&self, index: &AccountIndex) -> bool {
247        self.indexes.contains(index)
248    }
249    pub fn include_key(&self, key: &Pubkey) -> bool {
250        match &self.keys {
251            Some(options) => options.exclude ^ options.keys.contains(key),
252            None => true, // include all keys
253        }
254    }
255}
256
257#[derive(Debug, Default)]
258/// data per entry in in-mem accounts index
259/// used to keep track of consistency with disk index
260pub struct AccountMapEntryMeta {
261    /// true if entry in in-mem idx has changes and needs to be written to disk
262    pub dirty: AtomicBool,
263    /// 'age' at which this entry should be purged from the cache (implements lru)
264    pub age: AtomicAge,
265}
266
267impl AccountMapEntryMeta {
268    pub fn new_dirty<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>>(
269        storage: &Arc<BucketMapHolder<T, U>>,
270        is_cached: bool,
271    ) -> Self {
272        AccountMapEntryMeta {
273            dirty: AtomicBool::new(true),
274            age: AtomicAge::new(storage.future_age_to_flush(is_cached)),
275        }
276    }
277    pub fn new_clean<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>>(
278        storage: &Arc<BucketMapHolder<T, U>>,
279    ) -> Self {
280        AccountMapEntryMeta {
281            dirty: AtomicBool::new(false),
282            age: AtomicAge::new(storage.future_age_to_flush(false)),
283        }
284    }
285}
286
287#[derive(Debug, Default)]
288/// one entry in the in-mem accounts index
289/// Represents the value for an account key in the in-memory accounts index
290pub struct AccountMapEntryInner<T> {
291    /// number of alive slots that contain >= 1 instances of account data for this pubkey
292    /// where alive represents a slot that has not yet been removed by clean via AccountsDB::clean_stored_dead_slots() for containing no up to date account information
293    ref_count: AtomicU64,
294    /// list of slots in which this pubkey was updated
295    /// Note that 'clean' removes outdated entries (ie. older roots) from this slot_list
296    /// purge_slot() also removes non-rooted slots from this list
297    pub slot_list: RwLock<SlotList<T>>,
298    /// synchronization metadata for in-memory state since last flush to disk accounts index
299    pub meta: AccountMapEntryMeta,
300}
301
302impl<T: IndexValue> AccountMapEntryInner<T> {
303    pub fn new(slot_list: SlotList<T>, ref_count: RefCount, meta: AccountMapEntryMeta) -> Self {
304        Self {
305            slot_list: RwLock::new(slot_list),
306            ref_count: AtomicU64::new(ref_count),
307            meta,
308        }
309    }
310    pub fn ref_count(&self) -> RefCount {
311        self.ref_count.load(Ordering::Acquire)
312    }
313
314    pub fn addref(&self) {
315        self.ref_count.fetch_add(1, Ordering::Release);
316        self.set_dirty(true);
317    }
318
319    /// decrement the ref count
320    /// return the refcount prior to subtracting 1
321    /// 0 indicates an under refcounting error in the system.
322    pub fn unref(&self) -> RefCount {
323        let previous = self.ref_count.fetch_sub(1, Ordering::Release);
324        self.set_dirty(true);
325        if previous == 0 {
326            inc_new_counter_info!("accounts_index-deref_from_0", 1);
327        }
328        previous
329    }
330
331    pub fn dirty(&self) -> bool {
332        self.meta.dirty.load(Ordering::Acquire)
333    }
334
335    pub fn set_dirty(&self, value: bool) {
336        self.meta.dirty.store(value, Ordering::Release)
337    }
338
339    /// set dirty to false, return true if was dirty
340    pub fn clear_dirty(&self) -> bool {
341        self.meta
342            .dirty
343            .compare_exchange(true, false, Ordering::AcqRel, Ordering::Relaxed)
344            .is_ok()
345    }
346
347    pub fn age(&self) -> Age {
348        self.meta.age.load(Ordering::Acquire)
349    }
350
351    pub fn set_age(&self, value: Age) {
352        self.meta.age.store(value, Ordering::Release)
353    }
354
355    /// set age to 'next_age' if 'self.age' is 'expected_age'
356    pub fn try_exchange_age(&self, next_age: Age, expected_age: Age) {
357        let _ = self.meta.age.compare_exchange(
358            expected_age,
359            next_age,
360            Ordering::AcqRel,
361            Ordering::Relaxed,
362        );
363    }
364}
365
366/// can be used to pre-allocate structures for insertion into accounts index outside of lock
367pub enum PreAllocatedAccountMapEntry<T: IndexValue> {
368    Entry(AccountMapEntry<T>),
369    Raw((Slot, T)),
370}
371
372impl<T: IndexValue> ZeroLamport for PreAllocatedAccountMapEntry<T> {
373    fn is_zero_lamport(&self) -> bool {
374        match self {
375            PreAllocatedAccountMapEntry::Entry(entry) => {
376                entry.slot_list.read().unwrap()[0].1.is_zero_lamport()
377            }
378            PreAllocatedAccountMapEntry::Raw(raw) => raw.1.is_zero_lamport(),
379        }
380    }
381}
382
383impl<T: IndexValue> From<PreAllocatedAccountMapEntry<T>> for (Slot, T) {
384    fn from(source: PreAllocatedAccountMapEntry<T>) -> (Slot, T) {
385        match source {
386            PreAllocatedAccountMapEntry::Entry(entry) => entry.slot_list.read().unwrap()[0],
387            PreAllocatedAccountMapEntry::Raw(raw) => raw,
388        }
389    }
390}
391
392impl<T: IndexValue> PreAllocatedAccountMapEntry<T> {
393    /// create an entry that is equivalent to this process:
394    /// 1. new empty (refcount=0, slot_list={})
395    /// 2. update(slot, account_info)
396    ///
397    /// This code is called when the first entry [ie. (slot,account_info)] for a pubkey is inserted into the index.
398    pub fn new<U: DiskIndexValue + From<T> + Into<T>>(
399        slot: Slot,
400        account_info: T,
401        storage: &Arc<BucketMapHolder<T, U>>,
402        store_raw: bool,
403    ) -> PreAllocatedAccountMapEntry<T> {
404        if store_raw {
405            Self::Raw((slot, account_info))
406        } else {
407            Self::Entry(Self::allocate(slot, account_info, storage))
408        }
409    }
410
411    fn allocate<U: DiskIndexValue + From<T> + Into<T>>(
412        slot: Slot,
413        account_info: T,
414        storage: &Arc<BucketMapHolder<T, U>>,
415    ) -> AccountMapEntry<T> {
416        let is_cached = account_info.is_cached();
417        let ref_count = u64::from(!is_cached);
418        let meta = AccountMapEntryMeta::new_dirty(storage, is_cached);
419        Arc::new(AccountMapEntryInner::new(
420            vec![(slot, account_info)],
421            ref_count,
422            meta,
423        ))
424    }
425
426    pub fn into_account_map_entry<U: DiskIndexValue + From<T> + Into<T>>(
427        self,
428        storage: &Arc<BucketMapHolder<T, U>>,
429    ) -> AccountMapEntry<T> {
430        match self {
431            Self::Entry(entry) => entry,
432            Self::Raw((slot, account_info)) => Self::allocate(slot, account_info, storage),
433        }
434    }
435}
436
437#[derive(Debug)]
438pub struct RootsTracker {
439    /// Current roots where appendvecs or write cache has account data.
440    /// Constructed during load from snapshots.
441    /// Updated every time we add a new root or clean/shrink an append vec into irrelevancy.
442    /// Range is approximately the last N slots where N is # slots per epoch.
443    pub alive_roots: RollingBitField,
444    uncleaned_roots: IntSet<Slot>,
445}
446
447impl Default for RootsTracker {
448    fn default() -> Self {
449        // we expect to keep a rolling set of 400k slots around at a time
450        // 4M gives us plenty of extra(?!) room to handle a width 10x what we should need.
451        // cost is 4M bits of memory, which is .5MB
452        RootsTracker::new(4194304)
453    }
454}
455
456impl RootsTracker {
457    pub fn new(max_width: u64) -> Self {
458        Self {
459            alive_roots: RollingBitField::new(max_width),
460            uncleaned_roots: IntSet::default(),
461        }
462    }
463
464    pub fn min_alive_root(&self) -> Option<Slot> {
465        self.alive_roots.min()
466    }
467}
468
469#[derive(Debug, Default)]
470pub struct AccountsIndexRootsStats {
471    pub roots_len: Option<usize>,
472    pub uncleaned_roots_len: Option<usize>,
473    pub roots_range: Option<u64>,
474    pub rooted_cleaned_count: usize,
475    pub unrooted_cleaned_count: usize,
476    pub clean_unref_from_storage_us: u64,
477    pub clean_dead_slot_us: u64,
478}
479
480pub struct AccountsIndexIterator<'a, T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> {
481    account_maps: &'a LockMapTypeSlice<T, U>,
482    bin_calculator: &'a PubkeyBinCalculator24,
483    start_bound: Bound<Pubkey>,
484    end_bound: Bound<Pubkey>,
485    is_finished: bool,
486    collect_all_unsorted: bool,
487}
488
489impl<'a, T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> AccountsIndexIterator<'a, T, U> {
490    fn range<R>(
491        map: &AccountMaps<T, U>,
492        range: R,
493        collect_all_unsorted: bool,
494    ) -> Vec<(Pubkey, AccountMapEntry<T>)>
495    where
496        R: RangeBounds<Pubkey> + std::fmt::Debug,
497    {
498        let mut result = map.items(&range);
499        if !collect_all_unsorted {
500            result.sort_unstable_by(|a, b| a.0.cmp(&b.0));
501        }
502        result
503    }
504
505    fn clone_bound(bound: Bound<&Pubkey>) -> Bound<Pubkey> {
506        match bound {
507            Unbounded => Unbounded,
508            Included(k) => Included(*k),
509            Excluded(k) => Excluded(*k),
510        }
511    }
512
513    fn bin_from_bound(&self, bound: &Bound<Pubkey>, unbounded_bin: usize) -> usize {
514        match bound {
515            Bound::Included(bound) | Bound::Excluded(bound) => {
516                self.bin_calculator.bin_from_pubkey(bound)
517            }
518            Bound::Unbounded => unbounded_bin,
519        }
520    }
521
522    fn start_bin(&self) -> usize {
523        // start in bin where 'start_bound' would exist
524        self.bin_from_bound(&self.start_bound, 0)
525    }
526
527    fn end_bin_inclusive(&self) -> usize {
528        // end in bin where 'end_bound' would exist
529        self.bin_from_bound(&self.end_bound, usize::MAX)
530    }
531
532    fn bin_start_and_range(&self) -> (usize, usize) {
533        let start_bin = self.start_bin();
534        // calculate the max range of bins to look in
535        let end_bin_inclusive = self.end_bin_inclusive();
536        let bin_range = if start_bin > end_bin_inclusive {
537            0 // empty range
538        } else if end_bin_inclusive == usize::MAX {
539            usize::MAX
540        } else {
541            // the range is end_inclusive + 1 - start
542            // end_inclusive could be usize::MAX already if no bound was specified
543            end_bin_inclusive.saturating_add(1) - start_bin
544        };
545        (start_bin, bin_range)
546    }
547
548    pub fn new<R>(
549        index: &'a AccountsIndex<T, U>,
550        range: Option<&R>,
551        collect_all_unsorted: bool,
552    ) -> Self
553    where
554        R: RangeBounds<Pubkey>,
555    {
556        Self {
557            start_bound: range
558                .as_ref()
559                .map(|r| Self::clone_bound(r.start_bound()))
560                .unwrap_or(Unbounded),
561            end_bound: range
562                .as_ref()
563                .map(|r| Self::clone_bound(r.end_bound()))
564                .unwrap_or(Unbounded),
565            account_maps: &index.account_maps,
566            is_finished: false,
567            bin_calculator: &index.bin_calculator,
568            collect_all_unsorted,
569        }
570    }
571
572    pub fn hold_range_in_memory<R>(&self, range: &R, start_holding: bool, thread_pool: &ThreadPool)
573    where
574        R: RangeBounds<Pubkey> + Debug + Sync,
575    {
576        // forward this hold request ONLY to the bins which contain keys in the specified range
577        let (start_bin, bin_range) = self.bin_start_and_range();
578        // the idea is this range shouldn't be more than a few buckets, but the process of loading from disk buckets is very slow
579        // so, parallelize the bucket loads
580        thread_pool.install(|| {
581            (0..bin_range).into_par_iter().for_each(|idx| {
582                let map = &self.account_maps[idx + start_bin];
583                map.hold_range_in_memory(range, start_holding);
584            });
585        });
586    }
587}
588
589impl<'a, T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> Iterator
590    for AccountsIndexIterator<'a, T, U>
591{
592    type Item = Vec<(Pubkey, AccountMapEntry<T>)>;
593    fn next(&mut self) -> Option<Self::Item> {
594        if self.is_finished {
595            return None;
596        }
597        let (start_bin, bin_range) = self.bin_start_and_range();
598        let mut chunk = Vec::with_capacity(ITER_BATCH_SIZE);
599        'outer: for i in self.account_maps.iter().skip(start_bin).take(bin_range) {
600            for (pubkey, account_map_entry) in Self::range(
601                &i,
602                (self.start_bound, self.end_bound),
603                self.collect_all_unsorted,
604            ) {
605                if chunk.len() >= ITER_BATCH_SIZE && !self.collect_all_unsorted {
606                    break 'outer;
607                }
608                let item = (pubkey, account_map_entry);
609                chunk.push(item);
610            }
611        }
612
613        if chunk.is_empty() {
614            self.is_finished = true;
615            return None;
616        } else if self.collect_all_unsorted {
617            self.is_finished = true;
618        }
619
620        self.start_bound = Excluded(chunk.last().unwrap().0);
621        Some(chunk)
622    }
623}
624
625pub trait ZeroLamport {
626    fn is_zero_lamport(&self) -> bool;
627}
628
629type MapType<T, U> = AccountMap<T, U>;
630type LockMapType<T, U> = Vec<MapType<T, U>>;
631type LockMapTypeSlice<T, U> = [MapType<T, U>];
632type AccountMaps<'a, T, U> = &'a MapType<T, U>;
633
634#[derive(Debug, Default)]
635pub struct ScanSlotTracker {
636    is_removed: bool,
637}
638
639impl ScanSlotTracker {
640    pub fn is_removed(&self) -> bool {
641        self.is_removed
642    }
643
644    pub fn mark_removed(&mut self) {
645        self.is_removed = true;
646    }
647}
648
649#[derive(Copy, Clone)]
650pub enum AccountsIndexScanResult {
651    /// if the entry is not in the in-memory index, do not add it unless the entry becomes dirty
652    OnlyKeepInMemoryIfDirty,
653    /// keep the entry in the in-memory index
654    KeepInMemory,
655    /// reduce refcount by 1
656    Unref,
657    /// reduce refcount by 1 and assert that ref_count = 0 after unref
658    UnrefAssert0,
659    /// reduce refcount by 1 and log if ref_count != 0 after unref
660    UnrefLog0,
661}
662
663#[derive(Debug)]
664/// T: account info type to interact in in-memory items
665/// U: account info type to be persisted to disk
666pub struct AccountsIndex<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> {
667    pub account_maps: LockMapType<T, U>,
668    pub bin_calculator: PubkeyBinCalculator24,
669    program_id_index: SecondaryIndex<RwLockSecondaryIndexEntry>,
670    spl_token_mint_index: SecondaryIndex<RwLockSecondaryIndexEntry>,
671    spl_token_owner_index: SecondaryIndex<RwLockSecondaryIndexEntry>,
672    pub roots_tracker: RwLock<RootsTracker>,
673    ongoing_scan_roots: RwLock<BTreeMap<Slot, u64>>,
674    // Each scan has some latest slot `S` that is the tip of the fork the scan
675    // is iterating over. The unique id of that slot `S` is recorded here (note we don't use
676    // `S` as the id because there can be more than one version of a slot `S`). If a fork
677    // is abandoned, all of the slots on that fork up to `S` will be removed via
678    // `AccountsDb::remove_unrooted_slots()`. When the scan finishes, it'll realize that the
679    // results of the scan may have been corrupted by `remove_unrooted_slots` and abort its results.
680    //
681    // `removed_bank_ids` tracks all the slot ids that were removed via `remove_unrooted_slots()` so any attempted scans
682    // on any of these slots fails. This is safe to purge once the associated Bank is dropped and
683    // scanning the fork with that Bank at the tip is no longer possible.
684    pub removed_bank_ids: Mutex<HashSet<BankId>>,
685
686    storage: AccountsIndexStorage<T, U>,
687
688    /// when a scan's accumulated data exceeds this limit, abort the scan
689    pub scan_results_limit_bytes: Option<usize>,
690
691    pub purge_older_root_entries_one_slot_list: AtomicUsize,
692
693    /// # roots added since last check
694    pub roots_added: AtomicUsize,
695    /// # roots removed since last check
696    pub roots_removed: AtomicUsize,
697    /// # scans active currently
698    pub active_scans: AtomicUsize,
699    /// # of slots between latest max and latest scan
700    pub max_distance_to_min_scan_slot: AtomicU64,
701    // # of unref when the account's ref_count is zero
702    pub unref_zero_count: AtomicU64,
703
704    /// populated at generate_index time - accounts that could possibly be rent paying
705    pub rent_paying_accounts_by_partition: OnceLock<RentPayingAccountsByPartition>,
706}
707
708impl<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> AccountsIndex<T, U> {
709    pub fn default_for_tests() -> Self {
710        Self::new(Some(ACCOUNTS_INDEX_CONFIG_FOR_TESTING), Arc::default())
711    }
712
713    pub fn new(config: Option<AccountsIndexConfig>, exit: Arc<AtomicBool>) -> Self {
714        let scan_results_limit_bytes = config
715            .as_ref()
716            .and_then(|config| config.scan_results_limit_bytes);
717        let (account_maps, bin_calculator, storage) = Self::allocate_accounts_index(config, exit);
718        Self {
719            purge_older_root_entries_one_slot_list: AtomicUsize::default(),
720            account_maps,
721            bin_calculator,
722            program_id_index: SecondaryIndex::<RwLockSecondaryIndexEntry>::new(
723                "program_id_index_stats",
724            ),
725            spl_token_mint_index: SecondaryIndex::<RwLockSecondaryIndexEntry>::new(
726                "spl_token_mint_index_stats",
727            ),
728            spl_token_owner_index: SecondaryIndex::<RwLockSecondaryIndexEntry>::new(
729                "spl_token_owner_index_stats",
730            ),
731            roots_tracker: RwLock::<RootsTracker>::default(),
732            ongoing_scan_roots: RwLock::<BTreeMap<Slot, u64>>::default(),
733            removed_bank_ids: Mutex::<HashSet<BankId>>::default(),
734            storage,
735            scan_results_limit_bytes,
736            roots_added: AtomicUsize::default(),
737            roots_removed: AtomicUsize::default(),
738            active_scans: AtomicUsize::default(),
739            max_distance_to_min_scan_slot: AtomicU64::default(),
740            unref_zero_count: AtomicU64::default(),
741            rent_paying_accounts_by_partition: OnceLock::default(),
742        }
743    }
744
745    fn allocate_accounts_index(
746        config: Option<AccountsIndexConfig>,
747        exit: Arc<AtomicBool>,
748    ) -> (
749        LockMapType<T, U>,
750        PubkeyBinCalculator24,
751        AccountsIndexStorage<T, U>,
752    ) {
753        let bins = config
754            .as_ref()
755            .and_then(|config| config.bins)
756            .unwrap_or(BINS_DEFAULT);
757        // create bin_calculator early to verify # bins is reasonable
758        let bin_calculator = PubkeyBinCalculator24::new(bins);
759        let storage = AccountsIndexStorage::new(bins, &config, exit);
760        let account_maps = (0..bins)
761            .map(|bin| Arc::clone(&storage.in_mem[bin]))
762            .collect::<Vec<_>>();
763        (account_maps, bin_calculator, storage)
764    }
765
766    fn iter<R>(&self, range: Option<&R>, collect_all_unsorted: bool) -> AccountsIndexIterator<T, U>
767    where
768        R: RangeBounds<Pubkey>,
769    {
770        AccountsIndexIterator::new(self, range, collect_all_unsorted)
771    }
772
773    /// is the accounts index using disk as a backing store
774    pub fn is_disk_index_enabled(&self) -> bool {
775        self.storage.storage.is_disk_index_enabled()
776    }
777
778    fn min_ongoing_scan_root_from_btree(ongoing_scan_roots: &BTreeMap<Slot, u64>) -> Option<Slot> {
779        ongoing_scan_roots.keys().next().cloned()
780    }
781
782    fn do_checked_scan_accounts<F, R>(
783        &self,
784        metric_name: &'static str,
785        ancestors: &Ancestors,
786        scan_bank_id: BankId,
787        func: F,
788        scan_type: ScanTypes<R>,
789        config: &ScanConfig,
790    ) -> Result<(), ScanError>
791    where
792        F: FnMut(&Pubkey, (&T, Slot)),
793        R: RangeBounds<Pubkey> + std::fmt::Debug,
794    {
795        {
796            let locked_removed_bank_ids = self.removed_bank_ids.lock().unwrap();
797            if locked_removed_bank_ids.contains(&scan_bank_id) {
798                return Err(ScanError::SlotRemoved {
799                    slot: ancestors.max_slot(),
800                    bank_id: scan_bank_id,
801                });
802            }
803        }
804
805        self.active_scans.fetch_add(1, Ordering::Relaxed);
806        let max_root = {
807            let mut w_ongoing_scan_roots = self
808                // This lock is also grabbed by clean_accounts(), so clean
809                // has at most cleaned up to the current `max_root` (since
810                // clean only happens *after* BankForks::set_root() which sets
811                // the `max_root`)
812                .ongoing_scan_roots
813                .write()
814                .unwrap();
815            // `max_root()` grabs a lock while
816            // the `ongoing_scan_roots` lock is held,
817            // make sure inverse doesn't happen to avoid
818            // deadlock
819            let max_root_inclusive = self.max_root_inclusive();
820            if let Some(min_ongoing_scan_root) =
821                Self::min_ongoing_scan_root_from_btree(&w_ongoing_scan_roots)
822            {
823                if min_ongoing_scan_root < max_root_inclusive {
824                    let current = max_root_inclusive - min_ongoing_scan_root;
825                    self.max_distance_to_min_scan_slot
826                        .fetch_max(current, Ordering::Relaxed);
827                }
828            }
829            *w_ongoing_scan_roots.entry(max_root_inclusive).or_default() += 1;
830
831            max_root_inclusive
832        };
833
834        // First we show that for any bank `B` that is a descendant of
835        // the current `max_root`, it must be true that and `B.ancestors.contains(max_root)`,
836        // regardless of the pattern of `squash()` behavior, where `ancestors` is the set
837        // of ancestors that is tracked in each bank.
838        //
839        // Proof: At startup, if starting from a snapshot, generate_index() adds all banks
840        // in the snapshot to the index via `add_root()` and so `max_root` will be the
841        // greatest of these. Thus, so the claim holds at startup since there are no
842        // descendants of `max_root`.
843        //
844        // Now we proceed by induction on each `BankForks::set_root()`.
845        // Assume the claim holds when the `max_root` is `R`. Call the set of
846        // descendants of `R` present in BankForks `R_descendants`.
847        //
848        // Then for any banks `B` in `R_descendants`, it must be that `B.ancestors.contains(S)`,
849        // where `S` is any ancestor of `B` such that `S >= R`.
850        //
851        // For example:
852        //          `R` -> `A` -> `C` -> `B`
853        // Then `B.ancestors == {R, A, C}`
854        //
855        // Next we call `BankForks::set_root()` at some descendant of `R`, `R_new`,
856        // where `R_new > R`.
857        //
858        // When we squash `R_new`, `max_root` in the AccountsIndex here is now set to `R_new`,
859        // and all nondescendants of `R_new` are pruned.
860        //
861        // Now consider any outstanding references to banks in the system that are descended from
862        // `max_root == R_new`. Take any one of these references and call it `B`. Because `B` is
863        // a descendant of `R_new`, this means `B` was also a descendant of `R`. Thus `B`
864        // must be a member of `R_descendants` because `B` was constructed and added to
865        // BankForks before the `set_root`.
866        //
867        // This means by the guarantees of `R_descendants` described above, because
868        // `R_new` is an ancestor of `B`, and `R < R_new < B`, then `B.ancestors.contains(R_new)`.
869        //
870        // Now until the next `set_root`, any new banks constructed from `new_from_parent` will
871        // also have `max_root == R_new` in their ancestor set, so the claim holds for those descendants
872        // as well. Once the next `set_root` happens, we once again update `max_root` and the same
873        // inductive argument can be applied again to show the claim holds.
874
875        // Check that the `max_root` is present in `ancestors`. From the proof above, if
876        // `max_root` is not present in `ancestors`, this means the bank `B` with the
877        // given `ancestors` is not descended from `max_root, which means
878        // either:
879        // 1) `B` is on a different fork or
880        // 2) `B` is an ancestor of `max_root`.
881        // In both cases we can ignore the given ancestors and instead just rely on the roots
882        // present as `max_root` indicates the roots present in the index are more up to date
883        // than the ancestors given.
884        let empty = Ancestors::default();
885        let ancestors = if ancestors.contains_key(&max_root) {
886            ancestors
887        } else {
888            /*
889            This takes of edge cases like:
890
891            Diagram 1:
892
893                        slot 0
894                          |
895                        slot 1
896                      /        \
897                 slot 2         |
898                    |       slot 3 (max root)
899            slot 4 (scan)
900
901            By the time the scan on slot 4 is called, slot 2 may already have been
902            cleaned by a clean on slot 3, but slot 4 may not have been cleaned.
903            The state in slot 2 would have been purged and is not saved in any roots.
904            In this case, a scan on slot 4 wouldn't accurately reflect the state when bank 4
905            was frozen. In cases like this, we default to a scan on the latest roots by
906            removing all `ancestors`.
907            */
908            &empty
909        };
910
911        /*
912        Now there are two cases, either `ancestors` is empty or nonempty:
913
914        1) If ancestors is empty, then this is the same as a scan on a rooted bank,
915        and `ongoing_scan_roots` provides protection against cleanup of roots necessary
916        for the scan, and  passing `Some(max_root)` to `do_scan_accounts()` ensures newer
917        roots don't appear in the scan.
918
919        2) If ancestors is non-empty, then from the `ancestors_contains(&max_root)` above, we know
920        that the fork structure must look something like:
921
922        Diagram 2:
923
924                Build fork structure:
925                        slot 0
926                          |
927                    slot 1 (max_root)
928                    /            \
929             slot 2              |
930                |            slot 3 (potential newer max root)
931              slot 4
932                |
933             slot 5 (scan)
934
935        Consider both types of ancestors, ancestor <= `max_root` and
936        ancestor > `max_root`, where `max_root == 1` as illustrated above.
937
938        a) The set of `ancestors <= max_root` are all rooted, which means their state
939        is protected by the same guarantees as 1).
940
941        b) As for the `ancestors > max_root`, those banks have at least one reference discoverable
942        through the chain of `Bank::BankRc::parent` starting from the calling bank. For instance
943        bank 5's parent reference keeps bank 4 alive, which will prevent the `Bank::drop()` from
944        running and cleaning up bank 4. Furthermore, no cleans can happen past the saved max_root == 1,
945        so a potential newer max root at 3 will not clean up any of the ancestors > 1, so slot 4
946        will not be cleaned in the middle of the scan either. (NOTE similar reasoning is employed for
947        assert!() justification in AccountsDb::retry_to_get_account_accessor)
948        */
949        match scan_type {
950            ScanTypes::Unindexed(range) => {
951                // Pass "" not to log metrics, so RPC doesn't get spammy
952                self.do_scan_accounts(metric_name, ancestors, func, range, Some(max_root), config);
953            }
954            ScanTypes::Indexed(IndexKey::ProgramId(program_id)) => {
955                self.do_scan_secondary_index(
956                    ancestors,
957                    func,
958                    &self.program_id_index,
959                    &program_id,
960                    Some(max_root),
961                    config,
962                );
963            }
964            ScanTypes::Indexed(IndexKey::SplTokenMint(mint_key)) => {
965                self.do_scan_secondary_index(
966                    ancestors,
967                    func,
968                    &self.spl_token_mint_index,
969                    &mint_key,
970                    Some(max_root),
971                    config,
972                );
973            }
974            ScanTypes::Indexed(IndexKey::SplTokenOwner(owner_key)) => {
975                self.do_scan_secondary_index(
976                    ancestors,
977                    func,
978                    &self.spl_token_owner_index,
979                    &owner_key,
980                    Some(max_root),
981                    config,
982                );
983            }
984        }
985
986        {
987            self.active_scans.fetch_sub(1, Ordering::Relaxed);
988            let mut ongoing_scan_roots = self.ongoing_scan_roots.write().unwrap();
989            let count = ongoing_scan_roots.get_mut(&max_root).unwrap();
990            *count -= 1;
991            if *count == 0 {
992                ongoing_scan_roots.remove(&max_root);
993            }
994        }
995
996        // If the fork with tip at bank `scan_bank_id` was removed during our scan, then the scan
997        // may have been corrupted, so abort the results.
998        let was_scan_corrupted = self
999            .removed_bank_ids
1000            .lock()
1001            .unwrap()
1002            .contains(&scan_bank_id);
1003
1004        if was_scan_corrupted {
1005            Err(ScanError::SlotRemoved {
1006                slot: ancestors.max_slot(),
1007                bank_id: scan_bank_id,
1008            })
1009        } else {
1010            Ok(())
1011        }
1012    }
1013
1014    fn do_unchecked_scan_accounts<F, R>(
1015        &self,
1016        metric_name: &'static str,
1017        ancestors: &Ancestors,
1018        func: F,
1019        range: Option<R>,
1020        config: &ScanConfig,
1021    ) where
1022        F: FnMut(&Pubkey, (&T, Slot)),
1023        R: RangeBounds<Pubkey> + std::fmt::Debug,
1024    {
1025        self.do_scan_accounts(metric_name, ancestors, func, range, None, config);
1026    }
1027
1028    // Scan accounts and return latest version of each account that is either:
1029    // 1) rooted or
1030    // 2) present in ancestors
1031    fn do_scan_accounts<F, R>(
1032        &self,
1033        metric_name: &'static str,
1034        ancestors: &Ancestors,
1035        mut func: F,
1036        range: Option<R>,
1037        max_root: Option<Slot>,
1038        config: &ScanConfig,
1039    ) where
1040        F: FnMut(&Pubkey, (&T, Slot)),
1041        R: RangeBounds<Pubkey> + std::fmt::Debug,
1042    {
1043        // TODO: expand to use mint index to find the `pubkey_list` below more efficiently
1044        // instead of scanning the entire range
1045        let mut total_elapsed_timer = Measure::start("total");
1046        let mut num_keys_iterated = 0;
1047        let mut latest_slot_elapsed = 0;
1048        let mut load_account_elapsed = 0;
1049        let mut read_lock_elapsed = 0;
1050        let mut iterator_elapsed = 0;
1051        let mut iterator_timer = Measure::start("iterator_elapsed");
1052        for pubkey_list in self.iter(range.as_ref(), config.collect_all_unsorted) {
1053            iterator_timer.stop();
1054            iterator_elapsed += iterator_timer.as_us();
1055            for (pubkey, list) in pubkey_list {
1056                num_keys_iterated += 1;
1057                let mut read_lock_timer = Measure::start("read_lock");
1058                let list_r = &list.slot_list.read().unwrap();
1059                read_lock_timer.stop();
1060                read_lock_elapsed += read_lock_timer.as_us();
1061                let mut latest_slot_timer = Measure::start("latest_slot");
1062                if let Some(index) = self.latest_slot(Some(ancestors), list_r, max_root) {
1063                    latest_slot_timer.stop();
1064                    latest_slot_elapsed += latest_slot_timer.as_us();
1065                    let mut load_account_timer = Measure::start("load_account");
1066                    func(&pubkey, (&list_r[index].1, list_r[index].0));
1067                    load_account_timer.stop();
1068                    load_account_elapsed += load_account_timer.as_us();
1069                }
1070                if config.is_aborted() {
1071                    return;
1072                }
1073            }
1074            iterator_timer = Measure::start("iterator_elapsed");
1075        }
1076
1077        total_elapsed_timer.stop();
1078        if !metric_name.is_empty() {
1079            datapoint_info!(
1080                metric_name,
1081                ("total_elapsed", total_elapsed_timer.as_us(), i64),
1082                ("latest_slot_elapsed", latest_slot_elapsed, i64),
1083                ("read_lock_elapsed", read_lock_elapsed, i64),
1084                ("load_account_elapsed", load_account_elapsed, i64),
1085                ("iterator_elapsed", iterator_elapsed, i64),
1086                ("num_keys_iterated", num_keys_iterated, i64),
1087            )
1088        }
1089    }
1090
1091    fn do_scan_secondary_index<
1092        F,
1093        SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
1094    >(
1095        &self,
1096        ancestors: &Ancestors,
1097        mut func: F,
1098        index: &SecondaryIndex<SecondaryIndexEntryType>,
1099        index_key: &Pubkey,
1100        max_root: Option<Slot>,
1101        config: &ScanConfig,
1102    ) where
1103        F: FnMut(&Pubkey, (&T, Slot)),
1104    {
1105        for pubkey in index.get(index_key) {
1106            if config.is_aborted() {
1107                break;
1108            }
1109            if let Some(entry) = self.get_cloned(&pubkey) {
1110                self.get_account_info_with_and_then(
1111                    &entry,
1112                    Some(ancestors),
1113                    max_root,
1114                    |(slot, account_info)| func(&pubkey, (&account_info, slot)),
1115                );
1116            };
1117        }
1118    }
1119
1120    /// Gets the index's entry for `pubkey` and applies `callback` to it
1121    ///
1122    /// If `callback`'s boolean return value is true, add this entry to the in-mem cache.
1123    pub fn get_and_then<R>(
1124        &self,
1125        pubkey: &Pubkey,
1126        callback: impl FnOnce(Option<&AccountMapEntryInner<T>>) -> (bool, R),
1127    ) -> R {
1128        self.get_bin(pubkey).get_internal_inner(pubkey, callback)
1129    }
1130
1131    /// Gets the index's entry for `pubkey`, with `ancestors` and `max_root`,
1132    /// and applies `callback` to it
1133    pub(crate) fn get_with_and_then<R>(
1134        &self,
1135        pubkey: &Pubkey,
1136        ancestors: Option<&Ancestors>,
1137        max_root: Option<Slot>,
1138        should_add_to_in_mem_cache: bool,
1139        callback: impl FnOnce((Slot, T)) -> R,
1140    ) -> Option<R> {
1141        self.get_and_then(pubkey, |entry| {
1142            let callback_result = entry.and_then(|entry| {
1143                self.get_account_info_with_and_then(entry, ancestors, max_root, callback)
1144            });
1145            (should_add_to_in_mem_cache, callback_result)
1146        })
1147    }
1148
1149    /// Gets the account info (and slot) in `entry`, with `ancestors` and `max_root`,
1150    /// and applies `callback` to it
1151    pub(crate) fn get_account_info_with_and_then<R>(
1152        &self,
1153        entry: &AccountMapEntryInner<T>,
1154        ancestors: Option<&Ancestors>,
1155        max_root: Option<Slot>,
1156        callback: impl FnOnce((Slot, T)) -> R,
1157    ) -> Option<R> {
1158        let slot_list = entry.slot_list.read().unwrap();
1159        self.latest_slot(ancestors, &slot_list, max_root)
1160            .map(|found_index| callback(slot_list[found_index]))
1161    }
1162
1163    /// Gets the index's entry for `pubkey` and clones it
1164    ///
1165    /// Prefer `get_and_then()` whenever possible.
1166    pub fn get_cloned(&self, pubkey: &Pubkey) -> Option<AccountMapEntry<T>> {
1167        self.get_bin(pubkey)
1168            .get_internal_cloned(pubkey, |entry| entry)
1169    }
1170
1171    /// Is `pubkey` in the index?
1172    pub fn contains(&self, pubkey: &Pubkey) -> bool {
1173        self.get_and_then(pubkey, |entry| (false, entry.is_some()))
1174    }
1175
1176    /// Is `pubkey`, with `ancestors` and `max_root`, in the index?
1177    #[cfg(test)]
1178    pub(crate) fn contains_with(
1179        &self,
1180        pubkey: &Pubkey,
1181        ancestors: Option<&Ancestors>,
1182        max_root: Option<Slot>,
1183    ) -> bool {
1184        self.get_with_and_then(pubkey, ancestors, max_root, false, |_| ())
1185            .is_some()
1186    }
1187
1188    fn slot_list_mut<RT>(
1189        &self,
1190        pubkey: &Pubkey,
1191        user_fn: impl FnOnce(&mut SlotList<T>) -> RT,
1192    ) -> Option<RT> {
1193        let read_lock = self.get_bin(pubkey);
1194        read_lock.slot_list_mut(pubkey, user_fn)
1195    }
1196
1197    /// Remove keys from the account index if the key's slot list is empty.
1198    /// Returns the keys that were removed from the index. These keys should not be accessed again in the current code path.
1199    #[must_use]
1200    pub fn handle_dead_keys(
1201        &self,
1202        dead_keys: &[&Pubkey],
1203        account_indexes: &AccountSecondaryIndexes,
1204    ) -> HashSet<Pubkey> {
1205        let mut pubkeys_removed_from_accounts_index = HashSet::default();
1206        if !dead_keys.is_empty() {
1207            for key in dead_keys.iter() {
1208                let w_index = self.get_bin(key);
1209                if w_index.remove_if_slot_list_empty(**key) {
1210                    pubkeys_removed_from_accounts_index.insert(**key);
1211                    // Note it's only safe to remove all the entries for this key
1212                    // because we have the lock for this key's entry in the AccountsIndex,
1213                    // so no other thread is also updating the index
1214                    self.purge_secondary_indexes_by_inner_key(key, account_indexes);
1215                }
1216            }
1217        }
1218        pubkeys_removed_from_accounts_index
1219    }
1220
1221    /// call func with every pubkey and index visible from a given set of ancestors
1222    pub(crate) fn scan_accounts<F>(
1223        &self,
1224        ancestors: &Ancestors,
1225        scan_bank_id: BankId,
1226        func: F,
1227        config: &ScanConfig,
1228    ) -> Result<(), ScanError>
1229    where
1230        F: FnMut(&Pubkey, (&T, Slot)),
1231    {
1232        // Pass "" not to log metrics, so RPC doesn't get spammy
1233        self.do_checked_scan_accounts(
1234            "",
1235            ancestors,
1236            scan_bank_id,
1237            func,
1238            ScanTypes::Unindexed(None::<Range<Pubkey>>),
1239            config,
1240        )
1241    }
1242
1243    pub(crate) fn unchecked_scan_accounts<F>(
1244        &self,
1245        metric_name: &'static str,
1246        ancestors: &Ancestors,
1247        func: F,
1248        config: &ScanConfig,
1249    ) where
1250        F: FnMut(&Pubkey, (&T, Slot)),
1251    {
1252        self.do_unchecked_scan_accounts(
1253            metric_name,
1254            ancestors,
1255            func,
1256            None::<Range<Pubkey>>,
1257            config,
1258        );
1259    }
1260
1261    /// call func with every pubkey and index visible from a given set of ancestors with range
1262    /// Only guaranteed to be safe when called from rent collection
1263    pub(crate) fn range_scan_accounts<F, R>(
1264        &self,
1265        metric_name: &'static str,
1266        ancestors: &Ancestors,
1267        range: R,
1268        config: &ScanConfig,
1269        func: F,
1270    ) where
1271        F: FnMut(&Pubkey, (&T, Slot)),
1272        R: RangeBounds<Pubkey> + std::fmt::Debug,
1273    {
1274        // Only the rent logic should be calling this, which doesn't need the safety checks
1275        self.do_unchecked_scan_accounts(metric_name, ancestors, func, Some(range), config);
1276    }
1277
1278    /// call func with every pubkey and index visible from a given set of ancestors
1279    pub(crate) fn index_scan_accounts<F>(
1280        &self,
1281        ancestors: &Ancestors,
1282        scan_bank_id: BankId,
1283        index_key: IndexKey,
1284        func: F,
1285        config: &ScanConfig,
1286    ) -> Result<(), ScanError>
1287    where
1288        F: FnMut(&Pubkey, (&T, Slot)),
1289    {
1290        // Pass "" not to log metrics, so RPC doesn't get spammy
1291        self.do_checked_scan_accounts(
1292            "",
1293            ancestors,
1294            scan_bank_id,
1295            func,
1296            ScanTypes::<Range<Pubkey>>::Indexed(index_key),
1297            config,
1298        )
1299    }
1300
1301    pub fn get_rooted_entries(
1302        &self,
1303        slice: SlotSlice<T>,
1304        max_inclusive: Option<Slot>,
1305    ) -> SlotList<T> {
1306        let max_inclusive = max_inclusive.unwrap_or(Slot::MAX);
1307        let lock = &self.roots_tracker.read().unwrap().alive_roots;
1308        slice
1309            .iter()
1310            .filter(|(slot, _)| *slot <= max_inclusive && lock.contains(slot))
1311            .cloned()
1312            .collect()
1313    }
1314
1315    /// returns true if, after this fn call:
1316    /// accounts index entry for `pubkey` has an empty slot list
1317    /// or `pubkey` does not exist in accounts index
1318    pub(crate) fn purge_exact<'a, C>(
1319        &'a self,
1320        pubkey: &Pubkey,
1321        slots_to_purge: &'a C,
1322        reclaims: &mut SlotList<T>,
1323    ) -> bool
1324    where
1325        C: Contains<'a, Slot>,
1326    {
1327        self.slot_list_mut(pubkey, |slot_list| {
1328            slot_list.retain(|(slot, item)| {
1329                let should_purge = slots_to_purge.contains(slot);
1330                if should_purge {
1331                    reclaims.push((*slot, *item));
1332                    false
1333                } else {
1334                    true
1335                }
1336            });
1337            slot_list.is_empty()
1338        })
1339        .unwrap_or(true)
1340    }
1341
1342    pub fn min_ongoing_scan_root(&self) -> Option<Slot> {
1343        Self::min_ongoing_scan_root_from_btree(&self.ongoing_scan_roots.read().unwrap())
1344    }
1345
1346    // Given a SlotSlice `L`, a list of ancestors and a maximum slot, find the latest element
1347    // in `L`, where the slot `S` is an ancestor or root, and if `S` is a root, then `S <= max_root`
1348    pub(crate) fn latest_slot(
1349        &self,
1350        ancestors: Option<&Ancestors>,
1351        slice: SlotSlice<T>,
1352        max_root_inclusive: Option<Slot>,
1353    ) -> Option<usize> {
1354        let mut current_max = 0;
1355        let mut rv = None;
1356        if let Some(ancestors) = ancestors {
1357            if !ancestors.is_empty() {
1358                for (i, (slot, _t)) in slice.iter().rev().enumerate() {
1359                    if (rv.is_none() || *slot > current_max) && ancestors.contains_key(slot) {
1360                        rv = Some(i);
1361                        current_max = *slot;
1362                    }
1363                }
1364            }
1365        }
1366
1367        let max_root_inclusive = max_root_inclusive.unwrap_or(Slot::MAX);
1368        let mut tracker = None;
1369
1370        for (i, (slot, _t)) in slice.iter().rev().enumerate() {
1371            if (rv.is_none() || *slot > current_max) && *slot <= max_root_inclusive {
1372                let lock = match tracker {
1373                    Some(inner) => inner,
1374                    None => self.roots_tracker.read().unwrap(),
1375                };
1376                if lock.alive_roots.contains(slot) {
1377                    rv = Some(i);
1378                    current_max = *slot;
1379                }
1380                tracker = Some(lock);
1381            }
1382        }
1383
1384        rv.map(|index| slice.len() - 1 - index)
1385    }
1386
1387    pub fn hold_range_in_memory<R>(&self, range: &R, start_holding: bool, thread_pool: &ThreadPool)
1388    where
1389        R: RangeBounds<Pubkey> + Debug + Sync,
1390    {
1391        let iter = self.iter(Some(range), true);
1392        iter.hold_range_in_memory(range, start_holding, thread_pool);
1393    }
1394
1395    /// get stats related to startup
1396    pub(crate) fn get_startup_stats(&self) -> &StartupStats {
1397        &self.storage.storage.startup_stats
1398    }
1399
1400    pub fn set_startup(&self, value: Startup) {
1401        self.storage.set_startup(value);
1402    }
1403
1404    pub fn get_startup_remaining_items_to_flush_estimate(&self) -> usize {
1405        self.storage.get_startup_remaining_items_to_flush_estimate()
1406    }
1407
1408    /// Scan AccountsIndex for a given iterator of Pubkeys.
1409    ///
1410    /// This fn takes 4 arguments.
1411    ///  - an iterator of pubkeys to scan
1412    ///  - callback fn to run for each pubkey in the accounts index
1413    ///  - avoid_callback_result. If it is Some(default), then callback is ignored and
1414    ///    default is returned instead.
1415    ///  - provide_entry_in_callback. If true, populate the ref of the Arc of the
1416    ///    index entry to `callback` fn. Otherwise, provide None.
1417    ///
1418    /// The `callback` fn must return `AccountsIndexScanResult`, which is
1419    /// used to indicates whether the AccountIndex Entry should be added to
1420    /// in-memory cache. The `callback` fn takes in 3 arguments:
1421    ///   - the first an immutable ref of the pubkey,
1422    ///   - the second an option of the SlotList and RefCount
1423    ///   - the third an option of the AccountMapEntry, which is only populated
1424    ///     when `provide_entry_in_callback` is true. Otherwise, it will be
1425    ///     None.
1426    pub(crate) fn scan<'a, F, I>(
1427        &self,
1428        pubkeys: I,
1429        mut callback: F,
1430        avoid_callback_result: Option<AccountsIndexScanResult>,
1431        provide_entry_in_callback: bool,
1432        filter: ScanFilter,
1433    ) where
1434        F: FnMut(
1435            &'a Pubkey,
1436            Option<(&SlotList<T>, RefCount)>,
1437            Option<&AccountMapEntry<T>>,
1438        ) -> AccountsIndexScanResult,
1439        I: Iterator<Item = &'a Pubkey>,
1440    {
1441        let mut lock = None;
1442        let mut last_bin = self.bins(); // too big, won't match
1443        pubkeys.into_iter().for_each(|pubkey| {
1444            let bin = self.bin_calculator.bin_from_pubkey(pubkey);
1445            if bin != last_bin {
1446                // cannot re-use lock since next pubkey is in a different bin than previous one
1447                lock = Some(&self.account_maps[bin]);
1448                last_bin = bin;
1449            }
1450
1451            let mut internal_callback = |entry: Option<&AccountMapEntry<T>>| {
1452                let mut cache = false;
1453                match entry {
1454                    Some(locked_entry) => {
1455                        let result = if let Some(result) = avoid_callback_result.as_ref() {
1456                            *result
1457                        } else {
1458                            let slot_list = &locked_entry.slot_list.read().unwrap();
1459                            callback(
1460                                pubkey,
1461                                Some((slot_list, locked_entry.ref_count())),
1462                                provide_entry_in_callback.then_some(locked_entry),
1463                            )
1464                        };
1465                        cache = match result {
1466                            AccountsIndexScanResult::Unref => {
1467                                if locked_entry.unref() == 0 {
1468                                    info!("scan: refcount of item already at 0: {pubkey}");
1469                                    self.unref_zero_count.fetch_add(1, Ordering::Relaxed);
1470                                }
1471                                true
1472                            }
1473                            AccountsIndexScanResult::UnrefAssert0 => {
1474                                assert_eq!(
1475                                    locked_entry.unref(),
1476                                    1,
1477                                    "ref count expected to be zero, but is {}! {pubkey}, {:?}",
1478                                    locked_entry.ref_count(),
1479                                    locked_entry.slot_list.read().unwrap(),
1480                                );
1481                                true
1482                            }
1483                            AccountsIndexScanResult::UnrefLog0 => {
1484                                let old_ref = locked_entry.unref();
1485                                if old_ref != 1 {
1486                                    info!("Unexpected unref {pubkey} with {old_ref} {:?}, expect old_ref to be 1", locked_entry.slot_list.read().unwrap());
1487                                    datapoint_warn!(
1488                                        "accounts_db-unexpected-unref-zero",
1489                                        ("old_ref", old_ref, i64),
1490                                        ("pubkey", pubkey.to_string(), String),
1491                                    );
1492                                }
1493                                true
1494                            }
1495                            AccountsIndexScanResult::KeepInMemory => true,
1496                            AccountsIndexScanResult::OnlyKeepInMemoryIfDirty => false,
1497                        };
1498                    }
1499                    None => {
1500                        avoid_callback_result.unwrap_or_else(|| callback(pubkey, None, None));
1501                    }
1502                }
1503                (cache, ())
1504            };
1505
1506            match filter {
1507                ScanFilter::All => {
1508                    // SAFETY: The caller must ensure that if `provide_entry_in_callback` is true, and
1509                    // if it's possible for `callback` to clone the entry Arc, then it must also add
1510                    // the entry to the in-mem cache if the entry is made dirty.
1511                    lock.as_ref()
1512                        .unwrap()
1513                        .get_internal(pubkey, internal_callback);
1514                }
1515                ScanFilter::OnlyAbnormal | ScanFilter::OnlyAbnormalWithVerify => {
1516                    let found = lock
1517                        .as_ref()
1518                        .unwrap()
1519                        .get_only_in_mem(pubkey, false, |entry| {
1520                            internal_callback(entry);
1521                            entry.is_some()
1522                        });
1523                    if !found && matches!(filter, ScanFilter::OnlyAbnormalWithVerify) {
1524                        lock.as_ref().unwrap().get_internal(pubkey, |entry| {
1525                            assert!(entry.is_some(), "{pubkey}, entry: {entry:?}");
1526                            let entry = entry.unwrap();
1527                            assert_eq!(entry.ref_count(), 1, "{pubkey}");
1528                            assert_eq!(entry.slot_list.read().unwrap().len(), 1, "{pubkey}");
1529                            (false, ())
1530                        });
1531                    }
1532                }
1533            }
1534        });
1535    }
1536
1537    // Get the maximum root <= `max_allowed_root` from the given `slice`
1538    fn get_newest_root_in_slot_list(
1539        alive_roots: &RollingBitField,
1540        slice: SlotSlice<T>,
1541        max_allowed_root_inclusive: Option<Slot>,
1542    ) -> Slot {
1543        let mut max_root = 0;
1544        for (slot, _) in slice.iter() {
1545            if let Some(max_allowed_root_inclusive) = max_allowed_root_inclusive {
1546                if *slot > max_allowed_root_inclusive {
1547                    continue;
1548                }
1549            }
1550            if *slot > max_root && alive_roots.contains(slot) {
1551                max_root = *slot;
1552            }
1553        }
1554        max_root
1555    }
1556
1557    fn update_spl_token_secondary_indexes<G: solana_inline_spl::token::GenericTokenAccount>(
1558        &self,
1559        token_id: &Pubkey,
1560        pubkey: &Pubkey,
1561        account_owner: &Pubkey,
1562        account_data: &[u8],
1563        account_indexes: &AccountSecondaryIndexes,
1564    ) {
1565        if *account_owner == *token_id {
1566            if account_indexes.contains(&AccountIndex::SplTokenOwner) {
1567                if let Some(owner_key) = G::unpack_account_owner(account_data) {
1568                    if account_indexes.include_key(owner_key) {
1569                        self.spl_token_owner_index.insert(owner_key, pubkey);
1570                    }
1571                }
1572            }
1573
1574            if account_indexes.contains(&AccountIndex::SplTokenMint) {
1575                if let Some(mint_key) = G::unpack_account_mint(account_data) {
1576                    if account_indexes.include_key(mint_key) {
1577                        self.spl_token_mint_index.insert(mint_key, pubkey);
1578                    }
1579                }
1580            }
1581        }
1582    }
1583
1584    pub fn get_index_key_size(&self, index: &AccountIndex, index_key: &Pubkey) -> Option<usize> {
1585        match index {
1586            AccountIndex::ProgramId => self.program_id_index.index.get(index_key).map(|x| x.len()),
1587            AccountIndex::SplTokenOwner => self
1588                .spl_token_owner_index
1589                .index
1590                .get(index_key)
1591                .map(|x| x.len()),
1592            AccountIndex::SplTokenMint => self
1593                .spl_token_mint_index
1594                .index
1595                .get(index_key)
1596                .map(|x| x.len()),
1597        }
1598    }
1599
1600    /// log any secondary index counts, if non-zero
1601    pub(crate) fn log_secondary_indexes(&self) {
1602        if !self.program_id_index.index.is_empty() {
1603            info!("secondary index: {:?}", AccountIndex::ProgramId);
1604            self.program_id_index.log_contents();
1605        }
1606        if !self.spl_token_mint_index.index.is_empty() {
1607            info!("secondary index: {:?}", AccountIndex::SplTokenMint);
1608            self.spl_token_mint_index.log_contents();
1609        }
1610        if !self.spl_token_owner_index.index.is_empty() {
1611            info!("secondary index: {:?}", AccountIndex::SplTokenOwner);
1612            self.spl_token_owner_index.log_contents();
1613        }
1614    }
1615
1616    pub(crate) fn update_secondary_indexes(
1617        &self,
1618        pubkey: &Pubkey,
1619        account: &impl ReadableAccount,
1620        account_indexes: &AccountSecondaryIndexes,
1621    ) {
1622        if account_indexes.is_empty() {
1623            return;
1624        }
1625
1626        let account_owner = account.owner();
1627        let account_data = account.data();
1628
1629        if account_indexes.contains(&AccountIndex::ProgramId)
1630            && account_indexes.include_key(account_owner)
1631        {
1632            self.program_id_index.insert(account_owner, pubkey);
1633        }
1634        // Note because of the below check below on the account data length, when an
1635        // account hits zero lamports and is reset to AccountSharedData::Default, then we skip
1636        // the below updates to the secondary indexes.
1637        //
1638        // Skipping means not updating secondary index to mark the account as missing.
1639        // This doesn't introduce false positives during a scan because the caller to scan
1640        // provides the ancestors to check. So even if a zero-lamport account is not yet
1641        // removed from the secondary index, the scan function will:
1642        // 1) consult the primary index via `get(&pubkey, Some(ancestors), max_root)`
1643        // and find the zero-lamport version
1644        // 2) When the fetch from storage occurs, it will return AccountSharedData::Default
1645        // (as persisted tombstone for snapshots). This will then ultimately be
1646        // filtered out by post-scan filters, like in `get_filtered_spl_token_accounts_by_owner()`.
1647
1648        self.update_spl_token_secondary_indexes::<solana_inline_spl::token::Account>(
1649            &solana_inline_spl::token::id(),
1650            pubkey,
1651            account_owner,
1652            account_data,
1653            account_indexes,
1654        );
1655        self.update_spl_token_secondary_indexes::<solana_inline_spl::token_2022::Account>(
1656            &solana_inline_spl::token_2022::id(),
1657            pubkey,
1658            account_owner,
1659            account_data,
1660            account_indexes,
1661        );
1662    }
1663
1664    pub(crate) fn get_bin(&self, pubkey: &Pubkey) -> AccountMaps<T, U> {
1665        &self.account_maps[self.bin_calculator.bin_from_pubkey(pubkey)]
1666    }
1667
1668    pub fn bins(&self) -> usize {
1669        self.account_maps.len()
1670    }
1671
1672    /// remove the earlier instances of each pubkey when the pubkey exists later in the `Vec`.
1673    /// Could also be done with HashSet.
1674    /// Returns `HashSet` of duplicate pubkeys.
1675    fn remove_older_duplicate_pubkeys(
1676        items: &mut Vec<(Pubkey, (Slot, T))>,
1677    ) -> Option<Vec<(Pubkey, (Slot, T))>> {
1678        if items.len() < 2 {
1679            return None;
1680        }
1681        // stable sort by pubkey.
1682        // Earlier entries are overwritten by later entries
1683        items.sort_by(|a, b| a.0.cmp(&b.0));
1684        let mut duplicates = None::<Vec<(Pubkey, (Slot, T))>>;
1685
1686        // Iterate the items vec from the end to the beginning. Adjacent duplicated items will be
1687        // written to the front of the vec.
1688        let n = items.len();
1689        let mut last_key = items[n - 1].0;
1690        let mut write = n - 1;
1691        let mut curr = write;
1692
1693        while curr > 0 {
1694            let curr_item = items[curr - 1];
1695
1696            if curr_item.0 == last_key {
1697                let mut duplicates_insert = duplicates.unwrap_or_default();
1698                duplicates_insert.push(curr_item);
1699                duplicates = Some(duplicates_insert);
1700                curr -= 1;
1701            } else {
1702                if curr < write {
1703                    items[write - 1] = curr_item;
1704                }
1705                curr -= 1;
1706                write -= 1;
1707                last_key = curr_item.0;
1708            }
1709        }
1710
1711        items.drain(..(write - curr));
1712
1713        duplicates
1714    }
1715
1716    // Same functionally to upsert, but:
1717    // 1. operates on a batch of items
1718    // 2. holds the write lock for the duration of adding the items
1719    // Can save time when inserting lots of new keys.
1720    // But, does NOT update secondary index
1721    // This is designed to be called at startup time.
1722    // returns (dirty_pubkeys, insertion_time_us, GenerateIndexResult)
1723    pub(crate) fn insert_new_if_missing_into_primary_index(
1724        &self,
1725        slot: Slot,
1726        approx_items_len: usize,
1727        items: impl Iterator<Item = (Pubkey, T)>,
1728    ) -> (Vec<Pubkey>, u64, GenerateIndexResult<T>) {
1729        // big enough so not likely to re-allocate, small enough to not over-allocate by too much
1730        // this assumes the largest bin contains twice the expected amount of the average size per bin
1731        let bins = self.bins();
1732        let expected_items_per_bin = approx_items_len * 2 / bins;
1733        let use_disk = self.storage.storage.disk.is_some();
1734        let mut binned = (0..bins)
1735            .map(|_| Vec::with_capacity(expected_items_per_bin))
1736            .collect::<Vec<_>>();
1737        let mut count = 0;
1738        let mut dirty_pubkeys = items
1739            .filter_map(|(pubkey, account_info)| {
1740                let pubkey_bin = self.bin_calculator.bin_from_pubkey(&pubkey);
1741                // this value is equivalent to what update() below would have created if we inserted a new item
1742                let is_zero_lamport = account_info.is_zero_lamport();
1743                let result = if is_zero_lamport { Some(pubkey) } else { None };
1744
1745                binned[pubkey_bin].push((pubkey, (slot, account_info)));
1746                result
1747            })
1748            .collect::<Vec<_>>();
1749
1750        let insertion_time = AtomicU64::new(0);
1751
1752        // offset bin processing in the 'binned' array by a random amount.
1753        // This results in calls to insert_new_entry_if_missing_with_lock from different threads starting at different bins to avoid
1754        // lock contention.
1755        let random_offset = thread_rng().gen_range(0..bins);
1756        let mut duplicates = Vec::default();
1757        (0..bins).for_each(|pubkey_bin| {
1758            let pubkey_bin = (pubkey_bin + random_offset) % bins;
1759            let mut items = std::mem::take(&mut binned[pubkey_bin]);
1760            if items.is_empty() {
1761                return;
1762            }
1763
1764            let these_duplicates = Self::remove_older_duplicate_pubkeys(&mut items);
1765            if let Some(mut these_duplicates) = these_duplicates {
1766                duplicates.append(&mut these_duplicates);
1767            }
1768
1769            let r_account_maps = &self.account_maps[pubkey_bin];
1770            let mut insert_time = Measure::start("insert_into_primary_index");
1771            // count only considers non-duplicate accounts
1772            count += items.len();
1773            if use_disk {
1774                r_account_maps.startup_insert_only(items.into_iter());
1775            } else {
1776                // not using disk buckets, so just write to in-mem
1777                // this is no longer the default case
1778                items
1779                    .into_iter()
1780                    .for_each(|(pubkey, (slot, account_info))| {
1781                        let new_entry = PreAllocatedAccountMapEntry::new(
1782                            slot,
1783                            account_info,
1784                            &self.storage.storage,
1785                            use_disk,
1786                        );
1787                        match r_account_maps
1788                            .insert_new_entry_if_missing_with_lock(pubkey, new_entry)
1789                        {
1790                            InsertNewEntryResults::DidNotExist => {}
1791                            InsertNewEntryResults::ExistedNewEntryZeroLamports => {}
1792                            InsertNewEntryResults::ExistedNewEntryNonZeroLamports => {
1793                                dirty_pubkeys.push(pubkey);
1794                            }
1795                        }
1796                    });
1797            }
1798            insert_time.stop();
1799            insertion_time.fetch_add(insert_time.as_us(), Ordering::Relaxed);
1800        });
1801
1802        (
1803            dirty_pubkeys,
1804            insertion_time.load(Ordering::Relaxed),
1805            GenerateIndexResult {
1806                count,
1807                duplicates: (!duplicates.is_empty()).then_some(duplicates),
1808            },
1809        )
1810    }
1811
1812    /// use Vec<> because the internal vecs are already allocated per bin
1813    pub(crate) fn populate_and_retrieve_duplicate_keys_from_startup(
1814        &self,
1815        f: impl Fn(Vec<(Slot, Pubkey)>) + Sync + Send,
1816    ) {
1817        (0..self.bins())
1818            .into_par_iter()
1819            .map(|pubkey_bin| {
1820                let r_account_maps = &self.account_maps[pubkey_bin];
1821                r_account_maps.populate_and_retrieve_duplicate_keys_from_startup()
1822            })
1823            .for_each(f);
1824    }
1825
1826    /// Updates the given pubkey at the given slot with the new account information.
1827    /// on return, the index's previous account info may be returned in 'reclaims' depending on 'previous_slot_entry_was_cached'
1828    pub fn upsert(
1829        &self,
1830        new_slot: Slot,
1831        old_slot: Slot,
1832        pubkey: &Pubkey,
1833        account: &impl ReadableAccount,
1834        account_indexes: &AccountSecondaryIndexes,
1835        account_info: T,
1836        reclaims: &mut SlotList<T>,
1837        reclaim: UpsertReclaim,
1838    ) {
1839        // vast majority of updates are to item already in accounts index, so store as raw to avoid unnecessary allocations
1840        let store_raw = true;
1841
1842        // We don't atomically update both primary index and secondary index together.
1843        // This certainly creates a small time window with inconsistent state across the two indexes.
1844        // However, this is acceptable because:
1845        //
1846        //  - A strict consistent view at any given moment of time is not necessary, because the only
1847        //  use case for the secondary index is `scan`, and `scans` are only supported/require consistency
1848        //  on frozen banks, and this inconsistency is only possible on working banks.
1849        //
1850        //  - The secondary index is never consulted as primary source of truth for gets/stores.
1851        //  So, what the accounts_index sees alone is sufficient as a source of truth for other non-scan
1852        //  account operations.
1853        let new_item = PreAllocatedAccountMapEntry::new(
1854            new_slot,
1855            account_info,
1856            &self.storage.storage,
1857            store_raw,
1858        );
1859        let map = self.get_bin(pubkey);
1860
1861        map.upsert(pubkey, new_item, Some(old_slot), reclaims, reclaim);
1862        self.update_secondary_indexes(pubkey, account, account_indexes);
1863    }
1864
1865    pub fn ref_count_from_storage(&self, pubkey: &Pubkey) -> RefCount {
1866        let map = self.get_bin(pubkey);
1867        map.get_internal_inner(pubkey, |entry| {
1868            (
1869                false,
1870                entry.map(|entry| entry.ref_count()).unwrap_or_default(),
1871            )
1872        })
1873    }
1874
1875    fn purge_secondary_indexes_by_inner_key(
1876        &self,
1877        inner_key: &Pubkey,
1878        account_indexes: &AccountSecondaryIndexes,
1879    ) {
1880        if account_indexes.contains(&AccountIndex::ProgramId) {
1881            self.program_id_index.remove_by_inner_key(inner_key);
1882        }
1883
1884        if account_indexes.contains(&AccountIndex::SplTokenOwner) {
1885            self.spl_token_owner_index.remove_by_inner_key(inner_key);
1886        }
1887
1888        if account_indexes.contains(&AccountIndex::SplTokenMint) {
1889            self.spl_token_mint_index.remove_by_inner_key(inner_key);
1890        }
1891    }
1892
1893    fn purge_older_root_entries(
1894        &self,
1895        slot_list: &mut SlotList<T>,
1896        reclaims: &mut SlotList<T>,
1897        max_clean_root_inclusive: Option<Slot>,
1898    ) {
1899        if slot_list.len() <= 1 {
1900            self.purge_older_root_entries_one_slot_list
1901                .fetch_add(1, Ordering::Relaxed);
1902        }
1903        let newest_root_in_slot_list;
1904        let max_clean_root_inclusive = {
1905            let roots_tracker = &self.roots_tracker.read().unwrap();
1906            newest_root_in_slot_list = Self::get_newest_root_in_slot_list(
1907                &roots_tracker.alive_roots,
1908                slot_list,
1909                max_clean_root_inclusive,
1910            );
1911            max_clean_root_inclusive.unwrap_or_else(|| roots_tracker.alive_roots.max_inclusive())
1912        };
1913
1914        slot_list.retain(|(slot, value)| {
1915            let should_purge = Self::can_purge_older_entries(
1916                // Note that we have a root that is inclusive here.
1917                // Calling a function that expects 'exclusive'
1918                // This is expected behavior for this call.
1919                max_clean_root_inclusive,
1920                newest_root_in_slot_list,
1921                *slot,
1922            ) && !value.is_cached();
1923            if should_purge {
1924                reclaims.push((*slot, *value));
1925            }
1926            !should_purge
1927        });
1928    }
1929
1930    /// return true if pubkey was removed from the accounts index
1931    ///  or does not exist in the accounts index
1932    /// This means it should NOT be unref'd later.
1933    #[must_use]
1934    pub fn clean_rooted_entries(
1935        &self,
1936        pubkey: &Pubkey,
1937        reclaims: &mut SlotList<T>,
1938        max_clean_root_inclusive: Option<Slot>,
1939    ) -> bool {
1940        let mut is_slot_list_empty = false;
1941        let missing_in_accounts_index = self
1942            .slot_list_mut(pubkey, |slot_list| {
1943                self.purge_older_root_entries(slot_list, reclaims, max_clean_root_inclusive);
1944                is_slot_list_empty = slot_list.is_empty();
1945            })
1946            .is_none();
1947
1948        let mut removed = false;
1949        // If the slot list is empty, remove the pubkey from `account_maps`. Make sure to grab the
1950        // lock and double check the slot list is still empty, because another writer could have
1951        // locked and inserted the pubkey in-between when `is_slot_list_empty=true` and the call to
1952        // remove() below.
1953        if is_slot_list_empty {
1954            let w_maps = self.get_bin(pubkey);
1955            removed = w_maps.remove_if_slot_list_empty(*pubkey);
1956        }
1957        removed || missing_in_accounts_index
1958    }
1959
1960    /// When can an entry be purged?
1961    ///
1962    /// If we get a slot update where slot != newest_root_in_slot_list for an account where slot <
1963    /// max_clean_root_exclusive, then we know it's safe to delete because:
1964    ///
1965    /// a) If slot < newest_root_in_slot_list, then we know the update is outdated by a later rooted
1966    /// update, namely the one in newest_root_in_slot_list
1967    ///
1968    /// b) If slot > newest_root_in_slot_list, then because slot < max_clean_root_exclusive and we know there are
1969    /// no roots in the slot list between newest_root_in_slot_list and max_clean_root_exclusive, (otherwise there
1970    /// would be a bigger newest_root_in_slot_list, which is a contradiction), then we know slot must be
1971    /// an unrooted slot less than max_clean_root_exclusive and thus safe to clean as well.
1972    fn can_purge_older_entries(
1973        max_clean_root_exclusive: Slot,
1974        newest_root_in_slot_list: Slot,
1975        slot: Slot,
1976    ) -> bool {
1977        slot < max_clean_root_exclusive && slot != newest_root_in_slot_list
1978    }
1979
1980    /// Given a list of slots, return a new list of only the slots that are rooted
1981    pub fn get_rooted_from_list<'a>(&self, slots: impl Iterator<Item = &'a Slot>) -> Vec<Slot> {
1982        let roots_tracker = self.roots_tracker.read().unwrap();
1983        slots
1984            .filter_map(|s| {
1985                if roots_tracker.alive_roots.contains(s) {
1986                    Some(*s)
1987                } else {
1988                    None
1989                }
1990            })
1991            .collect()
1992    }
1993
1994    pub fn is_alive_root(&self, slot: Slot) -> bool {
1995        self.roots_tracker
1996            .read()
1997            .unwrap()
1998            .alive_roots
1999            .contains(&slot)
2000    }
2001
2002    pub fn add_root(&self, slot: Slot) {
2003        self.roots_added.fetch_add(1, Ordering::Relaxed);
2004        let mut w_roots_tracker = self.roots_tracker.write().unwrap();
2005        // `AccountsDb::flush_accounts_cache()` relies on roots being added in order
2006        assert!(
2007            slot >= w_roots_tracker.alive_roots.max_inclusive(),
2008            "Roots must be added in order: {} < {}",
2009            slot,
2010            w_roots_tracker.alive_roots.max_inclusive()
2011        );
2012        // 'slot' is a root, so it is both 'root' and 'original'
2013        w_roots_tracker.alive_roots.insert(slot);
2014    }
2015
2016    pub fn add_uncleaned_roots<I>(&self, roots: I)
2017    where
2018        I: IntoIterator<Item = Slot>,
2019    {
2020        let mut w_roots_tracker = self.roots_tracker.write().unwrap();
2021        w_roots_tracker.uncleaned_roots.extend(roots);
2022    }
2023
2024    /// Removes `root` from `uncleaned_roots` and returns whether it was previously present
2025    #[cfg(feature = "dev-context-only-utils")]
2026    pub fn remove_uncleaned_root(&self, root: Slot) -> bool {
2027        self.roots_tracker
2028            .write()
2029            .unwrap()
2030            .uncleaned_roots
2031            .remove(&root)
2032    }
2033
2034    pub fn max_root_inclusive(&self) -> Slot {
2035        self.roots_tracker
2036            .read()
2037            .unwrap()
2038            .alive_roots
2039            .max_inclusive()
2040    }
2041
2042    /// Remove the slot when the storage for the slot is freed
2043    /// Accounts no longer reference this slot.
2044    /// return true if slot was a root
2045    pub fn clean_dead_slot(&self, slot: Slot) -> bool {
2046        let mut w_roots_tracker = self.roots_tracker.write().unwrap();
2047        let removed_from_unclean_roots = w_roots_tracker.uncleaned_roots.remove(&slot);
2048        if !w_roots_tracker.alive_roots.remove(&slot) {
2049            if removed_from_unclean_roots {
2050                error!("clean_dead_slot-removed_from_unclean_roots: {}", slot);
2051                inc_new_counter_error!("clean_dead_slot-removed_from_unclean_roots", 1, 1);
2052            }
2053            false
2054        } else {
2055            drop(w_roots_tracker);
2056            self.roots_removed.fetch_add(1, Ordering::Relaxed);
2057            true
2058        }
2059    }
2060
2061    pub(crate) fn update_roots_stats(&self, stats: &mut AccountsIndexRootsStats) {
2062        let roots_tracker = self.roots_tracker.read().unwrap();
2063        stats.roots_len = Some(roots_tracker.alive_roots.len());
2064        stats.uncleaned_roots_len = Some(roots_tracker.uncleaned_roots.len());
2065        stats.roots_range = Some(roots_tracker.alive_roots.range_width());
2066    }
2067
2068    pub fn min_alive_root(&self) -> Option<Slot> {
2069        self.roots_tracker.read().unwrap().min_alive_root()
2070    }
2071
2072    pub(crate) fn reset_uncleaned_roots(&self, max_clean_root: Option<Slot>) {
2073        let mut w_roots_tracker = self.roots_tracker.write().unwrap();
2074        w_roots_tracker.uncleaned_roots.retain(|root| {
2075            let is_cleaned = max_clean_root
2076                .map(|max_clean_root| *root <= max_clean_root)
2077                .unwrap_or(true);
2078            // Only keep the slots that have yet to be cleaned
2079            !is_cleaned
2080        });
2081    }
2082
2083    pub fn num_alive_roots(&self) -> usize {
2084        self.roots_tracker.read().unwrap().alive_roots.len()
2085    }
2086
2087    pub fn all_alive_roots(&self) -> Vec<Slot> {
2088        let tracker = self.roots_tracker.read().unwrap();
2089        tracker.alive_roots.get_all()
2090    }
2091
2092    pub fn clone_uncleaned_roots(&self) -> IntSet<Slot> {
2093        self.roots_tracker.read().unwrap().uncleaned_roots.clone()
2094    }
2095
2096    pub fn uncleaned_roots_len(&self) -> usize {
2097        self.roots_tracker.read().unwrap().uncleaned_roots.len()
2098    }
2099
2100    // These functions/fields are only usable from a dev context (i.e. tests and benches)
2101    #[cfg(feature = "dev-context-only-utils")]
2102    // filter any rooted entries and return them along with a bool that indicates
2103    // if this account has no more entries. Note this does not update the secondary
2104    // indexes!
2105    pub fn purge_roots(&self, pubkey: &Pubkey) -> (SlotList<T>, bool) {
2106        self.slot_list_mut(pubkey, |slot_list| {
2107            let reclaims = self.get_rooted_entries(slot_list, None);
2108            slot_list.retain(|(slot, _)| !self.is_alive_root(*slot));
2109            (reclaims, slot_list.is_empty())
2110        })
2111        .unwrap()
2112    }
2113}
2114
2115#[cfg(test)]
2116pub mod tests {
2117    use {
2118        super::*,
2119        solana_inline_spl::token::SPL_TOKEN_ACCOUNT_OWNER_OFFSET,
2120        solana_sdk::{
2121            account::{AccountSharedData, WritableAccount},
2122            pubkey::PUBKEY_BYTES,
2123        },
2124        std::ops::RangeInclusive,
2125    };
2126
2127    const SPL_TOKENS: &[Pubkey] = &[
2128        solana_inline_spl::token::id(),
2129        solana_inline_spl::token_2022::id(),
2130    ];
2131
2132    pub enum SecondaryIndexTypes<'a> {
2133        RwLock(&'a SecondaryIndex<RwLockSecondaryIndexEntry>),
2134        DashMap(&'a SecondaryIndex<DashMapSecondaryIndexEntry>),
2135    }
2136
2137    pub fn spl_token_mint_index_enabled() -> AccountSecondaryIndexes {
2138        let mut account_indexes = HashSet::new();
2139        account_indexes.insert(AccountIndex::SplTokenMint);
2140        AccountSecondaryIndexes {
2141            indexes: account_indexes,
2142            keys: None,
2143        }
2144    }
2145
2146    pub fn spl_token_owner_index_enabled() -> AccountSecondaryIndexes {
2147        let mut account_indexes = HashSet::new();
2148        account_indexes.insert(AccountIndex::SplTokenOwner);
2149        AccountSecondaryIndexes {
2150            indexes: account_indexes,
2151            keys: None,
2152        }
2153    }
2154
2155    fn create_spl_token_mint_secondary_index_state() -> (usize, usize, AccountSecondaryIndexes) {
2156        {
2157            // Check that we're actually testing the correct variant
2158            let index = AccountsIndex::<bool, bool>::default_for_tests();
2159            let _type_check = SecondaryIndexTypes::RwLock(&index.spl_token_mint_index);
2160        }
2161
2162        (0, PUBKEY_BYTES, spl_token_mint_index_enabled())
2163    }
2164
2165    fn create_spl_token_owner_secondary_index_state() -> (usize, usize, AccountSecondaryIndexes) {
2166        {
2167            // Check that we're actually testing the correct variant
2168            let index = AccountsIndex::<bool, bool>::default_for_tests();
2169            let _type_check = SecondaryIndexTypes::RwLock(&index.spl_token_owner_index);
2170        }
2171
2172        (
2173            SPL_TOKEN_ACCOUNT_OWNER_OFFSET,
2174            SPL_TOKEN_ACCOUNT_OWNER_OFFSET + PUBKEY_BYTES,
2175            spl_token_owner_index_enabled(),
2176        )
2177    }
2178
2179    impl<T: IndexValue> Clone for PreAllocatedAccountMapEntry<T> {
2180        fn clone(&self) -> Self {
2181            // clone the AccountMapEntryInner into a new Arc
2182            match self {
2183                PreAllocatedAccountMapEntry::Entry(entry) => {
2184                    let (slot, account_info) = entry.slot_list.read().unwrap()[0];
2185                    let meta = AccountMapEntryMeta {
2186                        dirty: AtomicBool::new(entry.dirty()),
2187                        age: AtomicAge::new(entry.age()),
2188                    };
2189                    PreAllocatedAccountMapEntry::Entry(Arc::new(AccountMapEntryInner::new(
2190                        vec![(slot, account_info)],
2191                        entry.ref_count(),
2192                        meta,
2193                    )))
2194                }
2195                PreAllocatedAccountMapEntry::Raw(raw) => PreAllocatedAccountMapEntry::Raw(*raw),
2196            }
2197        }
2198    }
2199
2200    const COLLECT_ALL_UNSORTED_FALSE: bool = false;
2201
2202    #[test]
2203    fn test_get_empty() {
2204        let key = solana_sdk::pubkey::new_rand();
2205        let index = AccountsIndex::<bool, bool>::default_for_tests();
2206        let ancestors = Ancestors::default();
2207        let key = &key;
2208        assert!(!index.contains_with(key, Some(&ancestors), None));
2209        assert!(!index.contains_with(key, None, None));
2210
2211        let mut num = 0;
2212        index.unchecked_scan_accounts(
2213            "",
2214            &ancestors,
2215            |_pubkey, _index| num += 1,
2216            &ScanConfig::default(),
2217        );
2218        assert_eq!(num, 0);
2219    }
2220
2221    #[test]
2222    fn test_remove_older_duplicate_pubkeys() {
2223        let pk1 = Pubkey::new_from_array([0; 32]);
2224        let pk2 = Pubkey::new_from_array([1; 32]);
2225        let slot0 = 0;
2226        let info2 = 55;
2227        let mut items = vec![];
2228        let removed = AccountsIndex::<u64, u64>::remove_older_duplicate_pubkeys(&mut items);
2229        assert!(items.is_empty());
2230        assert!(removed.is_none());
2231        let mut items = vec![(pk1, (slot0, 1u64)), (pk2, (slot0, 2))];
2232        let expected = items.clone();
2233        let removed = AccountsIndex::<u64, u64>::remove_older_duplicate_pubkeys(&mut items);
2234        assert_eq!(items, expected);
2235        assert!(removed.is_none());
2236
2237        for dup in 0..3 {
2238            for other in 0..dup + 2 {
2239                let first_info = 10u64;
2240                let mut items = vec![(pk1, (slot0, first_info))];
2241                let mut expected_dups = items.clone();
2242                for i in 0..dup {
2243                    let this_dup = (pk1, (slot0, i + 10u64 + 1));
2244                    if i < dup.saturating_sub(1) {
2245                        expected_dups.push(this_dup);
2246                    }
2247                    items.push(this_dup);
2248                }
2249                let mut expected = vec![*items.last().unwrap()];
2250                let other_item = (pk2, (slot0, info2));
2251                if other == dup + 1 {
2252                    // don't insert
2253                } else if other == dup {
2254                    expected.push(other_item);
2255                    items.push(other_item);
2256                } else {
2257                    expected.push(other_item);
2258                    items.insert(other as usize, other_item);
2259                }
2260                let result = AccountsIndex::<u64, u64>::remove_older_duplicate_pubkeys(&mut items);
2261                assert_eq!(items, expected);
2262                if dup != 0 {
2263                    expected_dups.reverse();
2264                    assert_eq!(result.unwrap(), expected_dups);
2265                } else {
2266                    assert!(result.is_none());
2267                }
2268            }
2269        }
2270    }
2271
2272    #[test]
2273    fn test_secondary_index_include_exclude() {
2274        let pk1 = Pubkey::new_unique();
2275        let pk2 = Pubkey::new_unique();
2276        let mut index = AccountSecondaryIndexes::default();
2277
2278        assert!(!index.contains(&AccountIndex::ProgramId));
2279        index.indexes.insert(AccountIndex::ProgramId);
2280        assert!(index.contains(&AccountIndex::ProgramId));
2281        assert!(index.include_key(&pk1));
2282        assert!(index.include_key(&pk2));
2283
2284        let exclude = false;
2285        index.keys = Some(AccountSecondaryIndexesIncludeExclude {
2286            keys: [pk1].iter().cloned().collect::<HashSet<_>>(),
2287            exclude,
2288        });
2289        assert!(index.include_key(&pk1));
2290        assert!(!index.include_key(&pk2));
2291
2292        let exclude = true;
2293        index.keys = Some(AccountSecondaryIndexesIncludeExclude {
2294            keys: [pk1].iter().cloned().collect::<HashSet<_>>(),
2295            exclude,
2296        });
2297        assert!(!index.include_key(&pk1));
2298        assert!(index.include_key(&pk2));
2299
2300        let exclude = true;
2301        index.keys = Some(AccountSecondaryIndexesIncludeExclude {
2302            keys: [pk1, pk2].iter().cloned().collect::<HashSet<_>>(),
2303            exclude,
2304        });
2305        assert!(!index.include_key(&pk1));
2306        assert!(!index.include_key(&pk2));
2307
2308        let exclude = false;
2309        index.keys = Some(AccountSecondaryIndexesIncludeExclude {
2310            keys: [pk1, pk2].iter().cloned().collect::<HashSet<_>>(),
2311            exclude,
2312        });
2313        assert!(index.include_key(&pk1));
2314        assert!(index.include_key(&pk2));
2315    }
2316
2317    const UPSERT_POPULATE_RECLAIMS: UpsertReclaim = UpsertReclaim::PopulateReclaims;
2318
2319    #[test]
2320    fn test_insert_no_ancestors() {
2321        let key = solana_sdk::pubkey::new_rand();
2322        let index = AccountsIndex::<bool, bool>::default_for_tests();
2323        let mut gc = Vec::new();
2324        index.upsert(
2325            0,
2326            0,
2327            &key,
2328            &AccountSharedData::default(),
2329            &AccountSecondaryIndexes::default(),
2330            true,
2331            &mut gc,
2332            UPSERT_POPULATE_RECLAIMS,
2333        );
2334        assert!(gc.is_empty());
2335
2336        let ancestors = Ancestors::default();
2337        assert!(!index.contains_with(&key, Some(&ancestors), None));
2338        assert!(!index.contains_with(&key, None, None));
2339
2340        let mut num = 0;
2341        index.unchecked_scan_accounts(
2342            "",
2343            &ancestors,
2344            |_pubkey, _index| num += 1,
2345            &ScanConfig::default(),
2346        );
2347        assert_eq!(num, 0);
2348    }
2349
2350    type AccountInfoTest = f64;
2351
2352    impl IndexValue for AccountInfoTest {}
2353    impl DiskIndexValue for AccountInfoTest {}
2354    impl IsCached for AccountInfoTest {
2355        fn is_cached(&self) -> bool {
2356            true
2357        }
2358    }
2359
2360    impl ZeroLamport for AccountInfoTest {
2361        fn is_zero_lamport(&self) -> bool {
2362            true
2363        }
2364    }
2365
2366    #[test]
2367    fn test_insert_duplicates() {
2368        let key = solana_sdk::pubkey::new_rand();
2369        let pubkey = &key;
2370        let slot = 0;
2371        let mut ancestors = Ancestors::default();
2372        ancestors.insert(slot, 0);
2373
2374        let account_info = true;
2375        let index = AccountsIndex::<bool, bool>::default_for_tests();
2376        let account_info2: bool = !account_info;
2377        let items = vec![(*pubkey, account_info), (*pubkey, account_info2)];
2378        index.set_startup(Startup::Startup);
2379        let (_, _, result) =
2380            index.insert_new_if_missing_into_primary_index(slot, items.len(), items.into_iter());
2381        assert_eq!(result.count, 1);
2382        index.set_startup(Startup::Normal);
2383        let index_entry = index.get_cloned(pubkey).unwrap();
2384        let slot_list = index_entry.slot_list.read().unwrap();
2385        // make sure the one with the correct info is added, and wasn't inserted twice
2386        assert_eq!(slot_list.len(), 1);
2387        assert_eq!(slot_list[0], (slot, account_info2));
2388    }
2389
2390    #[test]
2391    fn test_insert_new_with_lock_no_ancestors() {
2392        let key = solana_sdk::pubkey::new_rand();
2393        let pubkey = &key;
2394        let slot = 0;
2395
2396        let index = AccountsIndex::<bool, bool>::default_for_tests();
2397        let account_info = true;
2398        let items = vec![(*pubkey, account_info)];
2399        index.set_startup(Startup::Startup);
2400        let expected_len = items.len();
2401        let (_, _, result) =
2402            index.insert_new_if_missing_into_primary_index(slot, items.len(), items.into_iter());
2403        assert_eq!(result.count, expected_len);
2404        index.set_startup(Startup::Normal);
2405
2406        let mut ancestors = Ancestors::default();
2407        assert!(!index.contains_with(pubkey, Some(&ancestors), None));
2408        assert!(!index.contains_with(pubkey, None, None));
2409
2410        let mut num = 0;
2411        index.unchecked_scan_accounts(
2412            "",
2413            &ancestors,
2414            |_pubkey, _index| num += 1,
2415            &ScanConfig::default(),
2416        );
2417        assert_eq!(num, 0);
2418        ancestors.insert(slot, 0);
2419        assert!(index.contains_with(pubkey, Some(&ancestors), None));
2420        assert_eq!(index.ref_count_from_storage(pubkey), 1);
2421        index.unchecked_scan_accounts(
2422            "",
2423            &ancestors,
2424            |_pubkey, _index| num += 1,
2425            &ScanConfig::default(),
2426        );
2427        assert_eq!(num, 1);
2428
2429        // not zero lamports
2430        let index = AccountsIndex::<bool, bool>::default_for_tests();
2431        let account_info = false;
2432        let items = vec![(*pubkey, account_info)];
2433        index.set_startup(Startup::Startup);
2434        let expected_len = items.len();
2435        let (_, _, result) =
2436            index.insert_new_if_missing_into_primary_index(slot, items.len(), items.into_iter());
2437        assert_eq!(result.count, expected_len);
2438        index.set_startup(Startup::Normal);
2439
2440        let mut ancestors = Ancestors::default();
2441        assert!(!index.contains_with(pubkey, Some(&ancestors), None));
2442        assert!(!index.contains_with(pubkey, None, None));
2443
2444        let mut num = 0;
2445        index.unchecked_scan_accounts(
2446            "",
2447            &ancestors,
2448            |_pubkey, _index| num += 1,
2449            &ScanConfig::default(),
2450        );
2451        assert_eq!(num, 0);
2452        ancestors.insert(slot, 0);
2453        assert!(index.contains_with(pubkey, Some(&ancestors), None));
2454        assert_eq!(index.ref_count_from_storage(pubkey), 1);
2455        index.unchecked_scan_accounts(
2456            "",
2457            &ancestors,
2458            |_pubkey, _index| num += 1,
2459            &ScanConfig::default(),
2460        );
2461        assert_eq!(num, 1);
2462    }
2463
2464    fn get_pre_allocated<T: IndexValue>(
2465        slot: Slot,
2466        account_info: T,
2467        storage: &Arc<BucketMapHolder<T, T>>,
2468        store_raw: bool,
2469        to_raw_first: bool,
2470    ) -> PreAllocatedAccountMapEntry<T> {
2471        let entry = PreAllocatedAccountMapEntry::new(slot, account_info, storage, store_raw);
2472
2473        if to_raw_first {
2474            // convert to raw
2475            let (slot2, account_info2) = entry.into();
2476            // recreate using extracted raw
2477            PreAllocatedAccountMapEntry::new(slot2, account_info2, storage, store_raw)
2478        } else {
2479            entry
2480        }
2481    }
2482
2483    #[test]
2484    fn test_new_entry() {
2485        for store_raw in [false, true] {
2486            for to_raw_first in [false, true] {
2487                let slot = 0;
2488                // account_info type that IS cached
2489                let account_info = AccountInfoTest::default();
2490                let index = AccountsIndex::default_for_tests();
2491
2492                let new_entry = get_pre_allocated(
2493                    slot,
2494                    account_info,
2495                    &index.storage.storage,
2496                    store_raw,
2497                    to_raw_first,
2498                )
2499                .into_account_map_entry(&index.storage.storage);
2500                assert_eq!(new_entry.ref_count(), 0);
2501                assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1);
2502                assert_eq!(
2503                    new_entry.slot_list.read().unwrap().to_vec(),
2504                    vec![(slot, account_info)]
2505                );
2506
2507                // account_info type that is NOT cached
2508                let account_info = true;
2509                let index = AccountsIndex::default_for_tests();
2510
2511                let new_entry = get_pre_allocated(
2512                    slot,
2513                    account_info,
2514                    &index.storage.storage,
2515                    store_raw,
2516                    to_raw_first,
2517                )
2518                .into_account_map_entry(&index.storage.storage);
2519                assert_eq!(new_entry.ref_count(), 1);
2520                assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1);
2521                assert_eq!(
2522                    new_entry.slot_list.read().unwrap().to_vec(),
2523                    vec![(slot, account_info)]
2524                );
2525            }
2526        }
2527    }
2528
2529    #[test]
2530    fn test_batch_insert() {
2531        let slot0 = 0;
2532        let key0 = solana_sdk::pubkey::new_rand();
2533        let key1 = solana_sdk::pubkey::new_rand();
2534
2535        let index = AccountsIndex::<bool, bool>::default_for_tests();
2536        let account_infos = [true, false];
2537
2538        index.set_startup(Startup::Startup);
2539        let items = vec![(key0, account_infos[0]), (key1, account_infos[1])];
2540        let expected_len = items.len();
2541        let (_, _, result) =
2542            index.insert_new_if_missing_into_primary_index(slot0, items.len(), items.into_iter());
2543        assert_eq!(result.count, expected_len);
2544        index.set_startup(Startup::Normal);
2545
2546        for (i, key) in [key0, key1].iter().enumerate() {
2547            let entry = index.get_cloned(key).unwrap();
2548            assert_eq!(entry.ref_count.load(Ordering::Relaxed), 1);
2549            assert_eq!(
2550                entry.slot_list.read().unwrap().as_slice(),
2551                &[(slot0, account_infos[i])],
2552            );
2553        }
2554    }
2555
2556    fn test_new_entry_code_paths_helper<T: IndexValue>(
2557        account_infos: [T; 2],
2558        is_cached: bool,
2559        upsert: bool,
2560        use_disk: bool,
2561    ) {
2562        if is_cached && !upsert {
2563            // This is an illegal combination when we are using queued lazy inserts.
2564            // Cached items don't ever leave the in-mem cache.
2565            // But the queued lazy insert code relies on there being nothing in the in-mem cache.
2566            return;
2567        }
2568
2569        let slot0 = 0;
2570        let slot1 = 1;
2571        let key = solana_sdk::pubkey::new_rand();
2572
2573        let mut config = ACCOUNTS_INDEX_CONFIG_FOR_TESTING;
2574        config.index_limit_mb = if use_disk {
2575            IndexLimitMb::Unlimited
2576        } else {
2577            IndexLimitMb::InMemOnly // in-mem only
2578        };
2579        let index = AccountsIndex::<T, T>::new(Some(config), Arc::default());
2580        let mut gc = Vec::new();
2581
2582        if upsert {
2583            // insert first entry for pubkey. This will use new_entry_after_update and not call update.
2584            index.upsert(
2585                slot0,
2586                slot0,
2587                &key,
2588                &AccountSharedData::default(),
2589                &AccountSecondaryIndexes::default(),
2590                account_infos[0],
2591                &mut gc,
2592                UPSERT_POPULATE_RECLAIMS,
2593            );
2594        } else {
2595            let items = vec![(key, account_infos[0])];
2596            index.set_startup(Startup::Startup);
2597            let expected_len = items.len();
2598            let (_, _, result) = index.insert_new_if_missing_into_primary_index(
2599                slot0,
2600                items.len(),
2601                items.into_iter(),
2602            );
2603            assert_eq!(result.count, expected_len);
2604            index.set_startup(Startup::Normal);
2605        }
2606        assert!(gc.is_empty());
2607
2608        // verify the added entry matches expected
2609        {
2610            let entry = index.get_cloned(&key).unwrap();
2611            let slot_list = entry.slot_list.read().unwrap();
2612            assert_eq!(entry.ref_count(), u64::from(!is_cached));
2613            assert_eq!(slot_list.as_slice(), &[(slot0, account_infos[0])]);
2614            let new_entry: AccountMapEntry<_> = PreAllocatedAccountMapEntry::new(
2615                slot0,
2616                account_infos[0],
2617                &index.storage.storage,
2618                false,
2619            )
2620            .into_account_map_entry(&index.storage.storage);
2621            assert_eq!(
2622                slot_list.as_slice(),
2623                new_entry.slot_list.read().unwrap().as_slice(),
2624            );
2625        }
2626
2627        // insert second entry for pubkey. This will use update and NOT use new_entry_after_update.
2628        if upsert {
2629            index.upsert(
2630                slot1,
2631                slot1,
2632                &key,
2633                &AccountSharedData::default(),
2634                &AccountSecondaryIndexes::default(),
2635                account_infos[1],
2636                &mut gc,
2637                UPSERT_POPULATE_RECLAIMS,
2638            );
2639        } else {
2640            // this has the effect of aging out everything in the in-mem cache
2641            for _ in 0..5 {
2642                index.set_startup(Startup::Startup);
2643                index.set_startup(Startup::Normal);
2644            }
2645
2646            let items = vec![(key, account_infos[1])];
2647            index.set_startup(Startup::Startup);
2648            let expected_len = items.len();
2649            let (_, _, result) = index.insert_new_if_missing_into_primary_index(
2650                slot1,
2651                items.len(),
2652                items.into_iter(),
2653            );
2654            assert_eq!(result.count, expected_len);
2655            index.set_startup(Startup::Normal);
2656        }
2657        assert!(gc.is_empty());
2658        index.populate_and_retrieve_duplicate_keys_from_startup(|_slot_keys| {});
2659
2660        let entry = index.get_cloned(&key).unwrap();
2661        let slot_list = entry.slot_list.read().unwrap();
2662
2663        assert_eq!(entry.ref_count(), if is_cached { 0 } else { 2 });
2664        assert_eq!(
2665            slot_list.as_slice(),
2666            &[(slot0, account_infos[0]), (slot1, account_infos[1])],
2667        );
2668
2669        let new_entry = PreAllocatedAccountMapEntry::new(
2670            slot1,
2671            account_infos[1],
2672            &index.storage.storage,
2673            false,
2674        );
2675        assert_eq!(slot_list[1], new_entry.into());
2676    }
2677
2678    #[test]
2679    fn test_new_entry_and_update_code_paths() {
2680        for use_disk in [false, true] {
2681            for is_upsert in &[false, true] {
2682                // account_info type that IS cached
2683                test_new_entry_code_paths_helper([1.0, 2.0], true, *is_upsert, use_disk);
2684
2685                // account_info type that is NOT cached
2686                test_new_entry_code_paths_helper([true, false], false, *is_upsert, use_disk);
2687            }
2688        }
2689    }
2690
2691    #[test]
2692    fn test_insert_with_lock_no_ancestors() {
2693        let key = solana_sdk::pubkey::new_rand();
2694        let index = AccountsIndex::<bool, bool>::default_for_tests();
2695        let slot = 0;
2696        let account_info = true;
2697
2698        let new_entry =
2699            PreAllocatedAccountMapEntry::new(slot, account_info, &index.storage.storage, false);
2700        assert_eq!(0, account_maps_stats_len(&index));
2701        assert_eq!((slot, account_info), new_entry.clone().into());
2702
2703        assert_eq!(0, account_maps_stats_len(&index));
2704        let r_account_maps = index.get_bin(&key);
2705        r_account_maps.upsert(
2706            &key,
2707            new_entry,
2708            None,
2709            &mut SlotList::default(),
2710            UPSERT_POPULATE_RECLAIMS,
2711        );
2712        assert_eq!(1, account_maps_stats_len(&index));
2713
2714        let mut ancestors = Ancestors::default();
2715        assert!(!index.contains_with(&key, Some(&ancestors), None));
2716        assert!(!index.contains_with(&key, None, None));
2717
2718        let mut num = 0;
2719        index.unchecked_scan_accounts(
2720            "",
2721            &ancestors,
2722            |_pubkey, _index| num += 1,
2723            &ScanConfig::default(),
2724        );
2725        assert_eq!(num, 0);
2726        ancestors.insert(slot, 0);
2727        assert!(index.contains_with(&key, Some(&ancestors), None));
2728        index.unchecked_scan_accounts(
2729            "",
2730            &ancestors,
2731            |_pubkey, _index| num += 1,
2732            &ScanConfig::default(),
2733        );
2734        assert_eq!(num, 1);
2735    }
2736
2737    #[test]
2738    fn test_insert_wrong_ancestors() {
2739        let key = solana_sdk::pubkey::new_rand();
2740        let index = AccountsIndex::<bool, bool>::default_for_tests();
2741        let mut gc = Vec::new();
2742        index.upsert(
2743            0,
2744            0,
2745            &key,
2746            &AccountSharedData::default(),
2747            &AccountSecondaryIndexes::default(),
2748            true,
2749            &mut gc,
2750            UPSERT_POPULATE_RECLAIMS,
2751        );
2752        assert!(gc.is_empty());
2753
2754        let ancestors = vec![(1, 1)].into_iter().collect();
2755        assert!(!index.contains_with(&key, Some(&ancestors), None));
2756
2757        let mut num = 0;
2758        index.unchecked_scan_accounts(
2759            "",
2760            &ancestors,
2761            |_pubkey, _index| num += 1,
2762            &ScanConfig::default(),
2763        );
2764        assert_eq!(num, 0);
2765    }
2766    #[test]
2767    fn test_insert_ignore_reclaims() {
2768        {
2769            // non-cached
2770            let key = solana_sdk::pubkey::new_rand();
2771            let index = AccountsIndex::<u64, u64>::default_for_tests();
2772            let mut reclaims = Vec::new();
2773            let slot = 0;
2774            let value = 1;
2775            assert!(!value.is_cached());
2776            index.upsert(
2777                slot,
2778                slot,
2779                &key,
2780                &AccountSharedData::default(),
2781                &AccountSecondaryIndexes::default(),
2782                value,
2783                &mut reclaims,
2784                UpsertReclaim::PopulateReclaims,
2785            );
2786            assert!(reclaims.is_empty());
2787            index.upsert(
2788                slot,
2789                slot,
2790                &key,
2791                &AccountSharedData::default(),
2792                &AccountSecondaryIndexes::default(),
2793                value,
2794                &mut reclaims,
2795                UpsertReclaim::PopulateReclaims,
2796            );
2797            // reclaimed
2798            assert!(!reclaims.is_empty());
2799            reclaims.clear();
2800            index.upsert(
2801                slot,
2802                slot,
2803                &key,
2804                &AccountSharedData::default(),
2805                &AccountSecondaryIndexes::default(),
2806                value,
2807                &mut reclaims,
2808                // since IgnoreReclaims, we should expect reclaims to be empty
2809                UpsertReclaim::IgnoreReclaims,
2810            );
2811            // reclaims is ignored
2812            assert!(reclaims.is_empty());
2813        }
2814        {
2815            // cached
2816            let key = solana_sdk::pubkey::new_rand();
2817            let index = AccountsIndex::<AccountInfoTest, AccountInfoTest>::default_for_tests();
2818            let mut reclaims = Vec::new();
2819            let slot = 0;
2820            let value = 1.0;
2821            assert!(value.is_cached());
2822            index.upsert(
2823                slot,
2824                slot,
2825                &key,
2826                &AccountSharedData::default(),
2827                &AccountSecondaryIndexes::default(),
2828                value,
2829                &mut reclaims,
2830                UpsertReclaim::PopulateReclaims,
2831            );
2832            assert!(reclaims.is_empty());
2833            index.upsert(
2834                slot,
2835                slot,
2836                &key,
2837                &AccountSharedData::default(),
2838                &AccountSecondaryIndexes::default(),
2839                value,
2840                &mut reclaims,
2841                UpsertReclaim::PopulateReclaims,
2842            );
2843            // reclaimed
2844            assert!(!reclaims.is_empty());
2845            reclaims.clear();
2846            index.upsert(
2847                slot,
2848                slot,
2849                &key,
2850                &AccountSharedData::default(),
2851                &AccountSecondaryIndexes::default(),
2852                value,
2853                &mut reclaims,
2854                // since IgnoreReclaims, we should expect reclaims to be empty
2855                UpsertReclaim::IgnoreReclaims,
2856            );
2857            // reclaims is ignored
2858            assert!(reclaims.is_empty());
2859        }
2860    }
2861
2862    #[test]
2863    fn test_insert_with_ancestors() {
2864        let key = solana_sdk::pubkey::new_rand();
2865        let index = AccountsIndex::<bool, bool>::default_for_tests();
2866        let mut gc = Vec::new();
2867        index.upsert(
2868            0,
2869            0,
2870            &key,
2871            &AccountSharedData::default(),
2872            &AccountSecondaryIndexes::default(),
2873            true,
2874            &mut gc,
2875            UPSERT_POPULATE_RECLAIMS,
2876        );
2877        assert!(gc.is_empty());
2878
2879        let ancestors = vec![(0, 0)].into_iter().collect();
2880        index
2881            .get_with_and_then(
2882                &key,
2883                Some(&ancestors),
2884                None,
2885                false,
2886                |(slot, account_info)| {
2887                    assert_eq!(slot, 0);
2888                    assert!(account_info);
2889                },
2890            )
2891            .unwrap();
2892
2893        let mut num = 0;
2894        let mut found_key = false;
2895        index.unchecked_scan_accounts(
2896            "",
2897            &ancestors,
2898            |pubkey, _index| {
2899                if pubkey == &key {
2900                    found_key = true
2901                };
2902                num += 1
2903            },
2904            &ScanConfig::default(),
2905        );
2906        assert_eq!(num, 1);
2907        assert!(found_key);
2908    }
2909
2910    fn setup_accounts_index_keys(num_pubkeys: usize) -> (AccountsIndex<bool, bool>, Vec<Pubkey>) {
2911        let index = AccountsIndex::<bool, bool>::default_for_tests();
2912        let root_slot = 0;
2913
2914        let mut pubkeys: Vec<Pubkey> = std::iter::repeat_with(|| {
2915            let new_pubkey = solana_sdk::pubkey::new_rand();
2916            index.upsert(
2917                root_slot,
2918                root_slot,
2919                &new_pubkey,
2920                &AccountSharedData::default(),
2921                &AccountSecondaryIndexes::default(),
2922                true,
2923                &mut vec![],
2924                UPSERT_POPULATE_RECLAIMS,
2925            );
2926            new_pubkey
2927        })
2928        .take(num_pubkeys.saturating_sub(1))
2929        .collect();
2930
2931        if num_pubkeys != 0 {
2932            pubkeys.push(Pubkey::default());
2933            index.upsert(
2934                root_slot,
2935                root_slot,
2936                &Pubkey::default(),
2937                &AccountSharedData::default(),
2938                &AccountSecondaryIndexes::default(),
2939                true,
2940                &mut vec![],
2941                UPSERT_POPULATE_RECLAIMS,
2942            );
2943        }
2944
2945        index.add_root(root_slot);
2946
2947        (index, pubkeys)
2948    }
2949
2950    fn run_test_range(
2951        index: &AccountsIndex<bool, bool>,
2952        pubkeys: &[Pubkey],
2953        start_bound: Bound<usize>,
2954        end_bound: Bound<usize>,
2955    ) {
2956        // Exclusive `index_start`
2957        let (pubkey_start, index_start) = match start_bound {
2958            Unbounded => (Unbounded, 0),
2959            Included(i) => (Included(pubkeys[i]), i),
2960            Excluded(i) => (Excluded(pubkeys[i]), i + 1),
2961        };
2962
2963        // Exclusive `index_end`
2964        let (pubkey_end, index_end) = match end_bound {
2965            Unbounded => (Unbounded, pubkeys.len()),
2966            Included(i) => (Included(pubkeys[i]), i + 1),
2967            Excluded(i) => (Excluded(pubkeys[i]), i),
2968        };
2969        let pubkey_range = (pubkey_start, pubkey_end);
2970
2971        let ancestors = Ancestors::default();
2972        let mut scanned_keys = HashSet::new();
2973        index.range_scan_accounts(
2974            "",
2975            &ancestors,
2976            pubkey_range,
2977            &ScanConfig::default(),
2978            |pubkey, _index| {
2979                scanned_keys.insert(*pubkey);
2980            },
2981        );
2982
2983        let mut expected_len = 0;
2984        for key in &pubkeys[index_start..index_end] {
2985            expected_len += 1;
2986            assert!(scanned_keys.contains(key));
2987        }
2988
2989        assert_eq!(scanned_keys.len(), expected_len);
2990    }
2991
2992    fn run_test_range_indexes(
2993        index: &AccountsIndex<bool, bool>,
2994        pubkeys: &[Pubkey],
2995        start: Option<usize>,
2996        end: Option<usize>,
2997    ) {
2998        let start_options = start
2999            .map(|i| vec![Included(i), Excluded(i)])
3000            .unwrap_or_else(|| vec![Unbounded]);
3001        let end_options = end
3002            .map(|i| vec![Included(i), Excluded(i)])
3003            .unwrap_or_else(|| vec![Unbounded]);
3004
3005        for start in &start_options {
3006            for end in &end_options {
3007                run_test_range(index, pubkeys, *start, *end);
3008            }
3009        }
3010    }
3011
3012    #[test]
3013    fn test_range_scan_accounts() {
3014        let (index, mut pubkeys) = setup_accounts_index_keys(3 * ITER_BATCH_SIZE);
3015        pubkeys.sort();
3016
3017        run_test_range_indexes(&index, &pubkeys, None, None);
3018
3019        run_test_range_indexes(&index, &pubkeys, Some(ITER_BATCH_SIZE), None);
3020
3021        run_test_range_indexes(&index, &pubkeys, None, Some(2 * ITER_BATCH_SIZE));
3022
3023        run_test_range_indexes(
3024            &index,
3025            &pubkeys,
3026            Some(ITER_BATCH_SIZE),
3027            Some(2 * ITER_BATCH_SIZE),
3028        );
3029
3030        run_test_range_indexes(
3031            &index,
3032            &pubkeys,
3033            Some(ITER_BATCH_SIZE),
3034            Some(2 * ITER_BATCH_SIZE - 1),
3035        );
3036
3037        run_test_range_indexes(
3038            &index,
3039            &pubkeys,
3040            Some(ITER_BATCH_SIZE - 1_usize),
3041            Some(2 * ITER_BATCH_SIZE + 1),
3042        );
3043    }
3044
3045    fn run_test_scan_accounts(num_pubkeys: usize) {
3046        let (index, _) = setup_accounts_index_keys(num_pubkeys);
3047        let ancestors = Ancestors::default();
3048
3049        let mut scanned_keys = HashSet::new();
3050        index.unchecked_scan_accounts(
3051            "",
3052            &ancestors,
3053            |pubkey, _index| {
3054                scanned_keys.insert(*pubkey);
3055            },
3056            &ScanConfig::default(),
3057        );
3058        assert_eq!(scanned_keys.len(), num_pubkeys);
3059    }
3060
3061    #[test]
3062    fn test_scan_accounts() {
3063        run_test_scan_accounts(0);
3064        run_test_scan_accounts(1);
3065        run_test_scan_accounts(ITER_BATCH_SIZE * 10);
3066        run_test_scan_accounts(ITER_BATCH_SIZE * 10 - 1);
3067        run_test_scan_accounts(ITER_BATCH_SIZE * 10 + 1);
3068    }
3069
3070    #[test]
3071    fn test_accounts_iter_finished() {
3072        let (index, _) = setup_accounts_index_keys(0);
3073        let mut iter = index.iter(None::<&Range<Pubkey>>, COLLECT_ALL_UNSORTED_FALSE);
3074        assert!(iter.next().is_none());
3075        let mut gc = vec![];
3076        index.upsert(
3077            0,
3078            0,
3079            &solana_sdk::pubkey::new_rand(),
3080            &AccountSharedData::default(),
3081            &AccountSecondaryIndexes::default(),
3082            true,
3083            &mut gc,
3084            UPSERT_POPULATE_RECLAIMS,
3085        );
3086        assert!(iter.next().is_none());
3087    }
3088
3089    #[test]
3090    fn test_is_alive_root() {
3091        let index = AccountsIndex::<bool, bool>::default_for_tests();
3092        assert!(!index.is_alive_root(0));
3093        index.add_root(0);
3094        assert!(index.is_alive_root(0));
3095    }
3096
3097    #[test]
3098    fn test_insert_with_root() {
3099        let key = solana_sdk::pubkey::new_rand();
3100        let index = AccountsIndex::<bool, bool>::default_for_tests();
3101        let mut gc = Vec::new();
3102        index.upsert(
3103            0,
3104            0,
3105            &key,
3106            &AccountSharedData::default(),
3107            &AccountSecondaryIndexes::default(),
3108            true,
3109            &mut gc,
3110            UPSERT_POPULATE_RECLAIMS,
3111        );
3112        assert!(gc.is_empty());
3113
3114        index.add_root(0);
3115        index
3116            .get_with_and_then(&key, None, None, false, |(slot, account_info)| {
3117                assert_eq!(slot, 0);
3118                assert!(account_info);
3119            })
3120            .unwrap();
3121    }
3122
3123    #[test]
3124    fn test_clean_first() {
3125        let index = AccountsIndex::<bool, bool>::default_for_tests();
3126        index.add_root(0);
3127        index.add_root(1);
3128        index.clean_dead_slot(0);
3129        assert!(index.is_alive_root(1));
3130        assert!(!index.is_alive_root(0));
3131    }
3132
3133    #[test]
3134    fn test_clean_last() {
3135        //this behavior might be undefined, clean up should only occur on older slots
3136        let index = AccountsIndex::<bool, bool>::default_for_tests();
3137        index.add_root(0);
3138        index.add_root(1);
3139        index.clean_dead_slot(1);
3140        assert!(!index.is_alive_root(1));
3141        assert!(index.is_alive_root(0));
3142    }
3143
3144    #[test]
3145    fn test_clean_and_unclean_slot() {
3146        let index = AccountsIndex::<bool, bool>::default_for_tests();
3147        assert_eq!(0, index.roots_tracker.read().unwrap().uncleaned_roots.len());
3148        index.add_root(0);
3149        index.add_root(1);
3150        index.add_uncleaned_roots([0, 1]);
3151        assert_eq!(2, index.roots_tracker.read().unwrap().uncleaned_roots.len());
3152
3153        index.reset_uncleaned_roots(None);
3154        assert_eq!(2, index.roots_tracker.read().unwrap().alive_roots.len());
3155        assert_eq!(0, index.roots_tracker.read().unwrap().uncleaned_roots.len());
3156
3157        index.add_root(2);
3158        index.add_root(3);
3159        index.add_uncleaned_roots([2, 3]);
3160        assert_eq!(4, index.roots_tracker.read().unwrap().alive_roots.len());
3161        assert_eq!(2, index.roots_tracker.read().unwrap().uncleaned_roots.len());
3162
3163        index.clean_dead_slot(1);
3164        assert_eq!(3, index.roots_tracker.read().unwrap().alive_roots.len());
3165        assert_eq!(2, index.roots_tracker.read().unwrap().uncleaned_roots.len());
3166
3167        index.clean_dead_slot(2);
3168        assert_eq!(2, index.roots_tracker.read().unwrap().alive_roots.len());
3169        assert_eq!(1, index.roots_tracker.read().unwrap().uncleaned_roots.len());
3170    }
3171
3172    #[test]
3173    fn test_update_last_wins() {
3174        let key = solana_sdk::pubkey::new_rand();
3175        let index = AccountsIndex::<bool, bool>::default_for_tests();
3176        let ancestors = vec![(0, 0)].into_iter().collect();
3177        let mut gc = Vec::new();
3178        index.upsert(
3179            0,
3180            0,
3181            &key,
3182            &AccountSharedData::default(),
3183            &AccountSecondaryIndexes::default(),
3184            true,
3185            &mut gc,
3186            UPSERT_POPULATE_RECLAIMS,
3187        );
3188        assert!(gc.is_empty());
3189        index
3190            .get_with_and_then(
3191                &key,
3192                Some(&ancestors),
3193                None,
3194                false,
3195                |(slot, account_info)| {
3196                    assert_eq!(slot, 0);
3197                    assert!(account_info);
3198                },
3199            )
3200            .unwrap();
3201
3202        let mut gc = Vec::new();
3203        index.upsert(
3204            0,
3205            0,
3206            &key,
3207            &AccountSharedData::default(),
3208            &AccountSecondaryIndexes::default(),
3209            false,
3210            &mut gc,
3211            UPSERT_POPULATE_RECLAIMS,
3212        );
3213        assert_eq!(gc, vec![(0, true)]);
3214        index
3215            .get_with_and_then(
3216                &key,
3217                Some(&ancestors),
3218                None,
3219                false,
3220                |(slot, account_info)| {
3221                    assert_eq!(slot, 0);
3222                    assert!(!account_info);
3223                },
3224            )
3225            .unwrap();
3226    }
3227
3228    #[test]
3229    fn test_update_new_slot() {
3230        solana_logger::setup();
3231        let key = solana_sdk::pubkey::new_rand();
3232        let index = AccountsIndex::<bool, bool>::default_for_tests();
3233        let ancestors = vec![(0, 0)].into_iter().collect();
3234        let mut gc = Vec::new();
3235        index.upsert(
3236            0,
3237            0,
3238            &key,
3239            &AccountSharedData::default(),
3240            &AccountSecondaryIndexes::default(),
3241            true,
3242            &mut gc,
3243            UPSERT_POPULATE_RECLAIMS,
3244        );
3245        assert!(gc.is_empty());
3246        index.upsert(
3247            1,
3248            1,
3249            &key,
3250            &AccountSharedData::default(),
3251            &AccountSecondaryIndexes::default(),
3252            false,
3253            &mut gc,
3254            UPSERT_POPULATE_RECLAIMS,
3255        );
3256        assert!(gc.is_empty());
3257        index
3258            .get_with_and_then(
3259                &key,
3260                Some(&ancestors),
3261                None,
3262                false,
3263                |(slot, account_info)| {
3264                    assert_eq!(slot, 0);
3265                    assert!(account_info);
3266                },
3267            )
3268            .unwrap();
3269        let ancestors = vec![(1, 0)].into_iter().collect();
3270        index
3271            .get_with_and_then(
3272                &key,
3273                Some(&ancestors),
3274                None,
3275                false,
3276                |(slot, account_info)| {
3277                    assert_eq!(slot, 1);
3278                    assert!(!account_info);
3279                },
3280            )
3281            .unwrap();
3282    }
3283
3284    #[test]
3285    fn test_update_gc_purged_slot() {
3286        let key = solana_sdk::pubkey::new_rand();
3287        let index = AccountsIndex::<bool, bool>::default_for_tests();
3288        let mut gc = Vec::new();
3289        index.upsert(
3290            0,
3291            0,
3292            &key,
3293            &AccountSharedData::default(),
3294            &AccountSecondaryIndexes::default(),
3295            true,
3296            &mut gc,
3297            UPSERT_POPULATE_RECLAIMS,
3298        );
3299        assert!(gc.is_empty());
3300        index.upsert(
3301            1,
3302            1,
3303            &key,
3304            &AccountSharedData::default(),
3305            &AccountSecondaryIndexes::default(),
3306            false,
3307            &mut gc,
3308            UPSERT_POPULATE_RECLAIMS,
3309        );
3310        index.upsert(
3311            2,
3312            2,
3313            &key,
3314            &AccountSharedData::default(),
3315            &AccountSecondaryIndexes::default(),
3316            true,
3317            &mut gc,
3318            UPSERT_POPULATE_RECLAIMS,
3319        );
3320        index.upsert(
3321            3,
3322            3,
3323            &key,
3324            &AccountSharedData::default(),
3325            &AccountSecondaryIndexes::default(),
3326            true,
3327            &mut gc,
3328            UPSERT_POPULATE_RECLAIMS,
3329        );
3330        index.add_root(0);
3331        index.add_root(1);
3332        index.add_root(3);
3333        index.upsert(
3334            4,
3335            4,
3336            &key,
3337            &AccountSharedData::default(),
3338            &AccountSecondaryIndexes::default(),
3339            true,
3340            &mut gc,
3341            UPSERT_POPULATE_RECLAIMS,
3342        );
3343
3344        // Updating index should not purge older roots, only purges
3345        // previous updates within the same slot
3346        assert_eq!(gc, vec![]);
3347        index
3348            .get_with_and_then(&key, None, None, false, |(slot, account_info)| {
3349                assert_eq!(slot, 3);
3350                assert!(account_info);
3351            })
3352            .unwrap();
3353
3354        let mut num = 0;
3355        let mut found_key = false;
3356        index.unchecked_scan_accounts(
3357            "",
3358            &Ancestors::default(),
3359            |pubkey, index| {
3360                if pubkey == &key {
3361                    found_key = true;
3362                    assert_eq!(index, (&true, 3));
3363                };
3364                num += 1
3365            },
3366            &ScanConfig::default(),
3367        );
3368        assert_eq!(num, 1);
3369        assert!(found_key);
3370    }
3371
3372    fn account_maps_stats_len<T: IndexValue>(index: &AccountsIndex<T, T>) -> usize {
3373        index.storage.storage.stats.total_count()
3374    }
3375
3376    #[test]
3377    fn test_purge() {
3378        let key = solana_sdk::pubkey::new_rand();
3379        let index = AccountsIndex::<u64, u64>::default_for_tests();
3380        let mut gc = Vec::new();
3381        assert_eq!(0, account_maps_stats_len(&index));
3382        index.upsert(
3383            1,
3384            1,
3385            &key,
3386            &AccountSharedData::default(),
3387            &AccountSecondaryIndexes::default(),
3388            12,
3389            &mut gc,
3390            UPSERT_POPULATE_RECLAIMS,
3391        );
3392        assert_eq!(1, account_maps_stats_len(&index));
3393
3394        index.upsert(
3395            1,
3396            1,
3397            &key,
3398            &AccountSharedData::default(),
3399            &AccountSecondaryIndexes::default(),
3400            10,
3401            &mut gc,
3402            UPSERT_POPULATE_RECLAIMS,
3403        );
3404        assert_eq!(1, account_maps_stats_len(&index));
3405
3406        let purges = index.purge_roots(&key);
3407        assert_eq!(purges, (vec![], false));
3408        index.add_root(1);
3409
3410        let purges = index.purge_roots(&key);
3411        assert_eq!(purges, (vec![(1, 10)], true));
3412
3413        assert_eq!(1, account_maps_stats_len(&index));
3414        index.upsert(
3415            1,
3416            1,
3417            &key,
3418            &AccountSharedData::default(),
3419            &AccountSecondaryIndexes::default(),
3420            9,
3421            &mut gc,
3422            UPSERT_POPULATE_RECLAIMS,
3423        );
3424        assert_eq!(1, account_maps_stats_len(&index));
3425    }
3426
3427    #[test]
3428    fn test_latest_slot() {
3429        let slot_slice = vec![(0, true), (5, true), (3, true), (7, true)];
3430        let index = AccountsIndex::<bool, bool>::default_for_tests();
3431
3432        // No ancestors, no root, should return None
3433        assert!(index.latest_slot(None, &slot_slice, None).is_none());
3434
3435        // Given a root, should return the root
3436        index.add_root(5);
3437        assert_eq!(index.latest_slot(None, &slot_slice, None).unwrap(), 1);
3438
3439        // Given a max_root == root, should still return the root
3440        assert_eq!(index.latest_slot(None, &slot_slice, Some(5)).unwrap(), 1);
3441
3442        // Given a max_root < root, should filter out the root
3443        assert!(index.latest_slot(None, &slot_slice, Some(4)).is_none());
3444
3445        // Given a max_root, should filter out roots < max_root, but specified
3446        // ancestors should not be affected
3447        let ancestors = vec![(3, 1), (7, 1)].into_iter().collect();
3448        assert_eq!(
3449            index
3450                .latest_slot(Some(&ancestors), &slot_slice, Some(4))
3451                .unwrap(),
3452            3
3453        );
3454        assert_eq!(
3455            index
3456                .latest_slot(Some(&ancestors), &slot_slice, Some(7))
3457                .unwrap(),
3458            3
3459        );
3460
3461        // Given no max_root, should just return the greatest ancestor or root
3462        assert_eq!(
3463            index
3464                .latest_slot(Some(&ancestors), &slot_slice, None)
3465                .unwrap(),
3466            3
3467        );
3468    }
3469
3470    fn make_empty_token_account_data() -> Vec<u8> {
3471        vec![0; solana_inline_spl::token::Account::get_packed_len()]
3472    }
3473
3474    fn run_test_purge_exact_secondary_index<
3475        SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
3476    >(
3477        index: &AccountsIndex<bool, bool>,
3478        secondary_index: &SecondaryIndex<SecondaryIndexEntryType>,
3479        key_start: usize,
3480        key_end: usize,
3481        secondary_indexes: &AccountSecondaryIndexes,
3482    ) {
3483        // No roots, should be no reclaims
3484        let slots = vec![1, 2, 5, 9];
3485        let index_key = Pubkey::new_unique();
3486        let account_key = Pubkey::new_unique();
3487
3488        let mut account_data = make_empty_token_account_data();
3489        account_data[key_start..key_end].clone_from_slice(&(index_key.to_bytes()));
3490
3491        // Insert slots into secondary index
3492        for slot in &slots {
3493            index.upsert(
3494                *slot,
3495                *slot,
3496                &account_key,
3497                // Make sure these accounts are added to secondary index
3498                &AccountSharedData::create(
3499                    0,
3500                    account_data.to_vec(),
3501                    solana_inline_spl::token::id(),
3502                    false,
3503                    0,
3504                ),
3505                secondary_indexes,
3506                true,
3507                &mut vec![],
3508                UPSERT_POPULATE_RECLAIMS,
3509            );
3510        }
3511
3512        // Only one top level index entry exists
3513        assert_eq!(secondary_index.index.get(&index_key).unwrap().len(), 1);
3514
3515        // In the reverse index, one account maps across multiple slots
3516        // to the same top level key
3517        assert_eq!(
3518            secondary_index
3519                .reverse_index
3520                .get(&account_key)
3521                .unwrap()
3522                .value()
3523                .read()
3524                .unwrap()
3525                .len(),
3526            1
3527        );
3528
3529        index.purge_exact(
3530            &account_key,
3531            &slots.into_iter().collect::<HashSet<Slot>>(),
3532            &mut vec![],
3533        );
3534
3535        let _ = index.handle_dead_keys(&[&account_key], secondary_indexes);
3536        assert!(secondary_index.index.is_empty());
3537        assert!(secondary_index.reverse_index.is_empty());
3538    }
3539
3540    #[test]
3541    fn test_purge_exact_spl_token_mint_secondary_index() {
3542        let (key_start, key_end, secondary_indexes) = create_spl_token_mint_secondary_index_state();
3543        let index = AccountsIndex::<bool, bool>::default_for_tests();
3544        run_test_purge_exact_secondary_index(
3545            &index,
3546            &index.spl_token_mint_index,
3547            key_start,
3548            key_end,
3549            &secondary_indexes,
3550        );
3551    }
3552
3553    #[test]
3554    fn test_purge_exact_spl_token_owner_secondary_index() {
3555        let (key_start, key_end, secondary_indexes) =
3556            create_spl_token_owner_secondary_index_state();
3557        let index = AccountsIndex::<bool, bool>::default_for_tests();
3558        run_test_purge_exact_secondary_index(
3559            &index,
3560            &index.spl_token_owner_index,
3561            key_start,
3562            key_end,
3563            &secondary_indexes,
3564        );
3565    }
3566
3567    #[test]
3568    fn test_purge_older_root_entries() {
3569        // No roots, should be no reclaims
3570        let index = AccountsIndex::<bool, bool>::default_for_tests();
3571        let mut slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3572        let mut reclaims = vec![];
3573        index.purge_older_root_entries(&mut slot_list, &mut reclaims, None);
3574        assert!(reclaims.is_empty());
3575        assert_eq!(slot_list, vec![(1, true), (2, true), (5, true), (9, true)]);
3576
3577        // Add a later root, earlier slots should be reclaimed
3578        slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3579        index.add_root(1);
3580        // Note 2 is not a root
3581        index.add_root(5);
3582        reclaims = vec![];
3583        index.purge_older_root_entries(&mut slot_list, &mut reclaims, None);
3584        assert_eq!(reclaims, vec![(1, true), (2, true)]);
3585        assert_eq!(slot_list, vec![(5, true), (9, true)]);
3586
3587        // Add a later root that is not in the list, should not affect the outcome
3588        slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3589        index.add_root(6);
3590        reclaims = vec![];
3591        index.purge_older_root_entries(&mut slot_list, &mut reclaims, None);
3592        assert_eq!(reclaims, vec![(1, true), (2, true)]);
3593        assert_eq!(slot_list, vec![(5, true), (9, true)]);
3594
3595        // Pass a max root >= than any root in the slot list, should not affect
3596        // outcome
3597        slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3598        reclaims = vec![];
3599        index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(6));
3600        assert_eq!(reclaims, vec![(1, true), (2, true)]);
3601        assert_eq!(slot_list, vec![(5, true), (9, true)]);
3602
3603        // Pass a max root, earlier slots should be reclaimed
3604        slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3605        reclaims = vec![];
3606        index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(5));
3607        assert_eq!(reclaims, vec![(1, true), (2, true)]);
3608        assert_eq!(slot_list, vec![(5, true), (9, true)]);
3609
3610        // Pass a max root 2. This means the latest root < 2 is 1 because 2 is not a root
3611        // so nothing will be purged
3612        slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3613        reclaims = vec![];
3614        index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(2));
3615        assert!(reclaims.is_empty());
3616        assert_eq!(slot_list, vec![(1, true), (2, true), (5, true), (9, true)]);
3617
3618        // Pass a max root 1. This means the latest root < 3 is 1 because 2 is not a root
3619        // so nothing will be purged
3620        slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3621        reclaims = vec![];
3622        index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(1));
3623        assert!(reclaims.is_empty());
3624        assert_eq!(slot_list, vec![(1, true), (2, true), (5, true), (9, true)]);
3625
3626        // Pass a max root that doesn't exist in the list but is greater than
3627        // some of the roots in the list, shouldn't return those smaller roots
3628        slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3629        reclaims = vec![];
3630        index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(7));
3631        assert_eq!(reclaims, vec![(1, true), (2, true)]);
3632        assert_eq!(slot_list, vec![(5, true), (9, true)]);
3633    }
3634
3635    fn check_secondary_index_mapping_correct<SecondaryIndexEntryType>(
3636        secondary_index: &SecondaryIndex<SecondaryIndexEntryType>,
3637        secondary_index_keys: &[Pubkey],
3638        account_key: &Pubkey,
3639    ) where
3640        SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
3641    {
3642        // Check secondary index has unique mapping from secondary index key
3643        // to the account key and slot
3644        for secondary_index_key in secondary_index_keys {
3645            assert_eq!(secondary_index.index.len(), secondary_index_keys.len());
3646            let account_key_map = secondary_index.get(secondary_index_key);
3647            assert_eq!(account_key_map.len(), 1);
3648            assert_eq!(account_key_map, vec![*account_key]);
3649        }
3650        // Check reverse index contains all of the `secondary_index_keys`
3651        let secondary_index_key_map = secondary_index.reverse_index.get(account_key).unwrap();
3652        assert_eq!(
3653            &*secondary_index_key_map.value().read().unwrap(),
3654            secondary_index_keys
3655        );
3656    }
3657
3658    fn run_test_spl_token_secondary_indexes<
3659        SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
3660    >(
3661        token_id: &Pubkey,
3662        index: &AccountsIndex<bool, bool>,
3663        secondary_index: &SecondaryIndex<SecondaryIndexEntryType>,
3664        key_start: usize,
3665        key_end: usize,
3666        secondary_indexes: &AccountSecondaryIndexes,
3667    ) {
3668        let mut secondary_indexes = secondary_indexes.clone();
3669        let account_key = Pubkey::new_unique();
3670        let index_key = Pubkey::new_unique();
3671        let mut account_data = make_empty_token_account_data();
3672        account_data[key_start..key_end].clone_from_slice(&(index_key.to_bytes()));
3673
3674        // Wrong program id
3675        index.upsert(
3676            0,
3677            0,
3678            &account_key,
3679            &AccountSharedData::create(0, account_data.to_vec(), Pubkey::default(), false, 0),
3680            &secondary_indexes,
3681            true,
3682            &mut vec![],
3683            UPSERT_POPULATE_RECLAIMS,
3684        );
3685        assert!(secondary_index.index.is_empty());
3686        assert!(secondary_index.reverse_index.is_empty());
3687
3688        // Wrong account data size
3689        index.upsert(
3690            0,
3691            0,
3692            &account_key,
3693            &AccountSharedData::create(0, account_data[1..].to_vec(), *token_id, false, 0),
3694            &secondary_indexes,
3695            true,
3696            &mut vec![],
3697            UPSERT_POPULATE_RECLAIMS,
3698        );
3699        assert!(secondary_index.index.is_empty());
3700        assert!(secondary_index.reverse_index.is_empty());
3701
3702        secondary_indexes.keys = None;
3703
3704        // Just right. Inserting the same index multiple times should be ok
3705        for _ in 0..2 {
3706            index.update_secondary_indexes(
3707                &account_key,
3708                &AccountSharedData::create(0, account_data.to_vec(), *token_id, false, 0),
3709                &secondary_indexes,
3710            );
3711            check_secondary_index_mapping_correct(secondary_index, &[index_key], &account_key);
3712        }
3713
3714        // included
3715        assert!(!secondary_index.index.is_empty());
3716        assert!(!secondary_index.reverse_index.is_empty());
3717
3718        secondary_indexes.keys = Some(AccountSecondaryIndexesIncludeExclude {
3719            keys: [index_key].iter().cloned().collect::<HashSet<_>>(),
3720            exclude: false,
3721        });
3722        secondary_index.index.clear();
3723        secondary_index.reverse_index.clear();
3724        index.update_secondary_indexes(
3725            &account_key,
3726            &AccountSharedData::create(0, account_data.to_vec(), *token_id, false, 0),
3727            &secondary_indexes,
3728        );
3729        assert!(!secondary_index.index.is_empty());
3730        assert!(!secondary_index.reverse_index.is_empty());
3731        check_secondary_index_mapping_correct(secondary_index, &[index_key], &account_key);
3732
3733        // not-excluded
3734        secondary_indexes.keys = Some(AccountSecondaryIndexesIncludeExclude {
3735            keys: [].iter().cloned().collect::<HashSet<_>>(),
3736            exclude: true,
3737        });
3738        secondary_index.index.clear();
3739        secondary_index.reverse_index.clear();
3740        index.update_secondary_indexes(
3741            &account_key,
3742            &AccountSharedData::create(0, account_data.to_vec(), *token_id, false, 0),
3743            &secondary_indexes,
3744        );
3745        assert!(!secondary_index.index.is_empty());
3746        assert!(!secondary_index.reverse_index.is_empty());
3747        check_secondary_index_mapping_correct(secondary_index, &[index_key], &account_key);
3748
3749        secondary_indexes.keys = None;
3750
3751        index.slot_list_mut(&account_key, |slot_list| slot_list.clear());
3752
3753        // Everything should be deleted
3754        let _ = index.handle_dead_keys(&[&account_key], &secondary_indexes);
3755        assert!(secondary_index.index.is_empty());
3756        assert!(secondary_index.reverse_index.is_empty());
3757    }
3758
3759    #[test]
3760    fn test_spl_token_mint_secondary_index() {
3761        let (key_start, key_end, secondary_indexes) = create_spl_token_mint_secondary_index_state();
3762        let index = AccountsIndex::<bool, bool>::default_for_tests();
3763        for token_id in SPL_TOKENS {
3764            run_test_spl_token_secondary_indexes(
3765                token_id,
3766                &index,
3767                &index.spl_token_mint_index,
3768                key_start,
3769                key_end,
3770                &secondary_indexes,
3771            );
3772        }
3773    }
3774
3775    #[test]
3776    fn test_spl_token_owner_secondary_index() {
3777        let (key_start, key_end, secondary_indexes) =
3778            create_spl_token_owner_secondary_index_state();
3779        let index = AccountsIndex::<bool, bool>::default_for_tests();
3780        for token_id in SPL_TOKENS {
3781            run_test_spl_token_secondary_indexes(
3782                token_id,
3783                &index,
3784                &index.spl_token_owner_index,
3785                key_start,
3786                key_end,
3787                &secondary_indexes,
3788            );
3789        }
3790    }
3791
3792    fn run_test_secondary_indexes_same_slot_and_forks<
3793        SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
3794    >(
3795        token_id: &Pubkey,
3796        index: &AccountsIndex<bool, bool>,
3797        secondary_index: &SecondaryIndex<SecondaryIndexEntryType>,
3798        index_key_start: usize,
3799        index_key_end: usize,
3800        secondary_indexes: &AccountSecondaryIndexes,
3801    ) {
3802        let account_key = Pubkey::new_unique();
3803        let secondary_key1 = Pubkey::new_unique();
3804        let secondary_key2 = Pubkey::new_unique();
3805        let slot = 1;
3806        let mut account_data1 = make_empty_token_account_data();
3807        account_data1[index_key_start..index_key_end]
3808            .clone_from_slice(&(secondary_key1.to_bytes()));
3809        let mut account_data2 = make_empty_token_account_data();
3810        account_data2[index_key_start..index_key_end]
3811            .clone_from_slice(&(secondary_key2.to_bytes()));
3812
3813        // First write one mint index
3814        index.upsert(
3815            slot,
3816            slot,
3817            &account_key,
3818            &AccountSharedData::create(0, account_data1.to_vec(), *token_id, false, 0),
3819            secondary_indexes,
3820            true,
3821            &mut vec![],
3822            UPSERT_POPULATE_RECLAIMS,
3823        );
3824
3825        // Now write a different mint index for the same account
3826        index.upsert(
3827            slot,
3828            slot,
3829            &account_key,
3830            &AccountSharedData::create(0, account_data2.to_vec(), *token_id, false, 0),
3831            secondary_indexes,
3832            true,
3833            &mut vec![],
3834            UPSERT_POPULATE_RECLAIMS,
3835        );
3836
3837        // Both pubkeys will now be present in the index
3838        check_secondary_index_mapping_correct(
3839            secondary_index,
3840            &[secondary_key1, secondary_key2],
3841            &account_key,
3842        );
3843
3844        // If a later slot also introduces secondary_key1, then it should still exist in the index
3845        let later_slot = slot + 1;
3846        index.upsert(
3847            later_slot,
3848            later_slot,
3849            &account_key,
3850            &AccountSharedData::create(0, account_data1.to_vec(), *token_id, false, 0),
3851            secondary_indexes,
3852            true,
3853            &mut vec![],
3854            UPSERT_POPULATE_RECLAIMS,
3855        );
3856        assert_eq!(secondary_index.get(&secondary_key1), vec![account_key]);
3857
3858        // If we set a root at `later_slot`, and clean, then even though the account with secondary_key1
3859        // was outdated by the update in the later slot, the primary account key is still alive,
3860        // so both secondary keys will still be kept alive.
3861        index.add_root(later_slot);
3862        index.slot_list_mut(&account_key, |slot_list| {
3863            index.purge_older_root_entries(slot_list, &mut vec![], None)
3864        });
3865
3866        check_secondary_index_mapping_correct(
3867            secondary_index,
3868            &[secondary_key1, secondary_key2],
3869            &account_key,
3870        );
3871
3872        // Removing the remaining entry for this pubkey in the index should mark the
3873        // pubkey as dead and finally remove all the secondary indexes
3874        let mut reclaims = vec![];
3875        index.purge_exact(&account_key, &later_slot, &mut reclaims);
3876        let _ = index.handle_dead_keys(&[&account_key], secondary_indexes);
3877        assert!(secondary_index.index.is_empty());
3878        assert!(secondary_index.reverse_index.is_empty());
3879    }
3880
3881    #[test]
3882    fn test_spl_token_mint_secondary_index_same_slot_and_forks() {
3883        let (key_start, key_end, account_index) = create_spl_token_mint_secondary_index_state();
3884        let index = AccountsIndex::<bool, bool>::default_for_tests();
3885        for token_id in SPL_TOKENS {
3886            run_test_secondary_indexes_same_slot_and_forks(
3887                token_id,
3888                &index,
3889                &index.spl_token_mint_index,
3890                key_start,
3891                key_end,
3892                &account_index,
3893            );
3894        }
3895    }
3896
3897    #[test]
3898    fn test_rwlock_secondary_index_same_slot_and_forks() {
3899        let (key_start, key_end, account_index) = create_spl_token_owner_secondary_index_state();
3900        let index = AccountsIndex::<bool, bool>::default_for_tests();
3901        for token_id in SPL_TOKENS {
3902            run_test_secondary_indexes_same_slot_and_forks(
3903                token_id,
3904                &index,
3905                &index.spl_token_owner_index,
3906                key_start,
3907                key_end,
3908                &account_index,
3909            );
3910        }
3911    }
3912
3913    impl IndexValue for bool {}
3914    impl IndexValue for u64 {}
3915    impl DiskIndexValue for bool {}
3916    impl DiskIndexValue for u64 {}
3917    impl IsCached for bool {
3918        fn is_cached(&self) -> bool {
3919            false
3920        }
3921    }
3922    impl IsCached for u64 {
3923        fn is_cached(&self) -> bool {
3924            false
3925        }
3926    }
3927    impl ZeroLamport for bool {
3928        fn is_zero_lamport(&self) -> bool {
3929            false
3930        }
3931    }
3932
3933    impl ZeroLamport for u64 {
3934        fn is_zero_lamport(&self) -> bool {
3935            false
3936        }
3937    }
3938
3939    #[test]
3940    fn test_bin_start_and_range() {
3941        let index = AccountsIndex::<bool, bool>::default_for_tests();
3942        let iter = AccountsIndexIterator::new(
3943            &index,
3944            None::<&RangeInclusive<Pubkey>>,
3945            COLLECT_ALL_UNSORTED_FALSE,
3946        );
3947        assert_eq!((0, usize::MAX), iter.bin_start_and_range());
3948
3949        let key_0 = Pubkey::from([0; 32]);
3950        let key_ff = Pubkey::from([0xff; 32]);
3951
3952        let iter = AccountsIndexIterator::new(
3953            &index,
3954            Some(&RangeInclusive::new(key_0, key_ff)),
3955            COLLECT_ALL_UNSORTED_FALSE,
3956        );
3957        let bins = index.bins();
3958        assert_eq!((0, bins), iter.bin_start_and_range());
3959        let iter = AccountsIndexIterator::new(
3960            &index,
3961            Some(&RangeInclusive::new(key_ff, key_0)),
3962            COLLECT_ALL_UNSORTED_FALSE,
3963        );
3964        assert_eq!((bins - 1, 0), iter.bin_start_and_range());
3965        let iter = AccountsIndexIterator::new(
3966            &index,
3967            Some(&(Included(key_0), Unbounded)),
3968            COLLECT_ALL_UNSORTED_FALSE,
3969        );
3970        assert_eq!((0, usize::MAX), iter.bin_start_and_range());
3971        let iter = AccountsIndexIterator::new(
3972            &index,
3973            Some(&(Included(key_ff), Unbounded)),
3974            COLLECT_ALL_UNSORTED_FALSE,
3975        );
3976        assert_eq!((bins - 1, usize::MAX), iter.bin_start_and_range());
3977
3978        assert_eq!((0..2).skip(1).take(usize::MAX).collect::<Vec<_>>(), vec![1]);
3979    }
3980
3981    #[test]
3982    fn test_get_newest_root_in_slot_list() {
3983        let index = AccountsIndex::<bool, bool>::default_for_tests();
3984        let return_0 = 0;
3985        let slot1 = 1;
3986        let slot2 = 2;
3987        let slot99 = 99;
3988
3989        // no roots, so always 0
3990        {
3991            let roots_tracker = &index.roots_tracker.read().unwrap();
3992            let slot_list = Vec::<(Slot, bool)>::default();
3993            assert_eq!(
3994                return_0,
3995                AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
3996                    &roots_tracker.alive_roots,
3997                    &slot_list,
3998                    Some(slot1),
3999                )
4000            );
4001            assert_eq!(
4002                return_0,
4003                AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
4004                    &roots_tracker.alive_roots,
4005                    &slot_list,
4006                    Some(slot2),
4007                )
4008            );
4009            assert_eq!(
4010                return_0,
4011                AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
4012                    &roots_tracker.alive_roots,
4013                    &slot_list,
4014                    Some(slot99),
4015                )
4016            );
4017        }
4018
4019        index.add_root(slot2);
4020
4021        {
4022            let roots_tracker = &index.roots_tracker.read().unwrap();
4023            let slot_list = vec![(slot2, true)];
4024            assert_eq!(
4025                slot2,
4026                AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
4027                    &roots_tracker.alive_roots,
4028                    &slot_list,
4029                    Some(slot2),
4030                )
4031            );
4032            // no newest root
4033            assert_eq!(
4034                return_0,
4035                AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
4036                    &roots_tracker.alive_roots,
4037                    &slot_list,
4038                    Some(slot1),
4039                )
4040            );
4041            assert_eq!(
4042                slot2,
4043                AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
4044                    &roots_tracker.alive_roots,
4045                    &slot_list,
4046                    Some(slot99),
4047                )
4048            );
4049        }
4050    }
4051
4052    impl<T: IndexValue> AccountsIndex<T, T> {
4053        fn upsert_simple_test(&self, key: &Pubkey, slot: Slot, value: T) {
4054            let mut gc = Vec::new();
4055            self.upsert(
4056                slot,
4057                slot,
4058                key,
4059                &AccountSharedData::default(),
4060                &AccountSecondaryIndexes::default(),
4061                value,
4062                &mut gc,
4063                UPSERT_POPULATE_RECLAIMS,
4064            );
4065            assert!(gc.is_empty());
4066        }
4067
4068        pub fn clear_uncleaned_roots(&self, max_clean_root: Option<Slot>) -> HashSet<Slot> {
4069            let mut cleaned_roots = HashSet::new();
4070            let mut w_roots_tracker = self.roots_tracker.write().unwrap();
4071            w_roots_tracker.uncleaned_roots.retain(|root| {
4072                let is_cleaned = max_clean_root
4073                    .map(|max_clean_root| *root <= max_clean_root)
4074                    .unwrap_or(true);
4075                if is_cleaned {
4076                    cleaned_roots.insert(*root);
4077                }
4078                // Only keep the slots that have yet to be cleaned
4079                !is_cleaned
4080            });
4081            cleaned_roots
4082        }
4083
4084        pub(crate) fn is_uncleaned_root(&self, slot: Slot) -> bool {
4085            self.roots_tracker
4086                .read()
4087                .unwrap()
4088                .uncleaned_roots
4089                .contains(&slot)
4090        }
4091
4092        pub fn clear_roots(&self) {
4093            self.roots_tracker.write().unwrap().alive_roots.clear()
4094        }
4095    }
4096
4097    #[test]
4098    fn test_unref() {
4099        let value = true;
4100        let key = solana_sdk::pubkey::new_rand();
4101        let index = AccountsIndex::<bool, bool>::default_for_tests();
4102        let slot1 = 1;
4103
4104        index.upsert_simple_test(&key, slot1, value);
4105
4106        let map = index.get_bin(&key);
4107        for expected in [false, true] {
4108            assert!(map.get_internal_inner(&key, |entry| {
4109                // check refcount BEFORE the unref
4110                assert_eq!(u64::from(!expected), entry.unwrap().ref_count());
4111                // first time, ref count was at 1, we can unref once. Unref should return 1.
4112                // second time, ref count was at 0, it is an error to unref. Unref should return 0
4113                assert_eq!(u64::from(!expected), entry.unwrap().unref());
4114                // check refcount AFTER the unref
4115                assert_eq!(
4116                    if expected {
4117                        (0 as RefCount).wrapping_sub(1)
4118                    } else {
4119                        0
4120                    },
4121                    entry.unwrap().ref_count()
4122                );
4123                (false, true)
4124            }));
4125        }
4126    }
4127
4128    #[test]
4129    fn test_clean_rooted_entries_return() {
4130        solana_logger::setup();
4131        let value = true;
4132        let key = solana_sdk::pubkey::new_rand();
4133        let key_unknown = solana_sdk::pubkey::new_rand();
4134        let index = AccountsIndex::<bool, bool>::default_for_tests();
4135        let slot1 = 1;
4136
4137        let mut gc = Vec::new();
4138        // return true if we don't know anything about 'key_unknown'
4139        // the item did not exist in the accounts index at all, so index is up to date
4140        assert!(index.clean_rooted_entries(&key_unknown, &mut gc, None));
4141
4142        index.upsert_simple_test(&key, slot1, value);
4143
4144        let slot2 = 2;
4145        // none for max root because we don't want to delete the entry yet
4146        assert!(!index.clean_rooted_entries(&key, &mut gc, None));
4147        // this is because of inclusive vs exclusive in the call to can_purge_older_entries
4148        assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot1)));
4149        // this will delete the entry because it is <= max_root_inclusive and NOT a root
4150        // note this has to be slot2 because of inclusive vs exclusive in the call to can_purge_older_entries
4151        {
4152            let mut gc = Vec::new();
4153            assert!(index.clean_rooted_entries(&key, &mut gc, Some(slot2)));
4154            assert_eq!(gc, vec![(slot1, value)]);
4155        }
4156
4157        // re-add it
4158        index.upsert_simple_test(&key, slot1, value);
4159
4160        index.add_root(slot1);
4161        assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot2)));
4162        index.upsert_simple_test(&key, slot2, value);
4163
4164        {
4165            let account_map_entry = index.get_cloned(&key).unwrap();
4166            let slot_list = account_map_entry.slot_list.read().unwrap();
4167            assert_eq!(2, slot_list.len());
4168            assert_eq!(&[(slot1, value), (slot2, value)], slot_list.as_slice());
4169        }
4170        assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot2)));
4171        assert_eq!(
4172            2,
4173            index
4174                .get_cloned(&key)
4175                .unwrap()
4176                .slot_list
4177                .read()
4178                .unwrap()
4179                .len()
4180        );
4181        assert!(gc.is_empty());
4182        {
4183            {
4184                let roots_tracker = &index.roots_tracker.read().unwrap();
4185                let slot_list = vec![(slot2, value)];
4186                assert_eq!(
4187                    0,
4188                    AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
4189                        &roots_tracker.alive_roots,
4190                        &slot_list,
4191                        None,
4192                    )
4193                );
4194            }
4195            index.add_root(slot2);
4196            {
4197                let roots_tracker = &index.roots_tracker.read().unwrap();
4198                let slot_list = vec![(slot2, value)];
4199                assert_eq!(
4200                    slot2,
4201                    AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
4202                        &roots_tracker.alive_roots,
4203                        &slot_list,
4204                        None,
4205                    )
4206                );
4207                assert_eq!(
4208                    0,
4209                    AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
4210                        &roots_tracker.alive_roots,
4211                        &slot_list,
4212                        Some(0),
4213                    )
4214                );
4215            }
4216        }
4217
4218        assert!(gc.is_empty());
4219        assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot2)));
4220        assert_eq!(gc, vec![(slot1, value)]);
4221        gc.clear();
4222        index.clean_dead_slot(slot2);
4223        let slot3 = 3;
4224        assert!(index.clean_rooted_entries(&key, &mut gc, Some(slot3)));
4225        assert_eq!(gc, vec![(slot2, value)]);
4226    }
4227
4228    #[test]
4229    fn test_handle_dead_keys_return() {
4230        let key = solana_sdk::pubkey::new_rand();
4231        let index = AccountsIndex::<bool, bool>::default_for_tests();
4232
4233        assert_eq!(
4234            index.handle_dead_keys(&[&key], &AccountSecondaryIndexes::default()),
4235            vec![key].into_iter().collect::<HashSet<_>>()
4236        );
4237    }
4238
4239    #[test]
4240    fn test_start_end_bin() {
4241        let index = AccountsIndex::<bool, bool>::default_for_tests();
4242        assert_eq!(index.bins(), BINS_FOR_TESTING);
4243        let iter = AccountsIndexIterator::new(
4244            &index,
4245            None::<&RangeInclusive<Pubkey>>,
4246            COLLECT_ALL_UNSORTED_FALSE,
4247        );
4248        assert_eq!(iter.start_bin(), 0); // no range, so 0
4249        assert_eq!(iter.end_bin_inclusive(), usize::MAX); // no range, so max
4250
4251        let key = Pubkey::from([0; 32]);
4252        let iter = AccountsIndexIterator::new(
4253            &index,
4254            Some(&RangeInclusive::new(key, key)),
4255            COLLECT_ALL_UNSORTED_FALSE,
4256        );
4257        assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
4258        assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
4259        let iter = AccountsIndexIterator::new(
4260            &index,
4261            Some(&(Included(key), Excluded(key))),
4262            COLLECT_ALL_UNSORTED_FALSE,
4263        );
4264        assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
4265        assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
4266        let iter = AccountsIndexIterator::new(
4267            &index,
4268            Some(&(Excluded(key), Excluded(key))),
4269            COLLECT_ALL_UNSORTED_FALSE,
4270        );
4271        assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
4272        assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
4273
4274        let key = Pubkey::from([0xff; 32]);
4275        let iter = AccountsIndexIterator::new(
4276            &index,
4277            Some(&RangeInclusive::new(key, key)),
4278            COLLECT_ALL_UNSORTED_FALSE,
4279        );
4280        let bins = index.bins();
4281        assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1
4282        assert_eq!(iter.end_bin_inclusive(), bins - 1);
4283        let iter = AccountsIndexIterator::new(
4284            &index,
4285            Some(&(Included(key), Excluded(key))),
4286            COLLECT_ALL_UNSORTED_FALSE,
4287        );
4288        assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1
4289        assert_eq!(iter.end_bin_inclusive(), bins - 1);
4290        let iter = AccountsIndexIterator::new(
4291            &index,
4292            Some(&(Excluded(key), Excluded(key))),
4293            COLLECT_ALL_UNSORTED_FALSE,
4294        );
4295        assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1
4296        assert_eq!(iter.end_bin_inclusive(), bins - 1);
4297    }
4298
4299    #[test]
4300    #[should_panic(expected = "bins.is_power_of_two()")]
4301    #[allow(clippy::field_reassign_with_default)]
4302    fn test_illegal_bins() {
4303        let mut config = AccountsIndexConfig::default();
4304        config.bins = Some(3);
4305        AccountsIndex::<bool, bool>::new(Some(config), Arc::default());
4306    }
4307
4308    #[test]
4309    fn test_scan_config() {
4310        for collect_all_unsorted in [false, true] {
4311            let config = ScanConfig::new(collect_all_unsorted);
4312            assert_eq!(config.collect_all_unsorted, collect_all_unsorted);
4313            assert!(config.abort.is_none()); // not allocated
4314            assert!(!config.is_aborted());
4315            config.abort(); // has no effect
4316            assert!(!config.is_aborted());
4317        }
4318
4319        let config = ScanConfig::new(false);
4320        assert!(!config.collect_all_unsorted);
4321        assert!(config.abort.is_none());
4322
4323        let config = ScanConfig::default();
4324        assert!(config.collect_all_unsorted);
4325        assert!(config.abort.is_none());
4326
4327        let config = config.recreate_with_abort();
4328        assert!(config.abort.is_some());
4329        assert!(!config.is_aborted());
4330        config.abort();
4331        assert!(config.is_aborted());
4332
4333        let config = config.recreate_with_abort();
4334        assert!(config.is_aborted());
4335    }
4336}