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