solana_accounts_db/
append_vec.rs

1//! Persistent storage for accounts.
2//!
3//! For more information, see:
4//!
5//! <https://docs.solanalabs.com/implemented-proposals/persistent-account-storage>
6
7use {
8    crate::{
9        account_storage::meta::{AccountMeta, StoredAccountMeta, StoredMeta},
10        accounts_file::{
11            AccountsFileError, InternalsForArchive, MatchAccountOwnerError, Result, StorageAccess,
12            StoredAccountsInfo, ALIGN_BOUNDARY_OFFSET,
13        },
14        accounts_hash::AccountHash,
15        accounts_index::ZeroLamport,
16        buffered_reader::{BufferedReader, BufferedReaderStatus},
17        file_io::read_into_buffer,
18        storable_accounts::StorableAccounts,
19        u64_align,
20    },
21    log::*,
22    memmap2::MmapMut,
23    solana_sdk::{
24        account::{AccountSharedData, ReadableAccount, WritableAccount},
25        hash::Hash,
26        pubkey::Pubkey,
27        stake_history::Epoch,
28        system_instruction::MAX_PERMITTED_DATA_LENGTH,
29    },
30    std::{
31        convert::TryFrom,
32        fs::{remove_file, File, OpenOptions},
33        io::{Seek, SeekFrom, Write},
34        mem,
35        path::{Path, PathBuf},
36        ptr,
37        sync::{
38            atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
39            Mutex,
40        },
41    },
42    thiserror::Error,
43};
44
45pub mod test_utils;
46#[cfg(test)]
47use solana_sdk::account::accounts_equal;
48
49/// size of the fixed sized fields in an append vec
50/// we need to add data len and align it to get the actual stored size
51pub const STORE_META_OVERHEAD: usize = 136;
52
53// Ensure the STORE_META_OVERHEAD constant remains accurate
54const _: () = assert!(
55    STORE_META_OVERHEAD
56        == mem::size_of::<StoredMeta>()
57            + mem::size_of::<AccountMeta>()
58            + mem::size_of::<AccountHash>()
59);
60
61/// Returns the size this item will take to store plus possible alignment padding bytes before the next entry.
62/// fixed-size portion of per-account data written
63/// plus 'data_len', aligned to next boundary
64pub fn aligned_stored_size(data_len: usize) -> usize {
65    u64_align!(STORE_META_OVERHEAD + data_len)
66}
67
68pub const MAXIMUM_APPEND_VEC_FILE_SIZE: u64 = 16 * 1024 * 1024 * 1024; // 16 GiB
69
70#[derive(Error, Debug)]
71/// An enum for AppendVec related errors.
72pub enum AppendVecError {
73    #[error("too small file size {0} for AppendVec")]
74    FileSizeTooSmall(usize),
75
76    #[error("too large file size {0} for AppendVec")]
77    FileSizeTooLarge(usize),
78
79    #[error("incorrect layout/length/data in the appendvec at path {}", .0.display())]
80    IncorrectLayout(PathBuf),
81
82    #[error("offset ({0}) is larger than file size ({1})")]
83    OffsetOutOfBounds(usize, usize),
84}
85
86/// A slice whose contents are known to be valid.
87/// The slice contains no undefined bytes.
88#[derive(Debug, Copy, Clone)]
89pub(crate) struct ValidSlice<'a>(&'a [u8]);
90
91impl<'a> ValidSlice<'a> {
92    pub(crate) fn new(data: &'a [u8]) -> Self {
93        Self(data)
94    }
95
96    pub(crate) fn len(&self) -> usize {
97        self.0.len()
98    }
99
100    #[cfg(all(unix, test))]
101    pub(crate) fn slice(&self) -> &[u8] {
102        self.0
103    }
104}
105
106/// References to account data stored elsewhere. Getting an `Account` requires cloning
107/// (see `StoredAccountMeta::clone_account()`).
108#[derive(PartialEq, Eq, Debug)]
109pub struct AppendVecStoredAccountMeta<'append_vec> {
110    pub meta: &'append_vec StoredMeta,
111    /// account data
112    pub account_meta: &'append_vec AccountMeta,
113    pub(crate) data: &'append_vec [u8],
114    pub(crate) offset: usize,
115    pub(crate) stored_size: usize,
116    pub(crate) hash: &'append_vec AccountHash,
117}
118
119impl<'append_vec> AppendVecStoredAccountMeta<'append_vec> {
120    pub fn pubkey(&self) -> &'append_vec Pubkey {
121        &self.meta.pubkey
122    }
123
124    pub fn hash(&self) -> &'append_vec AccountHash {
125        self.hash
126    }
127
128    pub fn stored_size(&self) -> usize {
129        self.stored_size
130    }
131
132    pub fn offset(&self) -> usize {
133        self.offset
134    }
135
136    pub fn data(&self) -> &'append_vec [u8] {
137        self.data
138    }
139
140    pub fn data_len(&self) -> u64 {
141        self.meta.data_len
142    }
143
144    pub fn meta(&self) -> &StoredMeta {
145        self.meta
146    }
147
148    pub(crate) fn sanitize(&self) -> bool {
149        self.sanitize_executable() && self.sanitize_lamports()
150    }
151
152    fn sanitize_executable(&self) -> bool {
153        // Sanitize executable to ensure higher 7-bits are cleared correctly.
154        self.ref_executable_byte() & !1 == 0
155    }
156
157    fn sanitize_lamports(&self) -> bool {
158        // Sanitize 0 lamports to ensure to be same as AccountSharedData::default()
159        self.account_meta.lamports != 0
160            || self.to_account_shared_data() == AccountSharedData::default()
161    }
162
163    fn ref_executable_byte(&self) -> &u8 {
164        // Use extra references to avoid value silently clamped to 1 (=true) and 0 (=false)
165        // Yes, this really happens; see test_new_from_file_crafted_executable
166        let executable_bool: &bool = &self.account_meta.executable;
167        let executable_bool_ptr = ptr::from_ref(executable_bool);
168        // UNSAFE: Force to interpret mmap-backed bool as u8 to really read the actual memory content
169        let executable_byte: &u8 = unsafe { &*(executable_bool_ptr.cast()) };
170        executable_byte
171    }
172}
173
174impl<'append_vec> ReadableAccount for AppendVecStoredAccountMeta<'append_vec> {
175    fn lamports(&self) -> u64 {
176        self.account_meta.lamports
177    }
178    fn data(&self) -> &'append_vec [u8] {
179        self.data()
180    }
181    fn owner(&self) -> &'append_vec Pubkey {
182        &self.account_meta.owner
183    }
184    fn executable(&self) -> bool {
185        self.account_meta.executable
186    }
187    fn rent_epoch(&self) -> Epoch {
188        self.account_meta.rent_epoch
189    }
190}
191
192/// info from an entry useful for building an index
193pub(crate) struct IndexInfo {
194    /// size of entry, aligned to next u64
195    /// This matches the return of `get_account`
196    pub stored_size_aligned: usize,
197    /// info on the entry
198    pub index_info: IndexInfoInner,
199}
200
201/// info from an entry useful for building an index
202pub(crate) struct IndexInfoInner {
203    /// offset to this entry
204    pub offset: usize,
205    pub pubkey: Pubkey,
206    pub lamports: u64,
207    pub rent_epoch: Epoch,
208    pub executable: bool,
209    pub data_len: u64,
210}
211
212/// offsets to help navigate the persisted format of `AppendVec`
213#[derive(Debug)]
214struct AccountOffsets {
215    /// offset to the end of the &[u8] data
216    offset_to_end_of_data: usize,
217    /// offset to the next account. This will be aligned.
218    next_account_offset: usize,
219    /// # of bytes (aligned) to store this account, including variable sized data
220    stored_size_aligned: usize,
221}
222
223#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
224#[derive(Debug)]
225enum AppendVecFileBacking {
226    /// A file-backed block of memory that is used to store the data for each appended item.
227    Mmap(Mmap),
228    /// This was opened as a read only file
229    #[cfg_attr(not(unix), allow(dead_code))]
230    File(File),
231}
232
233#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
234#[derive(Debug)]
235struct Mmap {
236    mmap: MmapMut,
237    /// Flags if the mmap is dirty or not.
238    /// Since fastboot requires that all mmaps are flushed to disk, be smart about it.
239    /// AppendVecs are (almost) always write-once.  The common case is that an AppendVec
240    /// will only need to be flushed once.  This avoids unnecessary syscalls/kernel work
241    /// when nothing in the AppendVec has changed.
242    is_dirty: AtomicBool,
243}
244
245/// A thread-safe, file-backed block of memory used to store `Account` instances. Append operations
246/// are serialized such that only one thread updates the internal `append_lock` at a time. No
247/// restrictions are placed on reading. That is, one may read items from one thread while another
248/// is appending new items.
249#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
250#[derive(Debug)]
251pub struct AppendVec {
252    /// The file path where the data is stored.
253    path: PathBuf,
254
255    /// access the file data
256    backing: AppendVecFileBacking,
257
258    /// A lock used to serialize append operations.
259    append_lock: Mutex<()>,
260
261    /// The number of bytes used to store items, not the number of items.
262    current_len: AtomicUsize,
263
264    /// The number of bytes available for storing items.
265    file_size: u64,
266
267    /// if true, remove file when dropped
268    remove_file_on_drop: AtomicBool,
269}
270
271const PAGE_SIZE: u64 = 4 * 1024;
272/// big enough for 3x the largest account size
273const SCAN_BUFFER_SIZE: usize =
274    page_align((STORE_META_OVERHEAD as u64 + MAX_PERMITTED_DATA_LENGTH) * 3) as usize;
275const fn page_align(size: u64) -> u64 {
276    (size + (PAGE_SIZE - 1)) & !(PAGE_SIZE - 1)
277}
278
279/// Buffer size to use when scanning *without* needing account data
280///
281/// When scanning without needing account data, it is desirable to only read the account metadata
282/// and skip over the account data.  In theory, we could read a single account's metadata at a time,
283/// then skip ahead to the next account, entirely bypassing the account's data.  However this comes
284/// at the cost of requiring one syscall per scanning each account, which is expensive.  Ideally
285/// we'd like to use the fewest syscalls and also read the least amount of extraneous account data.
286/// As a compromise, we use a much smaller buffer, yet still large enough to amortize syscall cost.
287///
288/// On mnb, the overwhelming majority of accounts are token accounts, which use 165 bytes of data.
289/// Including storage overhead and alignment, that's 304 bytes per account.
290/// Per slot, *with* rent rewrites, we store 1,200 to 1,500 accounts.  With a 64 KiB buffer, we'd
291/// be able to hold about 215 accounts, so there would not be many syscalls needed to scan
292/// the file.  Since we also expect some larger accounts, this will also avoid reading/copying
293/// large account data.  This should be a decent starting value, and can be modified over time.
294#[cfg_attr(feature = "dev-context-only-utils", qualifier_attr::qualifiers(pub))]
295const SCAN_BUFFER_SIZE_WITHOUT_DATA: usize = 1 << 16;
296
297lazy_static! {
298    pub static ref APPEND_VEC_MMAPPED_FILES_OPEN: AtomicU64 = AtomicU64::default();
299    pub static ref APPEND_VEC_MMAPPED_FILES_DIRTY: AtomicU64 = AtomicU64::default();
300    pub static ref APPEND_VEC_OPEN_AS_FILE_IO: AtomicU64 = AtomicU64::default();
301}
302
303impl Drop for AppendVec {
304    fn drop(&mut self) {
305        APPEND_VEC_MMAPPED_FILES_OPEN.fetch_sub(1, Ordering::Relaxed);
306        match &self.backing {
307            AppendVecFileBacking::Mmap(mmap_only) => {
308                if mmap_only.is_dirty.load(Ordering::Acquire) {
309                    APPEND_VEC_MMAPPED_FILES_DIRTY.fetch_sub(1, Ordering::Relaxed);
310                }
311            }
312            AppendVecFileBacking::File(_) => {
313                APPEND_VEC_OPEN_AS_FILE_IO.fetch_sub(1, Ordering::Relaxed);
314            }
315        }
316        if self.remove_file_on_drop.load(Ordering::Acquire) {
317            // If we're reopening in readonly mode, we don't delete the file. See
318            // AppendVec::reopen_as_readonly.
319            if let Err(_err) = remove_file(&self.path) {
320                // promote this to panic soon.
321                // disabled due to many false positive warnings while running tests.
322                // blocked by rpc's upgrade to jsonrpc v17
323                //error!("AppendVec failed to remove {}: {err}", &self.path.display());
324                inc_new_counter_info!("append_vec_drop_fail", 1);
325            }
326        }
327    }
328}
329
330impl AppendVec {
331    pub fn new(file: impl Into<PathBuf>, create: bool, size: usize) -> Self {
332        let file = file.into();
333        let initial_len = 0;
334        AppendVec::sanitize_len_and_size(initial_len, size).unwrap();
335
336        if create {
337            let _ignored = remove_file(&file);
338        }
339
340        let mut data = OpenOptions::new()
341            .read(true)
342            .write(true)
343            .create(create)
344            .open(&file)
345            .map_err(|e| {
346                panic!(
347                    "Unable to {} data file {} in current dir({:?}): {:?}",
348                    if create { "create" } else { "open" },
349                    file.display(),
350                    std::env::current_dir(),
351                    e
352                );
353            })
354            .unwrap();
355
356        // Theoretical performance optimization: write a zero to the end of
357        // the file so that we won't have to resize it later, which may be
358        // expensive.
359        data.seek(SeekFrom::Start((size - 1) as u64)).unwrap();
360        data.write_all(&[0]).unwrap();
361        data.rewind().unwrap();
362        data.flush().unwrap();
363
364        //UNSAFE: Required to create a Mmap
365        let mmap = unsafe { MmapMut::map_mut(&data) };
366        let mmap = mmap.unwrap_or_else(|e| {
367            error!(
368                "Failed to map the data file (size: {}): {}.\n
369                    Please increase sysctl vm.max_map_count or equivalent for your platform.",
370                size, e
371            );
372            std::process::exit(1);
373        });
374        APPEND_VEC_MMAPPED_FILES_OPEN.fetch_add(1, Ordering::Relaxed);
375
376        AppendVec {
377            path: file,
378            backing: AppendVecFileBacking::Mmap(Mmap {
379                mmap,
380                is_dirty: AtomicBool::new(false),
381            }),
382            // This mutex forces append to be single threaded, but concurrent with reads
383            // See UNSAFE usage in `append_ptr`
384            append_lock: Mutex::new(()),
385            current_len: AtomicUsize::new(initial_len),
386            file_size: size as u64,
387            remove_file_on_drop: AtomicBool::new(true),
388        }
389    }
390
391    fn sanitize_len_and_size(current_len: usize, file_size: usize) -> Result<()> {
392        if file_size == 0 {
393            Err(AccountsFileError::AppendVecError(
394                AppendVecError::FileSizeTooSmall(file_size),
395            ))
396        } else if usize::try_from(MAXIMUM_APPEND_VEC_FILE_SIZE)
397            .map(|max| file_size > max)
398            .unwrap_or(true)
399        {
400            Err(AccountsFileError::AppendVecError(
401                AppendVecError::FileSizeTooLarge(file_size),
402            ))
403        } else if current_len > file_size {
404            Err(AccountsFileError::AppendVecError(
405                AppendVecError::OffsetOutOfBounds(current_len, file_size),
406            ))
407        } else {
408            Ok(())
409        }
410    }
411
412    pub fn flush(&self) -> Result<()> {
413        match &self.backing {
414            AppendVecFileBacking::Mmap(mmap_only) => {
415                // Check to see if the mmap is actually dirty before flushing.
416                if mmap_only.is_dirty.load(Ordering::Acquire) {
417                    mmap_only.mmap.flush()?;
418                    mmap_only.is_dirty.store(false, Ordering::Release);
419                    APPEND_VEC_MMAPPED_FILES_DIRTY.fetch_sub(1, Ordering::Relaxed);
420                }
421                Ok(())
422            }
423            // File also means read only, so nothing to flush.
424            AppendVecFileBacking::File(_file) => Ok(()),
425        }
426    }
427
428    pub fn reset(&self) {
429        // This mutex forces append to be single threaded, but concurrent with reads
430        // See UNSAFE usage in `append_ptr`
431        let _lock = self.append_lock.lock().unwrap();
432        self.current_len.store(0, Ordering::Release);
433    }
434
435    /// when we can use file i/o as opposed to mmap, this is the trigger to tell us
436    /// that no more appending will occur and we can close the initial mmap.
437    #[cfg_attr(not(unix), allow(dead_code))]
438    pub(crate) fn reopen_as_readonly(&self) -> Option<Self> {
439        #[cfg(not(unix))]
440        // must open as mmmap on non-unix
441        return None;
442
443        #[cfg(unix)]
444        match &self.backing {
445            // already a file, so already read-only
446            AppendVecFileBacking::File(_file) => None,
447            AppendVecFileBacking::Mmap(_mmap) => {
448                // we are a map, so re-open as a file
449                self.flush().expect("flush must succeed");
450                // we are re-opening the file, so don't remove the file on disk when the old mmapped one is dropped
451                self.remove_file_on_drop.store(false, Ordering::Release);
452
453                // The file should have already been sanitized. Don't need to check when we open the file again.
454                AppendVec::new_from_file_unchecked(
455                    self.path.clone(),
456                    self.len(),
457                    StorageAccess::File,
458                )
459                .ok()
460            }
461        }
462    }
463
464    /// how many more bytes can be stored in this append vec
465    pub fn remaining_bytes(&self) -> u64 {
466        self.capacity()
467            .saturating_sub(u64_align!(self.len()) as u64)
468    }
469
470    pub fn len(&self) -> usize {
471        self.current_len.load(Ordering::Acquire)
472    }
473
474    pub fn is_empty(&self) -> bool {
475        self.len() == 0
476    }
477
478    pub fn capacity(&self) -> u64 {
479        self.file_size
480    }
481
482    pub fn new_from_file(
483        path: impl Into<PathBuf>,
484        current_len: usize,
485        storage_access: StorageAccess,
486    ) -> Result<(Self, usize)> {
487        let path = path.into();
488        let new = Self::new_from_file_unchecked(path, current_len, storage_access)?;
489
490        let (sanitized, num_accounts) = new.sanitize_layout_and_length();
491        if !sanitized {
492            return Err(AccountsFileError::AppendVecError(
493                AppendVecError::IncorrectLayout(new.path.clone()),
494            ));
495        }
496
497        Ok((new, num_accounts))
498    }
499
500    /// Creates an appendvec from file without performing sanitize checks or counting the number of accounts
501    #[cfg_attr(not(unix), allow(unused_variables))]
502    pub fn new_from_file_unchecked(
503        path: impl Into<PathBuf>,
504        current_len: usize,
505        storage_access: StorageAccess,
506    ) -> Result<Self> {
507        let path = path.into();
508        let file_size = std::fs::metadata(&path)?.len();
509        Self::sanitize_len_and_size(current_len, file_size as usize)?;
510
511        let data = OpenOptions::new()
512            .read(true)
513            .write(true)
514            .create(false)
515            .open(&path)?;
516
517        #[cfg(unix)]
518        // we must use mmap on non-linux
519        if storage_access == StorageAccess::File {
520            APPEND_VEC_MMAPPED_FILES_OPEN.fetch_add(1, Ordering::Relaxed);
521            APPEND_VEC_OPEN_AS_FILE_IO.fetch_add(1, Ordering::Relaxed);
522
523            return Ok(AppendVec {
524                path,
525                backing: AppendVecFileBacking::File(data),
526                append_lock: Mutex::new(()),
527                current_len: AtomicUsize::new(current_len),
528                file_size,
529                remove_file_on_drop: AtomicBool::new(true),
530            });
531        }
532
533        let mmap = unsafe {
534            let result = MmapMut::map_mut(&data);
535            if result.is_err() {
536                // for vm.max_map_count, error is: {code: 12, kind: Other, message: "Cannot allocate memory"}
537                info!("memory map error: {:?}. This may be because vm.max_map_count is not set correctly.", result);
538            }
539            result?
540        };
541        APPEND_VEC_MMAPPED_FILES_OPEN.fetch_add(1, Ordering::Relaxed);
542
543        Ok(AppendVec {
544            path,
545            backing: AppendVecFileBacking::Mmap(Mmap {
546                mmap,
547                is_dirty: AtomicBool::new(false),
548            }),
549            append_lock: Mutex::new(()),
550            current_len: AtomicUsize::new(current_len),
551            file_size,
552            remove_file_on_drop: AtomicBool::new(true),
553        })
554    }
555
556    /// Opens the AppendVec at `path` for use by `store-tool`
557    #[cfg(feature = "dev-context-only-utils")]
558    pub fn new_for_store_tool(path: impl Into<PathBuf>) -> Result<Self> {
559        let path = path.into();
560        let file_size = std::fs::metadata(&path)?.len();
561        Self::new_from_file_unchecked(path, file_size as usize, StorageAccess::default())
562    }
563
564    fn sanitize_layout_and_length(&self) -> (bool, usize) {
565        // This discards allocated accounts immediately after check at each loop iteration.
566        //
567        // This code should not reuse AppendVec.accounts() method as the current form or
568        // extend it to be reused here because it would allow attackers to accumulate
569        // some measurable amount of memory needlessly.
570        let mut num_accounts = 0;
571        let mut matches = true;
572        let mut last_offset = 0;
573        self.scan_accounts(|account| {
574            if !matches || !account.sanitize() {
575                matches = false;
576                return;
577            }
578            last_offset = account.offset() + account.stored_size();
579            num_accounts += 1;
580        });
581        if !matches {
582            return (false, num_accounts);
583        }
584        let aligned_current_len = u64_align!(self.current_len.load(Ordering::Acquire));
585
586        (last_offset == aligned_current_len, num_accounts)
587    }
588
589    /// Get a reference to the data at `offset` of `size` bytes if that slice
590    /// doesn't overrun the internal buffer. Otherwise return None.
591    /// Also return the offset of the first byte after the requested data that
592    /// falls on a 64-byte boundary.
593    fn get_slice(slice: ValidSlice, offset: usize, size: usize) -> Option<(&[u8], usize)> {
594        // SAFETY: Wrapping math is safe here because if `end` does wrap, the Range
595        // parameter to `.get()` will be invalid, and `.get()` will correctly return None.
596        let end = offset.wrapping_add(size);
597        slice
598            .0
599            .get(offset..end)
600            .map(|subslice| (subslice, u64_align!(end)))
601    }
602
603    /// Copy `len` bytes from `src` to the first 64-byte boundary after position `offset` of
604    /// the internal buffer. Then update `offset` to the first byte after the copied data.
605    fn append_ptr(&self, offset: &mut usize, src: *const u8, len: usize) {
606        let pos = u64_align!(*offset);
607        match &self.backing {
608            AppendVecFileBacking::Mmap(mmap_only) => {
609                let data = &mmap_only.mmap[pos..(pos + len)];
610                //UNSAFE: This mut append is safe because only 1 thread can append at a time
611                //Mutex<()> guarantees exclusive write access to the memory occupied in
612                //the range.
613                unsafe {
614                    let dst = data.as_ptr() as *mut _;
615                    ptr::copy(src, dst, len);
616                };
617                *offset = pos + len;
618            }
619            AppendVecFileBacking::File(_file) => {
620                unimplemented!();
621            }
622        }
623    }
624
625    /// Copy each value in `vals`, in order, to the first 64-byte boundary after position `offset`.
626    /// If there is sufficient space, then update `offset` and the internal `current_len` to the
627    /// first byte after the copied data and return the starting position of the copied data.
628    /// Otherwise return None and leave `offset` unchanged.
629    fn append_ptrs_locked(&self, offset: &mut usize, vals: &[(*const u8, usize)]) -> Option<usize> {
630        let mut end = *offset;
631        for val in vals {
632            end = u64_align!(end);
633            end += val.1;
634        }
635
636        if (self.file_size as usize) < end {
637            return None;
638        }
639
640        let pos = u64_align!(*offset);
641        for val in vals {
642            self.append_ptr(offset, val.0, val.1)
643        }
644        self.current_len.store(*offset, Ordering::Release);
645        Some(pos)
646    }
647
648    /// Return a reference to the type at `offset` if its data doesn't overrun the internal buffer.
649    /// Otherwise return None. Also return the offset of the first byte after the requested data
650    /// that falls on a 64-byte boundary.
651    fn get_type<T>(slice: ValidSlice, offset: usize) -> Option<(&T, usize)> {
652        let (data, next) = Self::get_slice(slice, offset, mem::size_of::<T>())?;
653        let ptr = data.as_ptr().cast();
654        //UNSAFE: The cast is safe because the slice is aligned and fits into the memory
655        //and the lifetime of the &T is tied to self, which holds the underlying memory map
656        Some((unsafe { &*ptr }, next))
657    }
658
659    /// MmapMut could have more capacity than `len()` knows is valid.
660    /// Return the subset of `mmap` that is known to be valid.
661    /// This allows comparisons against the slice len.
662    fn get_valid_slice_from_mmap<'a>(&self, mmap: &'a MmapMut) -> ValidSlice<'a> {
663        ValidSlice(&mmap[..self.len()])
664    }
665
666    /// calls `callback` with the stored account metadata for the account at `offset` if its data doesn't overrun
667    /// the internal buffer. Otherwise return None.
668    pub fn get_stored_account_meta_callback<Ret>(
669        &self,
670        offset: usize,
671        mut callback: impl for<'local> FnMut(StoredAccountMeta<'local>) -> Ret,
672    ) -> Option<Ret> {
673        match &self.backing {
674            AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
675                let slice = self.get_valid_slice_from_mmap(mmap);
676                let (meta, next): (&StoredMeta, _) = Self::get_type(slice, offset)?;
677                let (account_meta, next): (&AccountMeta, _) = Self::get_type(slice, next)?;
678                let (hash, next): (&AccountHash, _) = Self::get_type(slice, next)?;
679                let (data, next) = Self::get_slice(slice, next, meta.data_len as usize)?;
680                let stored_size = next - offset;
681                Some(callback(StoredAccountMeta::AppendVec(
682                    AppendVecStoredAccountMeta {
683                        meta,
684                        account_meta,
685                        data,
686                        offset,
687                        stored_size,
688                        hash,
689                    },
690                )))
691            }
692            AppendVecFileBacking::File(file) => {
693                // 4096 was just picked to be a single page size
694                let mut buf = [0u8; PAGE_SIZE as usize];
695                let bytes_read = read_into_buffer(file, self.len(), offset, &mut buf).ok()?;
696                let valid_bytes = ValidSlice(&buf[..bytes_read]);
697                let (meta, next): (&StoredMeta, _) = Self::get_type(valid_bytes, 0)?;
698                let (account_meta, next): (&AccountMeta, _) = Self::get_type(valid_bytes, next)?;
699                let (hash, next): (&AccountHash, _) = Self::get_type(valid_bytes, next)?;
700                let data_len = meta.data_len;
701                let remaining_bytes_for_data = bytes_read - next;
702                Some(if remaining_bytes_for_data >= data_len as usize {
703                    // we already read enough data to load this account
704                    let (data, next) = Self::get_slice(valid_bytes, next, meta.data_len as usize)?;
705                    let stored_size = next;
706                    let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
707                        meta,
708                        account_meta,
709                        data,
710                        offset,
711                        stored_size,
712                        hash,
713                    });
714                    callback(account)
715                } else {
716                    // not enough was read from file to get `data`
717                    assert!(data_len <= MAX_PERMITTED_DATA_LENGTH, "{data_len}");
718                    let mut data = vec![0u8; data_len as usize];
719                    // instead, we could piece together what we already read here. Maybe we just needed 1 more byte.
720                    // Note here `next` is a 0-based offset from the beginning of this account.
721                    let bytes_read =
722                        read_into_buffer(file, self.len(), offset + next, &mut data).ok()?;
723                    if bytes_read < data_len as usize {
724                        // eof or otherwise couldn't read all the data
725                        return None;
726                    }
727                    let stored_size = aligned_stored_size(data_len as usize);
728                    let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
729                        meta,
730                        account_meta,
731                        data: &data[..],
732                        offset,
733                        stored_size,
734                        hash,
735                    });
736                    callback(account)
737                })
738            }
739        }
740    }
741
742    /// return an `AccountSharedData` for an account at `offset`.
743    /// This fn can efficiently return exactly what is needed by a caller.
744    /// This is on the critical path of tx processing for accounts not in the read or write caches.
745    pub(crate) fn get_account_shared_data(&self, offset: usize) -> Option<AccountSharedData> {
746        match &self.backing {
747            AppendVecFileBacking::Mmap(_) => self
748                .get_stored_account_meta_callback(offset, |account| {
749                    account.to_account_shared_data()
750                }),
751            AppendVecFileBacking::File(file) => {
752                let mut buf = [0u8; PAGE_SIZE as usize];
753                let bytes_read = read_into_buffer(file, self.len(), offset, &mut buf).ok()?;
754                let valid_bytes = ValidSlice(&buf[..bytes_read]);
755                let (meta, next): (&StoredMeta, _) = Self::get_type(valid_bytes, 0)?;
756                let (account_meta, next): (&AccountMeta, _) = Self::get_type(valid_bytes, next)?;
757                let (hash, next): (&AccountHash, _) = Self::get_type(valid_bytes, next)?;
758                let data_len = meta.data_len;
759                let remaining_bytes_for_data = bytes_read - next;
760                Some(if remaining_bytes_for_data >= data_len as usize {
761                    // we already read enough data to load this account
762                    let (data, next) = Self::get_slice(valid_bytes, next, meta.data_len as usize)?;
763                    let stored_size = next;
764                    let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
765                        meta,
766                        account_meta,
767                        data,
768                        offset,
769                        stored_size,
770                        hash,
771                    });
772                    // data is within `buf`, so just allocate a new vec for data
773                    account.to_account_shared_data()
774                } else {
775                    // not enough was read from file to get `data`
776                    assert!(data_len <= MAX_PERMITTED_DATA_LENGTH, "{data_len}");
777                    let mut data = vec![0u8; data_len as usize];
778                    // Note here `next` is a 0-based offset from the beginning of this account.
779                    let bytes_read =
780                        read_into_buffer(file, self.len(), offset + next, &mut data).ok()?;
781                    if bytes_read < data_len as usize {
782                        // eof or otherwise couldn't read all the data
783                        return None;
784                    }
785                    AccountSharedData::create(
786                        account_meta.lamports,
787                        data,
788                        account_meta.owner,
789                        account_meta.executable,
790                        account_meta.rent_epoch,
791                    )
792                })
793            }
794        }
795    }
796
797    /// Return Ok(index_of_matching_owner) if the account owner at `offset` is one of the pubkeys in `owners`.
798    /// Return Err(MatchAccountOwnerError::NoMatch) if the account has 0 lamports or the owner is not one of
799    /// the pubkeys in `owners`.
800    /// Return Err(MatchAccountOwnerError::UnableToLoad) if the `offset` value causes a data overrun.
801    pub fn account_matches_owners(
802        &self,
803        offset: usize,
804        owners: &[Pubkey],
805    ) -> std::result::Result<usize, MatchAccountOwnerError> {
806        self.get_stored_account_meta_callback(offset, |stored_account_meta| {
807            if stored_account_meta.lamports() == 0 {
808                Err(MatchAccountOwnerError::NoMatch)
809            } else {
810                owners
811                    .iter()
812                    .position(|entry| stored_account_meta.owner() == entry)
813                    .ok_or(MatchAccountOwnerError::NoMatch)
814            }
815        })
816        .unwrap_or(Err(MatchAccountOwnerError::UnableToLoad))
817    }
818
819    #[cfg(test)]
820    pub fn get_account_test(
821        &self,
822        offset: usize,
823    ) -> Option<(StoredMeta, solana_sdk::account::AccountSharedData)> {
824        let sizes = self.get_account_sizes(&[offset]);
825        let result = self.get_stored_account_meta_callback(offset, |r_callback| {
826            let r2 = self.get_account_shared_data(offset);
827            assert!(accounts_equal(&r_callback, r2.as_ref().unwrap()));
828            assert_eq!(sizes, vec![r_callback.stored_size()]);
829            let meta = r_callback.meta().clone();
830            Some((meta, r_callback.to_account_shared_data()))
831        });
832        if result.is_none() {
833            assert!(self
834                .get_stored_account_meta_callback(offset, |_| {})
835                .is_none());
836            assert!(self.get_account_shared_data(offset).is_none());
837            // it has different rules for checking len and returning None
838            assert!(sizes.is_empty());
839        }
840        result.flatten()
841    }
842
843    /// Returns the path to the file where the data is stored
844    pub fn path(&self) -> &Path {
845        self.path.as_path()
846    }
847
848    /// help with the math of offsets when navigating the on-disk layout in an AppendVec.
849    /// data is at the end of each account and is variable sized
850    /// the next account is then aligned on a 64 bit boundary.
851    /// With these helpers, we can skip over reading some of the data depending on what the caller wants.
852    ///
853    /// *Safety* - The caller must ensure that the `stored_meta.data_len` won't overflow the calculation.
854    fn next_account_offset(start_offset: usize, stored_meta: &StoredMeta) -> AccountOffsets {
855        let stored_size_unaligned = STORE_META_OVERHEAD
856            .checked_add(stored_meta.data_len as usize)
857            .expect("stored size cannot overflow");
858        let stored_size_aligned = u64_align!(stored_size_unaligned);
859        let offset_to_end_of_data = start_offset + stored_size_unaligned;
860        let next_account_offset = start_offset + stored_size_aligned;
861
862        AccountOffsets {
863            next_account_offset,
864            offset_to_end_of_data,
865            stored_size_aligned,
866        }
867    }
868
869    /// Iterate over all accounts and call `callback` with `IndexInfo` for each.
870    /// This fn can help generate an index of the data in this storage.
871    pub(crate) fn scan_index(&self, mut callback: impl FnMut(IndexInfo)) {
872        // self.len() is an atomic load, so only do it once
873        let self_len = self.len();
874        match &self.backing {
875            AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
876                let mut offset = 0;
877                let slice = self.get_valid_slice_from_mmap(mmap);
878                loop {
879                    let Some((stored_meta, next)) = Self::get_type::<StoredMeta>(slice, offset)
880                    else {
881                        // eof
882                        break;
883                    };
884                    let Some((account_meta, _)) = Self::get_type::<AccountMeta>(slice, next) else {
885                        // eof
886                        break;
887                    };
888                    if account_meta.lamports == 0 && stored_meta.pubkey == Pubkey::default() {
889                        // we passed the last useful account
890                        break;
891                    }
892                    let next = Self::next_account_offset(offset, stored_meta);
893                    if next.offset_to_end_of_data > self_len {
894                        // data doesn't fit, so don't include this account
895                        break;
896                    }
897                    callback(IndexInfo {
898                        index_info: {
899                            IndexInfoInner {
900                                pubkey: stored_meta.pubkey,
901                                lamports: account_meta.lamports,
902                                offset,
903                                data_len: stored_meta.data_len,
904                                executable: account_meta.executable,
905                                rent_epoch: account_meta.rent_epoch,
906                            }
907                        },
908                        stored_size_aligned: next.stored_size_aligned,
909                    });
910                    offset = next.next_account_offset;
911                }
912            }
913            AppendVecFileBacking::File(file) => {
914                let buffer_size = std::cmp::min(SCAN_BUFFER_SIZE, self_len);
915                let mut reader =
916                    BufferedReader::new(buffer_size, self_len, file, STORE_META_OVERHEAD);
917                while reader.read().ok() == Some(BufferedReaderStatus::Success) {
918                    let (offset, bytes) = reader.get_offset_and_data();
919                    let (stored_meta, next) = Self::get_type::<StoredMeta>(bytes, 0).unwrap();
920                    let (account_meta, _) = Self::get_type::<AccountMeta>(bytes, next).unwrap();
921                    if account_meta.lamports == 0 && stored_meta.pubkey == Pubkey::default() {
922                        // we passed the last useful account
923                        break;
924                    }
925                    let next = Self::next_account_offset(offset, stored_meta);
926                    if next.offset_to_end_of_data > self_len {
927                        // data doesn't fit, so don't include this account
928                        break;
929                    }
930                    callback(IndexInfo {
931                        index_info: {
932                            IndexInfoInner {
933                                pubkey: stored_meta.pubkey,
934                                lamports: account_meta.lamports,
935                                offset,
936                                data_len: stored_meta.data_len,
937                                executable: account_meta.executable,
938                                rent_epoch: account_meta.rent_epoch,
939                            }
940                        },
941                        stored_size_aligned: next.stored_size_aligned,
942                    });
943                    reader.advance_offset(next.stored_size_aligned);
944                }
945            }
946        }
947    }
948
949    /// Iterate over all accounts and call `callback` with each account.
950    #[allow(clippy::blocks_in_conditions)]
951    pub fn scan_accounts(&self, mut callback: impl for<'local> FnMut(StoredAccountMeta<'local>)) {
952        match &self.backing {
953            AppendVecFileBacking::Mmap(_mmap) => {
954                let mut offset = 0;
955                while self
956                    .get_stored_account_meta_callback(offset, |account| {
957                        offset += account.stored_size();
958                        if account.is_zero_lamport() && account.pubkey() == &Pubkey::default() {
959                            // we passed the last useful account
960                            return false;
961                        }
962
963                        callback(account);
964                        true
965                    })
966                    .unwrap_or_default()
967                {}
968            }
969            AppendVecFileBacking::File(file) => {
970                let mut reader =
971                    BufferedReader::new(SCAN_BUFFER_SIZE, self.len(), file, STORE_META_OVERHEAD);
972                while reader.read().ok() == Some(BufferedReaderStatus::Success) {
973                    let (offset, bytes_subset) = reader.get_offset_and_data();
974                    let (meta, next): (&StoredMeta, _) = Self::get_type(bytes_subset, 0).unwrap();
975                    let (account_meta, next): (&AccountMeta, _) =
976                        Self::get_type(bytes_subset, next).unwrap();
977                    let (hash, next): (&AccountHash, _) =
978                        Self::get_type(bytes_subset, next).unwrap();
979                    let data_len = meta.data_len;
980                    if bytes_subset.len() - next >= data_len as usize {
981                        // we already read enough data to load this account
982                        let data = &bytes_subset.0[next..(next + data_len as usize)];
983                        let stored_size = u64_align!(next + (data_len as usize));
984                        let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
985                            meta,
986                            account_meta,
987                            data,
988                            offset,
989                            stored_size,
990                            hash,
991                        });
992                        callback(account);
993                        reader.advance_offset(stored_size);
994                    } else {
995                        // fall through and read the whole account again. we need refs for StoredMeta and data.
996                        reader.set_required_data_len(
997                            STORE_META_OVERHEAD.saturating_add(data_len as usize),
998                        )
999                    }
1000                }
1001            }
1002        }
1003    }
1004
1005    /// for each offset in `sorted_offsets`, get the size of the account. No other information is needed for the account.
1006    pub(crate) fn get_account_sizes(&self, sorted_offsets: &[usize]) -> Vec<usize> {
1007        // self.len() is an atomic load, so only do it once
1008        let self_len = self.len();
1009        let mut account_sizes = Vec::with_capacity(sorted_offsets.len());
1010        match &self.backing {
1011            AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
1012                let slice = self.get_valid_slice_from_mmap(mmap);
1013                for &offset in sorted_offsets {
1014                    let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(slice, offset) else {
1015                        break;
1016                    };
1017                    let next = Self::next_account_offset(offset, stored_meta);
1018                    if next.offset_to_end_of_data > self_len {
1019                        // data doesn't fit, so don't include
1020                        break;
1021                    }
1022                    account_sizes.push(next.stored_size_aligned);
1023                }
1024            }
1025            AppendVecFileBacking::File(file) => {
1026                let mut buffer = [0u8; mem::size_of::<StoredMeta>()];
1027                for &offset in sorted_offsets {
1028                    let Some(bytes_read) =
1029                        read_into_buffer(file, self_len, offset, &mut buffer).ok()
1030                    else {
1031                        break;
1032                    };
1033                    let bytes = ValidSlice(&buffer[..bytes_read]);
1034                    let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(bytes, 0) else {
1035                        break;
1036                    };
1037                    let next = Self::next_account_offset(offset, stored_meta);
1038                    if next.offset_to_end_of_data > self_len {
1039                        // data doesn't fit, so don't include
1040                        break;
1041                    }
1042                    account_sizes.push(next.stored_size_aligned);
1043                }
1044            }
1045        }
1046        account_sizes
1047    }
1048
1049    /// iterate over all pubkeys and call `callback`.
1050    /// This iteration does not deserialize and populate each field in `StoredAccountMeta`.
1051    /// `data` is completely ignored, for example.
1052    /// Also, no references have to be maintained/returned from an iterator function.
1053    /// This fn can operate on a batch of data at once.
1054    pub fn scan_pubkeys(&self, mut callback: impl FnMut(&Pubkey)) {
1055        // self.len() is an atomic load, so only do it once
1056        let self_len = self.len();
1057        match &self.backing {
1058            AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
1059                let mut offset = 0;
1060                let slice = self.get_valid_slice_from_mmap(mmap);
1061                loop {
1062                    let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(slice, offset) else {
1063                        // eof
1064                        break;
1065                    };
1066                    let next = Self::next_account_offset(offset, stored_meta);
1067                    if next.offset_to_end_of_data > self_len {
1068                        // data doesn't fit, so don't include this pubkey
1069                        break;
1070                    }
1071                    callback(&stored_meta.pubkey);
1072                    offset = next.next_account_offset;
1073                }
1074            }
1075            AppendVecFileBacking::File(file) => {
1076                let buffer_size = std::cmp::min(SCAN_BUFFER_SIZE_WITHOUT_DATA, self_len);
1077                let mut reader =
1078                    BufferedReader::new(buffer_size, self_len, file, STORE_META_OVERHEAD);
1079                while reader.read().ok() == Some(BufferedReaderStatus::Success) {
1080                    let (offset, bytes) = reader.get_offset_and_data();
1081                    let (stored_meta, _) = Self::get_type::<StoredMeta>(bytes, 0).unwrap();
1082                    let next = Self::next_account_offset(offset, stored_meta);
1083                    if next.offset_to_end_of_data > self.len() {
1084                        // data doesn't fit, so don't include this pubkey
1085                        break;
1086                    }
1087                    callback(&stored_meta.pubkey);
1088                    // since we only needed to read the pubkey, skip ahead to the next account
1089                    reader.advance_offset(next.stored_size_aligned);
1090                }
1091            }
1092        }
1093    }
1094
1095    /// Copy each account metadata, account and hash to the internal buffer.
1096    /// If there is no room to write the first entry, None is returned.
1097    /// Otherwise, returns the starting offset of each account metadata.
1098    /// Plus, the final return value is the offset where the next entry would be appended.
1099    /// So, return.len() is 1 + (number of accounts written)
1100    /// After each account is appended, the internal `current_len` is updated
1101    /// and will be available to other threads.
1102    pub fn append_accounts<'a>(
1103        &self,
1104        accounts: &impl StorableAccounts<'a>,
1105        skip: usize,
1106    ) -> Option<StoredAccountsInfo> {
1107        let _lock = self.append_lock.lock().unwrap();
1108        let default_hash = Hash::default();
1109        let mut offset = self.len();
1110        let len = accounts.len();
1111        // Here we have `len - skip` number of accounts.  The +1 extra capacity
1112        // is for storing the aligned offset of the last-plus-one entry,
1113        // which is used to compute the size of the last stored account.
1114        let offsets_len = len - skip + 1;
1115        let mut offsets = Vec::with_capacity(offsets_len);
1116        let mut stop = false;
1117        for i in skip..len {
1118            if stop {
1119                break;
1120            }
1121            accounts.account_default_if_zero_lamport(i, |account| {
1122                let account_meta = AccountMeta {
1123                    lamports: account.lamports(),
1124                    owner: *account.owner(),
1125                    rent_epoch: account.rent_epoch(),
1126                    executable: account.executable(),
1127                };
1128
1129                let stored_meta = StoredMeta {
1130                    pubkey: *account.pubkey(),
1131                    data_len: account.data().len() as u64,
1132                    write_version_obsolete: 0,
1133                };
1134                let stored_meta_ptr = ptr::from_ref(&stored_meta).cast();
1135                let account_meta_ptr = ptr::from_ref(&account_meta).cast();
1136                let hash_ptr = bytemuck::bytes_of(&default_hash).as_ptr();
1137                let data_ptr = account.data().as_ptr();
1138                let ptrs = [
1139                    (stored_meta_ptr, mem::size_of::<StoredMeta>()),
1140                    (account_meta_ptr, mem::size_of::<AccountMeta>()),
1141                    (hash_ptr, mem::size_of::<AccountHash>()),
1142                    (data_ptr, stored_meta.data_len as usize),
1143                ];
1144                if let Some(start_offset) = self.append_ptrs_locked(&mut offset, &ptrs) {
1145                    offsets.push(start_offset)
1146                } else {
1147                    stop = true;
1148                }
1149            });
1150        }
1151
1152        match &self.backing {
1153            AppendVecFileBacking::Mmap(mmap_only) => {
1154                if !offsets.is_empty() {
1155                    // If we've actually written to the AppendVec, make sure we mark it as dirty.
1156                    // This ensures we properly flush it later.
1157                    // As an optimization to reduce unnecessary cache line invalidations,
1158                    // only write the `is_dirty` atomic if currently *not* dirty.
1159                    // (This also ensures the 'dirty counter' datapoint is correct.)
1160                    if !mmap_only.is_dirty.load(Ordering::Acquire) {
1161                        mmap_only.is_dirty.store(true, Ordering::Release);
1162                        APPEND_VEC_MMAPPED_FILES_DIRTY.fetch_add(1, Ordering::Relaxed);
1163                    }
1164                }
1165            }
1166            AppendVecFileBacking::File(_) => {}
1167        }
1168
1169        (!offsets.is_empty()).then(|| {
1170            // The last entry in the offsets needs to be the u64 aligned `offset`, because that's
1171            // where the *next* entry will begin to be stored.
1172            // This is used to compute the size of the last stored account; make sure to remove
1173            // it afterwards!
1174            offsets.push(u64_align!(offset));
1175            let size = offsets.windows(2).map(|offset| offset[1] - offset[0]).sum();
1176            offsets.pop();
1177
1178            StoredAccountsInfo { offsets, size }
1179        })
1180    }
1181
1182    pub(crate) fn can_append(&self) -> bool {
1183        match &self.backing {
1184            AppendVecFileBacking::File(_file) => false,
1185            AppendVecFileBacking::Mmap(_mmap) => true,
1186        }
1187    }
1188
1189    /// Returns the way to access this accounts file when archiving
1190    pub(crate) fn internals_for_archive(&self) -> InternalsForArchive {
1191        match &self.backing {
1192            AppendVecFileBacking::File(_file) => InternalsForArchive::FileIo(self.path()),
1193            // note this returns the entire mmap slice, even bytes that we consider invalid
1194            AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => InternalsForArchive::Mmap(mmap),
1195        }
1196    }
1197}
1198
1199#[cfg(test)]
1200pub mod tests {
1201    use {
1202        super::{test_utils::*, *},
1203        assert_matches::assert_matches,
1204        memoffset::offset_of,
1205        rand::{thread_rng, Rng},
1206        solana_sdk::{
1207            account::{Account, AccountSharedData},
1208            clock::Slot,
1209        },
1210        std::{mem::ManuallyDrop, time::Instant},
1211        test_case::test_case,
1212    };
1213
1214    impl AppendVec {
1215        pub(crate) fn set_current_len_for_tests(&self, len: usize) {
1216            self.current_len.store(len, Ordering::Release);
1217        }
1218
1219        fn append_account_test(&self, data: &(StoredMeta, AccountSharedData)) -> Option<usize> {
1220            let slot_ignored = Slot::MAX;
1221            let accounts = [(&data.0.pubkey, &data.1)];
1222            let slice = &accounts[..];
1223            let storable_accounts = (slot_ignored, slice);
1224
1225            self.append_accounts(&storable_accounts, 0)
1226                .map(|res| res.offsets[0])
1227        }
1228    }
1229
1230    impl StoredAccountMeta<'_> {
1231        pub(crate) fn ref_executable_byte(&self) -> &u8 {
1232            match self {
1233                Self::AppendVec(av) => av.ref_executable_byte(),
1234                // Tests currently only cover AppendVec.
1235                Self::Hot(_) => unreachable!(),
1236            }
1237        }
1238    }
1239
1240    impl AppendVecStoredAccountMeta<'_> {
1241        fn set_data_len_unsafe(&self, new_data_len: u64) {
1242            // UNSAFE: cast away & (= const ref) to &mut to force to mutate append-only (=read-only) AppendVec
1243            unsafe {
1244                #[allow(invalid_reference_casting)]
1245                ptr::write(
1246                    std::mem::transmute::<*const u64, *mut u64>(&self.meta.data_len),
1247                    new_data_len,
1248                );
1249            }
1250        }
1251
1252        fn get_executable_byte(&self) -> u8 {
1253            let executable_bool: bool = self.executable();
1254            // UNSAFE: Force to interpret mmap-backed bool as u8 to really read the actual memory content
1255            let executable_byte: u8 = unsafe { std::mem::transmute::<bool, u8>(executable_bool) };
1256            executable_byte
1257        }
1258
1259        fn set_executable_as_byte(&self, new_executable_byte: u8) {
1260            // UNSAFE: Force to interpret mmap-backed &bool as &u8 to write some crafted value;
1261            unsafe {
1262                #[allow(invalid_reference_casting)]
1263                ptr::write(
1264                    std::mem::transmute::<*const bool, *mut u8>(&self.account_meta.executable),
1265                    new_executable_byte,
1266                );
1267            }
1268        }
1269    }
1270
1271    // Hash is [u8; 32], which has no alignment
1272    static_assertions::assert_eq_align!(u64, StoredMeta, AccountMeta);
1273
1274    #[test]
1275    fn test_account_meta_default() {
1276        let def1 = AccountMeta::default();
1277        let def2 = AccountMeta::from(&Account::default());
1278        assert_eq!(&def1, &def2);
1279        let def2 = AccountMeta::from(&AccountSharedData::default());
1280        assert_eq!(&def1, &def2);
1281        let def2 = AccountMeta::from(Some(&AccountSharedData::default()));
1282        assert_eq!(&def1, &def2);
1283        let none: Option<&AccountSharedData> = None;
1284        let def2 = AccountMeta::from(none);
1285        assert_eq!(&def1, &def2);
1286    }
1287
1288    #[test]
1289    fn test_account_meta_non_default() {
1290        let def1 = AccountMeta {
1291            lamports: 1,
1292            owner: Pubkey::new_unique(),
1293            executable: true,
1294            rent_epoch: 3,
1295        };
1296        let def2_account = Account {
1297            lamports: def1.lamports,
1298            owner: def1.owner,
1299            executable: def1.executable,
1300            rent_epoch: def1.rent_epoch,
1301            data: Vec::new(),
1302        };
1303        let def2 = AccountMeta::from(&def2_account);
1304        assert_eq!(&def1, &def2);
1305        let def2 = AccountMeta::from(&AccountSharedData::from(def2_account.clone()));
1306        assert_eq!(&def1, &def2);
1307        let def2 = AccountMeta::from(Some(&AccountSharedData::from(def2_account)));
1308        assert_eq!(&def1, &def2);
1309    }
1310
1311    #[test]
1312    #[should_panic(expected = "AppendVecError(FileSizeTooSmall(0))")]
1313    fn test_append_vec_new_bad_size() {
1314        let path = get_append_vec_path("test_append_vec_new_bad_size");
1315        let _av = AppendVec::new(&path.path, true, 0);
1316    }
1317
1318    #[test_case(StorageAccess::Mmap)]
1319    #[test_case(StorageAccess::File)]
1320    fn test_append_vec_new_from_file_bad_size(storage_access: StorageAccess) {
1321        let file = get_append_vec_path("test_append_vec_new_from_file_bad_size");
1322        let path = &file.path;
1323
1324        let _data = OpenOptions::new()
1325            .read(true)
1326            .write(true)
1327            .create_new(true)
1328            .open(path)
1329            .expect("create a test file for mmap");
1330
1331        let result = AppendVec::new_from_file(path, 0, storage_access);
1332        assert_matches!(result, Err(ref message) if message.to_string().contains("too small file size 0 for AppendVec"));
1333    }
1334
1335    #[test]
1336    fn test_append_vec_sanitize_len_and_size_too_small() {
1337        const LEN: usize = 0;
1338        const SIZE: usize = 0;
1339        let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1340        assert_matches!(result, Err(ref message) if message.to_string().contains("too small file size 0 for AppendVec"));
1341    }
1342
1343    #[test]
1344    fn test_append_vec_sanitize_len_and_size_maximum() {
1345        const LEN: usize = 0;
1346        const SIZE: usize = 16 * 1024 * 1024 * 1024;
1347        let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1348        assert_matches!(result, Ok(_));
1349    }
1350
1351    #[test]
1352    fn test_append_vec_sanitize_len_and_size_too_large() {
1353        const LEN: usize = 0;
1354        const SIZE: usize = 16 * 1024 * 1024 * 1024 + 1;
1355        let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1356        assert_matches!(result, Err(ref message) if message.to_string().contains("too large file size 17179869185 for AppendVec"));
1357    }
1358
1359    #[test]
1360    fn test_append_vec_sanitize_len_and_size_full_and_same_as_current_len() {
1361        const LEN: usize = 1024 * 1024;
1362        const SIZE: usize = 1024 * 1024;
1363        let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1364        assert_matches!(result, Ok(_));
1365    }
1366
1367    #[test]
1368    fn test_append_vec_sanitize_len_and_size_larger_current_len() {
1369        const LEN: usize = 1024 * 1024 + 1;
1370        const SIZE: usize = 1024 * 1024;
1371        let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1372        assert_matches!(result, Err(ref message) if message.to_string().contains("is larger than file size (1048576)"));
1373    }
1374
1375    #[test]
1376    fn test_append_vec_one() {
1377        let path = get_append_vec_path("test_append");
1378        let av = AppendVec::new(&path.path, true, 1024 * 1024);
1379        let account = create_test_account(0);
1380        let index = av.append_account_test(&account).unwrap();
1381        assert_eq!(av.get_account_test(index).unwrap(), account);
1382        truncate_and_test(av, index);
1383    }
1384
1385    /// truncate `av` and make sure that we fail to get an account. This verifies that the eof
1386    /// code is working correctly.
1387    fn truncate_and_test(av: AppendVec, index: usize) {
1388        // truncate the hash, 1 byte at a time
1389        let hash = std::mem::size_of::<AccountHash>();
1390        for _ in 0..hash {
1391            av.current_len.fetch_sub(1, Ordering::Relaxed);
1392            assert_eq!(av.get_account_test(index), None);
1393        }
1394        // truncate 1 byte into the AccountMeta
1395        av.current_len.fetch_sub(1, Ordering::Relaxed);
1396        assert_eq!(av.get_account_test(index), None);
1397    }
1398
1399    #[test]
1400    fn test_append_vec_one_with_data() {
1401        let path = get_append_vec_path("test_append");
1402        let av = AppendVec::new(&path.path, true, 1024 * 1024);
1403        let data_len = 1;
1404        let account = create_test_account(data_len);
1405        let index = av.append_account_test(&account).unwrap();
1406        // make the append vec 1 byte too short. we should get `None` since the append vec was truncated
1407        assert_eq!(
1408            STORE_META_OVERHEAD + data_len,
1409            av.current_len.load(Ordering::Relaxed)
1410        );
1411        assert_eq!(av.get_account_test(index).unwrap(), account);
1412        truncate_and_test(av, index);
1413    }
1414
1415    #[test]
1416    fn test_remaining_bytes() {
1417        let path = get_append_vec_path("test_append");
1418        let sz = 1024 * 1024;
1419        let sz64 = sz as u64;
1420        let av = AppendVec::new(&path.path, true, sz);
1421        assert_eq!(av.capacity(), sz64);
1422        assert_eq!(av.remaining_bytes(), sz64);
1423
1424        // append first account, an u64 aligned account (136 bytes)
1425        let mut av_len = 0;
1426        let account = create_test_account(0);
1427        av.append_account_test(&account).unwrap();
1428        av_len += STORE_META_OVERHEAD;
1429        assert_eq!(av.capacity(), sz64);
1430        assert_eq!(av.remaining_bytes(), sz64 - (STORE_META_OVERHEAD as u64));
1431        assert_eq!(av.len(), av_len);
1432
1433        // append second account, a *not* u64 aligned account (137 bytes)
1434        let account = create_test_account(1);
1435        let account_storage_len = STORE_META_OVERHEAD + 1;
1436        av_len += account_storage_len;
1437        av.append_account_test(&account).unwrap();
1438        assert_eq!(av.capacity(), sz64);
1439        assert_eq!(av.len(), av_len);
1440        let alignment_bytes = u64_align!(av_len) - av_len; // bytes used for alignment (7 bytes)
1441        assert_eq!(alignment_bytes, 7);
1442        assert_eq!(av.remaining_bytes(), sz64 - u64_align!(av_len) as u64);
1443
1444        // append third account, a *not* u64 aligned account (137 bytes)
1445        let account = create_test_account(1);
1446        av.append_account_test(&account).unwrap();
1447        let account_storage_len = STORE_META_OVERHEAD + 1;
1448        av_len += alignment_bytes; // bytes used for alignment at the end of previous account
1449        av_len += account_storage_len;
1450        assert_eq!(av.capacity(), sz64);
1451        assert_eq!(av.len(), av_len);
1452        assert_eq!(av.remaining_bytes(), sz64 - u64_align!(av_len) as u64);
1453    }
1454
1455    #[test]
1456    fn test_append_vec_data() {
1457        let path = get_append_vec_path("test_append_data");
1458        let av = AppendVec::new(&path.path, true, 1024 * 1024);
1459        let account = create_test_account(5);
1460        let index = av.append_account_test(&account).unwrap();
1461        assert_eq!(av.get_account_test(index).unwrap(), account);
1462        let account1 = create_test_account(6);
1463        let index1 = av.append_account_test(&account1).unwrap();
1464        assert_eq!(av.get_account_test(index).unwrap(), account);
1465        assert_eq!(av.get_account_test(index1).unwrap(), account1);
1466    }
1467
1468    #[test]
1469    fn test_account_matches_owners() {
1470        let path = get_append_vec_path("test_append_data");
1471        let av = AppendVec::new(&path.path, true, 1024 * 1024);
1472        let owners: Vec<Pubkey> = (0..2).map(|_| Pubkey::new_unique()).collect();
1473
1474        let mut account = create_test_account(5);
1475        account.1.set_owner(owners[0]);
1476        let index = av.append_account_test(&account).unwrap();
1477        assert_eq!(av.account_matches_owners(index, &owners), Ok(0));
1478
1479        let mut account1 = create_test_account(6);
1480        account1.1.set_owner(owners[1]);
1481        let index1 = av.append_account_test(&account1).unwrap();
1482        assert_eq!(av.account_matches_owners(index1, &owners), Ok(1));
1483        assert_eq!(av.account_matches_owners(index, &owners), Ok(0));
1484
1485        let mut account2 = create_test_account(6);
1486        account2.1.set_owner(Pubkey::new_unique());
1487        let index2 = av.append_account_test(&account2).unwrap();
1488        assert_eq!(
1489            av.account_matches_owners(index2, &owners),
1490            Err(MatchAccountOwnerError::NoMatch)
1491        );
1492
1493        // tests for overflow
1494        assert_eq!(
1495            av.account_matches_owners(usize::MAX - mem::size_of::<StoredMeta>(), &owners),
1496            Err(MatchAccountOwnerError::UnableToLoad)
1497        );
1498
1499        assert_eq!(
1500            av.account_matches_owners(
1501                usize::MAX - mem::size_of::<StoredMeta>() - mem::size_of::<AccountMeta>() + 1,
1502                &owners
1503            ),
1504            Err(MatchAccountOwnerError::UnableToLoad)
1505        );
1506    }
1507
1508    impl AppendVec {
1509        /// return how many accounts in the storage
1510        fn accounts_count(&self) -> usize {
1511            let mut count = 0;
1512            self.scan_accounts(|_| {
1513                count += 1;
1514            });
1515            count
1516        }
1517    }
1518
1519    #[test]
1520    fn test_append_vec_append_many() {
1521        let path = get_append_vec_path("test_append_many");
1522        let av = AppendVec::new(&path.path, true, 1024 * 1024);
1523        let size = 1000;
1524        let mut indexes = vec![];
1525        let now = Instant::now();
1526        let mut sizes = vec![];
1527        for sample in 0..size {
1528            // sample + 1 is so sample = 0 won't be used.
1529            // sample = 0 produces default account with default pubkey
1530            let account = create_test_account(sample + 1);
1531            sizes.push(aligned_stored_size(account.1.data().len()));
1532            let pos = av.append_account_test(&account).unwrap();
1533            assert_eq!(av.get_account_test(pos).unwrap(), account);
1534            indexes.push(pos);
1535            assert_eq!(sizes, av.get_account_sizes(&indexes));
1536        }
1537        trace!("append time: {} ms", now.elapsed().as_millis());
1538
1539        let now = Instant::now();
1540        for _ in 0..size {
1541            let sample = thread_rng().gen_range(0..indexes.len());
1542            let account = create_test_account(sample + 1);
1543            assert_eq!(av.get_account_test(indexes[sample]).unwrap(), account);
1544        }
1545        trace!("random read time: {} ms", now.elapsed().as_millis());
1546
1547        let now = Instant::now();
1548        assert_eq!(indexes.len(), size);
1549        assert_eq!(indexes[0], 0);
1550        let mut sample = 0;
1551        assert_eq!(av.accounts_count(), size);
1552        av.scan_accounts(|v| {
1553            let account = create_test_account(sample + 1);
1554            let recovered = v.to_account_shared_data();
1555            assert_eq!(recovered, account.1);
1556            sample += 1;
1557        });
1558        trace!("sequential read time: {} ms", now.elapsed().as_millis());
1559    }
1560
1561    #[test_case(StorageAccess::Mmap)]
1562    #[test_case(StorageAccess::File)]
1563    fn test_new_from_file_crafted_zero_lamport_account(storage_access: StorageAccess) {
1564        // This test verifies that when we sanitize on load, that we fail sanitizing if we load an account with zero lamports that does not have all default value fields.
1565        // This test writes an account with zero lamports, but with 3 bytes of data. On load, it asserts that load fails.
1566        // It used to be possible to use the append vec api to write an account to an append vec with zero lamports, but with non-default values for other account fields.
1567        // This will no longer be possible. Thus, to implement the write portion of this test would require additional test-only parameters to public apis or otherwise duplicating code paths.
1568        // So, the sanitizing on load behavior can be tested by capturing [u8] that would be created if such a write was possible (as it used to be).
1569        // The contents of [u8] written by an append vec cannot easily or reasonably change frequently since it has released a long time.
1570        /*
1571            solana_logger::setup();
1572            // uncomment this code to generate the invalid append vec that will fail on load
1573            let file = get_append_vec_path("test_append");
1574            let path = &file.path;
1575            let mut av = AppendVec::new(path, true, 256);
1576            av.set_no_remove_on_drop();
1577
1578            let pubkey = solana_sdk::pubkey::new_rand();
1579            let owner = Pubkey::default();
1580            let data_len = 3_u64;
1581            let mut account = AccountSharedData::new(0, data_len as usize, &owner);
1582            account.set_data(b"abc".to_vec());
1583            let stored_meta = StoredMeta {
1584                write_version: 0,
1585                pubkey,
1586                data_len,
1587            };
1588            let account_with_meta = (stored_meta, account);
1589            let index = av.append_account_test(&account_with_meta).unwrap();
1590            assert_eq!(av.get_account_test(index).unwrap(), account_with_meta);
1591
1592            av.flush().unwrap();
1593            let accounts_len = av.len();
1594            drop(av);
1595            // read file and log out as [u8]
1596            use std::fs::File;
1597            use std::io::BufReader;
1598            use std::io::Read;
1599            let f = File::open(path).unwrap();
1600            let mut reader = BufReader::new(f);
1601            let mut buffer = Vec::new();
1602            reader.read_to_end(&mut buffer).unwrap();
1603            error!("{:?}", buffer);
1604        */
1605
1606        // create an invalid append vec file using known bytes
1607        let file = get_append_vec_path("test_append_bytes");
1608        let path = &file.path;
1609
1610        let accounts_len = 139;
1611        {
1612            let append_vec_data = [
1613                0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 192, 118, 150, 1, 185, 209, 118,
1614                82, 154, 222, 172, 202, 110, 26, 218, 140, 143, 96, 61, 43, 212, 73, 203, 7, 190,
1615                88, 80, 222, 110, 114, 67, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1616                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1617                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1618                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1619                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1620                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1621                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1622                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1623            ];
1624
1625            let f = std::fs::File::create(path).unwrap();
1626            let mut writer = std::io::BufWriter::new(f);
1627            writer.write_all(append_vec_data.as_slice()).unwrap();
1628        }
1629
1630        let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1631        assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1632    }
1633
1634    #[test_case(StorageAccess::Mmap)]
1635    #[test_case(StorageAccess::File)]
1636    fn test_new_from_file_crafted_data_len(storage_access: StorageAccess) {
1637        let file = get_append_vec_path("test_new_from_file_crafted_data_len");
1638        let path = &file.path;
1639        let accounts_len = {
1640            // wrap AppendVec in ManuallyDrop to ensure we do not remove the backing file when dropped
1641            let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1642
1643            let crafted_data_len = 1;
1644
1645            av.append_account_test(&create_test_account(10)).unwrap();
1646
1647            av.get_stored_account_meta_callback(0, |account| {
1648                let StoredAccountMeta::AppendVec(account) = account else {
1649                    panic!("StoredAccountMeta can only be AppendVec in this test.");
1650                };
1651                account.set_data_len_unsafe(crafted_data_len);
1652                assert_eq!(account.data_len(), crafted_data_len);
1653
1654                // Reload accounts and observe crafted_data_len
1655                av.get_stored_account_meta_callback(0, |account| {
1656                    assert_eq!(account.data_len() as u64, crafted_data_len);
1657                });
1658            });
1659
1660            av.flush().unwrap();
1661            av.len()
1662        };
1663        let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1664        assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1665    }
1666
1667    #[test]
1668    fn test_append_vec_reset() {
1669        let file = get_append_vec_path("test_append_vec_reset");
1670        let path = &file.path;
1671        let av = AppendVec::new(path, true, 1024 * 1024);
1672        av.append_account_test(&create_test_account(10)).unwrap();
1673
1674        assert!(!av.is_empty());
1675        av.reset();
1676        assert_eq!(av.len(), 0);
1677    }
1678
1679    #[test_case(StorageAccess::Mmap)]
1680    #[test_case(StorageAccess::File)]
1681    fn test_append_vec_flush(storage_access: StorageAccess) {
1682        let file = get_append_vec_path("test_append_vec_flush");
1683        let path = &file.path;
1684        let accounts_len = {
1685            // wrap AppendVec in ManuallyDrop to ensure we do not remove the backing file when dropped
1686            let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1687            av.append_account_test(&create_test_account(10)).unwrap();
1688            av.len()
1689        };
1690
1691        let (av, num_account) =
1692            AppendVec::new_from_file(path, accounts_len, storage_access).unwrap();
1693        av.flush().unwrap();
1694        assert_eq!(num_account, 1);
1695    }
1696
1697    #[test_case(StorageAccess::Mmap)]
1698    #[test_case(StorageAccess::File)]
1699    fn test_append_vec_reopen_as_readonly(storage_access: StorageAccess) {
1700        let file = get_append_vec_path("test_append_vec_flush");
1701        let path = &file.path;
1702        let accounts_len = {
1703            // wrap AppendVec in ManuallyDrop to ensure we do not remove the backing file when dropped
1704            let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1705            av.append_account_test(&create_test_account(10)).unwrap();
1706            av.len()
1707        };
1708        let (av, _) = AppendVec::new_from_file(path, accounts_len, storage_access).unwrap();
1709        let reopen = av.reopen_as_readonly();
1710        if storage_access == StorageAccess::File {
1711            assert!(reopen.is_none());
1712        } else {
1713            assert!(reopen.is_some());
1714        }
1715    }
1716
1717    #[test_case(StorageAccess::Mmap)]
1718    #[test_case(StorageAccess::File)]
1719    fn test_new_from_file_too_large_data_len(storage_access: StorageAccess) {
1720        let file = get_append_vec_path("test_new_from_file_too_large_data_len");
1721        let path = &file.path;
1722        let accounts_len = {
1723            // wrap AppendVec in ManuallyDrop to ensure we do not remove the backing file when dropped
1724            let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1725
1726            let too_large_data_len = u64::MAX;
1727            av.append_account_test(&create_test_account(10)).unwrap();
1728
1729            av.get_stored_account_meta_callback(0, |account| {
1730                let StoredAccountMeta::AppendVec(account) = account else {
1731                    panic!("StoredAccountMeta can only be AppendVec in this test.");
1732                };
1733                account.set_data_len_unsafe(too_large_data_len);
1734                assert_eq!(account.data_len(), too_large_data_len);
1735            })
1736            .unwrap();
1737
1738            // Reload accounts and observe no account with bad offset
1739            assert!(av
1740                .get_stored_account_meta_callback(0, |_| {
1741                    panic!("unexpected");
1742                })
1743                .is_none());
1744            av.flush().unwrap();
1745            av.len()
1746        };
1747        let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1748        assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1749    }
1750
1751    #[test_case(StorageAccess::Mmap)]
1752    #[test_case(StorageAccess::File)]
1753    fn test_new_from_file_crafted_executable(storage_access: StorageAccess) {
1754        let file = get_append_vec_path("test_new_from_crafted_executable");
1755        let path = &file.path;
1756        let accounts_len = {
1757            // wrap AppendVec in ManuallyDrop to ensure we do not remove the backing file when dropped
1758            let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1759            av.append_account_test(&create_test_account(10)).unwrap();
1760            let offset_1 = {
1761                let mut executable_account = create_test_account(10);
1762                executable_account.1.set_executable(true);
1763                av.append_account_test(&executable_account).unwrap()
1764            };
1765
1766            let crafted_executable = u8::MAX - 1;
1767
1768            // reload accounts
1769            // ensure false is 0u8 and true is 1u8 actually
1770            av.get_stored_account_meta_callback(0, |account| {
1771                assert_eq!(*account.ref_executable_byte(), 0);
1772                let StoredAccountMeta::AppendVec(account) = account else {
1773                    panic!("StoredAccountMeta can only be AppendVec in this test.");
1774                };
1775                account.set_executable_as_byte(crafted_executable);
1776            })
1777            .unwrap();
1778            av.get_stored_account_meta_callback(offset_1, |account| {
1779                assert_eq!(*account.ref_executable_byte(), 1);
1780            })
1781            .unwrap();
1782
1783            // reload crafted accounts
1784            av.get_stored_account_meta_callback(0, |account| {
1785                let StoredAccountMeta::AppendVec(account) = account else {
1786                    panic!("StoredAccountMeta can only be AppendVec in this test.");
1787                };
1788
1789                // upper 7-bits are not 0, so sanitization should fail
1790                assert!(!account.sanitize_executable());
1791
1792                // we can observe crafted value by ref
1793                {
1794                    let executable_bool: &bool = &account.account_meta.executable;
1795                    // Depending on use, *executable_bool can be truthy or falsy due to direct memory manipulation
1796                    // assert_eq! thinks *executable_bool is equal to false but the if condition thinks it's not, contradictorily.
1797                    assert!(!*executable_bool);
1798                    assert_eq!(*account.ref_executable_byte(), crafted_executable);
1799                }
1800
1801                // we can NOT observe crafted value by value
1802                {
1803                    let executable_bool: bool = account.executable();
1804                    assert!(!executable_bool);
1805                    assert_eq!(account.get_executable_byte(), 0); // Wow, not crafted_executable!
1806                }
1807            })
1808            .unwrap();
1809
1810            av.flush().unwrap();
1811            av.len()
1812        };
1813        let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1814        assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1815    }
1816
1817    #[test]
1818    fn test_type_layout() {
1819        assert_eq!(offset_of!(StoredMeta, write_version_obsolete), 0x00);
1820        assert_eq!(offset_of!(StoredMeta, data_len), 0x08);
1821        assert_eq!(offset_of!(StoredMeta, pubkey), 0x10);
1822        assert_eq!(mem::size_of::<StoredMeta>(), 0x30);
1823
1824        assert_eq!(offset_of!(AccountMeta, lamports), 0x00);
1825        assert_eq!(offset_of!(AccountMeta, rent_epoch), 0x08);
1826        assert_eq!(offset_of!(AccountMeta, owner), 0x10);
1827        assert_eq!(offset_of!(AccountMeta, executable), 0x30);
1828        assert_eq!(mem::size_of::<AccountMeta>(), 0x38);
1829    }
1830
1831    #[test_case(StorageAccess::Mmap)]
1832    #[test_case(StorageAccess::File)]
1833    fn test_get_account_shared_data_from_truncated_file(storage_access: StorageAccess) {
1834        let file = get_append_vec_path("test_get_account_shared_data_from_truncated_file");
1835        let path = &file.path;
1836
1837        {
1838            // Set up a test account with data_len larger than PAGE_SIZE (i.e.
1839            // AppendVec internal buffer size is PAGESIZE).
1840            let data_len: usize = 2 * PAGE_SIZE as usize;
1841            let account = create_test_account_with(data_len);
1842            // wrap AppendVec in ManuallyDrop to ensure we do not remove the backing file when dropped
1843            let av = ManuallyDrop::new(AppendVec::new(path, true, aligned_stored_size(data_len)));
1844            av.append_account_test(&account).unwrap();
1845            av.flush().unwrap();
1846        }
1847
1848        // Truncate the AppendVec to PAGESIZE. This will cause get_account* to fail to load the account.
1849        let truncated_accounts_len: usize = PAGE_SIZE as usize;
1850        let av = AppendVec::new_from_file_unchecked(path, truncated_accounts_len, storage_access)
1851            .unwrap();
1852        let account = av.get_account_shared_data(0);
1853        assert!(account.is_none()); // Expect None to be returned.
1854
1855        let result = av.get_stored_account_meta_callback(0, |_| true);
1856        assert!(result.is_none()); // Expect None to be returned.
1857    }
1858
1859    #[test_case(StorageAccess::Mmap)]
1860    #[test_case(StorageAccess::File)]
1861    fn test_get_account_sizes(storage_access: StorageAccess) {
1862        const NUM_ACCOUNTS: usize = 37;
1863        let pubkeys: Vec<_> = std::iter::repeat_with(Pubkey::new_unique)
1864            .take(NUM_ACCOUNTS)
1865            .collect();
1866
1867        let mut rng = thread_rng();
1868        let mut accounts = Vec::with_capacity(pubkeys.len());
1869        let mut stored_sizes = Vec::with_capacity(pubkeys.len());
1870        for _ in &pubkeys {
1871            let lamports = rng.gen();
1872            let data_len = rng.gen_range(0..MAX_PERMITTED_DATA_LENGTH) as usize;
1873            let account = AccountSharedData::new(lamports, data_len, &Pubkey::default());
1874            accounts.push(account);
1875            stored_sizes.push(aligned_stored_size(data_len));
1876        }
1877        let accounts = accounts;
1878        let stored_sizes = stored_sizes;
1879        let total_stored_size = stored_sizes.iter().sum();
1880
1881        let temp_file = get_append_vec_path("test_get_account_sizes");
1882        let account_offsets = {
1883            let append_vec = AppendVec::new(&temp_file.path, true, total_stored_size);
1884            // wrap AppendVec in ManuallyDrop to ensure we do not remove the backing file when dropped
1885            let append_vec = ManuallyDrop::new(append_vec);
1886            let slot = 77; // the specific slot does not matter
1887            let storable_accounts: Vec<_> = std::iter::zip(&pubkeys, &accounts).collect();
1888            let stored_accounts_info = append_vec
1889                .append_accounts(&(slot, storable_accounts.as_slice()), 0)
1890                .unwrap();
1891            append_vec.flush().unwrap();
1892            stored_accounts_info.offsets
1893        };
1894
1895        // now open the append vec with the given storage access method
1896        // then get the account sizes to ensure they are correct
1897        let (append_vec, _) =
1898            AppendVec::new_from_file(&temp_file.path, total_stored_size, storage_access).unwrap();
1899
1900        let account_sizes = append_vec.get_account_sizes(account_offsets.as_slice());
1901        assert_eq!(account_sizes, stored_sizes);
1902    }
1903
1904    /// A helper function for testing different scenario for scan_*.
1905    ///
1906    /// `modify_fn` is used to (optionally) modify the append vec before checks are performed.
1907    /// `check_fn` performs the check for the scan.
1908    fn test_scan_helper(
1909        storage_access: StorageAccess,
1910        modify_fn: impl Fn(&PathBuf, usize) -> usize,
1911        check_fn: impl Fn(&AppendVec, &[Pubkey], &[usize], &[AccountSharedData]),
1912    ) {
1913        const NUM_ACCOUNTS: usize = 37;
1914        let pubkeys: Vec<_> = std::iter::repeat_with(solana_sdk::pubkey::new_rand)
1915            .take(NUM_ACCOUNTS)
1916            .collect();
1917
1918        let mut rng = thread_rng();
1919        let mut accounts = Vec::with_capacity(pubkeys.len());
1920        let mut total_stored_size = 0;
1921        for _ in &pubkeys {
1922            let lamports = rng.gen();
1923            let data_len = rng.gen_range(0..MAX_PERMITTED_DATA_LENGTH) as usize;
1924            let account = AccountSharedData::new(lamports, data_len, &Pubkey::default());
1925            accounts.push(account);
1926            total_stored_size += aligned_stored_size(data_len);
1927        }
1928        let accounts = accounts;
1929        let total_stored_size = total_stored_size;
1930
1931        let temp_file = get_append_vec_path("test_scan");
1932        let account_offsets = {
1933            // wrap AppendVec in ManuallyDrop to ensure we do not remove the backing file when dropped
1934            let append_vec =
1935                ManuallyDrop::new(AppendVec::new(&temp_file.path, true, total_stored_size));
1936            let slot = 42; // the specific slot does not matter
1937            let storable_accounts: Vec<_> = std::iter::zip(&pubkeys, &accounts).collect();
1938            let stored_accounts_info = append_vec
1939                .append_accounts(&(slot, storable_accounts.as_slice()), 0)
1940                .unwrap();
1941            append_vec.flush().unwrap();
1942            stored_accounts_info.offsets
1943        };
1944
1945        let total_stored_size = modify_fn(&temp_file.path, total_stored_size);
1946        // now open the append vec with the given storage access method
1947        // then perform the scan and check it is correct
1948        let append_vec = ManuallyDrop::new(
1949            AppendVec::new_from_file_unchecked(&temp_file.path, total_stored_size, storage_access)
1950                .unwrap(),
1951        );
1952
1953        check_fn(&append_vec, &pubkeys, &account_offsets, &accounts);
1954    }
1955
1956    /// A helper fn to test `scan_pubkeys`.
1957    fn test_scan_pubkeys_helper(
1958        storage_access: StorageAccess,
1959        modify_fn: impl Fn(&PathBuf, usize) -> usize,
1960    ) {
1961        test_scan_helper(
1962            storage_access,
1963            modify_fn,
1964            |append_vec, pubkeys, _account_offsets, _accounts| {
1965                let mut i = 0;
1966                append_vec.scan_pubkeys(|pubkey| {
1967                    assert_eq!(pubkey, pubkeys.get(i).unwrap());
1968                    i += 1;
1969                });
1970                assert_eq!(i, pubkeys.len());
1971            },
1972        )
1973    }
1974
1975    /// Test `scan_pubkey` for a valid account storage.
1976    #[test_case(StorageAccess::Mmap)]
1977    #[test_case(StorageAccess::File)]
1978    fn test_scan_pubkeys(storage_access: StorageAccess) {
1979        test_scan_pubkeys_helper(storage_access, |_, size| size);
1980    }
1981
1982    /// Test `scan_pubkey` for storage with incomplete account meta data.
1983    #[test_case(StorageAccess::Mmap)]
1984    #[test_case(StorageAccess::File)]
1985    fn test_scan_pubkeys_incomplete_data(storage_access: StorageAccess) {
1986        test_scan_pubkeys_helper(storage_access, |path, size| {
1987            // Append 1 byte of data at the end of the storage file to simulate
1988            // incomplete account's meta data.
1989            let mut f = OpenOptions::new()
1990                .read(true)
1991                .append(true)
1992                .open(path)
1993                .unwrap();
1994            f.write_all(&[0xFF]).unwrap();
1995            size + 1
1996        });
1997    }
1998
1999    /// Test `scan_pubkey` for storage which is missing the last account data
2000    #[test_case(StorageAccess::Mmap)]
2001    #[test_case(StorageAccess::File)]
2002    fn test_scan_pubkeys_missing_account_data(storage_access: StorageAccess) {
2003        test_scan_pubkeys_helper(storage_access, |path, size| {
2004            let fake_stored_meta = StoredMeta {
2005                write_version_obsolete: 0,
2006                data_len: 100,
2007                pubkey: solana_sdk::pubkey::new_rand(),
2008            };
2009            let fake_account_meta = AccountMeta {
2010                lamports: 100,
2011                rent_epoch: 10,
2012                owner: solana_sdk::pubkey::new_rand(),
2013                executable: false,
2014            };
2015
2016            let stored_meta_slice: &[u8] = unsafe {
2017                std::slice::from_raw_parts(
2018                    (&fake_stored_meta as *const StoredMeta) as *const u8,
2019                    mem::size_of::<StoredMeta>(),
2020                )
2021            };
2022            let account_meta_slice: &[u8] = unsafe {
2023                std::slice::from_raw_parts(
2024                    (&fake_account_meta as *const AccountMeta) as *const u8,
2025                    mem::size_of::<AccountMeta>(),
2026                )
2027            };
2028
2029            let mut f = OpenOptions::new()
2030                .read(true)
2031                .append(true)
2032                .open(path)
2033                .unwrap();
2034
2035            f.write_all(stored_meta_slice).unwrap();
2036            f.write_all(account_meta_slice).unwrap();
2037
2038            size + mem::size_of::<StoredMeta>() + mem::size_of::<AccountMeta>()
2039        });
2040    }
2041
2042    /// A helper fn to test scan_index
2043    fn test_scan_index_helper(
2044        storage_access: StorageAccess,
2045        modify_fn: impl Fn(&PathBuf, usize) -> usize,
2046    ) {
2047        test_scan_helper(
2048            storage_access,
2049            modify_fn,
2050            |append_vec, pubkeys, account_offsets, accounts| {
2051                let mut i = 0;
2052                append_vec.scan_index(|index_info| {
2053                    let pubkey = pubkeys.get(i).unwrap();
2054                    let account = accounts.get(i).unwrap();
2055                    let offset = account_offsets.get(i).unwrap();
2056
2057                    assert_eq!(
2058                        index_info.stored_size_aligned,
2059                        aligned_stored_size(account.data().len()),
2060                    );
2061                    assert_eq!(index_info.index_info.offset, *offset);
2062                    assert_eq!(index_info.index_info.pubkey, *pubkey);
2063                    assert_eq!(index_info.index_info.lamports, account.lamports());
2064                    assert_eq!(index_info.index_info.rent_epoch, account.rent_epoch());
2065                    assert_eq!(index_info.index_info.executable, account.executable());
2066                    assert_eq!(index_info.index_info.data_len, account.data().len() as u64);
2067
2068                    i += 1;
2069                });
2070                assert_eq!(i, accounts.len());
2071            },
2072        )
2073    }
2074
2075    #[test_case(StorageAccess::Mmap)]
2076    #[test_case(StorageAccess::File)]
2077    fn test_scan_index(storage_access: StorageAccess) {
2078        test_scan_index_helper(storage_access, |_, size| size);
2079    }
2080
2081    /// Test `scan_index` for storage with incomplete account meta data.
2082    #[test_case(StorageAccess::Mmap)]
2083    #[test_case(StorageAccess::File)]
2084    fn test_scan_index_incomplete_data(storage_access: StorageAccess) {
2085        test_scan_index_helper(storage_access, |path, size| {
2086            // Append 1 byte of data at the end of the storage file to simulate
2087            // incomplete account's meta data.
2088            let mut f = OpenOptions::new()
2089                .read(true)
2090                .append(true)
2091                .open(path)
2092                .unwrap();
2093            f.write_all(&[0xFF]).unwrap();
2094            size + 1
2095        });
2096    }
2097
2098    /// Test `scan_index` for storage which is missing the last account data
2099    #[test_case(StorageAccess::Mmap)]
2100    #[test_case(StorageAccess::File)]
2101    fn test_scan_index_missing_account_data(storage_access: StorageAccess) {
2102        test_scan_index_helper(storage_access, |path, size| {
2103            let fake_stored_meta = StoredMeta {
2104                write_version_obsolete: 0,
2105                data_len: 100,
2106                pubkey: solana_sdk::pubkey::new_rand(),
2107            };
2108            let fake_account_meta = AccountMeta {
2109                lamports: 100,
2110                rent_epoch: 10,
2111                owner: solana_sdk::pubkey::new_rand(),
2112                executable: false,
2113            };
2114
2115            let stored_meta_slice: &[u8] = unsafe {
2116                std::slice::from_raw_parts(
2117                    (&fake_stored_meta as *const StoredMeta) as *const u8,
2118                    mem::size_of::<StoredMeta>(),
2119                )
2120            };
2121            let account_meta_slice: &[u8] = unsafe {
2122                std::slice::from_raw_parts(
2123                    (&fake_account_meta as *const AccountMeta) as *const u8,
2124                    mem::size_of::<AccountMeta>(),
2125                )
2126            };
2127
2128            let mut f = OpenOptions::new()
2129                .read(true)
2130                .append(true)
2131                .open(path)
2132                .unwrap();
2133
2134            f.write_all(stored_meta_slice).unwrap();
2135            f.write_all(account_meta_slice).unwrap();
2136
2137            size + mem::size_of::<StoredMeta>() + mem::size_of::<AccountMeta>()
2138        });
2139    }
2140}