solana_accounts_db/
storable_accounts.rs

1//! trait for abstracting underlying storage of pubkey and account pairs to be written
2use {
3    crate::{
4        account_storage::meta::StoredAccountMeta,
5        accounts_db::{AccountFromStorage, AccountStorageEntry, AccountsDb},
6        accounts_index::ZeroLamport,
7    },
8    solana_pubkey::Pubkey,
9    solana_sdk::{
10        account::{AccountSharedData, ReadableAccount},
11        clock::{Epoch, Slot},
12    },
13    std::sync::{Arc, RwLock},
14};
15
16/// hold a ref to an account to store. The account could be represented in memory a few different ways
17#[derive(Debug, Copy, Clone)]
18pub enum AccountForStorage<'a> {
19    AddressAndAccount((&'a Pubkey, &'a AccountSharedData)),
20    StoredAccountMeta(&'a StoredAccountMeta<'a>),
21}
22
23impl<'a> From<(&'a Pubkey, &'a AccountSharedData)> for AccountForStorage<'a> {
24    fn from(source: (&'a Pubkey, &'a AccountSharedData)) -> Self {
25        Self::AddressAndAccount(source)
26    }
27}
28
29impl<'a> From<&'a StoredAccountMeta<'a>> for AccountForStorage<'a> {
30    fn from(source: &'a StoredAccountMeta<'a>) -> Self {
31        Self::StoredAccountMeta(source)
32    }
33}
34
35impl ZeroLamport for AccountForStorage<'_> {
36    fn is_zero_lamport(&self) -> bool {
37        self.lamports() == 0
38    }
39}
40
41impl<'a> AccountForStorage<'a> {
42    pub fn pubkey(&self) -> &'a Pubkey {
43        match self {
44            AccountForStorage::AddressAndAccount((pubkey, _account)) => pubkey,
45            AccountForStorage::StoredAccountMeta(account) => account.pubkey(),
46        }
47    }
48}
49
50impl ReadableAccount for AccountForStorage<'_> {
51    fn lamports(&self) -> u64 {
52        match self {
53            AccountForStorage::AddressAndAccount((_pubkey, account)) => account.lamports(),
54            AccountForStorage::StoredAccountMeta(account) => account.lamports(),
55        }
56    }
57    fn data(&self) -> &[u8] {
58        match self {
59            AccountForStorage::AddressAndAccount((_pubkey, account)) => account.data(),
60            AccountForStorage::StoredAccountMeta(account) => account.data(),
61        }
62    }
63    fn owner(&self) -> &Pubkey {
64        match self {
65            AccountForStorage::AddressAndAccount((_pubkey, account)) => account.owner(),
66            AccountForStorage::StoredAccountMeta(account) => account.owner(),
67        }
68    }
69    fn executable(&self) -> bool {
70        match self {
71            AccountForStorage::AddressAndAccount((_pubkey, account)) => account.executable(),
72            AccountForStorage::StoredAccountMeta(account) => account.executable(),
73        }
74    }
75    fn rent_epoch(&self) -> Epoch {
76        match self {
77            AccountForStorage::AddressAndAccount((_pubkey, account)) => account.rent_epoch(),
78            AccountForStorage::StoredAccountMeta(account) => account.rent_epoch(),
79        }
80    }
81    fn to_account_shared_data(&self) -> AccountSharedData {
82        match self {
83            AccountForStorage::AddressAndAccount((_pubkey, account)) => {
84                account.to_account_shared_data()
85            }
86            AccountForStorage::StoredAccountMeta(account) => account.to_account_shared_data(),
87        }
88    }
89}
90
91lazy_static! {
92    static ref DEFAULT_ACCOUNT_SHARED_DATA: AccountSharedData = AccountSharedData::default();
93}
94
95#[derive(Default, Debug)]
96pub struct StorableAccountsCacher {
97    slot: Slot,
98    storage: Option<Arc<AccountStorageEntry>>,
99}
100
101/// abstract access to pubkey, account, slot, target_slot of either:
102/// a. (slot, &[&Pubkey, &ReadableAccount])
103/// b. (slot, &[Pubkey, ReadableAccount])
104/// c. (slot, &[&Pubkey, &ReadableAccount, Slot]) (we will use this later)
105/// This trait avoids having to allocate redundant data when there is a duplicated slot parameter.
106/// All legacy callers do not have a unique slot per account to store.
107pub trait StorableAccounts<'a>: Sync {
108    /// account at 'index'
109    fn account<Ret>(
110        &self,
111        index: usize,
112        callback: impl for<'local> FnMut(AccountForStorage<'local>) -> Ret,
113    ) -> Ret;
114    /// None if account is zero lamports
115    fn account_default_if_zero_lamport<Ret>(
116        &self,
117        index: usize,
118        mut callback: impl for<'local> FnMut(AccountForStorage<'local>) -> Ret,
119    ) -> Ret {
120        self.account(index, |account| {
121            callback(if account.lamports() != 0 {
122                account
123            } else {
124                // preserve the pubkey, but use a default value for the account
125                AccountForStorage::AddressAndAccount((
126                    account.pubkey(),
127                    &DEFAULT_ACCOUNT_SHARED_DATA,
128                ))
129            })
130        })
131    }
132    // current slot for account at 'index'
133    fn slot(&self, index: usize) -> Slot;
134    /// slot that all accounts are to be written to
135    fn target_slot(&self) -> Slot;
136    /// true if no accounts to write
137    fn is_empty(&self) -> bool {
138        self.len() == 0
139    }
140    /// # accounts to write
141    fn len(&self) -> usize;
142    /// are there accounts from multiple slots
143    /// only used for an assert
144    fn contains_multiple_slots(&self) -> bool {
145        false
146    }
147}
148
149impl<'a: 'b, 'b> StorableAccounts<'a> for (Slot, &'b [(&'a Pubkey, &'a AccountSharedData)]) {
150    fn account<Ret>(
151        &self,
152        index: usize,
153        mut callback: impl for<'local> FnMut(AccountForStorage<'local>) -> Ret,
154    ) -> Ret {
155        callback((self.1[index].0, self.1[index].1).into())
156    }
157    fn slot(&self, _index: usize) -> Slot {
158        // per-index slot is not unique per slot when per-account slot is not included in the source data
159        self.target_slot()
160    }
161    fn target_slot(&self) -> Slot {
162        self.0
163    }
164    fn len(&self) -> usize {
165        self.1.len()
166    }
167}
168
169impl<'a: 'b, 'b> StorableAccounts<'a> for (Slot, &'b [(Pubkey, AccountSharedData)]) {
170    fn account<Ret>(
171        &self,
172        index: usize,
173        mut callback: impl for<'local> FnMut(AccountForStorage<'local>) -> Ret,
174    ) -> Ret {
175        callback((&self.1[index].0, &self.1[index].1).into())
176    }
177    fn slot(&self, _index: usize) -> Slot {
178        // per-index slot is not unique per slot when per-account slot is not included in the source data
179        self.target_slot()
180    }
181    fn target_slot(&self) -> Slot {
182        self.0
183    }
184    fn len(&self) -> usize {
185        self.1.len()
186    }
187}
188
189/// holds slices of accounts being moved FROM a common source slot to 'target_slot'
190pub struct StorableAccountsBySlot<'a> {
191    target_slot: Slot,
192    /// each element is (source slot, accounts moving FROM source slot)
193    slots_and_accounts: &'a [(Slot, &'a [&'a AccountFromStorage])],
194
195    /// This is calculated based off slots_and_accounts.
196    /// cumulative offset of all account slices prior to this one
197    /// starting_offsets[0] is the starting offset of slots_and_accounts[1]
198    /// The starting offset of slots_and_accounts[0] is always 0
199    starting_offsets: Vec<usize>,
200    /// true if there is more than 1 slot represented in slots_and_accounts
201    contains_multiple_slots: bool,
202    /// total len of all accounts, across all slots_and_accounts
203    len: usize,
204    db: &'a AccountsDb,
205    /// remember the last storage we looked up for a given slot
206    cached_storage: RwLock<StorableAccountsCacher>,
207}
208
209impl<'a> StorableAccountsBySlot<'a> {
210    /// each element of slots_and_accounts is (source slot, accounts moving FROM source slot)
211    pub fn new(
212        target_slot: Slot,
213        slots_and_accounts: &'a [(Slot, &'a [&'a AccountFromStorage])],
214        db: &'a AccountsDb,
215    ) -> Self {
216        let mut cumulative_len = 0usize;
217        let mut starting_offsets = Vec::with_capacity(slots_and_accounts.len());
218        let first_slot = slots_and_accounts
219            .first()
220            .map(|(slot, _)| *slot)
221            .unwrap_or_default();
222        let mut contains_multiple_slots = false;
223        for (slot, accounts) in slots_and_accounts {
224            cumulative_len = cumulative_len.saturating_add(accounts.len());
225            starting_offsets.push(cumulative_len);
226            contains_multiple_slots |= &first_slot != slot;
227        }
228        Self {
229            target_slot,
230            slots_and_accounts,
231            starting_offsets,
232            contains_multiple_slots,
233            len: cumulative_len,
234            db,
235            cached_storage: RwLock::default(),
236        }
237    }
238    /// given an overall index for all accounts in self:
239    /// return (slots_and_accounts index, index within those accounts)
240    fn find_internal_index(&self, index: usize) -> (usize, usize) {
241        // search offsets for the accounts slice that contains 'index'.
242        // This could be a binary search.
243        for (offset_index, next_offset) in self.starting_offsets.iter().enumerate() {
244            if next_offset > &index {
245                // offset of prior entry
246                let prior_offset = if offset_index > 0 {
247                    self.starting_offsets[offset_index.saturating_sub(1)]
248                } else {
249                    0
250                };
251                return (offset_index, index - prior_offset);
252            }
253        }
254        panic!("failed");
255    }
256}
257
258impl<'a> StorableAccounts<'a> for StorableAccountsBySlot<'a> {
259    fn account<Ret>(
260        &self,
261        index: usize,
262        mut callback: impl for<'local> FnMut(AccountForStorage<'local>) -> Ret,
263    ) -> Ret {
264        let indexes = self.find_internal_index(index);
265        let slot = self.slots_and_accounts[indexes.0].0;
266        let data = self.slots_and_accounts[indexes.0].1[indexes.1];
267        let offset = data.index_info.offset();
268        let mut call_callback = |storage: &AccountStorageEntry| {
269            storage
270                .accounts
271                .get_stored_account_meta_callback(offset, |account: StoredAccountMeta| {
272                    callback((&account).into())
273                })
274                .expect("account has to exist to be able to store it")
275        };
276        {
277            let reader = self.cached_storage.read().unwrap();
278            if reader.slot == slot {
279                if let Some(storage) = reader.storage.as_ref() {
280                    return call_callback(storage);
281                }
282            }
283        }
284        // cache doesn't contain a storage for this slot, so lookup storage in db.
285        // note we do not use file id here. We just want the normal unshrunk storage for this slot.
286        let storage = self
287            .db
288            .storage
289            .get_slot_storage_entry_shrinking_in_progress_ok(slot)
290            .expect("source slot has to have a storage to be able to store accounts");
291        let ret = call_callback(&storage);
292        let mut writer = self.cached_storage.write().unwrap();
293        writer.slot = slot;
294        writer.storage = Some(storage);
295        ret
296    }
297    fn slot(&self, index: usize) -> Slot {
298        let indexes = self.find_internal_index(index);
299        self.slots_and_accounts[indexes.0].0
300    }
301    fn target_slot(&self) -> Slot {
302        self.target_slot
303    }
304    fn len(&self) -> usize {
305        self.len
306    }
307    fn contains_multiple_slots(&self) -> bool {
308        self.contains_multiple_slots
309    }
310}
311
312#[cfg(test)]
313pub mod tests {
314    use {
315        super::*,
316        crate::{
317            account_info::{AccountInfo, StorageLocation},
318            account_storage::meta::{AccountMeta, StoredAccountMeta, StoredMeta},
319            accounts_db::{get_temp_accounts_paths, AccountStorageEntry},
320            accounts_file::AccountsFileProvider,
321            accounts_hash::AccountHash,
322            append_vec::AppendVecStoredAccountMeta,
323        },
324        solana_sdk::{
325            account::{accounts_equal, AccountSharedData, WritableAccount},
326            hash::Hash,
327        },
328        std::sync::Arc,
329    };
330
331    /// this is used in the test for generation of storages
332    /// this is no longer used in the validator.
333    /// It is very tricky to get these right. There are already tests for this. It is likely worth it to leave this here for a while until everything has settled.
334    impl<'a> StorableAccounts<'a> for (Slot, &'a [&'a StoredAccountMeta<'a>]) {
335        fn account<Ret>(
336            &self,
337            index: usize,
338            mut callback: impl for<'local> FnMut(AccountForStorage<'local>) -> Ret,
339        ) -> Ret {
340            callback(self.1[index].into())
341        }
342        fn slot(&self, _index: usize) -> Slot {
343            // per-index slot is not unique per slot when per-account slot is not included in the source data
344            self.0
345        }
346        fn target_slot(&self) -> Slot {
347            self.0
348        }
349        fn len(&self) -> usize {
350            self.1.len()
351        }
352    }
353
354    /// this is no longer used. It is very tricky to get these right. There are already tests for this. It is likely worth it to leave this here for a while until everything has settled.
355    impl<'a, T: ReadableAccount + Sync> StorableAccounts<'a> for (Slot, &'a [&'a (Pubkey, T)])
356    where
357        AccountForStorage<'a>: From<(&'a Pubkey, &'a T)>,
358    {
359        fn account<Ret>(
360            &self,
361            index: usize,
362            mut callback: impl for<'local> FnMut(AccountForStorage<'local>) -> Ret,
363        ) -> Ret {
364            callback((&self.1[index].0, &self.1[index].1).into())
365        }
366        fn slot(&self, _index: usize) -> Slot {
367            // per-index slot is not unique per slot when per-account slot is not included in the source data
368            self.target_slot()
369        }
370        fn target_slot(&self) -> Slot {
371            self.0
372        }
373        fn len(&self) -> usize {
374            self.1.len()
375        }
376    }
377
378    /// this is no longer used. It is very tricky to get these right. There are already tests for this. It is likely worth it to leave this here for a while until everything has settled.
379    /// this tuple contains a single different source slot that applies to all accounts
380    /// accounts are StoredAccountMeta
381    impl<'a> StorableAccounts<'a> for (Slot, &'a [&'a StoredAccountMeta<'a>], Slot) {
382        fn account<Ret>(
383            &self,
384            index: usize,
385            mut callback: impl for<'local> FnMut(AccountForStorage<'local>) -> Ret,
386        ) -> Ret {
387            callback(self.1[index].into())
388        }
389        fn slot(&self, _index: usize) -> Slot {
390            // same other slot for all accounts
391            self.2
392        }
393        fn target_slot(&self) -> Slot {
394            self.0
395        }
396        fn len(&self) -> usize {
397            self.1.len()
398        }
399    }
400
401    fn compare<'a>(a: &impl StorableAccounts<'a>, b: &impl StorableAccounts<'a>) {
402        assert_eq!(a.target_slot(), b.target_slot());
403        assert_eq!(a.len(), b.len());
404        assert_eq!(a.is_empty(), b.is_empty());
405        (0..a.len()).for_each(|i| {
406            b.account(i, |account| {
407                a.account(i, |account_a| {
408                    assert_eq!(account_a.pubkey(), account.pubkey());
409                    assert!(accounts_equal(&account_a, &account));
410                });
411            });
412        })
413    }
414
415    #[test]
416    fn test_contains_multiple_slots() {
417        let db = AccountsDb::new_single_for_tests();
418        let pk = Pubkey::from([1; 32]);
419        let slot = 0;
420        let lamports = 1;
421        let owner = Pubkey::default();
422        let executable = false;
423        let rent_epoch = 0;
424        let meta = StoredMeta {
425            write_version_obsolete: 5,
426            pubkey: pk,
427            data_len: 7,
428        };
429        let account_meta = AccountMeta {
430            lamports,
431            owner,
432            executable,
433            rent_epoch,
434        };
435        let data = Vec::default();
436        let offset = 99 * std::mem::size_of::<u64>(); // offset needs to be 8 byte aligned
437        let stored_size = 101;
438        let hash = AccountHash(Hash::new_unique());
439        let stored_account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
440            meta: &meta,
441            account_meta: &account_meta,
442            data: &data,
443            offset,
444            stored_size,
445            hash: &hash,
446        });
447
448        let account_from_storage = AccountFromStorage::new(&stored_account);
449
450        let accounts = [&account_from_storage, &account_from_storage];
451        let accounts2 = [(slot, &accounts[..])];
452        let test3 = StorableAccountsBySlot::new(slot, &accounts2[..], &db);
453        assert!(!test3.contains_multiple_slots());
454    }
455
456    pub fn build_accounts_from_storage<'a>(
457        accounts: impl Iterator<Item = &'a StoredAccountMeta<'a>>,
458    ) -> Vec<AccountFromStorage> {
459        accounts
460            .map(|account| AccountFromStorage::new(account))
461            .collect()
462    }
463
464    #[test]
465    fn test_storable_accounts() {
466        let max_slots = 3_u64;
467        for target_slot in 0..max_slots {
468            for entries in 0..2 {
469                for starting_slot in 0..max_slots {
470                    let db = AccountsDb::new_single_for_tests();
471                    let data = Vec::default();
472                    let hash = AccountHash(Hash::new_unique());
473                    let mut raw = Vec::new();
474                    let mut raw2 = Vec::new();
475                    let mut raw4 = Vec::new();
476                    for entry in 0..entries {
477                        let pk = Pubkey::from([entry; 32]);
478                        let account = AccountSharedData::create(
479                            (entry as u64) * starting_slot,
480                            Vec::default(),
481                            Pubkey::default(),
482                            false,
483                            0,
484                        );
485
486                        raw.push((
487                            pk,
488                            account.clone(),
489                            starting_slot % max_slots,
490                            StoredMeta {
491                                write_version_obsolete: 0, // just something
492                                pubkey: pk,
493                                data_len: u64::MAX, // just something
494                            },
495                            AccountMeta {
496                                lamports: account.lamports(),
497                                owner: *account.owner(),
498                                executable: account.executable(),
499                                rent_epoch: account.rent_epoch(),
500                            },
501                        ));
502                    }
503                    for entry in 0..entries {
504                        let offset = 99 * std::mem::size_of::<u64>(); // offset needs to be 8 byte aligned
505                        let stored_size = 101;
506                        let raw = &raw[entry as usize];
507                        raw2.push(StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
508                            meta: &raw.3,
509                            account_meta: &raw.4,
510                            data: &data,
511                            offset,
512                            stored_size,
513                            hash: &hash,
514                        }));
515                        raw4.push((raw.0, raw.1.clone()));
516                    }
517                    let raw2_accounts_from_storage = build_accounts_from_storage(raw2.iter());
518
519                    let mut two = Vec::new();
520                    let mut three = Vec::new();
521                    let mut three_accounts_from_storage_byval = Vec::new();
522                    let mut four_pubkey_and_account_value = Vec::new();
523                    raw.iter()
524                        .zip(
525                            raw2.iter()
526                                .zip(raw4.iter().zip(raw2_accounts_from_storage.iter())),
527                        )
528                        .for_each(|(raw, (raw2, (raw4, raw2_accounts_from_storage)))| {
529                            two.push((&raw.0, &raw.1)); // 2 item tuple
530                            three.push(raw2);
531                            three_accounts_from_storage_byval.push(*raw2_accounts_from_storage);
532                            four_pubkey_and_account_value.push(raw4);
533                        });
534                    let test2 = (target_slot, &two[..]);
535                    let test4 = (target_slot, &four_pubkey_and_account_value[..]);
536
537                    let source_slot = starting_slot % max_slots;
538
539                    let storage = setup_sample_storage(&db, source_slot);
540                    // since we're no longer storing `StoredAccountMeta`, we have to actually store the
541                    // accounts so they can be looked up later in `db`
542                    if let Some(offsets) = storage
543                        .accounts
544                        .append_accounts(&(source_slot, &three[..]), 0)
545                    {
546                        three_accounts_from_storage_byval
547                            .iter_mut()
548                            .zip(offsets.offsets.iter())
549                            .for_each(|(account, offset)| {
550                                account.index_info = AccountInfo::new(
551                                    StorageLocation::AppendVec(0, *offset),
552                                    if account.is_zero_lamport() { 0 } else { 1 },
553                                )
554                            });
555                    }
556                    let three_accounts_from_storage =
557                        three_accounts_from_storage_byval.iter().collect::<Vec<_>>();
558
559                    let accounts_with_slots = vec![(source_slot, &three_accounts_from_storage[..])];
560                    let test3 = StorableAccountsBySlot::new(target_slot, &accounts_with_slots, &db);
561                    let old_slot = starting_slot;
562                    let for_slice = [(old_slot, &three_accounts_from_storage[..])];
563                    let test_moving_slots2 =
564                        StorableAccountsBySlot::new(target_slot, &for_slice, &db);
565                    compare(&test2, &test3);
566                    compare(&test2, &test4);
567                    compare(&test2, &test_moving_slots2);
568                    for (i, raw) in raw.iter().enumerate() {
569                        test3.account(i, |account| {
570                            assert_eq!(raw.0, *account.pubkey());
571                            assert!(accounts_equal(&raw.1, &account));
572                        });
573                        assert_eq!(raw.2, test3.slot(i));
574                        assert_eq!(target_slot, test4.slot(i));
575                        assert_eq!(target_slot, test2.slot(i));
576                        assert_eq!(old_slot, test_moving_slots2.slot(i));
577                    }
578                    assert_eq!(target_slot, test3.target_slot());
579                    assert_eq!(target_slot, test4.target_slot());
580                    assert_eq!(target_slot, test_moving_slots2.target_slot());
581                    assert!(!test2.contains_multiple_slots());
582                    assert!(!test4.contains_multiple_slots());
583                    assert_eq!(test3.contains_multiple_slots(), entries > 1);
584                }
585            }
586        }
587    }
588
589    fn setup_sample_storage(db: &AccountsDb, slot: Slot) -> Arc<AccountStorageEntry> {
590        let id = 2;
591        let file_size = 10_000;
592        let (_temp_dirs, paths) = get_temp_accounts_paths(1).unwrap();
593        let data = AccountStorageEntry::new(
594            &paths[0],
595            slot,
596            id,
597            file_size,
598            AccountsFileProvider::AppendVec,
599        );
600        let storage = Arc::new(data);
601        db.storage.insert(slot, storage.clone());
602        storage
603    }
604
605    #[test]
606    fn test_storable_accounts_by_slot() {
607        for entries in 0..6 {
608            let data = Vec::default();
609            let hashes = (0..entries)
610                .map(|_| AccountHash(Hash::new_unique()))
611                .collect::<Vec<_>>();
612            let mut raw = Vec::new();
613            let mut raw2 = Vec::new();
614            for entry in 0..entries {
615                let pk = Pubkey::from([entry; 32]);
616                let account = AccountSharedData::create(
617                    entry as u64,
618                    Vec::default(),
619                    Pubkey::default(),
620                    false,
621                    0,
622                );
623                raw.push((
624                    pk,
625                    account.clone(),
626                    StoredMeta {
627                        write_version_obsolete: 500 + (entry * 3) as u64, // just something
628                        pubkey: pk,
629                        data_len: (entry * 2) as u64, // just something
630                    },
631                    AccountMeta {
632                        lamports: account.lamports(),
633                        owner: *account.owner(),
634                        executable: account.executable(),
635                        rent_epoch: account.rent_epoch(),
636                    },
637                ));
638            }
639
640            for entry in 0..entries {
641                let offset = 99 * std::mem::size_of::<u64>(); // offset needs to be 8 byte aligned
642                let stored_size = 101;
643                raw2.push(StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
644                    meta: &raw[entry as usize].2,
645                    account_meta: &raw[entry as usize].3,
646                    data: &data,
647                    offset,
648                    stored_size,
649                    hash: &hashes[entry as usize],
650                }));
651            }
652
653            let raw2_account_from_storage = raw2
654                .iter()
655                .map(|account| AccountFromStorage::new(account))
656                .collect::<Vec<_>>();
657            let raw2_refs = raw2.iter().collect::<Vec<_>>();
658
659            // enumerate through permutations of # entries (ie. accounts) in each slot. Each one is 0..=entries.
660            for entries0 in 0..=entries {
661                let remaining1 = entries.saturating_sub(entries0);
662                for entries1 in 0..=remaining1 {
663                    let remaining2 = entries.saturating_sub(entries0 + entries1);
664                    for entries2 in 0..=remaining2 {
665                        let db = AccountsDb::new_single_for_tests();
666                        let remaining3 = entries.saturating_sub(entries0 + entries1 + entries2);
667                        let entries_by_level = [entries0, entries1, entries2, remaining3];
668                        let mut overall_index = 0;
669                        let mut expected_slots = Vec::default();
670                        let slots_and_accounts_byval = entries_by_level
671                            .iter()
672                            .enumerate()
673                            .filter_map(|(slot, count)| {
674                                let slot = slot as Slot;
675                                let count = *count as usize;
676                                (overall_index < raw2.len()).then(|| {
677                                    let range = overall_index..(overall_index + count);
678                                    let mut result =
679                                        raw2_account_from_storage[range.clone()].to_vec();
680                                    // since we're no longer storing `StoredAccountMeta`, we have to actually store the
681                                    // accounts so they can be looked up later in `db`
682                                    let storage = setup_sample_storage(&db, slot);
683                                    if let Some(offsets) = storage
684                                        .accounts
685                                        .append_accounts(&(slot, &raw2_refs[range.clone()]), 0)
686                                    {
687                                        result.iter_mut().zip(offsets.offsets.iter()).for_each(
688                                            |(account, offset)| {
689                                                account.index_info = AccountInfo::new(
690                                                    StorageLocation::AppendVec(0, *offset),
691                                                    if account.is_zero_lamport() { 0 } else { 1 },
692                                                )
693                                            },
694                                        );
695                                    }
696
697                                    range.for_each(|_| expected_slots.push(slot));
698                                    overall_index += count;
699                                    (slot, result)
700                                })
701                            })
702                            .collect::<Vec<_>>();
703                        let slots_and_accounts_ref1 = slots_and_accounts_byval
704                            .iter()
705                            .map(|(slot, accounts)| (*slot, accounts.iter().collect::<Vec<_>>()))
706                            .collect::<Vec<_>>();
707                        let slots_and_accounts = slots_and_accounts_ref1
708                            .iter()
709                            .map(|(slot, accounts)| (*slot, &accounts[..]))
710                            .collect::<Vec<_>>();
711
712                        let storable =
713                            StorableAccountsBySlot::new(99, &slots_and_accounts[..], &db);
714                        assert_eq!(99, storable.target_slot());
715                        assert_eq!(entries0 != entries, storable.contains_multiple_slots());
716                        (0..entries).for_each(|index| {
717                            let index = index as usize;
718                            let mut called = false;
719                            storable.account(index, |account| {
720                                called = true;
721                                assert!(accounts_equal(&account, &raw2[index]));
722                                assert_eq!(account.pubkey(), raw2[index].pubkey());
723                            });
724                            assert!(called);
725                            assert_eq!(storable.slot(index), expected_slots[index]);
726                        })
727                    }
728                }
729            }
730        }
731    }
732}