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