1use crate::address::{address_eq, Address};
17use crate::borrow::{Ref, RefMut};
18use crate::borrow_registry::{self, BorrowToken};
19use crate::error::ProgramError;
20use crate::field_map::FieldInfo;
21use crate::layout::LayoutContract;
22use crate::native_boundary::{self, BackendAccountView};
23use crate::segment_borrow::SegmentBorrowRegistry;
24use crate::ProgramResult;
25
26#[inline(always)]
28fn check_typed_projection<T>(data_len: usize, offset: usize) -> Result<usize, ProgramError> {
29 let end = offset
30 .checked_add(core::mem::size_of::<T>())
31 .ok_or(ProgramError::ArithmeticOverflow)?;
32 if end > data_len {
33 return Err(ProgramError::AccountDataTooSmall);
34 }
35 Ok(end)
36}
37
38#[inline]
46unsafe fn release_registered<const N: usize>(
47 reg: &mut SegmentBorrowRegistry,
48 recs: &[core::mem::MaybeUninit<crate::segment_borrow::SegmentBorrow>; N],
49 count: usize,
50) {
51 let mut j = 0;
52 while j < count {
53 unsafe {
55 reg.release(recs[j].assume_init_ref());
56 }
57 j += 1;
58 }
59}
60
61#[repr(transparent)]
75pub struct AccountView<'info> {
76 inner: BackendAccountView<'info>,
77}
78
79const _: () = {
80 assert!(
81 core::mem::size_of::<AccountView<'static>>()
82 == core::mem::size_of::<BackendAccountView<'static>>()
83 );
84 assert!(
85 core::mem::align_of::<AccountView<'static>>()
86 == core::mem::align_of::<BackendAccountView<'static>>()
87 );
88 assert!(!core::mem::needs_drop::<AccountView<'static>>());
89};
90
91#[cfg(target_os = "solana")]
94unsafe impl<'info> Send for AccountView<'info> {}
95#[cfg(target_os = "solana")]
96unsafe impl<'info> Sync for AccountView<'info> {}
97
98impl<'info> Clone for AccountView<'info> {
99 #[inline(always)]
100 fn clone(&self) -> Self {
101 Self::from_inner(self.backend().clone())
102 }
103}
104
105impl<'info> PartialEq for AccountView<'info> {
106 #[inline(always)]
107 fn eq(&self, other: &Self) -> bool {
108 self.backend() == other.backend()
109 }
110}
111
112impl<'info> Eq for AccountView<'info> {}
113
114impl<'info> AccountView<'info> {
115 #[inline(always)]
118 pub(crate) fn from_inner(inner: BackendAccountView<'info>) -> Self {
119 Self { inner }
120 }
121
122 #[inline(always)]
123 fn backend(&self) -> &BackendAccountView<'info> {
124 &self.inner
125 }
126
127 #[cfg(test)]
128 #[inline(always)]
129 pub(crate) fn from_backend(inner: BackendAccountView<'info>) -> Self {
130 Self::from_inner(inner)
131 }
132
133 #[inline(always)]
137 pub fn address(&self) -> &Address {
138 native_boundary::account_address(self.backend())
139 }
140
141 #[inline(always)]
148 pub unsafe fn owner(&self) -> &Address {
149 unsafe { native_boundary::account_owner(self.backend()) }
151 }
152
153 #[inline(always)]
155 pub fn read_owner(&self) -> Address {
156 native_boundary::read_owner(self.backend())
157 }
158
159 #[inline(always)]
161 pub fn owned_by(&self, program: &Address) -> bool {
162 native_boundary::owned_by(self.backend(), program)
163 }
164
165 #[inline(always)]
167 pub fn is_signer(&self) -> bool {
168 self.backend().is_signer()
169 }
170
171 #[inline(always)]
173 pub fn is_writable(&self) -> bool {
174 self.backend().is_writable()
175 }
176
177 #[inline(always)]
179 pub fn executable(&self) -> bool {
180 self.backend().executable()
181 }
182
183 #[inline(always)]
185 pub fn data_len(&self) -> usize {
186 self.backend().data_len()
187 }
188
189 #[inline(always)]
191 pub fn lamports(&self) -> u64 {
192 self.backend().lamports()
193 }
194
195 #[inline(always)]
197 pub fn is_data_empty(&self) -> bool {
198 self.data_len() == 0
199 }
200
201 #[inline(always)]
207 pub fn try_set_lamports(&self, lamports: u64) -> ProgramResult {
208 native_boundary::try_set_lamports(self.backend(), lamports)
209 }
210
211 #[inline(always)]
213 pub fn set_lamports(&self, lamports: u64) -> ProgramResult {
214 self.try_set_lamports(lamports)
215 }
216
217 #[inline(always)]
221 pub fn try_borrow(&self) -> Result<Ref<'_, [u8]>, ProgramError> {
222 let token = BorrowToken::shared(self.address())?;
223 match self.backend().try_borrow() {
224 Ok(data) => Ok(Ref::from_backend(data, token)),
225 Err(error) => {
226 drop(token);
227 Err(ProgramError::from(error))
228 }
229 }
230 }
231
232 #[inline(always)]
252 pub fn try_borrow_mut(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
253 let len = self.data_len();
254 if len > 0 {
255 crate::write_policy::check_data_mutation(self.address(), 0, len as u32)?;
256 }
257 self.try_borrow_mut_ungated()
258 }
259
260 #[inline(always)]
281 pub(crate) fn try_borrow_mut_ungated(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
282 let token = BorrowToken::mutable(self.address())?;
283 match self.backend().try_borrow_mut() {
284 Ok(data) => Ok(RefMut::from_backend(data, token)),
285 Err(error) => {
286 drop(token);
287 Err(ProgramError::from(error))
288 }
289 }
290 }
291
292 #[inline(always)]
315 pub fn segment_ref<'a, T: crate::Pod>(
316 &'a self,
317 borrows: &'a mut SegmentBorrowRegistry,
318 abs_offset: u32,
319 size: u32,
320 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
321 let expected_size = core::mem::size_of::<T>() as u32;
322 if size != expected_size {
323 return ProgramError::err_invalid_argument();
324 }
325
326 let end = abs_offset
327 .checked_add(size)
328 .ok_or(ProgramError::ArithmeticOverflow)?;
329 if end as usize > self.data_len() {
330 return ProgramError::err_data_too_small();
331 }
332
333 let borrow = borrows.register_leased_read(self.address(), abs_offset, size)?;
334
335 #[cfg(target_os = "solana")]
337 let inner: Ref<'_, T> = {
338 let native_ref = self.backend().segment_ref::<T>(abs_offset, size);
342 let native_ref = match native_ref {
343 Ok(nr) => nr,
344 Err(e) => {
345 borrows.release(&borrow);
349 return Err(ProgramError::from(e));
350 }
351 };
352 let (typed_ref, state_ptr) = native_ref.into_raw_parts();
353 Ref::from_segment(typed_ref as *const T, state_ptr)
354 };
355 #[cfg(not(target_os = "solana"))]
356 let inner: Ref<'_, T> = {
357 let data = match self.try_borrow() {
358 Ok(d) => d,
359 Err(e) => {
360 borrows.release(&borrow);
361 return Err(e);
362 }
363 };
364 let ptr = unsafe { data.as_bytes_ptr().add(abs_offset as usize) as *const T };
366 unsafe { data.project(ptr) }
367 };
368
369 let lease = unsafe { crate::SegmentLease::new(borrows, borrow) };
372 Ok(crate::SegRef::new(inner, lease))
373 }
374
375 #[inline(always)]
386 pub fn segment_mut<'a, T: crate::Pod>(
387 &'a self,
388 borrows: &'a mut SegmentBorrowRegistry,
389 abs_offset: u32,
390 size: u32,
391 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
392 crate::write_policy::check_data_mutation(self.address(), abs_offset, size)?;
393 self.segment_mut_ungated::<T>(borrows, abs_offset, size)
394 }
395
396 #[inline(always)]
401 pub(crate) fn segment_mut_ungated<'a, T: crate::Pod>(
402 &'a self,
403 borrows: &'a mut SegmentBorrowRegistry,
404 abs_offset: u32,
405 size: u32,
406 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
407 self.check_writable()?;
408
409 let expected_size = core::mem::size_of::<T>() as u32;
410 if size != expected_size {
411 return ProgramError::err_invalid_argument();
412 }
413
414 let end = abs_offset
415 .checked_add(size)
416 .ok_or(ProgramError::ArithmeticOverflow)?;
417 if end as usize > self.data_len() {
418 return ProgramError::err_data_too_small();
419 }
420
421 let borrow = borrows.register_leased_write(self.address(), abs_offset, size)?;
422
423 #[cfg(target_os = "solana")]
424 let inner: RefMut<'_, T> = {
425 let native_ref = self.backend().segment_mut::<T>(abs_offset, size);
428 let native_ref = match native_ref {
429 Ok(nr) => nr,
430 Err(e) => {
431 borrows.release(&borrow);
432 return Err(ProgramError::from(e));
433 }
434 };
435 let (typed_ref, state_ptr) = native_ref.into_raw_parts();
436 RefMut::from_segment(typed_ref as *mut T, state_ptr)
437 };
438 #[cfg(not(target_os = "solana"))]
439 let inner: RefMut<'_, T> = {
440 let mut data = match self.try_borrow_mut_ungated() {
441 Ok(d) => d,
442 Err(e) => {
443 borrows.release(&borrow);
444 return Err(e);
445 }
446 };
447 let ptr = unsafe { data.as_bytes_mut_ptr().add(abs_offset as usize) as *mut T };
449 unsafe { data.project(ptr) }
450 };
451
452 let lease = unsafe { crate::SegmentLease::new(borrows, borrow) };
454 Ok(crate::SegRefMut::new(inner, lease))
455 }
456
457 pub fn split_segments_mut<'a, T: crate::Pod, const N: usize>(
484 &'a self,
485 borrows: &'a mut SegmentBorrowRegistry,
486 ranges: [(u32, u32); N],
487 ) -> Result<crate::SegmentsMut<'a, T, N>, ProgramError> {
488 for (off, size) in ranges {
493 crate::write_policy::check_data_mutation(self.address(), off, size)?;
494 }
495 self.split_segments_mut_ungated::<T, N>(borrows, ranges)
496 }
497
498 pub(crate) fn split_segments_mut_ungated<'a, T: crate::Pod, const N: usize>(
503 &'a self,
504 borrows: &'a mut SegmentBorrowRegistry,
505 ranges: [(u32, u32); N],
506 ) -> Result<crate::SegmentsMut<'a, T, N>, ProgramError> {
507 self.check_writable()?;
508 let expected = core::mem::size_of::<T>() as u32;
509 let data_len = self.data_len();
510
511 let mut recs: [core::mem::MaybeUninit<crate::segment_borrow::SegmentBorrow>; N] =
522 unsafe { core::mem::MaybeUninit::uninit().assume_init() };
523 let mut offsets = [0usize; N];
524 let mut i = 0;
525 while i < N {
526 let (off, size) = ranges[i];
527 let in_bounds = match off.checked_add(size) {
528 Some(end) => end as usize <= data_len,
529 None => false,
530 };
531 if size != expected || !in_bounds {
532 unsafe { release_registered(borrows, &recs, i) };
534 return if size != expected {
535 ProgramError::err_invalid_argument()
536 } else {
537 ProgramError::err_data_too_small()
538 };
539 }
540 match borrows.register_leased_write(self.address(), off, size) {
541 Ok(b) => {
542 recs[i] = core::mem::MaybeUninit::new(b);
543 offsets[i] = off as usize;
544 }
545 Err(e) => {
546 unsafe { release_registered(borrows, &recs, i) };
548 return Err(e);
549 }
550 }
551 i += 1;
552 }
553
554 let data = match self.try_borrow_mut_ungated() {
560 Ok(d) => d,
561 Err(e) => {
562 unsafe { release_registered(borrows, &recs, N) };
564 return Err(e);
565 }
566 };
567
568 let reg_ptr = borrows as *mut SegmentBorrowRegistry;
573
574 let mut leases: [core::mem::MaybeUninit<crate::SegmentLease<'a>>; N] =
578 unsafe { core::mem::MaybeUninit::uninit().assume_init() };
579 let mut k = 0;
580 while k < N {
581 let lease = unsafe { crate::SegmentLease::from_raw(reg_ptr, recs[k].assume_init()) };
584 leases[k] = core::mem::MaybeUninit::new(lease);
585 k += 1;
586 }
587 let leases = unsafe {
589 let out = core::ptr::read(&leases as *const _ as *const [crate::SegmentLease<'a>; N]);
590 #[allow(clippy::forget_non_drop)]
593 core::mem::forget(leases);
594 out
595 };
596
597 Ok(crate::SegmentsMut::new(data, offsets, leases))
598 }
599
600 #[inline(always)]
621 pub fn segment_ref_const<'a, T: crate::Pod>(
622 &'a self,
623 borrows: &'a mut SegmentBorrowRegistry,
624 segment: crate::segment::Segment,
625 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
626 self.segment_ref::<T>(borrows, segment.offset, segment.size)
627 }
628
629 #[inline(always)]
632 pub fn segment_mut_const<'a, T: crate::Pod>(
633 &'a self,
634 borrows: &'a mut SegmentBorrowRegistry,
635 segment: crate::segment::Segment,
636 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
637 self.segment_mut::<T>(borrows, segment.offset, segment.size)
638 }
639
640 #[inline(always)]
655 pub fn segment_ref_typed<'a, T: crate::Pod, const OFFSET: u32>(
656 &'a self,
657 borrows: &'a mut SegmentBorrowRegistry,
658 _segment: crate::segment::TypedSegment<T, OFFSET>,
659 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
660 self.segment_ref::<T>(borrows, OFFSET, core::mem::size_of::<T>() as u32)
661 }
662
663 #[inline(always)]
666 pub fn segment_mut_typed<'a, T: crate::Pod, const OFFSET: u32>(
667 &'a self,
668 borrows: &'a mut SegmentBorrowRegistry,
669 _segment: crate::segment::TypedSegment<T, OFFSET>,
670 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
671 self.segment_mut::<T>(borrows, OFFSET, core::mem::size_of::<T>() as u32)
672 }
673
674 #[inline(always)]
695 pub fn load<T: LayoutContract + crate::Pod>(&self) -> Result<Ref<'_, T>, ProgramError> {
696 let data = self.try_borrow()?;
697 check_typed_projection::<T>(data.len(), T::TYPE_OFFSET)?;
698 T::validate_header(&data)?;
699 if data.len() < T::required_len() {
700 return ProgramError::err_data_too_small();
701 }
702 let ptr = unsafe { data.as_bytes_ptr().add(T::TYPE_OFFSET) as *const T };
704 Ok(unsafe { data.project(ptr) })
706 }
707
708 #[inline]
714 pub fn with<T, R, F>(&self, f: F) -> Result<R, ProgramError>
715 where
716 T: LayoutContract + crate::Pod,
717 F: FnOnce(&T) -> Result<R, ProgramError>,
718 {
719 let account = self.load::<T>()?;
720 f(&*account)
721 }
722
723 #[inline(always)]
735 pub fn load_mut<T: LayoutContract + crate::Pod>(&self) -> Result<RefMut<'_, T>, ProgramError> {
736 let mut data = self.try_borrow_mut()?;
737 check_typed_projection::<T>(data.len(), T::TYPE_OFFSET)?;
738 T::validate_header(&data)?;
739 if data.len() < T::required_len() {
740 return ProgramError::err_data_too_small();
741 }
742 #[cfg(feature = "touch-map")]
749 crate::segment_borrow::touch_log::record_account(
750 self.address(),
751 data.len() as u32,
752 crate::segment_borrow::AccessKind::Write,
753 );
754 let ptr = unsafe { data.as_bytes_mut_ptr().add(T::TYPE_OFFSET) as *mut T };
756 Ok(unsafe { data.project(ptr) })
758 }
759
760 #[inline]
765 pub fn with_mut<T, R, F>(&self, f: F) -> Result<R, ProgramError>
766 where
767 T: LayoutContract + crate::Pod,
768 F: FnOnce(&mut T) -> Result<R, ProgramError>,
769 {
770 let mut account = self.load_mut::<T>()?;
771 f(&mut *account)
772 }
773
774 #[inline(always)]
789 pub fn load_compact<T: crate::CompactLayout>(&self) -> Result<Ref<'_, T>, ProgramError> {
790 let data = self.try_borrow()?;
791 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
792 T::validate_compact(&data)?;
793 let ptr =
795 unsafe { data.as_bytes_ptr().add(crate::compact::COMPACT_BODY_OFFSET) as *const T };
796 Ok(unsafe { data.project(ptr) })
798 }
799
800 #[inline(always)]
802 pub fn load_compact_mut<T: crate::CompactLayout>(&self) -> Result<RefMut<'_, T>, ProgramError> {
803 let mut data = self.try_borrow_mut()?;
804 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
805 T::validate_compact(&data)?;
806 #[cfg(feature = "touch-map")]
808 crate::segment_borrow::touch_log::record_account(
809 self.address(),
810 data.len() as u32,
811 crate::segment_borrow::AccessKind::Write,
812 );
813 let ptr = unsafe {
815 data.as_bytes_mut_ptr()
816 .add(crate::compact::COMPACT_BODY_OFFSET) as *mut T
817 };
818 Ok(unsafe { data.project(ptr) })
820 }
821
822 #[inline]
824 pub fn with_compact<T, R, F>(&self, f: F) -> Result<R, ProgramError>
825 where
826 T: crate::CompactLayout,
827 F: FnOnce(&T) -> Result<R, ProgramError>,
828 {
829 let account = self.load_compact::<T>()?;
830 f(&*account)
831 }
832
833 #[inline]
835 pub fn with_compact_mut<T, R, F>(&self, f: F) -> Result<R, ProgramError>
836 where
837 T: crate::CompactLayout,
838 F: FnOnce(&mut T) -> Result<R, ProgramError>,
839 {
840 let mut account = self.load_compact_mut::<T>()?;
841 f(&mut *account)
842 }
843
844 #[inline(always)]
851 pub fn init_compact<T: crate::CompactLayout>(&self) -> ProgramResult {
852 self.check_writable()?;
853 let mut data = self.try_borrow_mut()?;
854 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
855 if data.len() < T::COMPACT_LEN {
856 return Err(ProgramError::AccountDataTooSmall);
857 }
858 if data.len() != T::COMPACT_LEN {
859 return Err(ProgramError::InvalidAccountData);
860 }
861 data[0] = T::DISC;
862 Ok(())
863 }
864
865 #[inline(always)]
883 pub fn load_compact_dynamic<T: crate::CompactDynamicLayout>(
884 &self,
885 ) -> Result<Ref<'_, T>, ProgramError> {
886 let data = self.try_borrow()?;
887 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
888 T::validate_compact_dynamic(&data)?;
889 let ptr =
894 unsafe { data.as_bytes_ptr().add(crate::compact::COMPACT_BODY_OFFSET) as *const T };
895 Ok(unsafe { data.project(ptr) })
897 }
898
899 #[inline(always)]
902 pub fn load_compact_dynamic_mut<T: crate::CompactDynamicLayout>(
903 &self,
904 ) -> Result<RefMut<'_, T>, ProgramError> {
905 let mut data = self.try_borrow_mut()?;
906 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
907 T::validate_compact_dynamic(&data)?;
908 let ptr = unsafe {
911 data.as_bytes_mut_ptr()
912 .add(crate::compact::COMPACT_BODY_OFFSET) as *mut T
913 };
914 Ok(unsafe { data.project(ptr) })
916 }
917
918 #[inline]
920 pub fn with_compact_dynamic<T, R, F>(&self, f: F) -> Result<R, ProgramError>
921 where
922 T: crate::CompactDynamicLayout,
923 F: FnOnce(&T) -> Result<R, ProgramError>,
924 {
925 let account = self.load_compact_dynamic::<T>()?;
926 f(&*account)
927 }
928
929 #[inline]
931 pub fn with_compact_dynamic_mut<T, R, F>(&self, f: F) -> Result<R, ProgramError>
932 where
933 T: crate::CompactDynamicLayout,
934 F: FnOnce(&mut T) -> Result<R, ProgramError>,
935 {
936 let mut account = self.load_compact_dynamic_mut::<T>()?;
937 f(&mut *account)
938 }
939
940 #[inline(always)]
949 pub fn init_compact_dynamic<T: crate::CompactDynamicLayout>(&self) -> ProgramResult {
950 self.check_writable()?;
951 let mut data = self.try_borrow_mut()?;
952 let head_end =
953 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
954 if T::TAIL_OFFSET < head_end {
955 return Err(ProgramError::InvalidAccountData);
956 }
957 let tail_end = T::TAIL_OFFSET
958 .checked_add(4)
959 .ok_or(ProgramError::ArithmeticOverflow)?;
960 if data.len() < T::MIN_LEN {
961 return Err(ProgramError::AccountDataTooSmall);
962 }
963 data[0] = T::DISC;
964 if data.len() >= tail_end {
966 data[T::TAIL_OFFSET..tail_end].copy_from_slice(&0u32.to_le_bytes());
967 }
968 Ok(())
969 }
970
971 #[inline(always)]
976 pub unsafe fn raw_ref<T: crate::Pod>(&self) -> Result<Ref<'_, T>, ProgramError> {
981 let data = self.try_borrow()?;
982 if core::mem::size_of::<T>() > data.len() {
983 return Err(ProgramError::AccountDataTooSmall);
984 }
985 let ptr = data.as_ptr() as *const T;
986 Ok(unsafe { data.project(ptr) })
988 }
989
990 #[inline(always)]
995 pub unsafe fn raw_mut<T: crate::Pod>(&self) -> Result<RefMut<'_, T>, ProgramError> {
1000 self.check_writable()?;
1001 let mut data = self.try_borrow_mut_ungated()?;
1006 if core::mem::size_of::<T>() > data.len() {
1007 return Err(ProgramError::AccountDataTooSmall);
1008 }
1009 let ptr = data.as_bytes_mut_ptr() as *mut T;
1010 Ok(unsafe { data.project(ptr) })
1012 }
1013
1014 #[inline(always)]
1031 pub fn load_cross_program<T: LayoutContract + crate::Pod>(
1032 &self,
1033 ) -> Result<Ref<'_, T>, ProgramError> {
1034 let data = self.try_borrow()?;
1035 check_typed_projection::<T>(data.len(), T::TYPE_OFFSET)?;
1036 T::validate_header(&data)?;
1037 if data.len() < T::required_len() {
1040 return ProgramError::err_data_too_small();
1041 }
1042 let ptr = unsafe { data.as_bytes_ptr().add(T::TYPE_OFFSET) as *const T };
1044 Ok(unsafe { data.project(ptr) })
1046 }
1047
1048 #[inline(always)]
1054 pub fn layout_info(&self) -> Option<crate::layout::LayoutInfo> {
1055 let data = self.try_borrow().ok()?;
1056 crate::layout::LayoutInfo::from_data(&data)
1057 }
1058
1059 #[inline(always)]
1061 pub fn fields<T: LayoutContract>() -> &'static [FieldInfo] {
1062 T::fields()
1063 }
1064
1065 #[inline]
1073 pub fn field<T: LayoutContract>(name: &str) -> Option<&'static FieldInfo> {
1074 <T as crate::field_map::FieldMap>::field_by_name(name)
1075 }
1076
1077 #[inline(always)]
1082 pub fn extension_range<T: LayoutContract>(
1083 &self,
1084 ) -> Result<core::ops::Range<usize>, ProgramError> {
1085 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
1086 let data_len = self.data_len();
1087 if data_len < offset {
1088 return Err(ProgramError::AccountDataTooSmall);
1089 }
1090 Ok(offset..data_len)
1091 }
1092
1093 #[inline(always)]
1095 pub fn extension_bytes<T: LayoutContract>(&self) -> Result<Ref<'_, [u8]>, ProgramError> {
1096 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
1097 let data = self.try_borrow()?;
1098 if data.len() < offset {
1099 return Err(ProgramError::AccountDataTooSmall);
1100 }
1101 Ok(data.slice_from(offset))
1102 }
1103
1104 #[inline(always)]
1106 pub fn extension_bytes_mut<T: LayoutContract>(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
1107 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
1108 let len = self.data_len();
1109 if len < offset {
1110 return Err(ProgramError::AccountDataTooSmall);
1111 }
1112 if len > offset {
1118 crate::write_policy::check_data_mutation(
1119 self.address(),
1120 offset as u32,
1121 (len - offset) as u32,
1122 )?;
1123 }
1124 let data = self.try_borrow_mut_ungated()?;
1125 Ok(data.slice_from(offset))
1126 }
1127
1128 #[inline]
1141 pub fn zero_range(&self, start: usize, len: usize) -> ProgramResult {
1142 if len == 0 {
1143 return Ok(());
1144 }
1145 let end = start
1146 .checked_add(len)
1147 .ok_or(ProgramError::ArithmeticOverflow)?;
1148 if end > self.data_len() {
1149 return Err(ProgramError::AccountDataTooSmall);
1150 }
1151 let offset_u32 = u32::try_from(start).map_err(|_| ProgramError::ArithmeticOverflow)?;
1152 let len_u32 = u32::try_from(len).map_err(|_| ProgramError::ArithmeticOverflow)?;
1153 crate::write_policy::check_data_mutation(self.address(), offset_u32, len_u32)?;
1154 let mut data = self.try_borrow_mut_ungated()?;
1155 for byte in data[start..end].iter_mut() {
1156 *byte = 0;
1157 }
1158 Ok(())
1159 }
1160
1161 #[inline]
1186 pub fn zero_appended(&self, previous_len: usize) -> ProgramResult {
1187 let len = self.data_len();
1188 if previous_len >= len {
1189 return Ok(());
1190 }
1191 crate::write_policy::check_account_transition(self.address())?;
1192 let mut data = self.try_borrow_mut_ungated()?;
1193 for byte in data[previous_len..len].iter_mut() {
1194 *byte = 0;
1195 }
1196 Ok(())
1197 }
1198
1199 #[inline(always)]
1204 pub fn init_layout<T: LayoutContract>(&self) -> ProgramResult {
1205 let mut data = self.try_borrow_mut()?;
1206 crate::layout::init_header::<T>(&mut data)
1207 }
1208
1209 #[inline(always)]
1213 pub fn require_signer(&self) -> ProgramResult {
1214 if self.is_signer() {
1215 Ok(())
1216 } else {
1217 ProgramError::err_missing_signer()
1218 }
1219 }
1220
1221 #[inline(always)]
1223 pub fn require_writable(&self) -> ProgramResult {
1224 if self.is_writable() {
1225 Ok(())
1226 } else {
1227 ProgramError::err_immutable()
1228 }
1229 }
1230
1231 #[inline(always)]
1233 pub fn require_owned_by(&self, program: &Address) -> ProgramResult {
1234 if self.owned_by(program) {
1235 Ok(())
1236 } else {
1237 ProgramError::err_incorrect_program()
1238 }
1239 }
1240
1241 #[inline(always)]
1243 pub fn require_payer(&self) -> ProgramResult {
1244 self.require_signer()?;
1245 self.require_writable()
1246 }
1247
1248 #[inline(always)]
1252 pub fn check_signer(&self) -> Result<&Self, ProgramError> {
1253 if self.is_signer() {
1254 Ok(self)
1255 } else {
1256 ProgramError::err_missing_signer()
1257 }
1258 }
1259
1260 #[inline(always)]
1262 pub fn check_writable(&self) -> Result<&Self, ProgramError> {
1263 if self.is_writable() {
1264 Ok(self)
1265 } else {
1266 ProgramError::err_immutable()
1267 }
1268 }
1269
1270 #[inline(always)]
1272 pub fn check_owned_by(&self, program: &Address) -> Result<&Self, ProgramError> {
1273 if self.owned_by(program) {
1274 Ok(self)
1275 } else {
1276 ProgramError::err_incorrect_program()
1277 }
1278 }
1279
1280 #[inline]
1287 pub fn check_owned_by_any(&self, programs: &[&Address]) -> Result<&Self, ProgramError> {
1288 if programs.iter().any(|program| self.owned_by(program)) {
1289 Ok(self)
1290 } else {
1291 ProgramError::err_incorrect_program()
1292 }
1293 }
1294
1295 #[inline(always)]
1297 pub fn check_disc(&self, expected: u8) -> Result<&Self, ProgramError> {
1298 if self.disc() == expected {
1299 Ok(self)
1300 } else {
1301 Err(ProgramError::InvalidAccountData)
1302 }
1303 }
1304
1305 #[inline(always)]
1307 pub fn check_has_data(&self) -> Result<&Self, ProgramError> {
1308 if !self.is_data_empty() {
1309 Ok(self)
1310 } else {
1311 Err(ProgramError::AccountDataTooSmall)
1312 }
1313 }
1314
1315 #[inline(always)]
1317 pub fn check_executable(&self) -> Result<&Self, ProgramError> {
1318 if self.executable() {
1319 Ok(self)
1320 } else {
1321 Err(ProgramError::InvalidArgument)
1322 }
1323 }
1324
1325 #[inline(always)]
1327 pub fn check_address(&self, expected: &Address) -> Result<&Self, ProgramError> {
1328 if address_eq(self.address(), expected) {
1329 Ok(self)
1330 } else {
1331 Err(ProgramError::InvalidArgument)
1332 }
1333 }
1334
1335 #[inline(always)]
1337 pub fn check_data_len(&self, min_len: usize) -> Result<&Self, ProgramError> {
1338 if self.data_len() >= min_len {
1339 Ok(self)
1340 } else {
1341 Err(ProgramError::AccountDataTooSmall)
1342 }
1343 }
1344
1345 #[inline(always)]
1347 pub fn check_version(&self, expected: u8) -> Result<&Self, ProgramError> {
1348 if self.version() == expected {
1349 Ok(self)
1350 } else {
1351 Err(ProgramError::InvalidAccountData)
1352 }
1353 }
1354
1355 #[inline(always)]
1357 pub fn check_layout<T: LayoutContract>(&self) -> Result<&Self, ProgramError> {
1358 let data = self.try_borrow()?;
1359 T::validate_header(&data)?;
1360 Ok(self)
1361 }
1362
1363 #[inline(always)]
1365 pub const fn proof(&self) -> crate::proof::AccountProof<'_> {
1366 crate::proof::AccountProof::new(self)
1367 }
1368
1369 #[inline(always)]
1373 pub fn disc(&self) -> u8 {
1374 native_boundary::disc(self.backend())
1375 }
1376
1377 #[inline(always)]
1379 pub fn version(&self) -> u8 {
1380 native_boundary::version(self.backend())
1381 }
1382
1383 #[inline(always)]
1385 pub fn layout_id(&self) -> Option<&[u8; 8]> {
1386 native_boundary::layout_id(self.backend())
1387 }
1388
1389 #[inline(always)]
1391 pub fn require_disc(&self, expected: u8) -> ProgramResult {
1392 if self.disc() == expected {
1393 Ok(())
1394 } else {
1395 Err(ProgramError::InvalidAccountData)
1396 }
1397 }
1398
1399 #[inline(always)]
1410 pub fn flags(&self) -> u8 {
1411 self.backend().flags()
1412 }
1413
1414 #[inline(always)]
1416 pub fn expect_flags(&self, required: u8) -> ProgramResult {
1417 if self.flags() & required == required {
1418 Ok(())
1419 } else {
1420 Err(ProgramError::InvalidArgument)
1421 }
1422 }
1423
1424 #[inline(always)]
1435 pub fn expect_signer_writable(&self, need_signer: bool, need_writable: bool) -> ProgramResult {
1436 if self
1442 .backend()
1443 .is_signer_writable(need_signer, need_writable)
1444 {
1445 return Ok(());
1446 }
1447 if need_signer {
1449 self.require_signer()?;
1450 }
1451 if need_writable {
1452 self.require_writable()?;
1453 }
1454 Ok(())
1457 }
1458
1459 #[inline]
1467 pub fn resize(&self, new_len: usize) -> ProgramResult {
1468 crate::write_policy::check_account_transition(self.address())?;
1472 if new_len != self.data_len() {
1473 self.check_borrow_mut()?;
1474 }
1475 native_boundary::resize(self.backend(), new_len)
1476 }
1477
1478 #[inline]
1480 pub fn resize_raw(&self, new_len: usize) -> ProgramResult {
1481 crate::write_policy::check_account_transition(self.address())?;
1483 if new_len != self.data_len() {
1484 self.check_borrow_mut()?;
1485 }
1486 native_boundary::resize_raw(self.backend(), new_len)
1487 }
1488
1489 #[inline(always)]
1496 pub unsafe fn assign(&self, new_owner: &Address) {
1497 unsafe {
1499 native_boundary::assign(self.backend(), new_owner);
1500 }
1501 }
1502
1503 #[inline]
1505 pub fn close(&self) -> ProgramResult {
1506 crate::write_policy::check_account_transition(self.address())?;
1509 self.check_borrow_mut()?;
1510 native_boundary::close(self.backend())
1511 }
1512
1513 #[inline]
1539 pub fn close_to(&self, destination: &AccountView<'_>, program_id: &Address) -> ProgramResult {
1540 crate::write_policy::check_account_transition(self.address())?;
1544 self.require_writable()?;
1545 self.require_owned_by(program_id)?;
1546 destination.require_writable()?;
1547 self.close_to_preflighted(destination)
1548 }
1549
1550 #[inline]
1566 pub fn close_to_unchecked(&self, destination: &AccountView<'_>) -> ProgramResult {
1567 crate::write_policy::check_account_transition(self.address())?;
1568 self.close_to_preflighted(destination)
1569 }
1570
1571 #[inline]
1572 fn close_to_preflighted(&self, destination: &AccountView<'_>) -> ProgramResult {
1573 if crate::address::address_eq(self.address(), destination.address()) {
1574 return Err(ProgramError::InvalidArgument);
1575 }
1576 self.check_borrow_mut()?;
1577 self.require_writable()?;
1579 crate::write_policy::check_lamport_mutation(self.address())?;
1580 crate::write_policy::check_lamport_mutation(destination.address())?;
1581 let credited = destination
1582 .lamports()
1583 .checked_add(self.lamports())
1584 .ok_or(ProgramError::ArithmeticOverflow)?;
1585 native_boundary::zero_data(self.backend())?;
1588 self.try_set_lamports(0)?;
1589 destination.try_set_lamports(credited)?;
1590 Ok(())
1591 }
1592
1593 #[inline(always)]
1597 pub(crate) fn data_ptr_unchecked(&self) -> *mut u8 {
1598 self.backend().data_ptr_unchecked()
1599 }
1600
1601 #[inline(always)]
1603 pub(crate) fn account_ptr(&self) -> *const hopper_native::RuntimeAccount {
1604 self.backend().account_ptr()
1605 }
1606
1607 #[inline(always)]
1609 pub fn check_borrow(&self) -> Result<(), ProgramError> {
1610 borrow_registry::check_shared(self.address())?;
1611 self.backend().check_borrow().map_err(ProgramError::from)
1612 }
1613
1614 #[inline(always)]
1616 pub fn check_borrow_mut(&self) -> Result<(), ProgramError> {
1617 borrow_registry::check_mutable(self.address())?;
1618 self.backend()
1619 .check_borrow_mut()
1620 .map_err(ProgramError::from)
1621 }
1622
1623 #[inline(always)]
1629 pub unsafe fn borrow_unchecked(&self) -> &[u8] {
1630 unsafe { self.backend().borrow_unchecked() }
1632 }
1633
1634 #[allow(clippy::mut_from_ref)]
1645 #[inline(always)]
1646 pub unsafe fn borrow_unchecked_mut(&self) -> &mut [u8] {
1647 unsafe { self.backend().borrow_unchecked_mut() }
1650 }
1651
1652 #[inline(always)]
1658 pub unsafe fn resize_unchecked(&self, new_len: usize) {
1659 unsafe {
1661 self.backend().resize_unchecked(new_len);
1662 }
1663 }
1664
1665 #[inline(always)]
1671 pub unsafe fn close_unchecked(&self) {
1672 unsafe {
1674 self.backend().close_unchecked();
1675 }
1676 }
1677
1678 #[allow(dead_code)]
1682 #[inline(always)]
1683 pub(crate) fn as_backend(&self) -> &BackendAccountView<'_> {
1684 self.backend()
1685 }
1686}
1687
1688impl<'info> core::fmt::Debug for AccountView<'info> {
1689 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1690 f.debug_struct("AccountView")
1691 .field("address", self.address())
1692 .field("lamports", &self.lamports())
1693 .field("data_len", &self.data_len())
1694 .field("is_signer", &self.is_signer())
1695 .field("is_writable", &self.is_writable())
1696 .finish()
1697 }
1698}
1699
1700pub struct RemainingAccounts<'a> {
1704 accounts: &'a [AccountView<'a>],
1705 cursor: usize,
1706}
1707
1708impl<'a> RemainingAccounts<'a> {
1709 #[inline(always)]
1711 pub fn new(accounts: &'a [AccountView<'a>]) -> Self {
1712 Self {
1713 accounts,
1714 cursor: 0,
1715 }
1716 }
1717
1718 #[inline(always)]
1720 pub fn remaining(&self) -> usize {
1721 self.accounts.len() - self.cursor
1722 }
1723
1724 #[allow(clippy::should_implement_trait)]
1729 #[inline(always)]
1730 pub fn next(&mut self) -> Result<&'a AccountView<'a>, ProgramError> {
1731 if self.cursor >= self.accounts.len() {
1732 return Err(ProgramError::NotEnoughAccountKeys);
1733 }
1734 let account = &self.accounts[self.cursor];
1735 self.cursor += 1;
1736 Ok(account)
1737 }
1738
1739 #[inline(always)]
1741 pub fn next_signer(&mut self) -> Result<&'a AccountView<'a>, ProgramError> {
1742 let account = self.next()?;
1743 account.require_signer()?;
1744 Ok(account)
1745 }
1746
1747 #[inline(always)]
1749 pub fn next_writable(&mut self) -> Result<&'a AccountView<'a>, ProgramError> {
1750 let account = self.next()?;
1751 account.require_writable()?;
1752 Ok(account)
1753 }
1754
1755 #[inline(always)]
1757 pub fn next_owned_by(
1758 &mut self,
1759 program: &Address,
1760 ) -> Result<&'a AccountView<'a>, ProgramError> {
1761 let account = self.next()?;
1762 account.require_owned_by(program)?;
1763 Ok(account)
1764 }
1765}
1766
1767#[cfg(test)]
1768mod tests {
1769 use super::*;
1770 use crate::compact::CompactLayout;
1771 use crate::layout::HopperHeader;
1772
1773 use hopper_native::{
1774 AccountView as NativeAccountView, Address as NativeAddress, RuntimeAccount, NOT_BORROWED,
1775 };
1776
1777 #[repr(C)]
1778 #[derive(Clone, Copy, Debug, Default)]
1779 struct TestLayout {
1780 a: [u8; 8],
1781 b: [u8; 8],
1782 }
1783
1784 #[repr(C)]
1785 #[derive(Clone, Copy, Debug)]
1786 struct HeaderLayout {
1787 header: [u8; HopperHeader::SIZE],
1788 amount: [u8; 8],
1789 }
1790
1791 #[repr(C)]
1792 #[derive(Clone, Copy, Debug, Default)]
1793 struct EpochTwoLayout {
1794 amount: [u8; 8],
1795 }
1796
1797 unsafe impl crate::Zeroable for TestLayout {}
1798 unsafe impl crate::Zeroable for HeaderLayout {}
1799 unsafe impl crate::Zeroable for EpochTwoLayout {}
1800 unsafe impl crate::Pod for TestLayout {}
1801 unsafe impl crate::Pod for HeaderLayout {}
1802 unsafe impl crate::Pod for EpochTwoLayout {}
1803
1804 #[inline(always)]
1805 fn le_u64(v: u64) -> [u8; 8] {
1806 v.to_le_bytes()
1807 }
1808
1809 #[inline(always)]
1810 fn from_le_u64(bytes: [u8; 8]) -> u64 {
1811 u64::from_le_bytes(bytes)
1812 }
1813
1814 impl crate::field_map::FieldMap for TestLayout {
1815 const FIELDS: &'static [crate::field_map::FieldInfo] = &[
1816 crate::field_map::FieldInfo::new("a", HopperHeader::SIZE, 8),
1817 crate::field_map::FieldInfo::new("b", HopperHeader::SIZE + 8, 8),
1818 ];
1819 }
1820
1821 impl LayoutContract for TestLayout {
1822 const DISC: u8 = 7;
1823 const VERSION: u8 = 1;
1824 const LAYOUT_ID: [u8; 8] = [0xAB; 8];
1825 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1826 const EXTENSION_OFFSET: Option<usize> = Some(Self::SIZE);
1827 }
1828
1829 impl crate::field_map::FieldMap for HeaderLayout {
1830 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1831 "amount",
1832 HopperHeader::SIZE,
1833 8,
1834 )];
1835 }
1836
1837 impl LayoutContract for HeaderLayout {
1838 const DISC: u8 = 11;
1839 const VERSION: u8 = 2;
1840 const LAYOUT_ID: [u8; 8] = [0xCD; 8];
1841 const SIZE: usize = core::mem::size_of::<Self>();
1842 const TYPE_OFFSET: usize = 0;
1843 }
1844
1845 impl crate::field_map::FieldMap for EpochTwoLayout {
1846 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1847 "amount",
1848 HopperHeader::SIZE,
1849 8,
1850 )];
1851 }
1852
1853 impl LayoutContract for EpochTwoLayout {
1854 const DISC: u8 = 12;
1855 const VERSION: u8 = 1;
1856 const LAYOUT_ID: [u8; 8] = [0xEF; 8];
1857 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1858 const SCHEMA_EPOCH: u32 = 2;
1859 }
1860
1861 #[repr(C)]
1867 #[derive(Clone, Copy, Debug, Default)]
1868 struct LaxForeignLayout {
1869 amount: [u8; 8],
1870 }
1871 unsafe impl crate::Zeroable for LaxForeignLayout {}
1872 unsafe impl crate::Pod for LaxForeignLayout {}
1873 impl crate::field_map::FieldMap for LaxForeignLayout {
1874 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1875 "amount",
1876 HopperHeader::SIZE,
1877 8,
1878 )];
1879 }
1880 impl LayoutContract for LaxForeignLayout {
1881 const DISC: u8 = 0x5A;
1882 const VERSION: u8 = 1;
1883 const LAYOUT_ID: [u8; 8] = [0x5A; 8];
1884 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1885 fn validate_header(data: &[u8]) -> ProgramResult {
1887 if crate::layout::read_disc(data) != Some(Self::DISC) {
1888 return ProgramError::err_invalid_data();
1889 }
1890 Ok(())
1891 }
1892 }
1893
1894 #[test]
1895 fn load_cross_program_guards_length_even_with_lax_foreign_header() {
1896 let required = HopperHeader::SIZE + 8;
1899 assert_eq!(LaxForeignLayout::required_len(), required);
1900
1901 let (_short_backing, short) = make_account(required - 1, 60);
1905 {
1906 let mut d = short.try_borrow_mut().unwrap();
1907 d[0] = LaxForeignLayout::DISC;
1908 }
1909 assert!(matches!(
1910 short.load_cross_program::<LaxForeignLayout>(),
1911 Err(ProgramError::AccountDataTooSmall)
1912 ));
1913
1914 let (_ok_backing, ok) = make_account(required, 61);
1916 {
1917 let mut d = ok.try_borrow_mut().unwrap();
1918 d[0] = LaxForeignLayout::DISC;
1919 }
1920 let view = ok.load_cross_program::<LaxForeignLayout>().unwrap();
1921 assert_eq!(view.amount, [0u8; 8]);
1922 }
1923
1924 #[repr(transparent)]
1925 #[derive(Clone, Copy)]
1926 struct ForgedProjection<const OFFSET: usize>([u8; 8]);
1927 unsafe impl<const O: usize> crate::Zeroable for ForgedProjection<O> {}
1929 unsafe impl<const O: usize> crate::Pod for ForgedProjection<O> {}
1931 impl<const O: usize> crate::field_map::FieldMap for ForgedProjection<O> {
1932 const FIELDS: &'static [crate::field_map::FieldInfo] = &[];
1933 }
1934 impl<const O: usize> LayoutContract for ForgedProjection<O> {
1935 const DISC: u8 = 1;
1936 const VERSION: u8 = 1;
1937 const LAYOUT_ID: [u8; 8] = [0; 8];
1938 const SIZE: usize = 0;
1939 const TYPE_OFFSET: usize = O;
1940 fn required_len() -> usize {
1941 0
1942 }
1943 fn validate_header(_: &[u8]) -> ProgramResult {
1944 Ok(())
1945 }
1946 }
1947 impl<const O: usize> crate::CompactLayout for ForgedProjection<O> {
1948 const DISC: u8 = 1;
1949 const BODY_SIZE: usize = 0;
1950 const COMPACT_LEN: usize = 0;
1951 fn validate_compact(_: &[u8]) -> ProgramResult {
1952 Ok(())
1953 }
1954 }
1955 impl<const O: usize> crate::CompactDynamicLayout for ForgedProjection<O> {
1956 const DISC: u8 = 1;
1957 const MIN_LEN: usize = 0;
1958 const TAIL_OFFSET: usize = O;
1959 fn validate_compact_dynamic(_: &[u8]) -> ProgramResult {
1960 Ok(())
1961 }
1962 }
1963
1964 #[test]
1965 fn typed_loads_do_not_trust_overridden_sizing_and_validation() {
1966 for len in 0..24 {
1967 let (_backing, view) = make_account(len, 81);
1968 assert!(matches!(
1969 view.load::<ForgedProjection<16>>(),
1970 Err(ProgramError::AccountDataTooSmall)
1971 ));
1972 assert!(matches!(
1973 view.load_mut::<ForgedProjection<16>>(),
1974 Err(ProgramError::AccountDataTooSmall)
1975 ));
1976 assert!(matches!(
1977 view.load_cross_program::<ForgedProjection<16>>(),
1978 Err(ProgramError::AccountDataTooSmall)
1979 ));
1980 }
1981 let (_backing, view) = make_account(24, 82);
1982 assert_eq!(view.load::<ForgedProjection<16>>().unwrap().0, [0; 8]);
1983 assert!(matches!(
1984 view.load::<ForgedProjection<{ usize::MAX }>>(),
1985 Err(ProgramError::ArithmeticOverflow)
1986 ));
1987 }
1988
1989 #[test]
1990 fn compact_loads_recheck_actual_body_bounds() {
1991 for len in 0..9 {
1992 let (_backing, view) = make_account(len, 83);
1993 assert!(matches!(
1994 view.load_compact::<ForgedProjection<9>>(),
1995 Err(ProgramError::AccountDataTooSmall)
1996 ));
1997 assert!(matches!(
1998 view.load_compact_mut::<ForgedProjection<9>>(),
1999 Err(ProgramError::AccountDataTooSmall)
2000 ));
2001 assert!(matches!(
2002 view.load_compact_dynamic::<ForgedProjection<9>>(),
2003 Err(ProgramError::AccountDataTooSmall)
2004 ));
2005 assert!(matches!(
2006 view.load_compact_dynamic_mut::<ForgedProjection<9>>(),
2007 Err(ProgramError::AccountDataTooSmall)
2008 ));
2009 assert_eq!(
2010 view.init_compact::<ForgedProjection<9>>(),
2011 Err(ProgramError::AccountDataTooSmall)
2012 );
2013 assert_eq!(
2014 view.init_compact_dynamic::<ForgedProjection<9>>(),
2015 Err(ProgramError::AccountDataTooSmall)
2016 );
2017 }
2018 let (_backing, view) = make_account(9, 84);
2019 assert_eq!(
2020 view.load_compact::<ForgedProjection<9>>().unwrap().0,
2021 [0; 8]
2022 );
2023 assert_eq!(
2024 view.load_compact_dynamic::<ForgedProjection<9>>()
2025 .unwrap()
2026 .0,
2027 [0; 8]
2028 );
2029 }
2030
2031 #[test]
2032 fn compact_init_rejects_overlapping_or_overflowing_tail_before_writing() {
2033 let (_backing, view) = make_account(16, 85);
2034 assert_eq!(
2035 view.init_compact_dynamic::<ForgedProjection<0>>(),
2036 Err(ProgramError::InvalidAccountData)
2037 );
2038 assert_eq!(
2039 view.init_compact_dynamic::<ForgedProjection<{ usize::MAX }>>(),
2040 Err(ProgramError::ArithmeticOverflow)
2041 );
2042 assert_eq!(&*view.try_borrow().unwrap(), &[0; 16]);
2043 }
2044
2045 fn make_account(
2046 total_data_len: usize,
2047 address_byte: u8,
2048 ) -> (std::vec::Vec<u64>, AccountView<'static>) {
2049 let mut backing = std::vec![0u64; (RuntimeAccount::SIZE + total_data_len).div_ceil(8)];
2050 let raw = backing.as_mut_ptr() as *mut RuntimeAccount;
2051 unsafe {
2053 raw.write(RuntimeAccount {
2054 borrow_state: NOT_BORROWED,
2055 is_signer: 1,
2056 is_writable: 1,
2057 executable: 0,
2058 resize_delta: 0,
2059 address: NativeAddress::new_from_array([address_byte; 32]),
2060 owner: NativeAddress::new_from_array([2; 32]),
2061 lamports: 42,
2062 data_len: total_data_len as u64,
2063 });
2064 }
2065 let backend = unsafe { NativeAccountView::new_unchecked(raw) };
2067 let account = AccountView::from_backend(backend);
2068 (backing, account)
2069 }
2070
2071 fn make_flagged_account(
2075 is_signer: u8,
2076 is_writable: u8,
2077 ) -> (std::vec::Vec<u64>, AccountView<'static>) {
2078 let mut backing = std::vec![0u64; (RuntimeAccount::SIZE).div_ceil(8)];
2079 let raw = backing.as_mut_ptr() as *mut RuntimeAccount;
2080 unsafe {
2083 raw.write(RuntimeAccount {
2084 borrow_state: NOT_BORROWED,
2085 is_signer,
2086 is_writable,
2087 executable: 0,
2088 resize_delta: 0,
2089 address: NativeAddress::new_from_array([9; 32]),
2090 owner: NativeAddress::new_from_array([2; 32]),
2091 lamports: 0,
2092 data_len: 0,
2093 });
2094 }
2095 let backend = unsafe { NativeAccountView::new_unchecked(raw) };
2097 (backing, AccountView::from_backend(backend))
2098 }
2099
2100 #[test]
2101 fn expect_signer_writable_keeps_distinct_errors_and_passes_valid() {
2102 let (_b, both) = make_flagged_account(1, 1);
2104 assert!(both.expect_signer_writable(true, true).is_ok());
2105
2106 let (_b, no_signer) = make_flagged_account(0, 1);
2108 assert!(matches!(
2109 no_signer.expect_signer_writable(true, true),
2110 Err(ProgramError::MissingRequiredSignature)
2111 ));
2112
2113 let (_b, no_writable) = make_flagged_account(1, 0);
2115 assert!(matches!(
2116 no_writable.expect_signer_writable(true, true),
2117 Err(ProgramError::Immutable)
2118 ));
2119
2120 let (_b, signer_only) = make_flagged_account(1, 0);
2122 assert!(signer_only.expect_signer_writable(true, false).is_ok());
2123 let (_b, writable_only) = make_flagged_account(0, 1);
2124 assert!(writable_only.expect_signer_writable(false, true).is_ok());
2125
2126 let (_b, neither) = make_flagged_account(0, 0);
2128 assert!(neither.expect_signer_writable(false, false).is_ok());
2129
2130 assert!(matches!(
2132 neither.expect_signer_writable(true, false),
2133 Err(ProgramError::MissingRequiredSignature)
2134 ));
2135 assert!(matches!(
2137 neither.expect_signer_writable(false, true),
2138 Err(ProgramError::Immutable)
2139 ));
2140 }
2141
2142 #[test]
2143 fn load_mut_is_zero_copy_and_pointer_stable() {
2144 let (_backing, account) = make_account(TestLayout::SIZE + 8, 1);
2145
2146 {
2147 let mut data = account.try_borrow_mut().unwrap();
2148 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2149 data[HopperHeader::SIZE..HopperHeader::SIZE + 8].copy_from_slice(&10u64.to_le_bytes());
2150 data[HopperHeader::SIZE + 8..HopperHeader::SIZE + 16]
2151 .copy_from_slice(&20u64.to_le_bytes());
2152 data[TestLayout::SIZE..TestLayout::SIZE + 8].copy_from_slice(b"tailpass");
2153 }
2154
2155 let first_ptr = {
2156 let first = account.load::<TestLayout>().unwrap();
2157 assert_eq!(from_le_u64(first.a), 10);
2158 assert_eq!(from_le_u64(first.b), 20);
2159 first.as_ptr() as usize
2160 };
2161
2162 {
2163 let tail = account.extension_bytes::<TestLayout>().unwrap();
2164 assert_eq!(&tail[..8], b"tailpass");
2165 }
2166
2167 let mut second = account.load_mut::<TestLayout>().unwrap();
2168 let second_ptr = second.as_mut_ptr() as usize;
2169 second.b = le_u64(99);
2170 assert_eq!(first_ptr, second_ptr);
2171 drop(second);
2172
2173 let reread = account.load::<TestLayout>().unwrap();
2174 assert_eq!(from_le_u64(reread.a), 10);
2175 assert_eq!(from_le_u64(reread.b), 99);
2176 }
2177
2178 #[repr(C)]
2179 #[derive(Clone, Copy, Debug, Default)]
2180 struct CompactVault {
2181 authority: [u8; 32],
2182 balance: [u8; 8],
2183 }
2184 unsafe impl crate::Zeroable for CompactVault {}
2185 unsafe impl crate::Pod for CompactVault {}
2186 impl crate::CompactLayout for CompactVault {
2187 const DISC: u8 = 1;
2188 }
2189
2190 #[test]
2191 fn compact_load_uses_one_byte_header_and_body_at_offset_one() {
2192 assert_eq!(CompactVault::COMPACT_LEN, 1 + 40);
2195 let headered_len = HopperHeader::SIZE + CompactVault::BODY_SIZE;
2196 assert_eq!(
2197 headered_len - CompactVault::COMPACT_LEN,
2198 HopperHeader::SIZE - 1
2199 );
2200
2201 let (_backing, account) = make_account(CompactVault::COMPACT_LEN, 50);
2202
2203 account.init_compact::<CompactVault>().unwrap();
2204 {
2205 let data = account.try_borrow().unwrap();
2207 assert_eq!(data[0], 1);
2208 }
2209
2210 {
2211 let mut v = account.load_compact_mut::<CompactVault>().unwrap();
2212 v.authority = [9u8; 32];
2213 v.balance = 1234u64.to_le_bytes();
2214 }
2215
2216 let v = account.load_compact::<CompactVault>().unwrap();
2217 assert_eq!(v.authority, [9u8; 32]);
2218 assert_eq!(u64::from_le_bytes(v.balance), 1234);
2219
2220 let data = account.try_borrow().unwrap();
2222 let base = data.as_bytes_ptr() as usize;
2223 let body = (&*v) as *const CompactVault as usize;
2224 assert_eq!(body, base + 1);
2225 }
2226
2227 #[test]
2228 fn compact_load_rejects_wrong_disc() {
2229 let (_backing, account) = make_account(CompactVault::COMPACT_LEN, 51);
2230 {
2231 let mut data = account.try_borrow_mut().unwrap();
2232 data[0] = 2; }
2234 assert_eq!(
2235 account.load_compact::<CompactVault>().unwrap_err(),
2236 ProgramError::InvalidAccountData
2237 );
2238 }
2239
2240 #[test]
2241 fn compact_load_rejects_short_buffer() {
2242 let (_backing, account) = make_account(CompactVault::COMPACT_LEN - 1, 52);
2243 account
2244 .try_borrow_mut()
2245 .map(|mut d| d[0] = CompactVault::DISC)
2246 .unwrap();
2247 assert_eq!(
2248 account.load_compact::<CompactVault>().unwrap_err(),
2249 ProgramError::AccountDataTooSmall
2250 );
2251 }
2252
2253 #[test]
2254 fn compact_load_rejects_oversized_fixed_buffer() {
2255 let (_backing, account) = make_account(CompactVault::COMPACT_LEN + 1, 53);
2256 {
2257 let mut data = account.try_borrow_mut().unwrap();
2258 data[0] = CompactVault::DISC;
2259 }
2260 assert_eq!(
2261 account.load_compact::<CompactVault>().unwrap_err(),
2262 ProgramError::InvalidAccountData
2263 );
2264 assert_eq!(
2265 account.init_compact::<CompactVault>().unwrap_err(),
2266 ProgramError::InvalidAccountData
2267 );
2268 }
2269
2270 #[repr(C)]
2272 #[derive(Clone, Copy, Debug, Default)]
2273 struct CompactDynHead {
2274 owner: [u8; 32],
2275 count: [u8; 8],
2276 }
2277 unsafe impl crate::Zeroable for CompactDynHead {}
2278 unsafe impl crate::Pod for CompactDynHead {}
2279 impl crate::CompactDynamicLayout for CompactDynHead {
2280 const DISC: u8 = 9;
2281 }
2282
2283 #[test]
2284 fn compact_dynamic_loads_head_with_a_growable_tail() {
2285 use crate::CompactDynamicLayout;
2286 assert_eq!(CompactDynHead::FIXED_HEAD_SIZE, 40);
2287 assert_eq!(CompactDynHead::MIN_LEN, 41);
2288 assert_eq!(CompactDynHead::TAIL_OFFSET, 41);
2289
2290 let total = CompactDynHead::MIN_LEN + 4 + 16;
2292 let (_backing, account) = make_account(total, 70);
2293
2294 account.init_compact_dynamic::<CompactDynHead>().unwrap();
2296 {
2297 let data = account.try_borrow().unwrap();
2298 assert_eq!(data[0], 9);
2299 let prefix = u32::from_le_bytes(
2300 data[CompactDynHead::TAIL_OFFSET..CompactDynHead::TAIL_OFFSET + 4]
2301 .try_into()
2302 .unwrap(),
2303 );
2304 assert_eq!(prefix, 0);
2305 }
2306
2307 {
2310 let mut head = account
2311 .load_compact_dynamic_mut::<CompactDynHead>()
2312 .unwrap();
2313 head.owner = [7u8; 32];
2314 head.count = 5u64.to_le_bytes();
2315 }
2316 let head = account.load_compact_dynamic::<CompactDynHead>().unwrap();
2317 assert_eq!(head.owner, [7u8; 32]);
2318 assert_eq!(u64::from_le_bytes(head.count), 5);
2319
2320 let data = account.try_borrow().unwrap();
2322 let base = data.as_bytes_ptr() as usize;
2323 assert_eq!((&*head) as *const CompactDynHead as usize, base + 1);
2324 }
2325
2326 #[test]
2327 fn compact_dynamic_rejects_short_and_wrong_disc() {
2328 use crate::CompactDynamicLayout;
2329 let (_b1, short) = make_account(CompactDynHead::MIN_LEN - 1, 71);
2331 short
2332 .try_borrow_mut()
2333 .map(|mut d| d[0] = CompactDynHead::DISC)
2334 .unwrap();
2335 assert_eq!(
2336 short.load_compact_dynamic::<CompactDynHead>().unwrap_err(),
2337 ProgramError::AccountDataTooSmall
2338 );
2339
2340 let (_b2, bad) = make_account(CompactDynHead::MIN_LEN + 8, 72);
2342 bad.try_borrow_mut().map(|mut d| d[0] = 3).unwrap();
2343 assert_eq!(
2344 bad.load_compact_dynamic::<CompactDynHead>().unwrap_err(),
2345 ProgramError::InvalidAccountData
2346 );
2347 }
2348
2349 #[test]
2350 fn close_refuses_while_data_borrow_is_live() {
2351 let (_backing, account) = make_account(16, 90);
2355 {
2356 let _data = account.try_borrow().unwrap();
2357 assert_eq!(
2358 account.close().unwrap_err(),
2359 ProgramError::AccountBorrowFailed
2360 );
2361 }
2362 account.close().unwrap();
2364 assert_eq!(account.data_len(), 0);
2365 assert_eq!(account.lamports(), 0);
2366 }
2367
2368 #[test]
2369 fn close_to_refusal_preserves_source_and_recipient() {
2370 let (_source_backing, source) = make_account(16, 91);
2371 let (_dest_backing, destination) = make_account(16, 92);
2372 let before = (source.lamports(), destination.lamports());
2373 let borrowed = source.try_borrow().unwrap();
2374 assert_eq!(
2375 source.close_to(&destination, &Address::new([2; 32])),
2376 Err(ProgramError::AccountBorrowFailed)
2377 );
2378 assert_eq!((source.lamports(), destination.lamports()), before);
2379 assert_eq!(&*borrowed, &[0; 16]);
2380 }
2381
2382 #[test]
2383 fn close_to_rejects_the_same_account_as_recipient() {
2384 let (_backing, source) = make_account(16, 93);
2385 let before = source.lamports();
2386 assert_eq!(
2387 source.close_to(&source, &Address::new([2; 32])),
2388 Err(ProgramError::InvalidArgument)
2389 );
2390 assert_eq!(source.lamports(), before);
2391 assert_eq!(source.data_len(), 16);
2392 }
2393
2394 #[test]
2395 fn check_owned_by_any_accepts_listed_owner_and_rejects_others() {
2396 let (_backing, account) = make_account(8, 80);
2398 let token = Address::new([2; 32]); let token_2022 = Address::new([9; 32]);
2400 let other = Address::new([3; 32]);
2401
2402 assert!(account.check_owned_by_any(&[&token_2022, &token]).is_ok());
2405 assert!(account.check_owned_by_any(&[&token]).is_ok());
2406
2407 assert!(account.check_owned_by_any(&[&token_2022, &other]).is_err());
2409
2410 assert!(account.check_owned_by_any(&[]).is_err());
2412 }
2413
2414 #[test]
2415 fn default_layout_accepts_legacy_zero_epoch() {
2416 let (_backing, account) = make_account(TestLayout::SIZE, 43);
2417 {
2418 let mut data = account.try_borrow_mut().unwrap();
2419 crate::layout::write_header_with_epoch(
2420 &mut data,
2421 TestLayout::DISC,
2422 TestLayout::VERSION,
2423 &TestLayout::LAYOUT_ID,
2424 0,
2425 )
2426 .unwrap();
2427 }
2428
2429 assert!(account.load::<TestLayout>().is_ok());
2430 }
2431
2432 #[test]
2433 fn init_header_stamps_layout_schema_epoch() {
2434 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 44);
2435 {
2436 let mut data = account.try_borrow_mut().unwrap();
2437 crate::layout::init_header::<EpochTwoLayout>(&mut data).unwrap();
2438 assert_eq!(crate::layout::read_schema_epoch(&data), Some(2));
2439 }
2440
2441 assert!(account.load::<EpochTwoLayout>().is_ok());
2442 }
2443
2444 #[test]
2445 fn typed_load_rejects_schema_epoch_mismatch() {
2446 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 45);
2447 {
2448 let mut data = account.try_borrow_mut().unwrap();
2449 crate::layout::write_header_with_epoch(
2450 &mut data,
2451 EpochTwoLayout::DISC,
2452 EpochTwoLayout::VERSION,
2453 &EpochTwoLayout::LAYOUT_ID,
2454 1,
2455 )
2456 .unwrap();
2457 }
2458
2459 assert_eq!(
2460 account.load::<EpochTwoLayout>().unwrap_err(),
2461 ProgramError::InvalidAccountData
2462 );
2463 }
2464
2465 #[test]
2466 fn layout_info_matches_checks_schema_epoch() {
2467 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 46);
2468 {
2469 let mut data = account.try_borrow_mut().unwrap();
2470 crate::layout::write_header_with_epoch(
2471 &mut data,
2472 EpochTwoLayout::DISC,
2473 EpochTwoLayout::VERSION,
2474 &EpochTwoLayout::LAYOUT_ID,
2475 1,
2476 )
2477 .unwrap();
2478 }
2479 assert!(!account.layout_info().unwrap().matches::<EpochTwoLayout>());
2480
2481 {
2482 let mut data = account.try_borrow_mut().unwrap();
2483 crate::layout::write_header_with_epoch(
2484 &mut data,
2485 EpochTwoLayout::DISC,
2486 EpochTwoLayout::VERSION,
2487 &EpochTwoLayout::LAYOUT_ID,
2488 EpochTwoLayout::SCHEMA_EPOCH,
2489 )
2490 .unwrap();
2491 }
2492 assert!(account.layout_info().unwrap().matches::<EpochTwoLayout>());
2493 }
2494
2495 #[test]
2496 fn typed_load_holds_borrow_until_drop() {
2497 let (_backing, account) = make_account(TestLayout::SIZE, 3);
2498
2499 {
2500 let mut data = account.try_borrow_mut().unwrap();
2501 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2502 }
2503
2504 let shared = account.load::<TestLayout>().unwrap();
2505 assert_eq!(
2506 account.load_mut::<TestLayout>().unwrap_err(),
2507 ProgramError::AccountBorrowFailed
2508 );
2509 drop(shared);
2510 assert!(account.load_mut::<TestLayout>().is_ok());
2511 }
2512
2513 #[test]
2514 fn duplicate_address_aliases_are_rejected_across_views() {
2515 let (_first_backing, first) = make_account(TestLayout::SIZE, 9);
2516 let (_second_backing, second) = make_account(TestLayout::SIZE, 9);
2517
2518 let first_shared = first.try_borrow().unwrap();
2519 let second_shared = second.try_borrow().unwrap();
2520 assert_eq!(
2521 second.try_borrow_mut().unwrap_err(),
2522 ProgramError::AccountBorrowFailed
2523 );
2524 drop(first_shared);
2525 drop(second_shared);
2526 assert!(second.try_borrow_mut().is_ok());
2527 }
2528
2529 #[test]
2530 fn load_rejects_wrong_disc_and_wrong_version() {
2531 let (_backing, account) = make_account(TestLayout::SIZE, 4);
2532
2533 {
2534 let mut data = account.try_borrow_mut().unwrap();
2535 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2536 }
2537
2538 {
2539 let mut data = account.try_borrow_mut().unwrap();
2540 data[0] = TestLayout::DISC.wrapping_add(1);
2541 }
2542 assert_eq!(
2543 account.load::<TestLayout>().unwrap_err(),
2544 ProgramError::InvalidAccountData
2545 );
2546
2547 {
2548 let mut data = account.try_borrow_mut().unwrap();
2549 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2550 data[1] = TestLayout::VERSION.wrapping_add(1);
2551 }
2552 assert_eq!(
2553 account.load::<TestLayout>().unwrap_err(),
2554 ProgramError::InvalidAccountData
2555 );
2556 }
2557
2558 #[test]
2559 fn load_rejects_undersized_layout_body() {
2560 let (_backing, account) = make_account(TestLayout::SIZE - 1, 5);
2561
2562 {
2563 let mut data = account.try_borrow_mut().unwrap();
2564 data[0] = TestLayout::DISC;
2565 data[1] = TestLayout::VERSION;
2566 data[4..12].copy_from_slice(&TestLayout::LAYOUT_ID);
2567 }
2568
2569 assert_eq!(
2570 account.load::<TestLayout>().unwrap_err(),
2571 ProgramError::AccountDataTooSmall
2572 );
2573 }
2574
2575 #[test]
2576 fn load_supports_header_inclusive_layouts() {
2577 let (_backing, account) = make_account(HeaderLayout::SIZE, 6);
2578
2579 {
2580 let mut data = account.try_borrow_mut().unwrap();
2581 crate::layout::init_header::<HeaderLayout>(&mut data).unwrap();
2582 }
2583
2584 {
2585 let mut layout = account.load_mut::<HeaderLayout>().unwrap();
2586 layout.amount = le_u64(55);
2587 }
2588
2589 let layout = account.load::<HeaderLayout>().unwrap();
2590 assert_eq!(layout.header[0], HeaderLayout::DISC);
2591 assert_eq!(layout.header[1], HeaderLayout::VERSION);
2592 assert_eq!(from_le_u64(layout.amount), 55);
2593 }
2594
2595 #[test]
2605 fn live_load_blocks_segment_mut() {
2606 let (_backing, account) = make_account(TestLayout::SIZE, 10);
2607 {
2608 let mut data = account.try_borrow_mut().unwrap();
2609 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2610 }
2611
2612 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2613 let _read_view = account.load::<TestLayout>().unwrap();
2614
2615 let err = account
2617 .segment_mut::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2618 .unwrap_err();
2619 assert_eq!(err, ProgramError::AccountBorrowFailed);
2620 }
2621
2622 #[test]
2623 fn live_load_mut_blocks_segment_ref() {
2624 let (_backing, account) = make_account(TestLayout::SIZE, 11);
2625 {
2626 let mut data = account.try_borrow_mut().unwrap();
2627 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2628 }
2629
2630 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2631 let _write_view = account.load_mut::<TestLayout>().unwrap();
2632
2633 let err = account
2636 .segment_ref::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2637 .unwrap_err();
2638 assert_eq!(err, ProgramError::AccountBorrowFailed);
2639 }
2640
2641 #[test]
2642 fn every_access_path_is_tracked() {
2643 let (_backing, account) = make_account(TestLayout::SIZE, 40);
2651 {
2652 let mut data = account.try_borrow_mut().unwrap();
2653 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2654 }
2655 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2656
2657 {
2659 let _r = account.try_borrow().unwrap();
2660 assert!(account.try_borrow_mut().is_err());
2661 }
2662 {
2664 let _w = account.try_borrow_mut().unwrap();
2665 assert!(account.try_borrow().is_err());
2666 }
2667 {
2669 let _v = account.load::<TestLayout>().unwrap();
2670 assert!(account.load_mut::<TestLayout>().is_err());
2671 }
2672 {
2674 let _v = account.load_mut::<TestLayout>().unwrap();
2675 assert!(account.load::<TestLayout>().is_err());
2676 }
2677 {
2679 let _r = unsafe { account.raw_ref::<[u8; 16]>() }.unwrap();
2681 assert!(account.load_mut::<TestLayout>().is_err());
2682 }
2683 {
2685 let _w = unsafe { account.raw_mut::<[u8; 16]>() }.unwrap();
2687 assert!(account.load::<TestLayout>().is_err());
2688 }
2689 {
2692 let _r = account
2693 .segment_ref::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2694 .unwrap();
2695 }
2701 assert_eq!(borrows.len(), 0);
2707 let _w = account
2708 .segment_mut::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2709 .unwrap();
2710 }
2711
2712 #[test]
2717 fn seg_lease_releases_on_drop_and_allows_reacquire() {
2718 let (_backing, account) = make_account(TestLayout::SIZE, 41);
2719 {
2720 let mut data = account.try_borrow_mut().unwrap();
2721 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2722 }
2723 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2724 const OFF: u32 = crate::layout::HopperHeader::SIZE as u32;
2725
2726 {
2727 let mut first = account
2728 .segment_mut::<[u8; 8]>(&mut borrows, OFF, 8)
2729 .unwrap();
2730 *first = le_u64(100);
2731 }
2732 assert_eq!(borrows.len(), 0);
2734 {
2737 let mut second = account
2738 .segment_mut::<[u8; 8]>(&mut borrows, OFF, 8)
2739 .unwrap();
2740 assert_eq!(from_le_u64(*second), 100);
2741 *second = le_u64(200);
2742 }
2743 assert_eq!(borrows.len(), 0);
2744 let read = account
2745 .segment_ref::<[u8; 8]>(&mut borrows, OFF, 8)
2746 .unwrap();
2747 assert_eq!(from_le_u64(*read), 200);
2748 }
2749
2750 #[test]
2754 fn seg_lease_still_rejects_simultaneous_overlap() {
2755 let (_backing, account) = make_account(TestLayout::SIZE, 42);
2756 {
2757 let mut data = account.try_borrow_mut().unwrap();
2758 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2759 }
2760 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2761 const OFF: u32 = crate::layout::HopperHeader::SIZE as u32;
2762
2763 let _first = account
2764 .segment_mut::<[u8; 8]>(&mut borrows, OFF, 8)
2765 .unwrap();
2766 drop(_first);
2773 assert_eq!(borrows.len(), 0);
2774 }
2775
2776 #[test]
2777 fn split_segments_mut_borrows_two_disjoint_ranges() {
2778 let (_backing, account) = make_account(TestLayout::SIZE, 43);
2779 {
2780 let mut data = account.try_borrow_mut().unwrap();
2781 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2782 }
2783 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2784 const A: u32 = HopperHeader::SIZE as u32; const B: u32 = HopperHeader::SIZE as u32 + 8; {
2788 let mut segs = account
2789 .split_segments_mut::<[u8; 8], 2>(&mut borrows, [(A, 8), (B, 8)])
2790 .unwrap();
2791 assert_eq!(segs.len(), 2);
2792 let [a, b] = segs.all_mut();
2794 *a = le_u64(111);
2795 *b = le_u64(222);
2796 }
2797 assert_eq!(borrows.len(), 0);
2799
2800 let a = account.segment_ref::<[u8; 8]>(&mut borrows, A, 8).unwrap();
2801 assert_eq!(from_le_u64(*a), 111);
2802 drop(a);
2803 let b = account.segment_ref::<[u8; 8]>(&mut borrows, B, 8).unwrap();
2804 assert_eq!(from_le_u64(*b), 222);
2805 }
2806
2807 #[test]
2808 fn split_segments_mut_rejects_overlap_and_rolls_back() {
2809 let (_backing, account) = make_account(TestLayout::SIZE, 44);
2810 {
2811 let mut data = account.try_borrow_mut().unwrap();
2812 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2813 }
2814 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2815 const A: u32 = HopperHeader::SIZE as u32;
2816
2817 let err = account
2820 .split_segments_mut::<[u8; 8], 2>(&mut borrows, [(A, 8), (A + 4, 8)])
2821 .unwrap_err();
2822 assert_eq!(err, ProgramError::AccountBorrowFailed);
2823 assert_eq!(borrows.len(), 0);
2824
2825 let err = account
2827 .split_segments_mut::<[u8; 8], 2>(&mut borrows, [(A, 8), (9_000, 8)])
2828 .unwrap_err();
2829 assert_eq!(err, ProgramError::AccountDataTooSmall);
2830 assert_eq!(borrows.len(), 0);
2831 }
2832
2833 #[test]
2834 fn typed_segment_api_round_trips() {
2835 use crate::segment::TypedSegment;
2836
2837 let (_backing, account) = make_account(TestLayout::SIZE, 22);
2838 {
2839 let mut data = account.try_borrow_mut().unwrap();
2840 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2841 }
2842
2843 const A_TYPED: TypedSegment<[u8; 8], { crate::layout::HopperHeader::SIZE as u32 }> =
2844 TypedSegment::new();
2845
2846 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2850 {
2851 let mut a = account
2852 .segment_mut_typed::<[u8; 8], { crate::layout::HopperHeader::SIZE as u32 }>(
2853 &mut borrows,
2854 A_TYPED,
2855 )
2856 .unwrap();
2857 *a = le_u64(1337);
2858 }
2859 assert_eq!(borrows.len(), 0);
2860
2861 let read = account
2862 .segment_ref_typed::<[u8; 8], { crate::layout::HopperHeader::SIZE as u32 }>(
2863 &mut borrows,
2864 A_TYPED,
2865 )
2866 .unwrap();
2867 assert_eq!(from_le_u64(*read), 1337);
2868 }
2869
2870 #[test]
2871 fn const_segment_api_matches_manual_offsets() {
2872 use crate::segment::Segment;
2873
2874 let (_backing, account) = make_account(TestLayout::SIZE, 20);
2875 {
2876 let mut data = account.try_borrow_mut().unwrap();
2877 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2878 }
2879
2880 const A_SEG: Segment = Segment::body(0, 8); let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2885 {
2886 let mut a = account
2887 .segment_mut_const::<[u8; 8]>(&mut borrows, A_SEG)
2888 .unwrap();
2889 *a = le_u64(7);
2890 }
2891 let read = account
2892 .segment_ref::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2893 .unwrap();
2894 assert_eq!(from_le_u64(*read), 7);
2895 }
2896
2897 #[test]
2898 fn load_after_segment_drop_succeeds() {
2899 let (_backing, account) = make_account(TestLayout::SIZE, 12);
2900 {
2901 let mut data = account.try_borrow_mut().unwrap();
2902 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2903 }
2904
2905 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2906 {
2907 let mut seg = account
2908 .segment_mut::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2909 .unwrap();
2910 *seg = le_u64(42);
2911 }
2912 let view = account.load::<TestLayout>().unwrap();
2914 assert_eq!(from_le_u64(view.a), 42);
2915 }
2916
2917 #[test]
2923 #[cfg(not(feature = "unguarded-raw-surfaces"))]
2924 fn zero_range_is_gated_over_exactly_the_cleared_bytes() {
2925 use crate::write_policy::{
2926 install_lamport_gate, write_policy_violation, WritePolicy, WriteRange,
2927 };
2928
2929 let (_b0, a0) = make_account(32, 70);
2930 let accounts = [a0];
2931 static TAIL: WritePolicy = WritePolicy::new(&[WriteRange::tail_from(0, 16)]);
2933
2934 {
2935 let mut data = accounts[0].try_borrow_mut().unwrap();
2936 for byte in data.iter_mut() {
2937 *byte = 0xAA;
2938 }
2939 }
2940
2941 let _gate = install_lamport_gate(&accounts, &TAIL);
2942
2943 assert!(accounts[0].zero_range(16, 16).is_ok());
2945 assert_eq!(
2947 accounts[0].zero_range(8, 16),
2948 Err(write_policy_violation(0)),
2949 );
2950 assert_eq!(accounts[0].zero_range(0, 8), Err(write_policy_violation(0)));
2952 assert!(accounts[0].zero_range(0, 0).is_ok());
2954 assert_eq!(
2956 accounts[0].zero_range(24, 16),
2957 Err(ProgramError::AccountDataTooSmall),
2958 );
2959
2960 drop(_gate);
2961 let data = accounts[0].try_borrow().unwrap();
2962 assert!(
2963 data[16..32].iter().all(|b| *b == 0),
2964 "the authorized range was actually cleared"
2965 );
2966 assert!(
2967 data[0..16].iter().all(|b| *b == 0xAA),
2968 "refused ranges left the head untouched"
2969 );
2970 }
2971
2972 #[test]
2980 #[cfg(not(feature = "unguarded-raw-surfaces"))]
2981 fn zero_appended_rides_the_transition_authority_not_the_byte_ranges() {
2982 use crate::write_policy::{
2983 install_lamport_gate, write_policy_violation, WritePolicy, WriteRange,
2984 };
2985
2986 let (_b0, a0) = make_account(32, 72);
2987 let (_bf, foreign) = make_account(32, 73);
2988 let accounts = [a0];
2989 static NARROW: WritePolicy = WritePolicy::new(&[WriteRange::new(0, 0, 8)]);
2992
2993 {
2994 let mut data = accounts[0].try_borrow_mut().unwrap();
2995 for byte in data.iter_mut() {
2996 *byte = 0xCC;
2997 }
2998 }
2999
3000 let _gate = install_lamport_gate(&accounts, &NARROW);
3001
3002 assert!(accounts[0].zero_appended(16).is_ok());
3006
3007 assert_eq!(
3010 foreign.zero_appended(16),
3011 Err(write_policy_violation(u8::MAX)),
3012 );
3013
3014 assert!(accounts[0].zero_appended(32).is_ok());
3019 assert!(accounts[0].zero_appended(64).is_ok());
3020
3021 drop(_gate);
3022 let data = accounts[0].try_borrow().unwrap();
3023 assert!(
3024 data[16..32].iter().all(|b| *b == 0),
3025 "the appended region was cleared"
3026 );
3027 assert!(
3028 data[0..16].iter().all(|b| *b == 0xCC),
3029 "the pre-existing body was untouched"
3030 );
3031 }
3032
3033 #[test]
3041 #[cfg(not(feature = "unguarded-raw-surfaces"))]
3042 fn extension_bytes_mut_is_governed_over_its_exact_range() {
3043 use crate::write_policy::{
3044 install_lamport_gate, write_policy_violation, WritePolicy, WriteRange,
3045 };
3046
3047 const EXT_LEN: usize = 8;
3048 let (_backing, account) = make_account(TestLayout::SIZE + EXT_LEN, 60);
3049 {
3050 let mut data = account.try_borrow_mut().unwrap();
3051 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
3052 }
3053 let accounts = [account];
3054
3055 {
3057 let ext = accounts[0].extension_bytes_mut::<TestLayout>().unwrap();
3058 assert_eq!(ext.len(), EXT_LEN);
3059 }
3060
3061 {
3065 static HEAD_ONLY: WritePolicy = WritePolicy::new(&[WriteRange::new(0, 0, 8)]);
3066 let _gate = install_lamport_gate(&accounts, &HEAD_ONLY);
3067 assert_eq!(
3068 accounts[0].extension_bytes_mut::<TestLayout>().map(|_| ()),
3069 Err(write_policy_violation(0)),
3070 );
3071 }
3072
3073 {
3076 static TAIL: WritePolicy =
3077 WritePolicy::new(&[WriteRange::tail_from(0, TestLayout::SIZE as u32)]);
3078 let _gate = install_lamport_gate(&accounts, &TAIL);
3079 let ext = accounts[0].extension_bytes_mut::<TestLayout>().unwrap();
3080 assert_eq!(ext.len(), EXT_LEN);
3081 }
3082
3083 {
3085 static WHOLE: WritePolicy = WritePolicy::new(&[WriteRange::whole_account(0)]);
3086 let _gate = install_lamport_gate(&accounts, &WHOLE);
3087 assert!(accounts[0].extension_bytes_mut::<TestLayout>().is_ok());
3088 }
3089
3090 let (_short_backing, short) = make_account(TestLayout::SIZE - 1, 61);
3094 assert_eq!(
3095 short.extension_bytes_mut::<TestLayout>().map(|_| ()),
3096 Err(ProgramError::AccountDataTooSmall),
3097 );
3098 }
3099}