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_sdk::{
24 account::{AccountSharedData, ReadableAccount, WritableAccount},
25 hash::Hash,
26 pubkey::Pubkey,
27 stake_history::Epoch,
28 system_instruction::MAX_PERMITTED_DATA_LENGTH,
29 },
30 std::{
31 convert::TryFrom,
32 fs::{remove_file, File, OpenOptions},
33 io::{Seek, SeekFrom, Write},
34 mem,
35 path::{Path, PathBuf},
36 ptr,
37 sync::{
38 atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
39 Mutex,
40 },
41 },
42 thiserror::Error,
43};
44
45pub mod test_utils;
46#[cfg(test)]
47use solana_sdk::account::accounts_equal;
48
49pub const STORE_META_OVERHEAD: usize = 136;
52
53const _: () = assert!(
55 STORE_META_OVERHEAD
56 == mem::size_of::<StoredMeta>()
57 + mem::size_of::<AccountMeta>()
58 + mem::size_of::<AccountHash>()
59);
60
61pub fn aligned_stored_size(data_len: usize) -> usize {
65 u64_align!(STORE_META_OVERHEAD + data_len)
66}
67
68pub const MAXIMUM_APPEND_VEC_FILE_SIZE: u64 = 16 * 1024 * 1024 * 1024; #[derive(Error, Debug)]
71pub enum AppendVecError {
73 #[error("too small file size {0} for AppendVec")]
74 FileSizeTooSmall(usize),
75
76 #[error("too large file size {0} for AppendVec")]
77 FileSizeTooLarge(usize),
78
79 #[error("incorrect layout/length/data in the appendvec at path {}", .0.display())]
80 IncorrectLayout(PathBuf),
81
82 #[error("offset ({0}) is larger than file size ({1})")]
83 OffsetOutOfBounds(usize, usize),
84}
85
86#[derive(Debug, Copy, Clone)]
89pub(crate) struct ValidSlice<'a>(&'a [u8]);
90
91impl<'a> ValidSlice<'a> {
92 pub(crate) fn new(data: &'a [u8]) -> Self {
93 Self(data)
94 }
95
96 pub(crate) fn len(&self) -> usize {
97 self.0.len()
98 }
99
100 #[cfg(all(unix, test))]
101 pub(crate) fn slice(&self) -> &[u8] {
102 self.0
103 }
104}
105
106#[derive(PartialEq, Eq, Debug)]
109pub struct AppendVecStoredAccountMeta<'append_vec> {
110 pub meta: &'append_vec StoredMeta,
111 pub account_meta: &'append_vec AccountMeta,
113 pub(crate) data: &'append_vec [u8],
114 pub(crate) offset: usize,
115 pub(crate) stored_size: usize,
116 pub(crate) hash: &'append_vec AccountHash,
117}
118
119impl<'append_vec> AppendVecStoredAccountMeta<'append_vec> {
120 pub fn pubkey(&self) -> &'append_vec Pubkey {
121 &self.meta.pubkey
122 }
123
124 pub fn hash(&self) -> &'append_vec AccountHash {
125 self.hash
126 }
127
128 pub fn stored_size(&self) -> usize {
129 self.stored_size
130 }
131
132 pub fn offset(&self) -> usize {
133 self.offset
134 }
135
136 pub fn data(&self) -> &'append_vec [u8] {
137 self.data
138 }
139
140 pub fn data_len(&self) -> u64 {
141 self.meta.data_len
142 }
143
144 pub fn meta(&self) -> &StoredMeta {
145 self.meta
146 }
147
148 pub(crate) fn sanitize(&self) -> bool {
149 self.sanitize_executable() && self.sanitize_lamports()
150 }
151
152 fn sanitize_executable(&self) -> bool {
153 self.ref_executable_byte() & !1 == 0
155 }
156
157 fn sanitize_lamports(&self) -> bool {
158 self.account_meta.lamports != 0
160 || self.to_account_shared_data() == AccountSharedData::default()
161 }
162
163 fn ref_executable_byte(&self) -> &u8 {
164 let executable_bool: &bool = &self.account_meta.executable;
167 let executable_bool_ptr = ptr::from_ref(executable_bool);
168 let executable_byte: &u8 = unsafe { &*(executable_bool_ptr.cast()) };
170 executable_byte
171 }
172}
173
174impl<'append_vec> ReadableAccount for AppendVecStoredAccountMeta<'append_vec> {
175 fn lamports(&self) -> u64 {
176 self.account_meta.lamports
177 }
178 fn data(&self) -> &'append_vec [u8] {
179 self.data()
180 }
181 fn owner(&self) -> &'append_vec Pubkey {
182 &self.account_meta.owner
183 }
184 fn executable(&self) -> bool {
185 self.account_meta.executable
186 }
187 fn rent_epoch(&self) -> Epoch {
188 self.account_meta.rent_epoch
189 }
190}
191
192pub(crate) struct IndexInfo {
194 pub stored_size_aligned: usize,
197 pub index_info: IndexInfoInner,
199}
200
201pub(crate) struct IndexInfoInner {
203 pub offset: usize,
205 pub pubkey: Pubkey,
206 pub lamports: u64,
207 pub rent_epoch: Epoch,
208 pub executable: bool,
209 pub data_len: u64,
210}
211
212#[derive(Debug)]
214struct AccountOffsets {
215 offset_to_end_of_data: usize,
217 next_account_offset: usize,
219 stored_size_aligned: usize,
221}
222
223#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
224#[derive(Debug)]
225enum AppendVecFileBacking {
226 Mmap(Mmap),
228 #[cfg_attr(not(unix), allow(dead_code))]
230 File(File),
231}
232
233#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
234#[derive(Debug)]
235struct Mmap {
236 mmap: MmapMut,
237 is_dirty: AtomicBool,
243}
244
245#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
250#[derive(Debug)]
251pub struct AppendVec {
252 path: PathBuf,
254
255 backing: AppendVecFileBacking,
257
258 append_lock: Mutex<()>,
260
261 current_len: AtomicUsize,
263
264 file_size: u64,
266
267 remove_file_on_drop: AtomicBool,
269}
270
271const PAGE_SIZE: u64 = 4 * 1024;
272const SCAN_BUFFER_SIZE: usize =
274 page_align((STORE_META_OVERHEAD as u64 + MAX_PERMITTED_DATA_LENGTH) * 3) as usize;
275const fn page_align(size: u64) -> u64 {
276 (size + (PAGE_SIZE - 1)) & !(PAGE_SIZE - 1)
277}
278
279#[cfg_attr(feature = "dev-context-only-utils", qualifier_attr::qualifiers(pub))]
295const SCAN_BUFFER_SIZE_WITHOUT_DATA: usize = 1 << 16;
296
297lazy_static! {
298 pub static ref APPEND_VEC_MMAPPED_FILES_OPEN: AtomicU64 = AtomicU64::default();
299 pub static ref APPEND_VEC_MMAPPED_FILES_DIRTY: AtomicU64 = AtomicU64::default();
300 pub static ref APPEND_VEC_OPEN_AS_FILE_IO: AtomicU64 = AtomicU64::default();
301}
302
303impl Drop for AppendVec {
304 fn drop(&mut self) {
305 APPEND_VEC_MMAPPED_FILES_OPEN.fetch_sub(1, Ordering::Relaxed);
306 match &self.backing {
307 AppendVecFileBacking::Mmap(mmap_only) => {
308 if mmap_only.is_dirty.load(Ordering::Acquire) {
309 APPEND_VEC_MMAPPED_FILES_DIRTY.fetch_sub(1, Ordering::Relaxed);
310 }
311 }
312 AppendVecFileBacking::File(_) => {
313 APPEND_VEC_OPEN_AS_FILE_IO.fetch_sub(1, Ordering::Relaxed);
314 }
315 }
316 if self.remove_file_on_drop.load(Ordering::Acquire) {
317 if let Err(_err) = remove_file(&self.path) {
320 inc_new_counter_info!("append_vec_drop_fail", 1);
325 }
326 }
327 }
328}
329
330impl AppendVec {
331 pub fn new(file: impl Into<PathBuf>, create: bool, size: usize) -> Self {
332 let file = file.into();
333 let initial_len = 0;
334 AppendVec::sanitize_len_and_size(initial_len, size).unwrap();
335
336 if create {
337 let _ignored = remove_file(&file);
338 }
339
340 let mut data = OpenOptions::new()
341 .read(true)
342 .write(true)
343 .create(create)
344 .open(&file)
345 .map_err(|e| {
346 panic!(
347 "Unable to {} data file {} in current dir({:?}): {:?}",
348 if create { "create" } else { "open" },
349 file.display(),
350 std::env::current_dir(),
351 e
352 );
353 })
354 .unwrap();
355
356 data.seek(SeekFrom::Start((size - 1) as u64)).unwrap();
360 data.write_all(&[0]).unwrap();
361 data.rewind().unwrap();
362 data.flush().unwrap();
363
364 let mmap = unsafe { MmapMut::map_mut(&data) };
366 let mmap = mmap.unwrap_or_else(|e| {
367 error!(
368 "Failed to map the data file (size: {}): {}.\n
369 Please increase sysctl vm.max_map_count or equivalent for your platform.",
370 size, e
371 );
372 std::process::exit(1);
373 });
374 APPEND_VEC_MMAPPED_FILES_OPEN.fetch_add(1, Ordering::Relaxed);
375
376 AppendVec {
377 path: file,
378 backing: AppendVecFileBacking::Mmap(Mmap {
379 mmap,
380 is_dirty: AtomicBool::new(false),
381 }),
382 append_lock: Mutex::new(()),
385 current_len: AtomicUsize::new(initial_len),
386 file_size: size as u64,
387 remove_file_on_drop: AtomicBool::new(true),
388 }
389 }
390
391 fn sanitize_len_and_size(current_len: usize, file_size: usize) -> Result<()> {
392 if file_size == 0 {
393 Err(AccountsFileError::AppendVecError(
394 AppendVecError::FileSizeTooSmall(file_size),
395 ))
396 } else if usize::try_from(MAXIMUM_APPEND_VEC_FILE_SIZE)
397 .map(|max| file_size > max)
398 .unwrap_or(true)
399 {
400 Err(AccountsFileError::AppendVecError(
401 AppendVecError::FileSizeTooLarge(file_size),
402 ))
403 } else if current_len > file_size {
404 Err(AccountsFileError::AppendVecError(
405 AppendVecError::OffsetOutOfBounds(current_len, file_size),
406 ))
407 } else {
408 Ok(())
409 }
410 }
411
412 pub fn flush(&self) -> Result<()> {
413 match &self.backing {
414 AppendVecFileBacking::Mmap(mmap_only) => {
415 if mmap_only.is_dirty.load(Ordering::Acquire) {
417 mmap_only.mmap.flush()?;
418 mmap_only.is_dirty.store(false, Ordering::Release);
419 APPEND_VEC_MMAPPED_FILES_DIRTY.fetch_sub(1, Ordering::Relaxed);
420 }
421 Ok(())
422 }
423 AppendVecFileBacking::File(_file) => Ok(()),
425 }
426 }
427
428 pub fn reset(&self) {
429 let _lock = self.append_lock.lock().unwrap();
432 self.current_len.store(0, Ordering::Release);
433 }
434
435 #[cfg_attr(not(unix), allow(dead_code))]
438 pub(crate) fn reopen_as_readonly(&self) -> Option<Self> {
439 #[cfg(not(unix))]
440 return None;
442
443 #[cfg(unix)]
444 match &self.backing {
445 AppendVecFileBacking::File(_file) => None,
447 AppendVecFileBacking::Mmap(_mmap) => {
448 self.flush().expect("flush must succeed");
450 self.remove_file_on_drop.store(false, Ordering::Release);
452
453 AppendVec::new_from_file_unchecked(
455 self.path.clone(),
456 self.len(),
457 StorageAccess::File,
458 )
459 .ok()
460 }
461 }
462 }
463
464 pub fn remaining_bytes(&self) -> u64 {
466 self.capacity()
467 .saturating_sub(u64_align!(self.len()) as u64)
468 }
469
470 pub fn len(&self) -> usize {
471 self.current_len.load(Ordering::Acquire)
472 }
473
474 pub fn is_empty(&self) -> bool {
475 self.len() == 0
476 }
477
478 pub fn capacity(&self) -> u64 {
479 self.file_size
480 }
481
482 pub fn new_from_file(
483 path: impl Into<PathBuf>,
484 current_len: usize,
485 storage_access: StorageAccess,
486 ) -> Result<(Self, usize)> {
487 let path = path.into();
488 let new = Self::new_from_file_unchecked(path, current_len, storage_access)?;
489
490 let (sanitized, num_accounts) = new.sanitize_layout_and_length();
491 if !sanitized {
492 return Err(AccountsFileError::AppendVecError(
493 AppendVecError::IncorrectLayout(new.path.clone()),
494 ));
495 }
496
497 Ok((new, num_accounts))
498 }
499
500 #[cfg_attr(not(unix), allow(unused_variables))]
502 pub fn new_from_file_unchecked(
503 path: impl Into<PathBuf>,
504 current_len: usize,
505 storage_access: StorageAccess,
506 ) -> Result<Self> {
507 let path = path.into();
508 let file_size = std::fs::metadata(&path)?.len();
509 Self::sanitize_len_and_size(current_len, file_size as usize)?;
510
511 let data = OpenOptions::new()
512 .read(true)
513 .write(true)
514 .create(false)
515 .open(&path)?;
516
517 #[cfg(unix)]
518 if storage_access == StorageAccess::File {
520 APPEND_VEC_MMAPPED_FILES_OPEN.fetch_add(1, Ordering::Relaxed);
521 APPEND_VEC_OPEN_AS_FILE_IO.fetch_add(1, Ordering::Relaxed);
522
523 return Ok(AppendVec {
524 path,
525 backing: AppendVecFileBacking::File(data),
526 append_lock: Mutex::new(()),
527 current_len: AtomicUsize::new(current_len),
528 file_size,
529 remove_file_on_drop: AtomicBool::new(true),
530 });
531 }
532
533 let mmap = unsafe {
534 let result = MmapMut::map_mut(&data);
535 if result.is_err() {
536 info!("memory map error: {:?}. This may be because vm.max_map_count is not set correctly.", result);
538 }
539 result?
540 };
541 APPEND_VEC_MMAPPED_FILES_OPEN.fetch_add(1, Ordering::Relaxed);
542
543 Ok(AppendVec {
544 path,
545 backing: AppendVecFileBacking::Mmap(Mmap {
546 mmap,
547 is_dirty: AtomicBool::new(false),
548 }),
549 append_lock: Mutex::new(()),
550 current_len: AtomicUsize::new(current_len),
551 file_size,
552 remove_file_on_drop: AtomicBool::new(true),
553 })
554 }
555
556 #[cfg(feature = "dev-context-only-utils")]
558 pub fn new_for_store_tool(path: impl Into<PathBuf>) -> Result<Self> {
559 let path = path.into();
560 let file_size = std::fs::metadata(&path)?.len();
561 Self::new_from_file_unchecked(path, file_size as usize, StorageAccess::default())
562 }
563
564 fn sanitize_layout_and_length(&self) -> (bool, usize) {
565 let mut num_accounts = 0;
571 let mut matches = true;
572 let mut last_offset = 0;
573 self.scan_accounts(|account| {
574 if !matches || !account.sanitize() {
575 matches = false;
576 return;
577 }
578 last_offset = account.offset() + account.stored_size();
579 num_accounts += 1;
580 });
581 if !matches {
582 return (false, num_accounts);
583 }
584 let aligned_current_len = u64_align!(self.current_len.load(Ordering::Acquire));
585
586 (last_offset == aligned_current_len, num_accounts)
587 }
588
589 fn get_slice(slice: ValidSlice, offset: usize, size: usize) -> Option<(&[u8], usize)> {
594 let end = offset.wrapping_add(size);
597 slice
598 .0
599 .get(offset..end)
600 .map(|subslice| (subslice, u64_align!(end)))
601 }
602
603 fn append_ptr(&self, offset: &mut usize, src: *const u8, len: usize) {
606 let pos = u64_align!(*offset);
607 match &self.backing {
608 AppendVecFileBacking::Mmap(mmap_only) => {
609 let data = &mmap_only.mmap[pos..(pos + len)];
610 unsafe {
614 let dst = data.as_ptr() as *mut _;
615 ptr::copy(src, dst, len);
616 };
617 *offset = pos + len;
618 }
619 AppendVecFileBacking::File(_file) => {
620 unimplemented!();
621 }
622 }
623 }
624
625 fn append_ptrs_locked(&self, offset: &mut usize, vals: &[(*const u8, usize)]) -> Option<usize> {
630 let mut end = *offset;
631 for val in vals {
632 end = u64_align!(end);
633 end += val.1;
634 }
635
636 if (self.file_size as usize) < end {
637 return None;
638 }
639
640 let pos = u64_align!(*offset);
641 for val in vals {
642 self.append_ptr(offset, val.0, val.1)
643 }
644 self.current_len.store(*offset, Ordering::Release);
645 Some(pos)
646 }
647
648 fn get_type<T>(slice: ValidSlice, offset: usize) -> Option<(&T, usize)> {
652 let (data, next) = Self::get_slice(slice, offset, mem::size_of::<T>())?;
653 let ptr = data.as_ptr().cast();
654 Some((unsafe { &*ptr }, next))
657 }
658
659 fn get_valid_slice_from_mmap<'a>(&self, mmap: &'a MmapMut) -> ValidSlice<'a> {
663 ValidSlice(&mmap[..self.len()])
664 }
665
666 pub fn get_stored_account_meta_callback<Ret>(
669 &self,
670 offset: usize,
671 mut callback: impl for<'local> FnMut(StoredAccountMeta<'local>) -> Ret,
672 ) -> Option<Ret> {
673 match &self.backing {
674 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
675 let slice = self.get_valid_slice_from_mmap(mmap);
676 let (meta, next): (&StoredMeta, _) = Self::get_type(slice, offset)?;
677 let (account_meta, next): (&AccountMeta, _) = Self::get_type(slice, next)?;
678 let (hash, next): (&AccountHash, _) = Self::get_type(slice, next)?;
679 let (data, next) = Self::get_slice(slice, next, meta.data_len as usize)?;
680 let stored_size = next - offset;
681 Some(callback(StoredAccountMeta::AppendVec(
682 AppendVecStoredAccountMeta {
683 meta,
684 account_meta,
685 data,
686 offset,
687 stored_size,
688 hash,
689 },
690 )))
691 }
692 AppendVecFileBacking::File(file) => {
693 let mut buf = [0u8; PAGE_SIZE as usize];
695 let bytes_read = read_into_buffer(file, self.len(), offset, &mut buf).ok()?;
696 let valid_bytes = ValidSlice(&buf[..bytes_read]);
697 let (meta, next): (&StoredMeta, _) = Self::get_type(valid_bytes, 0)?;
698 let (account_meta, next): (&AccountMeta, _) = Self::get_type(valid_bytes, next)?;
699 let (hash, next): (&AccountHash, _) = Self::get_type(valid_bytes, next)?;
700 let data_len = meta.data_len;
701 let remaining_bytes_for_data = bytes_read - next;
702 Some(if remaining_bytes_for_data >= data_len as usize {
703 let (data, next) = Self::get_slice(valid_bytes, next, meta.data_len as usize)?;
705 let stored_size = next;
706 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
707 meta,
708 account_meta,
709 data,
710 offset,
711 stored_size,
712 hash,
713 });
714 callback(account)
715 } else {
716 assert!(data_len <= MAX_PERMITTED_DATA_LENGTH, "{data_len}");
718 let mut data = vec![0u8; data_len as usize];
719 let bytes_read =
722 read_into_buffer(file, self.len(), offset + next, &mut data).ok()?;
723 if bytes_read < data_len as usize {
724 return None;
726 }
727 let stored_size = aligned_stored_size(data_len as usize);
728 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
729 meta,
730 account_meta,
731 data: &data[..],
732 offset,
733 stored_size,
734 hash,
735 });
736 callback(account)
737 })
738 }
739 }
740 }
741
742 pub(crate) fn get_account_shared_data(&self, offset: usize) -> Option<AccountSharedData> {
746 match &self.backing {
747 AppendVecFileBacking::Mmap(_) => self
748 .get_stored_account_meta_callback(offset, |account| {
749 account.to_account_shared_data()
750 }),
751 AppendVecFileBacking::File(file) => {
752 let mut buf = [0u8; PAGE_SIZE as usize];
753 let bytes_read = read_into_buffer(file, self.len(), offset, &mut buf).ok()?;
754 let valid_bytes = ValidSlice(&buf[..bytes_read]);
755 let (meta, next): (&StoredMeta, _) = Self::get_type(valid_bytes, 0)?;
756 let (account_meta, next): (&AccountMeta, _) = Self::get_type(valid_bytes, next)?;
757 let (hash, next): (&AccountHash, _) = Self::get_type(valid_bytes, next)?;
758 let data_len = meta.data_len;
759 let remaining_bytes_for_data = bytes_read - next;
760 Some(if remaining_bytes_for_data >= data_len as usize {
761 let (data, next) = Self::get_slice(valid_bytes, next, meta.data_len as usize)?;
763 let stored_size = next;
764 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
765 meta,
766 account_meta,
767 data,
768 offset,
769 stored_size,
770 hash,
771 });
772 account.to_account_shared_data()
774 } else {
775 assert!(data_len <= MAX_PERMITTED_DATA_LENGTH, "{data_len}");
777 let mut data = vec![0u8; data_len as usize];
778 let bytes_read =
780 read_into_buffer(file, self.len(), offset + next, &mut data).ok()?;
781 if bytes_read < data_len as usize {
782 return None;
784 }
785 AccountSharedData::create(
786 account_meta.lamports,
787 data,
788 account_meta.owner,
789 account_meta.executable,
790 account_meta.rent_epoch,
791 )
792 })
793 }
794 }
795 }
796
797 pub fn account_matches_owners(
802 &self,
803 offset: usize,
804 owners: &[Pubkey],
805 ) -> std::result::Result<usize, MatchAccountOwnerError> {
806 self.get_stored_account_meta_callback(offset, |stored_account_meta| {
807 if stored_account_meta.lamports() == 0 {
808 Err(MatchAccountOwnerError::NoMatch)
809 } else {
810 owners
811 .iter()
812 .position(|entry| stored_account_meta.owner() == entry)
813 .ok_or(MatchAccountOwnerError::NoMatch)
814 }
815 })
816 .unwrap_or(Err(MatchAccountOwnerError::UnableToLoad))
817 }
818
819 #[cfg(test)]
820 pub fn get_account_test(
821 &self,
822 offset: usize,
823 ) -> Option<(StoredMeta, solana_sdk::account::AccountSharedData)> {
824 let sizes = self.get_account_sizes(&[offset]);
825 let result = self.get_stored_account_meta_callback(offset, |r_callback| {
826 let r2 = self.get_account_shared_data(offset);
827 assert!(accounts_equal(&r_callback, r2.as_ref().unwrap()));
828 assert_eq!(sizes, vec![r_callback.stored_size()]);
829 let meta = r_callback.meta().clone();
830 Some((meta, r_callback.to_account_shared_data()))
831 });
832 if result.is_none() {
833 assert!(self
834 .get_stored_account_meta_callback(offset, |_| {})
835 .is_none());
836 assert!(self.get_account_shared_data(offset).is_none());
837 assert!(sizes.is_empty());
839 }
840 result.flatten()
841 }
842
843 pub fn path(&self) -> &Path {
845 self.path.as_path()
846 }
847
848 fn next_account_offset(start_offset: usize, stored_meta: &StoredMeta) -> AccountOffsets {
855 let stored_size_unaligned = STORE_META_OVERHEAD
856 .checked_add(stored_meta.data_len as usize)
857 .expect("stored size cannot overflow");
858 let stored_size_aligned = u64_align!(stored_size_unaligned);
859 let offset_to_end_of_data = start_offset + stored_size_unaligned;
860 let next_account_offset = start_offset + stored_size_aligned;
861
862 AccountOffsets {
863 next_account_offset,
864 offset_to_end_of_data,
865 stored_size_aligned,
866 }
867 }
868
869 pub(crate) fn scan_index(&self, mut callback: impl FnMut(IndexInfo)) {
872 let self_len = self.len();
874 match &self.backing {
875 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
876 let mut offset = 0;
877 let slice = self.get_valid_slice_from_mmap(mmap);
878 loop {
879 let Some((stored_meta, next)) = Self::get_type::<StoredMeta>(slice, offset)
880 else {
881 break;
883 };
884 let Some((account_meta, _)) = Self::get_type::<AccountMeta>(slice, next) else {
885 break;
887 };
888 if account_meta.lamports == 0 && stored_meta.pubkey == Pubkey::default() {
889 break;
891 }
892 let next = Self::next_account_offset(offset, stored_meta);
893 if next.offset_to_end_of_data > self_len {
894 break;
896 }
897 callback(IndexInfo {
898 index_info: {
899 IndexInfoInner {
900 pubkey: stored_meta.pubkey,
901 lamports: account_meta.lamports,
902 offset,
903 data_len: stored_meta.data_len,
904 executable: account_meta.executable,
905 rent_epoch: account_meta.rent_epoch,
906 }
907 },
908 stored_size_aligned: next.stored_size_aligned,
909 });
910 offset = next.next_account_offset;
911 }
912 }
913 AppendVecFileBacking::File(file) => {
914 let buffer_size = std::cmp::min(SCAN_BUFFER_SIZE, self_len);
915 let mut reader =
916 BufferedReader::new(buffer_size, self_len, file, STORE_META_OVERHEAD);
917 while reader.read().ok() == Some(BufferedReaderStatus::Success) {
918 let (offset, bytes) = reader.get_offset_and_data();
919 let (stored_meta, next) = Self::get_type::<StoredMeta>(bytes, 0).unwrap();
920 let (account_meta, _) = Self::get_type::<AccountMeta>(bytes, next).unwrap();
921 if account_meta.lamports == 0 && stored_meta.pubkey == Pubkey::default() {
922 break;
924 }
925 let next = Self::next_account_offset(offset, stored_meta);
926 if next.offset_to_end_of_data > self_len {
927 break;
929 }
930 callback(IndexInfo {
931 index_info: {
932 IndexInfoInner {
933 pubkey: stored_meta.pubkey,
934 lamports: account_meta.lamports,
935 offset,
936 data_len: stored_meta.data_len,
937 executable: account_meta.executable,
938 rent_epoch: account_meta.rent_epoch,
939 }
940 },
941 stored_size_aligned: next.stored_size_aligned,
942 });
943 reader.advance_offset(next.stored_size_aligned);
944 }
945 }
946 }
947 }
948
949 #[allow(clippy::blocks_in_conditions)]
951 pub fn scan_accounts(&self, mut callback: impl for<'local> FnMut(StoredAccountMeta<'local>)) {
952 match &self.backing {
953 AppendVecFileBacking::Mmap(_mmap) => {
954 let mut offset = 0;
955 while self
956 .get_stored_account_meta_callback(offset, |account| {
957 offset += account.stored_size();
958 if account.is_zero_lamport() && account.pubkey() == &Pubkey::default() {
959 return false;
961 }
962
963 callback(account);
964 true
965 })
966 .unwrap_or_default()
967 {}
968 }
969 AppendVecFileBacking::File(file) => {
970 let mut reader =
971 BufferedReader::new(SCAN_BUFFER_SIZE, self.len(), file, STORE_META_OVERHEAD);
972 while reader.read().ok() == Some(BufferedReaderStatus::Success) {
973 let (offset, bytes_subset) = reader.get_offset_and_data();
974 let (meta, next): (&StoredMeta, _) = Self::get_type(bytes_subset, 0).unwrap();
975 let (account_meta, next): (&AccountMeta, _) =
976 Self::get_type(bytes_subset, next).unwrap();
977 let (hash, next): (&AccountHash, _) =
978 Self::get_type(bytes_subset, next).unwrap();
979 let data_len = meta.data_len;
980 if bytes_subset.len() - next >= data_len as usize {
981 let data = &bytes_subset.0[next..(next + data_len as usize)];
983 let stored_size = u64_align!(next + (data_len as usize));
984 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
985 meta,
986 account_meta,
987 data,
988 offset,
989 stored_size,
990 hash,
991 });
992 callback(account);
993 reader.advance_offset(stored_size);
994 } else {
995 reader.set_required_data_len(
997 STORE_META_OVERHEAD.saturating_add(data_len as usize),
998 )
999 }
1000 }
1001 }
1002 }
1003 }
1004
1005 pub(crate) fn get_account_sizes(&self, sorted_offsets: &[usize]) -> Vec<usize> {
1007 let self_len = self.len();
1009 let mut account_sizes = Vec::with_capacity(sorted_offsets.len());
1010 match &self.backing {
1011 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
1012 let slice = self.get_valid_slice_from_mmap(mmap);
1013 for &offset in sorted_offsets {
1014 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(slice, offset) else {
1015 break;
1016 };
1017 let next = Self::next_account_offset(offset, stored_meta);
1018 if next.offset_to_end_of_data > self_len {
1019 break;
1021 }
1022 account_sizes.push(next.stored_size_aligned);
1023 }
1024 }
1025 AppendVecFileBacking::File(file) => {
1026 let mut buffer = [0u8; mem::size_of::<StoredMeta>()];
1027 for &offset in sorted_offsets {
1028 let Some(bytes_read) =
1029 read_into_buffer(file, self_len, offset, &mut buffer).ok()
1030 else {
1031 break;
1032 };
1033 let bytes = ValidSlice(&buffer[..bytes_read]);
1034 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(bytes, 0) else {
1035 break;
1036 };
1037 let next = Self::next_account_offset(offset, stored_meta);
1038 if next.offset_to_end_of_data > self_len {
1039 break;
1041 }
1042 account_sizes.push(next.stored_size_aligned);
1043 }
1044 }
1045 }
1046 account_sizes
1047 }
1048
1049 pub fn scan_pubkeys(&self, mut callback: impl FnMut(&Pubkey)) {
1055 let self_len = self.len();
1057 match &self.backing {
1058 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
1059 let mut offset = 0;
1060 let slice = self.get_valid_slice_from_mmap(mmap);
1061 loop {
1062 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(slice, offset) else {
1063 break;
1065 };
1066 let next = Self::next_account_offset(offset, stored_meta);
1067 if next.offset_to_end_of_data > self_len {
1068 break;
1070 }
1071 callback(&stored_meta.pubkey);
1072 offset = next.next_account_offset;
1073 }
1074 }
1075 AppendVecFileBacking::File(file) => {
1076 let buffer_size = std::cmp::min(SCAN_BUFFER_SIZE_WITHOUT_DATA, self_len);
1077 let mut reader =
1078 BufferedReader::new(buffer_size, self_len, file, STORE_META_OVERHEAD);
1079 while reader.read().ok() == Some(BufferedReaderStatus::Success) {
1080 let (offset, bytes) = reader.get_offset_and_data();
1081 let (stored_meta, _) = Self::get_type::<StoredMeta>(bytes, 0).unwrap();
1082 let next = Self::next_account_offset(offset, stored_meta);
1083 if next.offset_to_end_of_data > self.len() {
1084 break;
1086 }
1087 callback(&stored_meta.pubkey);
1088 reader.advance_offset(next.stored_size_aligned);
1090 }
1091 }
1092 }
1093 }
1094
1095 pub fn append_accounts<'a>(
1103 &self,
1104 accounts: &impl StorableAccounts<'a>,
1105 skip: usize,
1106 ) -> Option<StoredAccountsInfo> {
1107 let _lock = self.append_lock.lock().unwrap();
1108 let default_hash = Hash::default();
1109 let mut offset = self.len();
1110 let len = accounts.len();
1111 let offsets_len = len - skip + 1;
1115 let mut offsets = Vec::with_capacity(offsets_len);
1116 let mut stop = false;
1117 for i in skip..len {
1118 if stop {
1119 break;
1120 }
1121 accounts.account_default_if_zero_lamport(i, |account| {
1122 let account_meta = AccountMeta {
1123 lamports: account.lamports(),
1124 owner: *account.owner(),
1125 rent_epoch: account.rent_epoch(),
1126 executable: account.executable(),
1127 };
1128
1129 let stored_meta = StoredMeta {
1130 pubkey: *account.pubkey(),
1131 data_len: account.data().len() as u64,
1132 write_version_obsolete: 0,
1133 };
1134 let stored_meta_ptr = ptr::from_ref(&stored_meta).cast();
1135 let account_meta_ptr = ptr::from_ref(&account_meta).cast();
1136 let hash_ptr = bytemuck::bytes_of(&default_hash).as_ptr();
1137 let data_ptr = account.data().as_ptr();
1138 let ptrs = [
1139 (stored_meta_ptr, mem::size_of::<StoredMeta>()),
1140 (account_meta_ptr, mem::size_of::<AccountMeta>()),
1141 (hash_ptr, mem::size_of::<AccountHash>()),
1142 (data_ptr, stored_meta.data_len as usize),
1143 ];
1144 if let Some(start_offset) = self.append_ptrs_locked(&mut offset, &ptrs) {
1145 offsets.push(start_offset)
1146 } else {
1147 stop = true;
1148 }
1149 });
1150 }
1151
1152 match &self.backing {
1153 AppendVecFileBacking::Mmap(mmap_only) => {
1154 if !offsets.is_empty() {
1155 if !mmap_only.is_dirty.load(Ordering::Acquire) {
1161 mmap_only.is_dirty.store(true, Ordering::Release);
1162 APPEND_VEC_MMAPPED_FILES_DIRTY.fetch_add(1, Ordering::Relaxed);
1163 }
1164 }
1165 }
1166 AppendVecFileBacking::File(_) => {}
1167 }
1168
1169 (!offsets.is_empty()).then(|| {
1170 offsets.push(u64_align!(offset));
1175 let size = offsets.windows(2).map(|offset| offset[1] - offset[0]).sum();
1176 offsets.pop();
1177
1178 StoredAccountsInfo { offsets, size }
1179 })
1180 }
1181
1182 pub(crate) fn can_append(&self) -> bool {
1183 match &self.backing {
1184 AppendVecFileBacking::File(_file) => false,
1185 AppendVecFileBacking::Mmap(_mmap) => true,
1186 }
1187 }
1188
1189 pub(crate) fn internals_for_archive(&self) -> InternalsForArchive {
1191 match &self.backing {
1192 AppendVecFileBacking::File(_file) => InternalsForArchive::FileIo(self.path()),
1193 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => InternalsForArchive::Mmap(mmap),
1195 }
1196 }
1197}
1198
1199#[cfg(test)]
1200pub mod tests {
1201 use {
1202 super::{test_utils::*, *},
1203 assert_matches::assert_matches,
1204 memoffset::offset_of,
1205 rand::{thread_rng, Rng},
1206 solana_sdk::{
1207 account::{Account, AccountSharedData},
1208 clock::Slot,
1209 },
1210 std::{mem::ManuallyDrop, time::Instant},
1211 test_case::test_case,
1212 };
1213
1214 impl AppendVec {
1215 pub(crate) fn set_current_len_for_tests(&self, len: usize) {
1216 self.current_len.store(len, Ordering::Release);
1217 }
1218
1219 fn append_account_test(&self, data: &(StoredMeta, AccountSharedData)) -> Option<usize> {
1220 let slot_ignored = Slot::MAX;
1221 let accounts = [(&data.0.pubkey, &data.1)];
1222 let slice = &accounts[..];
1223 let storable_accounts = (slot_ignored, slice);
1224
1225 self.append_accounts(&storable_accounts, 0)
1226 .map(|res| res.offsets[0])
1227 }
1228 }
1229
1230 impl StoredAccountMeta<'_> {
1231 pub(crate) fn ref_executable_byte(&self) -> &u8 {
1232 match self {
1233 Self::AppendVec(av) => av.ref_executable_byte(),
1234 Self::Hot(_) => unreachable!(),
1236 }
1237 }
1238 }
1239
1240 impl AppendVecStoredAccountMeta<'_> {
1241 fn set_data_len_unsafe(&self, new_data_len: u64) {
1242 unsafe {
1244 #[allow(invalid_reference_casting)]
1245 ptr::write(
1246 std::mem::transmute::<*const u64, *mut u64>(&self.meta.data_len),
1247 new_data_len,
1248 );
1249 }
1250 }
1251
1252 fn get_executable_byte(&self) -> u8 {
1253 let executable_bool: bool = self.executable();
1254 let executable_byte: u8 = unsafe { std::mem::transmute::<bool, u8>(executable_bool) };
1256 executable_byte
1257 }
1258
1259 fn set_executable_as_byte(&self, new_executable_byte: u8) {
1260 unsafe {
1262 #[allow(invalid_reference_casting)]
1263 ptr::write(
1264 std::mem::transmute::<*const bool, *mut u8>(&self.account_meta.executable),
1265 new_executable_byte,
1266 );
1267 }
1268 }
1269 }
1270
1271 static_assertions::assert_eq_align!(u64, StoredMeta, AccountMeta);
1273
1274 #[test]
1275 fn test_account_meta_default() {
1276 let def1 = AccountMeta::default();
1277 let def2 = AccountMeta::from(&Account::default());
1278 assert_eq!(&def1, &def2);
1279 let def2 = AccountMeta::from(&AccountSharedData::default());
1280 assert_eq!(&def1, &def2);
1281 let def2 = AccountMeta::from(Some(&AccountSharedData::default()));
1282 assert_eq!(&def1, &def2);
1283 let none: Option<&AccountSharedData> = None;
1284 let def2 = AccountMeta::from(none);
1285 assert_eq!(&def1, &def2);
1286 }
1287
1288 #[test]
1289 fn test_account_meta_non_default() {
1290 let def1 = AccountMeta {
1291 lamports: 1,
1292 owner: Pubkey::new_unique(),
1293 executable: true,
1294 rent_epoch: 3,
1295 };
1296 let def2_account = Account {
1297 lamports: def1.lamports,
1298 owner: def1.owner,
1299 executable: def1.executable,
1300 rent_epoch: def1.rent_epoch,
1301 data: Vec::new(),
1302 };
1303 let def2 = AccountMeta::from(&def2_account);
1304 assert_eq!(&def1, &def2);
1305 let def2 = AccountMeta::from(&AccountSharedData::from(def2_account.clone()));
1306 assert_eq!(&def1, &def2);
1307 let def2 = AccountMeta::from(Some(&AccountSharedData::from(def2_account)));
1308 assert_eq!(&def1, &def2);
1309 }
1310
1311 #[test]
1312 #[should_panic(expected = "AppendVecError(FileSizeTooSmall(0))")]
1313 fn test_append_vec_new_bad_size() {
1314 let path = get_append_vec_path("test_append_vec_new_bad_size");
1315 let _av = AppendVec::new(&path.path, true, 0);
1316 }
1317
1318 #[test_case(StorageAccess::Mmap)]
1319 #[test_case(StorageAccess::File)]
1320 fn test_append_vec_new_from_file_bad_size(storage_access: StorageAccess) {
1321 let file = get_append_vec_path("test_append_vec_new_from_file_bad_size");
1322 let path = &file.path;
1323
1324 let _data = OpenOptions::new()
1325 .read(true)
1326 .write(true)
1327 .create_new(true)
1328 .open(path)
1329 .expect("create a test file for mmap");
1330
1331 let result = AppendVec::new_from_file(path, 0, storage_access);
1332 assert_matches!(result, Err(ref message) if message.to_string().contains("too small file size 0 for AppendVec"));
1333 }
1334
1335 #[test]
1336 fn test_append_vec_sanitize_len_and_size_too_small() {
1337 const LEN: usize = 0;
1338 const SIZE: usize = 0;
1339 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1340 assert_matches!(result, Err(ref message) if message.to_string().contains("too small file size 0 for AppendVec"));
1341 }
1342
1343 #[test]
1344 fn test_append_vec_sanitize_len_and_size_maximum() {
1345 const LEN: usize = 0;
1346 const SIZE: usize = 16 * 1024 * 1024 * 1024;
1347 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1348 assert_matches!(result, Ok(_));
1349 }
1350
1351 #[test]
1352 fn test_append_vec_sanitize_len_and_size_too_large() {
1353 const LEN: usize = 0;
1354 const SIZE: usize = 16 * 1024 * 1024 * 1024 + 1;
1355 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1356 assert_matches!(result, Err(ref message) if message.to_string().contains("too large file size 17179869185 for AppendVec"));
1357 }
1358
1359 #[test]
1360 fn test_append_vec_sanitize_len_and_size_full_and_same_as_current_len() {
1361 const LEN: usize = 1024 * 1024;
1362 const SIZE: usize = 1024 * 1024;
1363 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1364 assert_matches!(result, Ok(_));
1365 }
1366
1367 #[test]
1368 fn test_append_vec_sanitize_len_and_size_larger_current_len() {
1369 const LEN: usize = 1024 * 1024 + 1;
1370 const SIZE: usize = 1024 * 1024;
1371 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1372 assert_matches!(result, Err(ref message) if message.to_string().contains("is larger than file size (1048576)"));
1373 }
1374
1375 #[test]
1376 fn test_append_vec_one() {
1377 let path = get_append_vec_path("test_append");
1378 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1379 let account = create_test_account(0);
1380 let index = av.append_account_test(&account).unwrap();
1381 assert_eq!(av.get_account_test(index).unwrap(), account);
1382 truncate_and_test(av, index);
1383 }
1384
1385 fn truncate_and_test(av: AppendVec, index: usize) {
1388 let hash = std::mem::size_of::<AccountHash>();
1390 for _ in 0..hash {
1391 av.current_len.fetch_sub(1, Ordering::Relaxed);
1392 assert_eq!(av.get_account_test(index), None);
1393 }
1394 av.current_len.fetch_sub(1, Ordering::Relaxed);
1396 assert_eq!(av.get_account_test(index), None);
1397 }
1398
1399 #[test]
1400 fn test_append_vec_one_with_data() {
1401 let path = get_append_vec_path("test_append");
1402 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1403 let data_len = 1;
1404 let account = create_test_account(data_len);
1405 let index = av.append_account_test(&account).unwrap();
1406 assert_eq!(
1408 STORE_META_OVERHEAD + data_len,
1409 av.current_len.load(Ordering::Relaxed)
1410 );
1411 assert_eq!(av.get_account_test(index).unwrap(), account);
1412 truncate_and_test(av, index);
1413 }
1414
1415 #[test]
1416 fn test_remaining_bytes() {
1417 let path = get_append_vec_path("test_append");
1418 let sz = 1024 * 1024;
1419 let sz64 = sz as u64;
1420 let av = AppendVec::new(&path.path, true, sz);
1421 assert_eq!(av.capacity(), sz64);
1422 assert_eq!(av.remaining_bytes(), sz64);
1423
1424 let mut av_len = 0;
1426 let account = create_test_account(0);
1427 av.append_account_test(&account).unwrap();
1428 av_len += STORE_META_OVERHEAD;
1429 assert_eq!(av.capacity(), sz64);
1430 assert_eq!(av.remaining_bytes(), sz64 - (STORE_META_OVERHEAD as u64));
1431 assert_eq!(av.len(), av_len);
1432
1433 let account = create_test_account(1);
1435 let account_storage_len = STORE_META_OVERHEAD + 1;
1436 av_len += account_storage_len;
1437 av.append_account_test(&account).unwrap();
1438 assert_eq!(av.capacity(), sz64);
1439 assert_eq!(av.len(), av_len);
1440 let alignment_bytes = u64_align!(av_len) - av_len; assert_eq!(alignment_bytes, 7);
1442 assert_eq!(av.remaining_bytes(), sz64 - u64_align!(av_len) as u64);
1443
1444 let account = create_test_account(1);
1446 av.append_account_test(&account).unwrap();
1447 let account_storage_len = STORE_META_OVERHEAD + 1;
1448 av_len += alignment_bytes; av_len += account_storage_len;
1450 assert_eq!(av.capacity(), sz64);
1451 assert_eq!(av.len(), av_len);
1452 assert_eq!(av.remaining_bytes(), sz64 - u64_align!(av_len) as u64);
1453 }
1454
1455 #[test]
1456 fn test_append_vec_data() {
1457 let path = get_append_vec_path("test_append_data");
1458 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1459 let account = create_test_account(5);
1460 let index = av.append_account_test(&account).unwrap();
1461 assert_eq!(av.get_account_test(index).unwrap(), account);
1462 let account1 = create_test_account(6);
1463 let index1 = av.append_account_test(&account1).unwrap();
1464 assert_eq!(av.get_account_test(index).unwrap(), account);
1465 assert_eq!(av.get_account_test(index1).unwrap(), account1);
1466 }
1467
1468 #[test]
1469 fn test_account_matches_owners() {
1470 let path = get_append_vec_path("test_append_data");
1471 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1472 let owners: Vec<Pubkey> = (0..2).map(|_| Pubkey::new_unique()).collect();
1473
1474 let mut account = create_test_account(5);
1475 account.1.set_owner(owners[0]);
1476 let index = av.append_account_test(&account).unwrap();
1477 assert_eq!(av.account_matches_owners(index, &owners), Ok(0));
1478
1479 let mut account1 = create_test_account(6);
1480 account1.1.set_owner(owners[1]);
1481 let index1 = av.append_account_test(&account1).unwrap();
1482 assert_eq!(av.account_matches_owners(index1, &owners), Ok(1));
1483 assert_eq!(av.account_matches_owners(index, &owners), Ok(0));
1484
1485 let mut account2 = create_test_account(6);
1486 account2.1.set_owner(Pubkey::new_unique());
1487 let index2 = av.append_account_test(&account2).unwrap();
1488 assert_eq!(
1489 av.account_matches_owners(index2, &owners),
1490 Err(MatchAccountOwnerError::NoMatch)
1491 );
1492
1493 assert_eq!(
1495 av.account_matches_owners(usize::MAX - mem::size_of::<StoredMeta>(), &owners),
1496 Err(MatchAccountOwnerError::UnableToLoad)
1497 );
1498
1499 assert_eq!(
1500 av.account_matches_owners(
1501 usize::MAX - mem::size_of::<StoredMeta>() - mem::size_of::<AccountMeta>() + 1,
1502 &owners
1503 ),
1504 Err(MatchAccountOwnerError::UnableToLoad)
1505 );
1506 }
1507
1508 impl AppendVec {
1509 fn accounts_count(&self) -> usize {
1511 let mut count = 0;
1512 self.scan_accounts(|_| {
1513 count += 1;
1514 });
1515 count
1516 }
1517 }
1518
1519 #[test]
1520 fn test_append_vec_append_many() {
1521 let path = get_append_vec_path("test_append_many");
1522 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1523 let size = 1000;
1524 let mut indexes = vec![];
1525 let now = Instant::now();
1526 let mut sizes = vec![];
1527 for sample in 0..size {
1528 let account = create_test_account(sample + 1);
1531 sizes.push(aligned_stored_size(account.1.data().len()));
1532 let pos = av.append_account_test(&account).unwrap();
1533 assert_eq!(av.get_account_test(pos).unwrap(), account);
1534 indexes.push(pos);
1535 assert_eq!(sizes, av.get_account_sizes(&indexes));
1536 }
1537 trace!("append time: {} ms", now.elapsed().as_millis());
1538
1539 let now = Instant::now();
1540 for _ in 0..size {
1541 let sample = thread_rng().gen_range(0..indexes.len());
1542 let account = create_test_account(sample + 1);
1543 assert_eq!(av.get_account_test(indexes[sample]).unwrap(), account);
1544 }
1545 trace!("random read time: {} ms", now.elapsed().as_millis());
1546
1547 let now = Instant::now();
1548 assert_eq!(indexes.len(), size);
1549 assert_eq!(indexes[0], 0);
1550 let mut sample = 0;
1551 assert_eq!(av.accounts_count(), size);
1552 av.scan_accounts(|v| {
1553 let account = create_test_account(sample + 1);
1554 let recovered = v.to_account_shared_data();
1555 assert_eq!(recovered, account.1);
1556 sample += 1;
1557 });
1558 trace!("sequential read time: {} ms", now.elapsed().as_millis());
1559 }
1560
1561 #[test_case(StorageAccess::Mmap)]
1562 #[test_case(StorageAccess::File)]
1563 fn test_new_from_file_crafted_zero_lamport_account(storage_access: StorageAccess) {
1564 let file = get_append_vec_path("test_append_bytes");
1608 let path = &file.path;
1609
1610 let accounts_len = 139;
1611 {
1612 let append_vec_data = [
1613 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 192, 118, 150, 1, 185, 209, 118,
1614 82, 154, 222, 172, 202, 110, 26, 218, 140, 143, 96, 61, 43, 212, 73, 203, 7, 190,
1615 88, 80, 222, 110, 114, 67, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1616 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1617 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1618 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1619 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1620 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1621 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1622 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1623 ];
1624
1625 let f = std::fs::File::create(path).unwrap();
1626 let mut writer = std::io::BufWriter::new(f);
1627 writer.write_all(append_vec_data.as_slice()).unwrap();
1628 }
1629
1630 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1631 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1632 }
1633
1634 #[test_case(StorageAccess::Mmap)]
1635 #[test_case(StorageAccess::File)]
1636 fn test_new_from_file_crafted_data_len(storage_access: StorageAccess) {
1637 let file = get_append_vec_path("test_new_from_file_crafted_data_len");
1638 let path = &file.path;
1639 let accounts_len = {
1640 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1642
1643 let crafted_data_len = 1;
1644
1645 av.append_account_test(&create_test_account(10)).unwrap();
1646
1647 av.get_stored_account_meta_callback(0, |account| {
1648 let StoredAccountMeta::AppendVec(account) = account else {
1649 panic!("StoredAccountMeta can only be AppendVec in this test.");
1650 };
1651 account.set_data_len_unsafe(crafted_data_len);
1652 assert_eq!(account.data_len(), crafted_data_len);
1653
1654 av.get_stored_account_meta_callback(0, |account| {
1656 assert_eq!(account.data_len() as u64, crafted_data_len);
1657 });
1658 });
1659
1660 av.flush().unwrap();
1661 av.len()
1662 };
1663 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1664 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1665 }
1666
1667 #[test]
1668 fn test_append_vec_reset() {
1669 let file = get_append_vec_path("test_append_vec_reset");
1670 let path = &file.path;
1671 let av = AppendVec::new(path, true, 1024 * 1024);
1672 av.append_account_test(&create_test_account(10)).unwrap();
1673
1674 assert!(!av.is_empty());
1675 av.reset();
1676 assert_eq!(av.len(), 0);
1677 }
1678
1679 #[test_case(StorageAccess::Mmap)]
1680 #[test_case(StorageAccess::File)]
1681 fn test_append_vec_flush(storage_access: StorageAccess) {
1682 let file = get_append_vec_path("test_append_vec_flush");
1683 let path = &file.path;
1684 let accounts_len = {
1685 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1687 av.append_account_test(&create_test_account(10)).unwrap();
1688 av.len()
1689 };
1690
1691 let (av, num_account) =
1692 AppendVec::new_from_file(path, accounts_len, storage_access).unwrap();
1693 av.flush().unwrap();
1694 assert_eq!(num_account, 1);
1695 }
1696
1697 #[test_case(StorageAccess::Mmap)]
1698 #[test_case(StorageAccess::File)]
1699 fn test_append_vec_reopen_as_readonly(storage_access: StorageAccess) {
1700 let file = get_append_vec_path("test_append_vec_flush");
1701 let path = &file.path;
1702 let accounts_len = {
1703 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1705 av.append_account_test(&create_test_account(10)).unwrap();
1706 av.len()
1707 };
1708 let (av, _) = AppendVec::new_from_file(path, accounts_len, storage_access).unwrap();
1709 let reopen = av.reopen_as_readonly();
1710 if storage_access == StorageAccess::File {
1711 assert!(reopen.is_none());
1712 } else {
1713 assert!(reopen.is_some());
1714 }
1715 }
1716
1717 #[test_case(StorageAccess::Mmap)]
1718 #[test_case(StorageAccess::File)]
1719 fn test_new_from_file_too_large_data_len(storage_access: StorageAccess) {
1720 let file = get_append_vec_path("test_new_from_file_too_large_data_len");
1721 let path = &file.path;
1722 let accounts_len = {
1723 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1725
1726 let too_large_data_len = u64::MAX;
1727 av.append_account_test(&create_test_account(10)).unwrap();
1728
1729 av.get_stored_account_meta_callback(0, |account| {
1730 let StoredAccountMeta::AppendVec(account) = account else {
1731 panic!("StoredAccountMeta can only be AppendVec in this test.");
1732 };
1733 account.set_data_len_unsafe(too_large_data_len);
1734 assert_eq!(account.data_len(), too_large_data_len);
1735 })
1736 .unwrap();
1737
1738 assert!(av
1740 .get_stored_account_meta_callback(0, |_| {
1741 panic!("unexpected");
1742 })
1743 .is_none());
1744 av.flush().unwrap();
1745 av.len()
1746 };
1747 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1748 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1749 }
1750
1751 #[test_case(StorageAccess::Mmap)]
1752 #[test_case(StorageAccess::File)]
1753 fn test_new_from_file_crafted_executable(storage_access: StorageAccess) {
1754 let file = get_append_vec_path("test_new_from_crafted_executable");
1755 let path = &file.path;
1756 let accounts_len = {
1757 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1759 av.append_account_test(&create_test_account(10)).unwrap();
1760 let offset_1 = {
1761 let mut executable_account = create_test_account(10);
1762 executable_account.1.set_executable(true);
1763 av.append_account_test(&executable_account).unwrap()
1764 };
1765
1766 let crafted_executable = u8::MAX - 1;
1767
1768 av.get_stored_account_meta_callback(0, |account| {
1771 assert_eq!(*account.ref_executable_byte(), 0);
1772 let StoredAccountMeta::AppendVec(account) = account else {
1773 panic!("StoredAccountMeta can only be AppendVec in this test.");
1774 };
1775 account.set_executable_as_byte(crafted_executable);
1776 })
1777 .unwrap();
1778 av.get_stored_account_meta_callback(offset_1, |account| {
1779 assert_eq!(*account.ref_executable_byte(), 1);
1780 })
1781 .unwrap();
1782
1783 av.get_stored_account_meta_callback(0, |account| {
1785 let StoredAccountMeta::AppendVec(account) = account else {
1786 panic!("StoredAccountMeta can only be AppendVec in this test.");
1787 };
1788
1789 assert!(!account.sanitize_executable());
1791
1792 {
1794 let executable_bool: &bool = &account.account_meta.executable;
1795 assert!(!*executable_bool);
1798 assert_eq!(*account.ref_executable_byte(), crafted_executable);
1799 }
1800
1801 {
1803 let executable_bool: bool = account.executable();
1804 assert!(!executable_bool);
1805 assert_eq!(account.get_executable_byte(), 0); }
1807 })
1808 .unwrap();
1809
1810 av.flush().unwrap();
1811 av.len()
1812 };
1813 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1814 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1815 }
1816
1817 #[test]
1818 fn test_type_layout() {
1819 assert_eq!(offset_of!(StoredMeta, write_version_obsolete), 0x00);
1820 assert_eq!(offset_of!(StoredMeta, data_len), 0x08);
1821 assert_eq!(offset_of!(StoredMeta, pubkey), 0x10);
1822 assert_eq!(mem::size_of::<StoredMeta>(), 0x30);
1823
1824 assert_eq!(offset_of!(AccountMeta, lamports), 0x00);
1825 assert_eq!(offset_of!(AccountMeta, rent_epoch), 0x08);
1826 assert_eq!(offset_of!(AccountMeta, owner), 0x10);
1827 assert_eq!(offset_of!(AccountMeta, executable), 0x30);
1828 assert_eq!(mem::size_of::<AccountMeta>(), 0x38);
1829 }
1830
1831 #[test_case(StorageAccess::Mmap)]
1832 #[test_case(StorageAccess::File)]
1833 fn test_get_account_shared_data_from_truncated_file(storage_access: StorageAccess) {
1834 let file = get_append_vec_path("test_get_account_shared_data_from_truncated_file");
1835 let path = &file.path;
1836
1837 {
1838 let data_len: usize = 2 * PAGE_SIZE as usize;
1841 let account = create_test_account_with(data_len);
1842 let av = ManuallyDrop::new(AppendVec::new(path, true, aligned_stored_size(data_len)));
1844 av.append_account_test(&account).unwrap();
1845 av.flush().unwrap();
1846 }
1847
1848 let truncated_accounts_len: usize = PAGE_SIZE as usize;
1850 let av = AppendVec::new_from_file_unchecked(path, truncated_accounts_len, storage_access)
1851 .unwrap();
1852 let account = av.get_account_shared_data(0);
1853 assert!(account.is_none()); let result = av.get_stored_account_meta_callback(0, |_| true);
1856 assert!(result.is_none()); }
1858
1859 #[test_case(StorageAccess::Mmap)]
1860 #[test_case(StorageAccess::File)]
1861 fn test_get_account_sizes(storage_access: StorageAccess) {
1862 const NUM_ACCOUNTS: usize = 37;
1863 let pubkeys: Vec<_> = std::iter::repeat_with(Pubkey::new_unique)
1864 .take(NUM_ACCOUNTS)
1865 .collect();
1866
1867 let mut rng = thread_rng();
1868 let mut accounts = Vec::with_capacity(pubkeys.len());
1869 let mut stored_sizes = Vec::with_capacity(pubkeys.len());
1870 for _ in &pubkeys {
1871 let lamports = rng.gen();
1872 let data_len = rng.gen_range(0..MAX_PERMITTED_DATA_LENGTH) as usize;
1873 let account = AccountSharedData::new(lamports, data_len, &Pubkey::default());
1874 accounts.push(account);
1875 stored_sizes.push(aligned_stored_size(data_len));
1876 }
1877 let accounts = accounts;
1878 let stored_sizes = stored_sizes;
1879 let total_stored_size = stored_sizes.iter().sum();
1880
1881 let temp_file = get_append_vec_path("test_get_account_sizes");
1882 let account_offsets = {
1883 let append_vec = AppendVec::new(&temp_file.path, true, total_stored_size);
1884 let append_vec = ManuallyDrop::new(append_vec);
1886 let slot = 77; let storable_accounts: Vec<_> = std::iter::zip(&pubkeys, &accounts).collect();
1888 let stored_accounts_info = append_vec
1889 .append_accounts(&(slot, storable_accounts.as_slice()), 0)
1890 .unwrap();
1891 append_vec.flush().unwrap();
1892 stored_accounts_info.offsets
1893 };
1894
1895 let (append_vec, _) =
1898 AppendVec::new_from_file(&temp_file.path, total_stored_size, storage_access).unwrap();
1899
1900 let account_sizes = append_vec.get_account_sizes(account_offsets.as_slice());
1901 assert_eq!(account_sizes, stored_sizes);
1902 }
1903
1904 fn test_scan_helper(
1909 storage_access: StorageAccess,
1910 modify_fn: impl Fn(&PathBuf, usize) -> usize,
1911 check_fn: impl Fn(&AppendVec, &[Pubkey], &[usize], &[AccountSharedData]),
1912 ) {
1913 const NUM_ACCOUNTS: usize = 37;
1914 let pubkeys: Vec<_> = std::iter::repeat_with(solana_sdk::pubkey::new_rand)
1915 .take(NUM_ACCOUNTS)
1916 .collect();
1917
1918 let mut rng = thread_rng();
1919 let mut accounts = Vec::with_capacity(pubkeys.len());
1920 let mut total_stored_size = 0;
1921 for _ in &pubkeys {
1922 let lamports = rng.gen();
1923 let data_len = rng.gen_range(0..MAX_PERMITTED_DATA_LENGTH) as usize;
1924 let account = AccountSharedData::new(lamports, data_len, &Pubkey::default());
1925 accounts.push(account);
1926 total_stored_size += aligned_stored_size(data_len);
1927 }
1928 let accounts = accounts;
1929 let total_stored_size = total_stored_size;
1930
1931 let temp_file = get_append_vec_path("test_scan");
1932 let account_offsets = {
1933 let append_vec =
1935 ManuallyDrop::new(AppendVec::new(&temp_file.path, true, total_stored_size));
1936 let slot = 42; let storable_accounts: Vec<_> = std::iter::zip(&pubkeys, &accounts).collect();
1938 let stored_accounts_info = append_vec
1939 .append_accounts(&(slot, storable_accounts.as_slice()), 0)
1940 .unwrap();
1941 append_vec.flush().unwrap();
1942 stored_accounts_info.offsets
1943 };
1944
1945 let total_stored_size = modify_fn(&temp_file.path, total_stored_size);
1946 let append_vec = ManuallyDrop::new(
1949 AppendVec::new_from_file_unchecked(&temp_file.path, total_stored_size, storage_access)
1950 .unwrap(),
1951 );
1952
1953 check_fn(&append_vec, &pubkeys, &account_offsets, &accounts);
1954 }
1955
1956 fn test_scan_pubkeys_helper(
1958 storage_access: StorageAccess,
1959 modify_fn: impl Fn(&PathBuf, usize) -> usize,
1960 ) {
1961 test_scan_helper(
1962 storage_access,
1963 modify_fn,
1964 |append_vec, pubkeys, _account_offsets, _accounts| {
1965 let mut i = 0;
1966 append_vec.scan_pubkeys(|pubkey| {
1967 assert_eq!(pubkey, pubkeys.get(i).unwrap());
1968 i += 1;
1969 });
1970 assert_eq!(i, pubkeys.len());
1971 },
1972 )
1973 }
1974
1975 #[test_case(StorageAccess::Mmap)]
1977 #[test_case(StorageAccess::File)]
1978 fn test_scan_pubkeys(storage_access: StorageAccess) {
1979 test_scan_pubkeys_helper(storage_access, |_, size| size);
1980 }
1981
1982 #[test_case(StorageAccess::Mmap)]
1984 #[test_case(StorageAccess::File)]
1985 fn test_scan_pubkeys_incomplete_data(storage_access: StorageAccess) {
1986 test_scan_pubkeys_helper(storage_access, |path, size| {
1987 let mut f = OpenOptions::new()
1990 .read(true)
1991 .append(true)
1992 .open(path)
1993 .unwrap();
1994 f.write_all(&[0xFF]).unwrap();
1995 size + 1
1996 });
1997 }
1998
1999 #[test_case(StorageAccess::Mmap)]
2001 #[test_case(StorageAccess::File)]
2002 fn test_scan_pubkeys_missing_account_data(storage_access: StorageAccess) {
2003 test_scan_pubkeys_helper(storage_access, |path, size| {
2004 let fake_stored_meta = StoredMeta {
2005 write_version_obsolete: 0,
2006 data_len: 100,
2007 pubkey: solana_sdk::pubkey::new_rand(),
2008 };
2009 let fake_account_meta = AccountMeta {
2010 lamports: 100,
2011 rent_epoch: 10,
2012 owner: solana_sdk::pubkey::new_rand(),
2013 executable: false,
2014 };
2015
2016 let stored_meta_slice: &[u8] = unsafe {
2017 std::slice::from_raw_parts(
2018 (&fake_stored_meta as *const StoredMeta) as *const u8,
2019 mem::size_of::<StoredMeta>(),
2020 )
2021 };
2022 let account_meta_slice: &[u8] = unsafe {
2023 std::slice::from_raw_parts(
2024 (&fake_account_meta as *const AccountMeta) as *const u8,
2025 mem::size_of::<AccountMeta>(),
2026 )
2027 };
2028
2029 let mut f = OpenOptions::new()
2030 .read(true)
2031 .append(true)
2032 .open(path)
2033 .unwrap();
2034
2035 f.write_all(stored_meta_slice).unwrap();
2036 f.write_all(account_meta_slice).unwrap();
2037
2038 size + mem::size_of::<StoredMeta>() + mem::size_of::<AccountMeta>()
2039 });
2040 }
2041
2042 fn test_scan_index_helper(
2044 storage_access: StorageAccess,
2045 modify_fn: impl Fn(&PathBuf, usize) -> usize,
2046 ) {
2047 test_scan_helper(
2048 storage_access,
2049 modify_fn,
2050 |append_vec, pubkeys, account_offsets, accounts| {
2051 let mut i = 0;
2052 append_vec.scan_index(|index_info| {
2053 let pubkey = pubkeys.get(i).unwrap();
2054 let account = accounts.get(i).unwrap();
2055 let offset = account_offsets.get(i).unwrap();
2056
2057 assert_eq!(
2058 index_info.stored_size_aligned,
2059 aligned_stored_size(account.data().len()),
2060 );
2061 assert_eq!(index_info.index_info.offset, *offset);
2062 assert_eq!(index_info.index_info.pubkey, *pubkey);
2063 assert_eq!(index_info.index_info.lamports, account.lamports());
2064 assert_eq!(index_info.index_info.rent_epoch, account.rent_epoch());
2065 assert_eq!(index_info.index_info.executable, account.executable());
2066 assert_eq!(index_info.index_info.data_len, account.data().len() as u64);
2067
2068 i += 1;
2069 });
2070 assert_eq!(i, accounts.len());
2071 },
2072 )
2073 }
2074
2075 #[test_case(StorageAccess::Mmap)]
2076 #[test_case(StorageAccess::File)]
2077 fn test_scan_index(storage_access: StorageAccess) {
2078 test_scan_index_helper(storage_access, |_, size| size);
2079 }
2080
2081 #[test_case(StorageAccess::Mmap)]
2083 #[test_case(StorageAccess::File)]
2084 fn test_scan_index_incomplete_data(storage_access: StorageAccess) {
2085 test_scan_index_helper(storage_access, |path, size| {
2086 let mut f = OpenOptions::new()
2089 .read(true)
2090 .append(true)
2091 .open(path)
2092 .unwrap();
2093 f.write_all(&[0xFF]).unwrap();
2094 size + 1
2095 });
2096 }
2097
2098 #[test_case(StorageAccess::Mmap)]
2100 #[test_case(StorageAccess::File)]
2101 fn test_scan_index_missing_account_data(storage_access: StorageAccess) {
2102 test_scan_index_helper(storage_access, |path, size| {
2103 let fake_stored_meta = StoredMeta {
2104 write_version_obsolete: 0,
2105 data_len: 100,
2106 pubkey: solana_sdk::pubkey::new_rand(),
2107 };
2108 let fake_account_meta = AccountMeta {
2109 lamports: 100,
2110 rent_epoch: 10,
2111 owner: solana_sdk::pubkey::new_rand(),
2112 executable: false,
2113 };
2114
2115 let stored_meta_slice: &[u8] = unsafe {
2116 std::slice::from_raw_parts(
2117 (&fake_stored_meta as *const StoredMeta) as *const u8,
2118 mem::size_of::<StoredMeta>(),
2119 )
2120 };
2121 let account_meta_slice: &[u8] = unsafe {
2122 std::slice::from_raw_parts(
2123 (&fake_account_meta as *const AccountMeta) as *const u8,
2124 mem::size_of::<AccountMeta>(),
2125 )
2126 };
2127
2128 let mut f = OpenOptions::new()
2129 .read(true)
2130 .append(true)
2131 .open(path)
2132 .unwrap();
2133
2134 f.write_all(stored_meta_slice).unwrap();
2135 f.write_all(account_meta_slice).unwrap();
2136
2137 size + mem::size_of::<StoredMeta>() + mem::size_of::<AccountMeta>()
2138 });
2139 }
2140}