1use {
8 crate::{
9 account_storage::meta::{AccountMeta, StoredAccountMeta, StoredMeta},
10 accounts_file::{
11 AccountsFileError, InternalsForArchive, MatchAccountOwnerError, Result, StorageAccess,
12 StoredAccountsInfo, ALIGN_BOUNDARY_OFFSET,
13 },
14 accounts_hash::AccountHash,
15 accounts_index::ZeroLamport,
16 buffered_reader::{BufferedReader, BufferedReaderStatus},
17 file_io::read_into_buffer,
18 storable_accounts::StorableAccounts,
19 u64_align,
20 },
21 log::*,
22 memmap2::MmapMut,
23 solana_pubkey::Pubkey,
24 solana_sdk::{
25 account::{AccountSharedData, ReadableAccount, WritableAccount},
26 hash::Hash,
27 stake_history::Epoch,
28 system_instruction::MAX_PERMITTED_DATA_LENGTH,
29 },
30 std::{
31 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
50pub const STORE_META_OVERHEAD: usize = 136;
53
54const _: () = assert!(
56 STORE_META_OVERHEAD
57 == mem::size_of::<StoredMeta>()
58 + mem::size_of::<AccountMeta>()
59 + mem::size_of::<AccountHash>()
60);
61
62pub 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; #[derive(Error, Debug)]
72pub 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#[derive(Debug, Copy, Clone)]
90pub(crate) struct ValidSlice<'a>(&'a [u8]);
91
92impl<'a> ValidSlice<'a> {
93 pub(crate) fn new(data: &'a [u8]) -> Self {
94 Self(data)
95 }
96
97 pub(crate) fn len(&self) -> usize {
98 self.0.len()
99 }
100
101 #[cfg(all(unix, test))]
102 pub(crate) fn slice(&self) -> &[u8] {
103 self.0
104 }
105}
106
107#[derive(PartialEq, Eq, Debug)]
110pub struct AppendVecStoredAccountMeta<'append_vec> {
111 pub meta: &'append_vec StoredMeta,
112 pub account_meta: &'append_vec AccountMeta,
114 pub(crate) data: &'append_vec [u8],
115 pub(crate) offset: usize,
116 pub(crate) stored_size: usize,
117 pub(crate) hash: &'append_vec AccountHash,
118}
119
120impl<'append_vec> AppendVecStoredAccountMeta<'append_vec> {
121 pub fn pubkey(&self) -> &'append_vec Pubkey {
122 &self.meta.pubkey
123 }
124
125 pub fn hash(&self) -> &'append_vec AccountHash {
126 self.hash
127 }
128
129 pub fn stored_size(&self) -> usize {
130 self.stored_size
131 }
132
133 pub fn offset(&self) -> usize {
134 self.offset
135 }
136
137 pub fn data(&self) -> &'append_vec [u8] {
138 self.data
139 }
140
141 pub fn data_len(&self) -> u64 {
142 self.meta.data_len
143 }
144
145 pub fn meta(&self) -> &StoredMeta {
146 self.meta
147 }
148
149 pub(crate) fn sanitize(&self) -> bool {
150 self.sanitize_executable() && self.sanitize_lamports()
151 }
152
153 fn sanitize_executable(&self) -> bool {
154 self.ref_executable_byte() & !1 == 0
156 }
157
158 fn sanitize_lamports(&self) -> bool {
159 self.account_meta.lamports != 0
161 || self.to_account_shared_data() == AccountSharedData::default()
162 }
163
164 fn ref_executable_byte(&self) -> &u8 {
165 let executable_bool: &bool = &self.account_meta.executable;
168 let executable_bool_ptr = ptr::from_ref(executable_bool);
169 let executable_byte: &u8 = unsafe { &*(executable_bool_ptr.cast()) };
171 executable_byte
172 }
173}
174
175impl<'append_vec> ReadableAccount for AppendVecStoredAccountMeta<'append_vec> {
176 fn lamports(&self) -> u64 {
177 self.account_meta.lamports
178 }
179 fn data(&self) -> &'append_vec [u8] {
180 self.data()
181 }
182 fn owner(&self) -> &'append_vec Pubkey {
183 &self.account_meta.owner
184 }
185 fn executable(&self) -> bool {
186 self.account_meta.executable
187 }
188 fn rent_epoch(&self) -> Epoch {
189 self.account_meta.rent_epoch
190 }
191}
192
193pub(crate) struct IndexInfo {
195 pub stored_size_aligned: usize,
198 pub index_info: IndexInfoInner,
200}
201
202pub(crate) struct IndexInfoInner {
204 pub offset: usize,
206 pub pubkey: Pubkey,
207 pub lamports: u64,
208 pub rent_epoch: Epoch,
209 pub executable: bool,
210 pub data_len: u64,
211}
212
213#[derive(Debug)]
215struct AccountOffsets {
216 offset_to_end_of_data: usize,
218 next_account_offset: usize,
220 stored_size_aligned: usize,
222}
223
224#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
225#[derive(Debug)]
226enum AppendVecFileBacking {
227 Mmap(Mmap),
229 #[cfg_attr(not(unix), allow(dead_code))]
231 File(File),
232}
233
234#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
235#[derive(Debug)]
236struct Mmap {
237 mmap: MmapMut,
238 is_dirty: AtomicBool,
244}
245
246#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
251#[derive(Debug)]
252pub struct AppendVec {
253 path: PathBuf,
255
256 backing: AppendVecFileBacking,
258
259 append_lock: Mutex<()>,
261
262 current_len: AtomicUsize,
264
265 file_size: u64,
267
268 remove_file_on_drop: AtomicBool,
270}
271
272const PAGE_SIZE: u64 = 4 * 1024;
273const SCAN_BUFFER_SIZE: usize =
275 page_align((STORE_META_OVERHEAD as u64 + MAX_PERMITTED_DATA_LENGTH) * 3) as usize;
276const fn page_align(size: u64) -> u64 {
277 (size + (PAGE_SIZE - 1)) & !(PAGE_SIZE - 1)
278}
279
280#[cfg_attr(feature = "dev-context-only-utils", qualifier_attr::qualifiers(pub))]
296const SCAN_BUFFER_SIZE_WITHOUT_DATA: usize = 1 << 16;
297
298pub struct AppendVecStat {
299 pub mmap_files_open: AtomicU64,
300 pub mmap_files_dirty: AtomicU64,
301 pub open_as_file_io: AtomicU64,
302}
303
304lazy_static! {
305 pub static ref APPEND_VEC_STATS: AppendVecStat = AppendVecStat {
306 mmap_files_open: AtomicU64::new(0),
307 mmap_files_dirty: AtomicU64::new(0),
308 open_as_file_io: AtomicU64::new(0),
309 };
310}
311
312impl Drop for AppendVec {
313 fn drop(&mut self) {
314 APPEND_VEC_STATS
315 .mmap_files_open
316 .fetch_sub(1, Ordering::Relaxed);
317 match &self.backing {
318 AppendVecFileBacking::Mmap(mmap_only) => {
319 if mmap_only.is_dirty.load(Ordering::Acquire) {
320 APPEND_VEC_STATS
321 .mmap_files_dirty
322 .fetch_sub(1, Ordering::Relaxed);
323 }
324 }
325 AppendVecFileBacking::File(_) => {
326 APPEND_VEC_STATS
327 .open_as_file_io
328 .fetch_sub(1, Ordering::Relaxed);
329 }
330 }
331 if self.remove_file_on_drop.load(Ordering::Acquire) {
332 if let Err(_err) = remove_file(&self.path) {
335 inc_new_counter_info!("append_vec_drop_fail", 1);
340 }
341 }
342 }
343}
344
345impl AppendVec {
346 pub fn new(file: impl Into<PathBuf>, create: bool, size: usize) -> Self {
347 let file = file.into();
348 let initial_len = 0;
349 AppendVec::sanitize_len_and_size(initial_len, size).unwrap();
350
351 if create {
352 let _ignored = remove_file(&file);
353 }
354
355 let mut data = OpenOptions::new()
356 .read(true)
357 .write(true)
358 .create(create)
359 .open(&file)
360 .map_err(|e| {
361 panic!(
362 "Unable to {} data file {} in current dir({:?}): {:?}",
363 if create { "create" } else { "open" },
364 file.display(),
365 std::env::current_dir(),
366 e
367 );
368 })
369 .unwrap();
370
371 data.seek(SeekFrom::Start((size - 1) as u64)).unwrap();
375 data.write_all(&[0]).unwrap();
376 data.rewind().unwrap();
377 data.flush().unwrap();
378
379 let mmap = unsafe { MmapMut::map_mut(&data) };
381 let mmap = mmap.unwrap_or_else(|e| {
382 error!(
383 "Failed to map the data file (size: {}): {}.\n
384 Please increase sysctl vm.max_map_count or equivalent for your platform.",
385 size, e
386 );
387 std::process::exit(1);
388 });
389 APPEND_VEC_STATS
390 .mmap_files_open
391 .fetch_add(1, Ordering::Relaxed);
392
393 AppendVec {
394 path: file,
395 backing: AppendVecFileBacking::Mmap(Mmap {
396 mmap,
397 is_dirty: AtomicBool::new(false),
398 }),
399 append_lock: Mutex::new(()),
402 current_len: AtomicUsize::new(initial_len),
403 file_size: size as u64,
404 remove_file_on_drop: AtomicBool::new(true),
405 }
406 }
407
408 fn sanitize_len_and_size(current_len: usize, file_size: usize) -> Result<()> {
409 if file_size == 0 {
410 Err(AccountsFileError::AppendVecError(
411 AppendVecError::FileSizeTooSmall(file_size),
412 ))
413 } else if usize::try_from(MAXIMUM_APPEND_VEC_FILE_SIZE)
414 .map(|max| file_size > max)
415 .unwrap_or(true)
416 {
417 Err(AccountsFileError::AppendVecError(
418 AppendVecError::FileSizeTooLarge(file_size),
419 ))
420 } else if current_len > file_size {
421 Err(AccountsFileError::AppendVecError(
422 AppendVecError::OffsetOutOfBounds(current_len, file_size),
423 ))
424 } else {
425 Ok(())
426 }
427 }
428
429 pub fn dead_bytes_due_to_zero_lamport_single_ref(&self, count: usize) -> usize {
430 aligned_stored_size(0) * count
431 }
432
433 pub fn flush(&self) -> Result<()> {
434 match &self.backing {
435 AppendVecFileBacking::Mmap(mmap_only) => {
436 let should_flush = mmap_only.is_dirty.swap(false, Ordering::AcqRel);
438 if should_flush {
439 mmap_only.mmap.flush()?;
440 APPEND_VEC_STATS
441 .mmap_files_dirty
442 .fetch_sub(1, Ordering::Relaxed);
443 }
444 Ok(())
445 }
446 AppendVecFileBacking::File(_file) => {
447 Ok(())
449 }
450 }
451 }
452
453 pub fn reset(&self) {
454 let _lock = self.append_lock.lock().unwrap();
457 self.current_len.store(0, Ordering::Release);
458 }
459
460 #[cfg_attr(not(unix), allow(dead_code))]
463 pub(crate) fn reopen_as_readonly(&self) -> Option<Self> {
464 #[cfg(not(unix))]
465 return None;
467
468 #[cfg(unix)]
469 match &self.backing {
470 AppendVecFileBacking::File(_file) => None,
472 AppendVecFileBacking::Mmap(_mmap) => {
473 self.flush().expect("flush must succeed");
475 self.remove_file_on_drop.store(false, Ordering::Release);
477
478 AppendVec::new_from_file_unchecked(
480 self.path.clone(),
481 self.len(),
482 StorageAccess::File,
483 )
484 .ok()
485 }
486 }
487 }
488
489 pub fn remaining_bytes(&self) -> u64 {
491 self.capacity()
492 .saturating_sub(u64_align!(self.len()) as u64)
493 }
494
495 pub fn len(&self) -> usize {
496 self.current_len.load(Ordering::Acquire)
497 }
498
499 pub fn is_empty(&self) -> bool {
500 self.len() == 0
501 }
502
503 pub fn capacity(&self) -> u64 {
504 self.file_size
505 }
506
507 pub fn new_from_file(
508 path: impl Into<PathBuf>,
509 current_len: usize,
510 storage_access: StorageAccess,
511 ) -> Result<(Self, usize)> {
512 let path = path.into();
513 let new = Self::new_from_file_unchecked(path, current_len, storage_access)?;
514
515 let (sanitized, num_accounts) = new.sanitize_layout_and_length();
516 if !sanitized {
517 return Err(AccountsFileError::AppendVecError(
518 AppendVecError::IncorrectLayout(new.path.clone()),
519 ));
520 }
521
522 Ok((new, num_accounts))
523 }
524
525 #[cfg_attr(not(unix), allow(unused_variables))]
527 pub fn new_from_file_unchecked(
528 path: impl Into<PathBuf>,
529 current_len: usize,
530 storage_access: StorageAccess,
531 ) -> Result<Self> {
532 let path = path.into();
533 let file_size = std::fs::metadata(&path)?.len();
534 Self::sanitize_len_and_size(current_len, file_size as usize)?;
535
536 let data = OpenOptions::new()
537 .read(true)
538 .write(true)
539 .create(false)
540 .open(&path)?;
541
542 #[cfg(unix)]
543 if storage_access == StorageAccess::File {
545 APPEND_VEC_STATS
546 .mmap_files_open
547 .fetch_add(1, Ordering::Relaxed);
548 APPEND_VEC_STATS
549 .open_as_file_io
550 .fetch_add(1, Ordering::Relaxed);
551
552 return Ok(AppendVec {
553 path,
554 backing: AppendVecFileBacking::File(data),
555 append_lock: Mutex::new(()),
556 current_len: AtomicUsize::new(current_len),
557 file_size,
558 remove_file_on_drop: AtomicBool::new(true),
559 });
560 }
561
562 let mmap = unsafe {
563 let result = MmapMut::map_mut(&data);
564 if result.is_err() {
565 info!("memory map error: {:?}. This may be because vm.max_map_count is not set correctly.", result);
567 }
568 result?
569 };
570 APPEND_VEC_STATS
571 .mmap_files_open
572 .fetch_add(1, Ordering::Relaxed);
573
574 Ok(AppendVec {
575 path,
576 backing: AppendVecFileBacking::Mmap(Mmap {
577 mmap,
578 is_dirty: AtomicBool::new(false),
579 }),
580 append_lock: Mutex::new(()),
581 current_len: AtomicUsize::new(current_len),
582 file_size,
583 remove_file_on_drop: AtomicBool::new(true),
584 })
585 }
586
587 #[cfg(feature = "dev-context-only-utils")]
589 pub fn new_for_store_tool(path: impl Into<PathBuf>) -> Result<Self> {
590 let path = path.into();
591 let file_size = std::fs::metadata(&path)?.len();
592 Self::new_from_file_unchecked(path, file_size as usize, StorageAccess::default())
593 }
594
595 fn sanitize_layout_and_length(&self) -> (bool, usize) {
596 let mut num_accounts = 0;
602 let mut matches = true;
603 let mut last_offset = 0;
604 self.scan_accounts(|account| {
605 if !matches || !account.sanitize() {
606 matches = false;
607 return;
608 }
609 last_offset = account.offset() + account.stored_size();
610 num_accounts += 1;
611 });
612 if !matches {
613 return (false, num_accounts);
614 }
615 let aligned_current_len = u64_align!(self.current_len.load(Ordering::Acquire));
616
617 (last_offset == aligned_current_len, num_accounts)
618 }
619
620 fn get_slice(slice: ValidSlice, offset: usize, size: usize) -> Option<(&[u8], usize)> {
625 let end = offset.wrapping_add(size);
628 slice
629 .0
630 .get(offset..end)
631 .map(|subslice| (subslice, u64_align!(end)))
632 }
633
634 fn append_ptr(&self, offset: &mut usize, src: *const u8, len: usize) {
637 let pos = u64_align!(*offset);
638 match &self.backing {
639 AppendVecFileBacking::Mmap(mmap_only) => {
640 let data = &mmap_only.mmap[pos..(pos + len)];
641 unsafe {
645 let dst = data.as_ptr() as *mut _;
646 ptr::copy(src, dst, len);
647 };
648 *offset = pos + len;
649 }
650 AppendVecFileBacking::File(_file) => {
651 unimplemented!();
652 }
653 }
654 }
655
656 fn append_ptrs_locked(&self, offset: &mut usize, vals: &[(*const u8, usize)]) -> Option<usize> {
661 let mut end = *offset;
662 for val in vals {
663 end = u64_align!(end);
664 end += val.1;
665 }
666
667 if (self.file_size as usize) < end {
668 return None;
669 }
670
671 let pos = u64_align!(*offset);
672 for val in vals {
673 self.append_ptr(offset, val.0, val.1)
674 }
675 self.current_len.store(*offset, Ordering::Release);
676 Some(pos)
677 }
678
679 fn get_type<T>(slice: ValidSlice, offset: usize) -> Option<(&T, usize)> {
683 let (data, next) = Self::get_slice(slice, offset, mem::size_of::<T>())?;
684 let ptr = data.as_ptr().cast();
685 Some((unsafe { &*ptr }, next))
688 }
689
690 fn get_valid_slice_from_mmap<'a>(&self, mmap: &'a MmapMut) -> ValidSlice<'a> {
694 ValidSlice(&mmap[..self.len()])
695 }
696
697 pub fn get_stored_account_meta_callback<Ret>(
700 &self,
701 offset: usize,
702 mut callback: impl for<'local> FnMut(StoredAccountMeta<'local>) -> Ret,
703 ) -> Option<Ret> {
704 match &self.backing {
705 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
706 let slice = self.get_valid_slice_from_mmap(mmap);
707 let (meta, next): (&StoredMeta, _) = Self::get_type(slice, offset)?;
708 let (account_meta, next): (&AccountMeta, _) = Self::get_type(slice, next)?;
709 let (hash, next): (&AccountHash, _) = Self::get_type(slice, next)?;
710 let (data, next) = Self::get_slice(slice, next, meta.data_len as usize)?;
711 let stored_size = next - offset;
712 Some(callback(StoredAccountMeta::AppendVec(
713 AppendVecStoredAccountMeta {
714 meta,
715 account_meta,
716 data,
717 offset,
718 stored_size,
719 hash,
720 },
721 )))
722 }
723 AppendVecFileBacking::File(file) => {
724 let mut buf = [MaybeUninit::<u8>::uninit(); PAGE_SIZE as usize];
726 let bytes_read = read_into_buffer(file, self.len(), offset, unsafe {
728 slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len())
729 })
730 .ok()?;
731 let valid_bytes = ValidSlice(unsafe {
733 slice::from_raw_parts(buf.as_ptr() as *const u8, bytes_read)
734 });
735 let (meta, next): (&StoredMeta, _) = Self::get_type(valid_bytes, 0)?;
736 let (account_meta, next): (&AccountMeta, _) = Self::get_type(valid_bytes, next)?;
737 let (hash, next): (&AccountHash, _) = Self::get_type(valid_bytes, next)?;
738 let data_len = meta.data_len;
739 let remaining_bytes_for_data = bytes_read - next;
740 Some(if remaining_bytes_for_data >= data_len as usize {
741 let (data, next) = Self::get_slice(valid_bytes, next, meta.data_len as usize)?;
743 let stored_size = next;
744 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
745 meta,
746 account_meta,
747 data,
748 offset,
749 stored_size,
750 hash,
751 });
752 callback(account)
753 } else {
754 assert!(data_len <= MAX_PERMITTED_DATA_LENGTH, "{data_len}");
756 let mut data: Box<[MaybeUninit<u8>]> = Box::new_uninit_slice(data_len as usize);
757 let bytes_read = read_into_buffer(file, self.len(), offset + next, unsafe {
761 slice::from_raw_parts_mut(data.as_mut_ptr() as *mut u8, data_len as usize)
762 })
763 .ok()?;
764 if bytes_read < data_len as usize {
765 return None;
767 }
768 let data = unsafe { data.assume_init() };
770 let stored_size = aligned_stored_size(data_len as usize);
771 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
772 meta,
773 account_meta,
774 data: &data[..],
775 offset,
776 stored_size,
777 hash,
778 });
779 callback(account)
780 })
781 }
782 }
783 }
784
785 pub(crate) fn get_account_shared_data(&self, offset: usize) -> Option<AccountSharedData> {
789 match &self.backing {
790 AppendVecFileBacking::Mmap(_) => self
791 .get_stored_account_meta_callback(offset, |account| {
792 account.to_account_shared_data()
793 }),
794 AppendVecFileBacking::File(file) => {
795 let mut buf = MaybeUninit::<[u8; PAGE_SIZE as usize]>::uninit();
796 let bytes_read =
797 read_into_buffer(file, self.len(), offset, unsafe { &mut *buf.as_mut_ptr() })
798 .ok()?;
799 let valid_bytes = ValidSlice(unsafe {
801 slice::from_raw_parts(buf.as_ptr() as *const u8, bytes_read)
802 });
803 let (meta, next): (&StoredMeta, _) = Self::get_type(valid_bytes, 0)?;
804 let (account_meta, next): (&AccountMeta, _) = Self::get_type(valid_bytes, next)?;
805 let (hash, next): (&AccountHash, _) = Self::get_type(valid_bytes, next)?;
806 let data_len = meta.data_len;
807 let remaining_bytes_for_data = bytes_read - next;
808 Some(if remaining_bytes_for_data >= data_len as usize {
809 let (data, next) = Self::get_slice(valid_bytes, next, meta.data_len as usize)?;
811 let stored_size = next;
812 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
813 meta,
814 account_meta,
815 data,
816 offset,
817 stored_size,
818 hash,
819 });
820 account.to_account_shared_data()
822 } else {
823 assert!(data_len <= MAX_PERMITTED_DATA_LENGTH, "{data_len}");
825 let mut data = Vec::with_capacity(data_len as usize);
826 let slice = data.spare_capacity_mut();
827 let bytes_read = read_into_buffer(file, self.len(), offset + next, unsafe {
830 slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, data_len as usize)
831 })
832 .ok()?;
833 if bytes_read < data_len as usize {
834 return None;
836 }
837 unsafe { data.set_len(data_len as usize) };
839 AccountSharedData::create(
840 account_meta.lamports,
841 data,
842 account_meta.owner,
843 account_meta.executable,
844 account_meta.rent_epoch,
845 )
846 })
847 }
848 }
849 }
850
851 pub fn account_matches_owners(
856 &self,
857 offset: usize,
858 owners: &[Pubkey],
859 ) -> std::result::Result<usize, MatchAccountOwnerError> {
860 self.get_stored_account_meta_callback(offset, |stored_account_meta| {
861 if stored_account_meta.lamports() == 0 {
862 Err(MatchAccountOwnerError::NoMatch)
863 } else {
864 owners
865 .iter()
866 .position(|entry| stored_account_meta.owner() == entry)
867 .ok_or(MatchAccountOwnerError::NoMatch)
868 }
869 })
870 .unwrap_or(Err(MatchAccountOwnerError::UnableToLoad))
871 }
872
873 #[cfg(test)]
874 pub fn get_account_test(
875 &self,
876 offset: usize,
877 ) -> Option<(StoredMeta, solana_sdk::account::AccountSharedData)> {
878 let sizes = self.get_account_sizes(&[offset]);
879 let result = self.get_stored_account_meta_callback(offset, |r_callback| {
880 let r2 = self.get_account_shared_data(offset);
881 assert!(accounts_equal(&r_callback, r2.as_ref().unwrap()));
882 assert_eq!(sizes, vec![r_callback.stored_size()]);
883 let meta = r_callback.meta().clone();
884 Some((meta, r_callback.to_account_shared_data()))
885 });
886 if result.is_none() {
887 assert!(self
888 .get_stored_account_meta_callback(offset, |_| {})
889 .is_none());
890 assert!(self.get_account_shared_data(offset).is_none());
891 assert!(sizes.is_empty());
893 }
894 result.flatten()
895 }
896
897 pub fn path(&self) -> &Path {
899 self.path.as_path()
900 }
901
902 fn next_account_offset(start_offset: usize, stored_meta: &StoredMeta) -> AccountOffsets {
909 let stored_size_unaligned = STORE_META_OVERHEAD
910 .checked_add(stored_meta.data_len as usize)
911 .expect("stored size cannot overflow");
912 let stored_size_aligned = u64_align!(stored_size_unaligned);
913 let offset_to_end_of_data = start_offset + stored_size_unaligned;
914 let next_account_offset = start_offset + stored_size_aligned;
915
916 AccountOffsets {
917 next_account_offset,
918 offset_to_end_of_data,
919 stored_size_aligned,
920 }
921 }
922
923 pub(crate) fn scan_index(&self, mut callback: impl FnMut(IndexInfo)) {
926 let self_len = self.len();
928 match &self.backing {
929 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
930 let mut offset = 0;
931 let slice = self.get_valid_slice_from_mmap(mmap);
932 loop {
933 let Some((stored_meta, next)) = Self::get_type::<StoredMeta>(slice, offset)
934 else {
935 break;
937 };
938 let Some((account_meta, _)) = Self::get_type::<AccountMeta>(slice, next) else {
939 break;
941 };
942 if account_meta.lamports == 0 && stored_meta.pubkey == Pubkey::default() {
943 break;
945 }
946 let next = Self::next_account_offset(offset, stored_meta);
947 if next.offset_to_end_of_data > self_len {
948 break;
950 }
951 callback(IndexInfo {
952 index_info: {
953 IndexInfoInner {
954 pubkey: stored_meta.pubkey,
955 lamports: account_meta.lamports,
956 offset,
957 data_len: stored_meta.data_len,
958 executable: account_meta.executable,
959 rent_epoch: account_meta.rent_epoch,
960 }
961 },
962 stored_size_aligned: next.stored_size_aligned,
963 });
964 offset = next.next_account_offset;
965 }
966 }
967 AppendVecFileBacking::File(file) => {
968 let buffer_size = std::cmp::min(SCAN_BUFFER_SIZE, self_len);
969 let mut reader =
970 BufferedReader::new(buffer_size, self_len, file, STORE_META_OVERHEAD);
971 while reader.read().ok() == Some(BufferedReaderStatus::Success) {
972 let (offset, bytes) = reader.get_offset_and_data();
973 let (stored_meta, next) = Self::get_type::<StoredMeta>(bytes, 0).unwrap();
974 let (account_meta, _) = Self::get_type::<AccountMeta>(bytes, next).unwrap();
975 if account_meta.lamports == 0 && stored_meta.pubkey == Pubkey::default() {
976 break;
978 }
979 let next = Self::next_account_offset(offset, stored_meta);
980 if next.offset_to_end_of_data > self_len {
981 break;
983 }
984 callback(IndexInfo {
985 index_info: {
986 IndexInfoInner {
987 pubkey: stored_meta.pubkey,
988 lamports: account_meta.lamports,
989 offset,
990 data_len: stored_meta.data_len,
991 executable: account_meta.executable,
992 rent_epoch: account_meta.rent_epoch,
993 }
994 },
995 stored_size_aligned: next.stored_size_aligned,
996 });
997 reader.advance_offset(next.stored_size_aligned);
998 }
999 }
1000 }
1001 }
1002
1003 #[allow(clippy::blocks_in_conditions)]
1005 pub fn scan_accounts(&self, mut callback: impl for<'local> FnMut(StoredAccountMeta<'local>)) {
1006 match &self.backing {
1007 AppendVecFileBacking::Mmap(_mmap) => {
1008 let mut offset = 0;
1009 while self
1010 .get_stored_account_meta_callback(offset, |account| {
1011 offset += account.stored_size();
1012 if account.is_zero_lamport() && account.pubkey() == &Pubkey::default() {
1013 return false;
1015 }
1016
1017 callback(account);
1018 true
1019 })
1020 .unwrap_or_default()
1021 {}
1022 }
1023 AppendVecFileBacking::File(file) => {
1024 let mut reader =
1025 BufferedReader::new(SCAN_BUFFER_SIZE, self.len(), file, STORE_META_OVERHEAD);
1026 while reader.read().ok() == Some(BufferedReaderStatus::Success) {
1027 let (offset, bytes_subset) = reader.get_offset_and_data();
1028 let (meta, next): (&StoredMeta, _) = Self::get_type(bytes_subset, 0).unwrap();
1029 let (account_meta, next): (&AccountMeta, _) =
1030 Self::get_type(bytes_subset, next).unwrap();
1031 let (hash, next): (&AccountHash, _) =
1032 Self::get_type(bytes_subset, next).unwrap();
1033 let data_len = meta.data_len;
1034 if bytes_subset.len() - next >= data_len as usize {
1035 let data = &bytes_subset.0[next..(next + data_len as usize)];
1037 let stored_size = u64_align!(next + (data_len as usize));
1038 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
1039 meta,
1040 account_meta,
1041 data,
1042 offset,
1043 stored_size,
1044 hash,
1045 });
1046 callback(account);
1047 reader.advance_offset(stored_size);
1048 } else {
1049 reader.set_required_data_len(
1051 STORE_META_OVERHEAD.saturating_add(data_len as usize),
1052 )
1053 }
1054 }
1055 }
1056 }
1057 }
1058
1059 pub(crate) fn get_account_sizes(&self, sorted_offsets: &[usize]) -> Vec<usize> {
1061 let self_len = self.len();
1063 let mut account_sizes = Vec::with_capacity(sorted_offsets.len());
1064 match &self.backing {
1065 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
1066 let slice = self.get_valid_slice_from_mmap(mmap);
1067 for &offset in sorted_offsets {
1068 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(slice, offset) else {
1069 break;
1070 };
1071 let next = Self::next_account_offset(offset, stored_meta);
1072 if next.offset_to_end_of_data > self_len {
1073 break;
1075 }
1076 account_sizes.push(next.stored_size_aligned);
1077 }
1078 }
1079 AppendVecFileBacking::File(file) => {
1080 let mut buffer = [MaybeUninit::<u8>::uninit(); mem::size_of::<StoredMeta>()];
1081 for &offset in sorted_offsets {
1082 let Some(bytes_read) = read_into_buffer(file, self_len, offset, unsafe {
1084 slice::from_raw_parts_mut(
1085 buffer.as_mut_ptr() as *mut u8,
1086 mem::size_of::<StoredMeta>(),
1087 )
1088 })
1089 .ok() else {
1090 break;
1091 };
1092 let bytes = ValidSlice(unsafe {
1094 slice::from_raw_parts(buffer.as_ptr() as *const u8, bytes_read)
1095 });
1096 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(bytes, 0) else {
1097 break;
1098 };
1099 let next = Self::next_account_offset(offset, stored_meta);
1100 if next.offset_to_end_of_data > self_len {
1101 break;
1103 }
1104 account_sizes.push(next.stored_size_aligned);
1105 }
1106 }
1107 }
1108 account_sizes
1109 }
1110
1111 pub fn scan_pubkeys(&self, mut callback: impl FnMut(&Pubkey)) {
1117 let self_len = self.len();
1119 match &self.backing {
1120 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
1121 let mut offset = 0;
1122 let slice = self.get_valid_slice_from_mmap(mmap);
1123 loop {
1124 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(slice, offset) else {
1125 break;
1127 };
1128 let next = Self::next_account_offset(offset, stored_meta);
1129 if next.offset_to_end_of_data > self_len {
1130 break;
1132 }
1133 callback(&stored_meta.pubkey);
1134 offset = next.next_account_offset;
1135 }
1136 }
1137 AppendVecFileBacking::File(file) => {
1138 let buffer_size = std::cmp::min(SCAN_BUFFER_SIZE_WITHOUT_DATA, self_len);
1139 let mut reader =
1140 BufferedReader::new(buffer_size, self_len, file, STORE_META_OVERHEAD);
1141 while reader.read().ok() == Some(BufferedReaderStatus::Success) {
1142 let (offset, bytes) = reader.get_offset_and_data();
1143 let (stored_meta, _) = Self::get_type::<StoredMeta>(bytes, 0).unwrap();
1144 let next = Self::next_account_offset(offset, stored_meta);
1145 if next.offset_to_end_of_data > self.len() {
1146 break;
1148 }
1149 callback(&stored_meta.pubkey);
1150 reader.advance_offset(next.stored_size_aligned);
1152 }
1153 }
1154 }
1155 }
1156
1157 pub fn append_accounts<'a>(
1165 &self,
1166 accounts: &impl StorableAccounts<'a>,
1167 skip: usize,
1168 ) -> Option<StoredAccountsInfo> {
1169 let _lock = self.append_lock.lock().unwrap();
1170 let default_hash = Hash::default();
1171 let mut offset = self.len();
1172 let len = accounts.len();
1173 let offsets_len = len - skip + 1;
1177 let mut offsets = Vec::with_capacity(offsets_len);
1178 let mut stop = false;
1179 for i in skip..len {
1180 if stop {
1181 break;
1182 }
1183 accounts.account_default_if_zero_lamport(i, |account| {
1184 let account_meta = AccountMeta {
1185 lamports: account.lamports(),
1186 owner: *account.owner(),
1187 rent_epoch: account.rent_epoch(),
1188 executable: account.executable(),
1189 };
1190
1191 let stored_meta = StoredMeta {
1192 pubkey: *account.pubkey(),
1193 data_len: account.data().len() as u64,
1194 write_version_obsolete: 0,
1195 };
1196 let stored_meta_ptr = ptr::from_ref(&stored_meta).cast();
1197 let account_meta_ptr = ptr::from_ref(&account_meta).cast();
1198 let hash_ptr = bytemuck::bytes_of(&default_hash).as_ptr();
1199 let data_ptr = account.data().as_ptr();
1200 let ptrs = [
1201 (stored_meta_ptr, mem::size_of::<StoredMeta>()),
1202 (account_meta_ptr, mem::size_of::<AccountMeta>()),
1203 (hash_ptr, mem::size_of::<AccountHash>()),
1204 (data_ptr, stored_meta.data_len as usize),
1205 ];
1206 if let Some(start_offset) = self.append_ptrs_locked(&mut offset, &ptrs) {
1207 offsets.push(start_offset)
1208 } else {
1209 stop = true;
1210 }
1211 });
1212 }
1213
1214 match &self.backing {
1215 AppendVecFileBacking::Mmap(mmap_only) => {
1216 if !offsets.is_empty() {
1217 if !mmap_only.is_dirty.load(Ordering::Acquire) {
1223 mmap_only.is_dirty.store(true, Ordering::Release);
1224 APPEND_VEC_STATS
1225 .mmap_files_dirty
1226 .fetch_add(1, Ordering::Relaxed);
1227 }
1228 }
1229 }
1230 AppendVecFileBacking::File(_) => {}
1231 }
1232
1233 (!offsets.is_empty()).then(|| {
1234 offsets.push(u64_align!(offset));
1239 let size = offsets.windows(2).map(|offset| offset[1] - offset[0]).sum();
1240 offsets.pop();
1241
1242 StoredAccountsInfo { offsets, size }
1243 })
1244 }
1245
1246 pub(crate) fn can_append(&self) -> bool {
1247 match &self.backing {
1248 AppendVecFileBacking::File(_file) => false,
1249 AppendVecFileBacking::Mmap(_mmap) => true,
1250 }
1251 }
1252
1253 pub(crate) fn internals_for_archive(&self) -> InternalsForArchive {
1255 match &self.backing {
1256 AppendVecFileBacking::File(_file) => InternalsForArchive::FileIo(self.path()),
1257 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => InternalsForArchive::Mmap(mmap),
1259 }
1260 }
1261}
1262
1263#[cfg(test)]
1264pub mod tests {
1265 use {
1266 super::{test_utils::*, *},
1267 assert_matches::assert_matches,
1268 memoffset::offset_of,
1269 rand::{thread_rng, Rng},
1270 solana_sdk::{
1271 account::{Account, AccountSharedData},
1272 clock::Slot,
1273 },
1274 std::{mem::ManuallyDrop, time::Instant},
1275 test_case::test_case,
1276 };
1277
1278 impl AppendVec {
1279 pub(crate) fn set_current_len_for_tests(&self, len: usize) {
1280 self.current_len.store(len, Ordering::Release);
1281 }
1282
1283 fn append_account_test(&self, data: &(StoredMeta, AccountSharedData)) -> Option<usize> {
1284 let slot_ignored = Slot::MAX;
1285 let accounts = [(&data.0.pubkey, &data.1)];
1286 let slice = &accounts[..];
1287 let storable_accounts = (slot_ignored, slice);
1288
1289 self.append_accounts(&storable_accounts, 0)
1290 .map(|res| res.offsets[0])
1291 }
1292 }
1293
1294 impl StoredAccountMeta<'_> {
1295 pub(crate) fn ref_executable_byte(&self) -> &u8 {
1296 match self {
1297 Self::AppendVec(av) => av.ref_executable_byte(),
1298 Self::Hot(_) => unreachable!(),
1300 }
1301 }
1302 }
1303
1304 impl AppendVecStoredAccountMeta<'_> {
1305 fn set_data_len_unsafe(&self, new_data_len: u64) {
1306 unsafe {
1308 #[allow(invalid_reference_casting)]
1309 ptr::write(
1310 std::mem::transmute::<*const u64, *mut u64>(&self.meta.data_len),
1311 new_data_len,
1312 );
1313 }
1314 }
1315
1316 fn get_executable_byte(&self) -> u8 {
1317 let executable_bool: bool = self.executable();
1318 let executable_byte: u8 = unsafe { std::mem::transmute::<bool, u8>(executable_bool) };
1320 executable_byte
1321 }
1322
1323 fn set_executable_as_byte(&self, new_executable_byte: u8) {
1324 unsafe {
1326 #[allow(invalid_reference_casting)]
1327 ptr::write(
1328 std::mem::transmute::<*const bool, *mut u8>(&self.account_meta.executable),
1329 new_executable_byte,
1330 );
1331 }
1332 }
1333 }
1334
1335 static_assertions::assert_eq_align!(u64, StoredMeta, AccountMeta);
1337
1338 #[test]
1339 fn test_account_meta_default() {
1340 let def1 = AccountMeta::default();
1341 let def2 = AccountMeta::from(&Account::default());
1342 assert_eq!(&def1, &def2);
1343 let def2 = AccountMeta::from(&AccountSharedData::default());
1344 assert_eq!(&def1, &def2);
1345 let def2 = AccountMeta::from(Some(&AccountSharedData::default()));
1346 assert_eq!(&def1, &def2);
1347 let none: Option<&AccountSharedData> = None;
1348 let def2 = AccountMeta::from(none);
1349 assert_eq!(&def1, &def2);
1350 }
1351
1352 #[test]
1353 fn test_account_meta_non_default() {
1354 let def1 = AccountMeta {
1355 lamports: 1,
1356 owner: Pubkey::new_unique(),
1357 executable: true,
1358 rent_epoch: 3,
1359 };
1360 let def2_account = Account {
1361 lamports: def1.lamports,
1362 owner: def1.owner,
1363 executable: def1.executable,
1364 rent_epoch: def1.rent_epoch,
1365 data: Vec::new(),
1366 };
1367 let def2 = AccountMeta::from(&def2_account);
1368 assert_eq!(&def1, &def2);
1369 let def2 = AccountMeta::from(&AccountSharedData::from(def2_account.clone()));
1370 assert_eq!(&def1, &def2);
1371 let def2 = AccountMeta::from(Some(&AccountSharedData::from(def2_account)));
1372 assert_eq!(&def1, &def2);
1373 }
1374
1375 #[test]
1376 #[should_panic(expected = "AppendVecError(FileSizeTooSmall(0))")]
1377 fn test_append_vec_new_bad_size() {
1378 let path = get_append_vec_path("test_append_vec_new_bad_size");
1379 let _av = AppendVec::new(&path.path, true, 0);
1380 }
1381
1382 #[test_case(StorageAccess::Mmap)]
1383 #[test_case(StorageAccess::File)]
1384 fn test_append_vec_new_from_file_bad_size(storage_access: StorageAccess) {
1385 let file = get_append_vec_path("test_append_vec_new_from_file_bad_size");
1386 let path = &file.path;
1387
1388 let _data = OpenOptions::new()
1389 .read(true)
1390 .write(true)
1391 .create_new(true)
1392 .open(path)
1393 .expect("create a test file for mmap");
1394
1395 let result = AppendVec::new_from_file(path, 0, storage_access);
1396 assert_matches!(result, Err(ref message) if message.to_string().contains("too small file size 0 for AppendVec"));
1397 }
1398
1399 #[test]
1400 fn test_append_vec_sanitize_len_and_size_too_small() {
1401 const LEN: usize = 0;
1402 const SIZE: usize = 0;
1403 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1404 assert_matches!(result, Err(ref message) if message.to_string().contains("too small file size 0 for AppendVec"));
1405 }
1406
1407 #[test]
1408 fn test_append_vec_sanitize_len_and_size_maximum() {
1409 const LEN: usize = 0;
1410 const SIZE: usize = 16 * 1024 * 1024 * 1024;
1411 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1412 assert_matches!(result, Ok(_));
1413 }
1414
1415 #[test]
1416 fn test_append_vec_sanitize_len_and_size_too_large() {
1417 const LEN: usize = 0;
1418 const SIZE: usize = 16 * 1024 * 1024 * 1024 + 1;
1419 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1420 assert_matches!(result, Err(ref message) if message.to_string().contains("too large file size 17179869185 for AppendVec"));
1421 }
1422
1423 #[test]
1424 fn test_append_vec_sanitize_len_and_size_full_and_same_as_current_len() {
1425 const LEN: usize = 1024 * 1024;
1426 const SIZE: usize = 1024 * 1024;
1427 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1428 assert_matches!(result, Ok(_));
1429 }
1430
1431 #[test]
1432 fn test_append_vec_sanitize_len_and_size_larger_current_len() {
1433 const LEN: usize = 1024 * 1024 + 1;
1434 const SIZE: usize = 1024 * 1024;
1435 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1436 assert_matches!(result, Err(ref message) if message.to_string().contains("is larger than file size (1048576)"));
1437 }
1438
1439 #[test]
1440 fn test_append_vec_one() {
1441 let path = get_append_vec_path("test_append");
1442 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1443 let account = create_test_account(0);
1444 let index = av.append_account_test(&account).unwrap();
1445 assert_eq!(av.get_account_test(index).unwrap(), account);
1446 truncate_and_test(av, index);
1447 }
1448
1449 fn truncate_and_test(av: AppendVec, index: usize) {
1452 let hash = std::mem::size_of::<AccountHash>();
1454 for _ in 0..hash {
1455 av.current_len.fetch_sub(1, Ordering::Relaxed);
1456 assert_eq!(av.get_account_test(index), None);
1457 }
1458 av.current_len.fetch_sub(1, Ordering::Relaxed);
1460 assert_eq!(av.get_account_test(index), None);
1461 }
1462
1463 #[test]
1464 fn test_append_vec_one_with_data() {
1465 let path = get_append_vec_path("test_append");
1466 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1467 let data_len = 1;
1468 let account = create_test_account(data_len);
1469 let index = av.append_account_test(&account).unwrap();
1470 assert_eq!(
1472 STORE_META_OVERHEAD + data_len,
1473 av.current_len.load(Ordering::Relaxed)
1474 );
1475 assert_eq!(av.get_account_test(index).unwrap(), account);
1476 truncate_and_test(av, index);
1477 }
1478
1479 #[test]
1480 fn test_remaining_bytes() {
1481 let path = get_append_vec_path("test_append");
1482 let sz = 1024 * 1024;
1483 let sz64 = sz as u64;
1484 let av = AppendVec::new(&path.path, true, sz);
1485 assert_eq!(av.capacity(), sz64);
1486 assert_eq!(av.remaining_bytes(), sz64);
1487
1488 let mut av_len = 0;
1490 let account = create_test_account(0);
1491 av.append_account_test(&account).unwrap();
1492 av_len += STORE_META_OVERHEAD;
1493 assert_eq!(av.capacity(), sz64);
1494 assert_eq!(av.remaining_bytes(), sz64 - (STORE_META_OVERHEAD as u64));
1495 assert_eq!(av.len(), av_len);
1496
1497 let account = create_test_account(1);
1499 let account_storage_len = STORE_META_OVERHEAD + 1;
1500 av_len += account_storage_len;
1501 av.append_account_test(&account).unwrap();
1502 assert_eq!(av.capacity(), sz64);
1503 assert_eq!(av.len(), av_len);
1504 let alignment_bytes = u64_align!(av_len) - av_len; assert_eq!(alignment_bytes, 7);
1506 assert_eq!(av.remaining_bytes(), sz64 - u64_align!(av_len) as u64);
1507
1508 let account = create_test_account(1);
1510 av.append_account_test(&account).unwrap();
1511 let account_storage_len = STORE_META_OVERHEAD + 1;
1512 av_len += alignment_bytes; av_len += account_storage_len;
1514 assert_eq!(av.capacity(), sz64);
1515 assert_eq!(av.len(), av_len);
1516 assert_eq!(av.remaining_bytes(), sz64 - u64_align!(av_len) as u64);
1517 }
1518
1519 #[test]
1520 fn test_append_vec_data() {
1521 let path = get_append_vec_path("test_append_data");
1522 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1523 let account = create_test_account(5);
1524 let index = av.append_account_test(&account).unwrap();
1525 assert_eq!(av.get_account_test(index).unwrap(), account);
1526 let account1 = create_test_account(6);
1527 let index1 = av.append_account_test(&account1).unwrap();
1528 assert_eq!(av.get_account_test(index).unwrap(), account);
1529 assert_eq!(av.get_account_test(index1).unwrap(), account1);
1530 }
1531
1532 #[test]
1533 fn test_account_matches_owners() {
1534 let path = get_append_vec_path("test_append_data");
1535 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1536 let owners: Vec<Pubkey> = (0..2).map(|_| Pubkey::new_unique()).collect();
1537
1538 let mut account = create_test_account(5);
1539 account.1.set_owner(owners[0]);
1540 let index = av.append_account_test(&account).unwrap();
1541 assert_eq!(av.account_matches_owners(index, &owners), Ok(0));
1542
1543 let mut account1 = create_test_account(6);
1544 account1.1.set_owner(owners[1]);
1545 let index1 = av.append_account_test(&account1).unwrap();
1546 assert_eq!(av.account_matches_owners(index1, &owners), Ok(1));
1547 assert_eq!(av.account_matches_owners(index, &owners), Ok(0));
1548
1549 let mut account2 = create_test_account(6);
1550 account2.1.set_owner(Pubkey::new_unique());
1551 let index2 = av.append_account_test(&account2).unwrap();
1552 assert_eq!(
1553 av.account_matches_owners(index2, &owners),
1554 Err(MatchAccountOwnerError::NoMatch)
1555 );
1556
1557 assert_eq!(
1559 av.account_matches_owners(usize::MAX - mem::size_of::<StoredMeta>(), &owners),
1560 Err(MatchAccountOwnerError::UnableToLoad)
1561 );
1562
1563 assert_eq!(
1564 av.account_matches_owners(
1565 usize::MAX - mem::size_of::<StoredMeta>() - mem::size_of::<AccountMeta>() + 1,
1566 &owners
1567 ),
1568 Err(MatchAccountOwnerError::UnableToLoad)
1569 );
1570 }
1571
1572 impl AppendVec {
1573 fn accounts_count(&self) -> usize {
1575 let mut count = 0;
1576 self.scan_accounts(|_| {
1577 count += 1;
1578 });
1579 count
1580 }
1581 }
1582
1583 #[test]
1584 fn test_append_vec_append_many() {
1585 let path = get_append_vec_path("test_append_many");
1586 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1587 let size = 1000;
1588 let mut indexes = vec![];
1589 let now = Instant::now();
1590 let mut sizes = vec![];
1591 for sample in 0..size {
1592 let account = create_test_account(sample + 1);
1595 sizes.push(aligned_stored_size(account.1.data().len()));
1596 let pos = av.append_account_test(&account).unwrap();
1597 assert_eq!(av.get_account_test(pos).unwrap(), account);
1598 indexes.push(pos);
1599 assert_eq!(sizes, av.get_account_sizes(&indexes));
1600 }
1601 trace!("append time: {} ms", now.elapsed().as_millis());
1602
1603 let now = Instant::now();
1604 for _ in 0..size {
1605 let sample = thread_rng().gen_range(0..indexes.len());
1606 let account = create_test_account(sample + 1);
1607 assert_eq!(av.get_account_test(indexes[sample]).unwrap(), account);
1608 }
1609 trace!("random read time: {} ms", now.elapsed().as_millis());
1610
1611 let now = Instant::now();
1612 assert_eq!(indexes.len(), size);
1613 assert_eq!(indexes[0], 0);
1614 let mut sample = 0;
1615 assert_eq!(av.accounts_count(), size);
1616 av.scan_accounts(|v| {
1617 let account = create_test_account(sample + 1);
1618 let recovered = v.to_account_shared_data();
1619 assert_eq!(recovered, account.1);
1620 sample += 1;
1621 });
1622 trace!("sequential read time: {} ms", now.elapsed().as_millis());
1623 }
1624
1625 #[test_case(StorageAccess::Mmap)]
1626 #[test_case(StorageAccess::File)]
1627 fn test_new_from_file_crafted_zero_lamport_account(storage_access: StorageAccess) {
1628 let file = get_append_vec_path("test_append_bytes");
1672 let path = &file.path;
1673
1674 let accounts_len = 139;
1675 {
1676 let append_vec_data = [
1677 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 192, 118, 150, 1, 185, 209, 118,
1678 82, 154, 222, 172, 202, 110, 26, 218, 140, 143, 96, 61, 43, 212, 73, 203, 7, 190,
1679 88, 80, 222, 110, 114, 67, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1680 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,
1681 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,
1682 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,
1683 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,
1684 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,
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,
1687 ];
1688
1689 let f = std::fs::File::create(path).unwrap();
1690 let mut writer = std::io::BufWriter::new(f);
1691 writer.write_all(append_vec_data.as_slice()).unwrap();
1692 }
1693
1694 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1695 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1696 }
1697
1698 #[test_case(StorageAccess::Mmap)]
1699 #[test_case(StorageAccess::File)]
1700 fn test_new_from_file_crafted_data_len(storage_access: StorageAccess) {
1701 let file = get_append_vec_path("test_new_from_file_crafted_data_len");
1702 let path = &file.path;
1703 let accounts_len = {
1704 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1706
1707 let crafted_data_len = 1;
1708
1709 av.append_account_test(&create_test_account(10)).unwrap();
1710
1711 av.get_stored_account_meta_callback(0, |account| {
1712 let StoredAccountMeta::AppendVec(account) = account else {
1713 panic!("StoredAccountMeta can only be AppendVec in this test.");
1714 };
1715 account.set_data_len_unsafe(crafted_data_len);
1716 assert_eq!(account.data_len(), crafted_data_len);
1717
1718 av.get_stored_account_meta_callback(0, |account| {
1720 assert_eq!(account.data_len() as u64, crafted_data_len);
1721 });
1722 });
1723
1724 av.flush().unwrap();
1725 av.len()
1726 };
1727 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1728 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1729 }
1730
1731 #[test]
1732 fn test_append_vec_reset() {
1733 let file = get_append_vec_path("test_append_vec_reset");
1734 let path = &file.path;
1735 let av = AppendVec::new(path, true, 1024 * 1024);
1736 av.append_account_test(&create_test_account(10)).unwrap();
1737
1738 assert!(!av.is_empty());
1739 av.reset();
1740 assert_eq!(av.len(), 0);
1741 }
1742
1743 #[test_case(StorageAccess::Mmap)]
1744 #[test_case(StorageAccess::File)]
1745 fn test_append_vec_flush(storage_access: StorageAccess) {
1746 let file = get_append_vec_path("test_append_vec_flush");
1747 let path = &file.path;
1748 let accounts_len = {
1749 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1751 av.append_account_test(&create_test_account(10)).unwrap();
1752 av.len()
1753 };
1754
1755 let (av, num_account) =
1756 AppendVec::new_from_file(path, accounts_len, storage_access).unwrap();
1757 av.flush().unwrap();
1758 assert_eq!(num_account, 1);
1759 }
1760
1761 #[test_case(StorageAccess::Mmap)]
1762 #[test_case(StorageAccess::File)]
1763 fn test_append_vec_reopen_as_readonly(storage_access: StorageAccess) {
1764 let file = get_append_vec_path("test_append_vec_flush");
1765 let path = &file.path;
1766 let accounts_len = {
1767 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1769 av.append_account_test(&create_test_account(10)).unwrap();
1770 av.len()
1771 };
1772 let (av, _) = AppendVec::new_from_file(path, accounts_len, storage_access).unwrap();
1773 let reopen = av.reopen_as_readonly();
1774 if storage_access == StorageAccess::File {
1775 assert!(reopen.is_none());
1776 } else {
1777 assert!(reopen.is_some());
1778 }
1779 }
1780
1781 #[test_case(StorageAccess::Mmap)]
1782 #[test_case(StorageAccess::File)]
1783 fn test_new_from_file_too_large_data_len(storage_access: StorageAccess) {
1784 let file = get_append_vec_path("test_new_from_file_too_large_data_len");
1785 let path = &file.path;
1786 let accounts_len = {
1787 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1789
1790 let too_large_data_len = u64::MAX;
1791 av.append_account_test(&create_test_account(10)).unwrap();
1792
1793 av.get_stored_account_meta_callback(0, |account| {
1794 let StoredAccountMeta::AppendVec(account) = account else {
1795 panic!("StoredAccountMeta can only be AppendVec in this test.");
1796 };
1797 account.set_data_len_unsafe(too_large_data_len);
1798 assert_eq!(account.data_len(), too_large_data_len);
1799 })
1800 .unwrap();
1801
1802 assert!(av
1804 .get_stored_account_meta_callback(0, |_| {
1805 panic!("unexpected");
1806 })
1807 .is_none());
1808 av.flush().unwrap();
1809 av.len()
1810 };
1811 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1812 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1813 }
1814
1815 #[test_case(StorageAccess::Mmap)]
1816 #[test_case(StorageAccess::File)]
1817 fn test_new_from_file_crafted_executable(storage_access: StorageAccess) {
1818 let file = get_append_vec_path("test_new_from_crafted_executable");
1819 let path = &file.path;
1820 let accounts_len = {
1821 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1823 av.append_account_test(&create_test_account(10)).unwrap();
1824 let offset_1 = {
1825 let mut executable_account = create_test_account(10);
1826 executable_account.1.set_executable(true);
1827 av.append_account_test(&executable_account).unwrap()
1828 };
1829
1830 let crafted_executable = u8::MAX - 1;
1831
1832 av.get_stored_account_meta_callback(0, |account| {
1835 assert_eq!(*account.ref_executable_byte(), 0);
1836 let StoredAccountMeta::AppendVec(account) = account else {
1837 panic!("StoredAccountMeta can only be AppendVec in this test.");
1838 };
1839 account.set_executable_as_byte(crafted_executable);
1840 })
1841 .unwrap();
1842 av.get_stored_account_meta_callback(offset_1, |account| {
1843 assert_eq!(*account.ref_executable_byte(), 1);
1844 })
1845 .unwrap();
1846
1847 av.get_stored_account_meta_callback(0, |account| {
1849 let StoredAccountMeta::AppendVec(account) = account else {
1850 panic!("StoredAccountMeta can only be AppendVec in this test.");
1851 };
1852
1853 assert!(!account.sanitize_executable());
1855
1856 {
1858 let executable_bool: &bool = &account.account_meta.executable;
1859 assert!(!*executable_bool);
1862 assert_eq!(*account.ref_executable_byte(), crafted_executable);
1863 }
1864
1865 {
1867 let executable_bool: bool = account.executable();
1868 assert!(!executable_bool);
1869 assert_eq!(account.get_executable_byte(), 0); }
1871 })
1872 .unwrap();
1873
1874 av.flush().unwrap();
1875 av.len()
1876 };
1877 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1878 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1879 }
1880
1881 #[test]
1882 fn test_type_layout() {
1883 assert_eq!(offset_of!(StoredMeta, write_version_obsolete), 0x00);
1884 assert_eq!(offset_of!(StoredMeta, data_len), 0x08);
1885 assert_eq!(offset_of!(StoredMeta, pubkey), 0x10);
1886 assert_eq!(mem::size_of::<StoredMeta>(), 0x30);
1887
1888 assert_eq!(offset_of!(AccountMeta, lamports), 0x00);
1889 assert_eq!(offset_of!(AccountMeta, rent_epoch), 0x08);
1890 assert_eq!(offset_of!(AccountMeta, owner), 0x10);
1891 assert_eq!(offset_of!(AccountMeta, executable), 0x30);
1892 assert_eq!(mem::size_of::<AccountMeta>(), 0x38);
1893 }
1894
1895 #[test_case(StorageAccess::Mmap)]
1896 #[test_case(StorageAccess::File)]
1897 fn test_get_account_shared_data_from_truncated_file(storage_access: StorageAccess) {
1898 let file = get_append_vec_path("test_get_account_shared_data_from_truncated_file");
1899 let path = &file.path;
1900
1901 {
1902 let data_len: usize = 2 * PAGE_SIZE as usize;
1905 let account = create_test_account_with(data_len);
1906 let av = ManuallyDrop::new(AppendVec::new(path, true, aligned_stored_size(data_len)));
1908 av.append_account_test(&account).unwrap();
1909 av.flush().unwrap();
1910 }
1911
1912 let truncated_accounts_len: usize = PAGE_SIZE as usize;
1914 let av = AppendVec::new_from_file_unchecked(path, truncated_accounts_len, storage_access)
1915 .unwrap();
1916 let account = av.get_account_shared_data(0);
1917 assert!(account.is_none()); let result = av.get_stored_account_meta_callback(0, |_| true);
1920 assert!(result.is_none()); }
1922
1923 #[test_case(StorageAccess::Mmap)]
1924 #[test_case(StorageAccess::File)]
1925 fn test_get_account_sizes(storage_access: StorageAccess) {
1926 const NUM_ACCOUNTS: usize = 37;
1927 let pubkeys: Vec<_> = std::iter::repeat_with(Pubkey::new_unique)
1928 .take(NUM_ACCOUNTS)
1929 .collect();
1930
1931 let mut rng = thread_rng();
1932 let mut accounts = Vec::with_capacity(pubkeys.len());
1933 let mut stored_sizes = Vec::with_capacity(pubkeys.len());
1934 for _ in &pubkeys {
1935 let lamports = rng.gen();
1936 let data_len = rng.gen_range(0..MAX_PERMITTED_DATA_LENGTH) as usize;
1937 let account = AccountSharedData::new(lamports, data_len, &Pubkey::default());
1938 accounts.push(account);
1939 stored_sizes.push(aligned_stored_size(data_len));
1940 }
1941 let accounts = accounts;
1942 let stored_sizes = stored_sizes;
1943 let total_stored_size = stored_sizes.iter().sum();
1944
1945 let temp_file = get_append_vec_path("test_get_account_sizes");
1946 let account_offsets = {
1947 let append_vec = AppendVec::new(&temp_file.path, true, total_stored_size);
1948 let append_vec = ManuallyDrop::new(append_vec);
1950 let slot = 77; let storable_accounts: Vec<_> = std::iter::zip(&pubkeys, &accounts).collect();
1952 let stored_accounts_info = append_vec
1953 .append_accounts(&(slot, storable_accounts.as_slice()), 0)
1954 .unwrap();
1955 append_vec.flush().unwrap();
1956 stored_accounts_info.offsets
1957 };
1958
1959 let (append_vec, _) =
1962 AppendVec::new_from_file(&temp_file.path, total_stored_size, storage_access).unwrap();
1963
1964 let account_sizes = append_vec.get_account_sizes(account_offsets.as_slice());
1965 assert_eq!(account_sizes, stored_sizes);
1966 }
1967
1968 fn test_scan_helper(
1973 storage_access: StorageAccess,
1974 modify_fn: impl Fn(&PathBuf, usize) -> usize,
1975 check_fn: impl Fn(&AppendVec, &[Pubkey], &[usize], &[AccountSharedData]),
1976 ) {
1977 const NUM_ACCOUNTS: usize = 37;
1978 let pubkeys: Vec<_> = std::iter::repeat_with(solana_pubkey::new_rand)
1979 .take(NUM_ACCOUNTS)
1980 .collect();
1981
1982 let mut rng = thread_rng();
1983 let mut accounts = Vec::with_capacity(pubkeys.len());
1984 let mut total_stored_size = 0;
1985 for _ in &pubkeys {
1986 let lamports = rng.gen();
1987 let data_len = rng.gen_range(0..MAX_PERMITTED_DATA_LENGTH) as usize;
1988 let account = AccountSharedData::new(lamports, data_len, &Pubkey::default());
1989 accounts.push(account);
1990 total_stored_size += aligned_stored_size(data_len);
1991 }
1992 let accounts = accounts;
1993 let total_stored_size = total_stored_size;
1994
1995 let temp_file = get_append_vec_path("test_scan");
1996 let account_offsets = {
1997 let append_vec =
1999 ManuallyDrop::new(AppendVec::new(&temp_file.path, true, total_stored_size));
2000 let slot = 42; let storable_accounts: Vec<_> = std::iter::zip(&pubkeys, &accounts).collect();
2002 let stored_accounts_info = append_vec
2003 .append_accounts(&(slot, storable_accounts.as_slice()), 0)
2004 .unwrap();
2005 append_vec.flush().unwrap();
2006 stored_accounts_info.offsets
2007 };
2008
2009 let total_stored_size = modify_fn(&temp_file.path, total_stored_size);
2010 let append_vec = ManuallyDrop::new(
2013 AppendVec::new_from_file_unchecked(&temp_file.path, total_stored_size, storage_access)
2014 .unwrap(),
2015 );
2016
2017 check_fn(&append_vec, &pubkeys, &account_offsets, &accounts);
2018 }
2019
2020 fn test_scan_pubkeys_helper(
2022 storage_access: StorageAccess,
2023 modify_fn: impl Fn(&PathBuf, usize) -> usize,
2024 ) {
2025 test_scan_helper(
2026 storage_access,
2027 modify_fn,
2028 |append_vec, pubkeys, _account_offsets, _accounts| {
2029 let mut i = 0;
2030 append_vec.scan_pubkeys(|pubkey| {
2031 assert_eq!(pubkey, pubkeys.get(i).unwrap());
2032 i += 1;
2033 });
2034 assert_eq!(i, pubkeys.len());
2035 },
2036 )
2037 }
2038
2039 #[test_case(StorageAccess::Mmap)]
2041 #[test_case(StorageAccess::File)]
2042 fn test_scan_pubkeys(storage_access: StorageAccess) {
2043 test_scan_pubkeys_helper(storage_access, |_, size| size);
2044 }
2045
2046 #[test_case(StorageAccess::Mmap)]
2048 #[test_case(StorageAccess::File)]
2049 fn test_scan_pubkeys_incomplete_data(storage_access: StorageAccess) {
2050 test_scan_pubkeys_helper(storage_access, |path, size| {
2051 let mut f = OpenOptions::new()
2054 .read(true)
2055 .append(true)
2056 .open(path)
2057 .unwrap();
2058 f.write_all(&[0xFF]).unwrap();
2059 size + 1
2060 });
2061 }
2062
2063 #[test_case(StorageAccess::Mmap)]
2065 #[test_case(StorageAccess::File)]
2066 fn test_scan_pubkeys_missing_account_data(storage_access: StorageAccess) {
2067 test_scan_pubkeys_helper(storage_access, |path, size| {
2068 let fake_stored_meta = StoredMeta {
2069 write_version_obsolete: 0,
2070 data_len: 100,
2071 pubkey: solana_pubkey::new_rand(),
2072 };
2073 let fake_account_meta = AccountMeta {
2074 lamports: 100,
2075 rent_epoch: 10,
2076 owner: solana_pubkey::new_rand(),
2077 executable: false,
2078 };
2079
2080 let stored_meta_slice: &[u8] = unsafe {
2081 std::slice::from_raw_parts(
2082 (&fake_stored_meta as *const StoredMeta) as *const u8,
2083 mem::size_of::<StoredMeta>(),
2084 )
2085 };
2086 let account_meta_slice: &[u8] = unsafe {
2087 std::slice::from_raw_parts(
2088 (&fake_account_meta as *const AccountMeta) as *const u8,
2089 mem::size_of::<AccountMeta>(),
2090 )
2091 };
2092
2093 let mut f = OpenOptions::new()
2094 .read(true)
2095 .append(true)
2096 .open(path)
2097 .unwrap();
2098
2099 f.write_all(stored_meta_slice).unwrap();
2100 f.write_all(account_meta_slice).unwrap();
2101
2102 size + mem::size_of::<StoredMeta>() + mem::size_of::<AccountMeta>()
2103 });
2104 }
2105
2106 fn test_scan_index_helper(
2108 storage_access: StorageAccess,
2109 modify_fn: impl Fn(&PathBuf, usize) -> usize,
2110 ) {
2111 test_scan_helper(
2112 storage_access,
2113 modify_fn,
2114 |append_vec, pubkeys, account_offsets, accounts| {
2115 let mut i = 0;
2116 append_vec.scan_index(|index_info| {
2117 let pubkey = pubkeys.get(i).unwrap();
2118 let account = accounts.get(i).unwrap();
2119 let offset = account_offsets.get(i).unwrap();
2120
2121 assert_eq!(
2122 index_info.stored_size_aligned,
2123 aligned_stored_size(account.data().len()),
2124 );
2125 assert_eq!(index_info.index_info.offset, *offset);
2126 assert_eq!(index_info.index_info.pubkey, *pubkey);
2127 assert_eq!(index_info.index_info.lamports, account.lamports());
2128 assert_eq!(index_info.index_info.rent_epoch, account.rent_epoch());
2129 assert_eq!(index_info.index_info.executable, account.executable());
2130 assert_eq!(index_info.index_info.data_len, account.data().len() as u64);
2131
2132 i += 1;
2133 });
2134 assert_eq!(i, accounts.len());
2135 },
2136 )
2137 }
2138
2139 #[test_case(StorageAccess::Mmap)]
2140 #[test_case(StorageAccess::File)]
2141 fn test_scan_index(storage_access: StorageAccess) {
2142 test_scan_index_helper(storage_access, |_, size| size);
2143 }
2144
2145 #[test_case(StorageAccess::Mmap)]
2147 #[test_case(StorageAccess::File)]
2148 fn test_scan_index_incomplete_data(storage_access: StorageAccess) {
2149 test_scan_index_helper(storage_access, |path, size| {
2150 let mut f = OpenOptions::new()
2153 .read(true)
2154 .append(true)
2155 .open(path)
2156 .unwrap();
2157 f.write_all(&[0xFF]).unwrap();
2158 size + 1
2159 });
2160 }
2161
2162 #[test_case(StorageAccess::Mmap)]
2164 #[test_case(StorageAccess::File)]
2165 fn test_scan_index_missing_account_data(storage_access: StorageAccess) {
2166 test_scan_index_helper(storage_access, |path, size| {
2167 let fake_stored_meta = StoredMeta {
2168 write_version_obsolete: 0,
2169 data_len: 100,
2170 pubkey: solana_pubkey::new_rand(),
2171 };
2172 let fake_account_meta = AccountMeta {
2173 lamports: 100,
2174 rent_epoch: 10,
2175 owner: solana_pubkey::new_rand(),
2176 executable: false,
2177 };
2178
2179 let stored_meta_slice: &[u8] = unsafe {
2180 std::slice::from_raw_parts(
2181 (&fake_stored_meta as *const StoredMeta) as *const u8,
2182 mem::size_of::<StoredMeta>(),
2183 )
2184 };
2185 let account_meta_slice: &[u8] = unsafe {
2186 std::slice::from_raw_parts(
2187 (&fake_account_meta as *const AccountMeta) as *const u8,
2188 mem::size_of::<AccountMeta>(),
2189 )
2190 };
2191
2192 let mut f = OpenOptions::new()
2193 .read(true)
2194 .append(true)
2195 .open(path)
2196 .unwrap();
2197
2198 f.write_all(stored_meta_slice).unwrap();
2199 f.write_all(account_meta_slice).unwrap();
2200
2201 size + mem::size_of::<StoredMeta>() + mem::size_of::<AccountMeta>()
2202 });
2203 }
2204}