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