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 = unsafe { self.backend().segment_ref_unchecked::<T>(abs_offset) };
340 let native_ref = match native_ref {
341 Ok(nr) => nr,
342 Err(e) => {
343 borrows.release(&borrow);
347 return Err(ProgramError::from(e));
348 }
349 };
350 let (typed_ref, state_ptr) = native_ref.into_raw_parts();
351 Ref::from_segment(typed_ref as *const T, state_ptr)
352 };
353 #[cfg(not(target_os = "solana"))]
354 let inner: Ref<'_, T> = {
355 let data = match self.try_borrow() {
356 Ok(d) => d,
357 Err(e) => {
358 borrows.release(&borrow);
359 return Err(e);
360 }
361 };
362 let ptr = unsafe { data.as_bytes_ptr().add(abs_offset as usize) as *const T };
364 unsafe { data.project(ptr) }
365 };
366
367 let lease = unsafe { crate::SegmentLease::new(borrows, borrow) };
370 Ok(crate::SegRef::new(inner, lease))
371 }
372
373 #[inline(always)]
384 pub fn segment_mut<'a, T: crate::Pod>(
385 &'a self,
386 borrows: &'a mut SegmentBorrowRegistry,
387 abs_offset: u32,
388 size: u32,
389 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
390 crate::write_policy::check_data_mutation(self.address(), abs_offset, size)?;
391 self.segment_mut_ungated::<T>(borrows, abs_offset, size)
392 }
393
394 #[inline(always)]
399 pub(crate) fn segment_mut_ungated<'a, T: crate::Pod>(
400 &'a self,
401 borrows: &'a mut SegmentBorrowRegistry,
402 abs_offset: u32,
403 size: u32,
404 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
405 self.check_writable()?;
406
407 let expected_size = core::mem::size_of::<T>() as u32;
408 if size != expected_size {
409 return ProgramError::err_invalid_argument();
410 }
411
412 let end = abs_offset
413 .checked_add(size)
414 .ok_or(ProgramError::ArithmeticOverflow)?;
415 if end as usize > self.data_len() {
416 return ProgramError::err_data_too_small();
417 }
418
419 let borrow = borrows.register_leased_write(self.address(), abs_offset, size)?;
420
421 #[cfg(target_os = "solana")]
422 let inner: RefMut<'_, T> = {
423 let native_ref = unsafe { self.backend().segment_mut_unchecked::<T>(abs_offset) };
425 let native_ref = match native_ref {
426 Ok(nr) => nr,
427 Err(e) => {
428 borrows.release(&borrow);
429 return Err(ProgramError::from(e));
430 }
431 };
432 let (typed_ref, state_ptr) = native_ref.into_raw_parts();
433 RefMut::from_segment(typed_ref as *mut T, state_ptr)
434 };
435 #[cfg(not(target_os = "solana"))]
436 let inner: RefMut<'_, T> = {
437 let mut data = match self.try_borrow_mut_ungated() {
438 Ok(d) => d,
439 Err(e) => {
440 borrows.release(&borrow);
441 return Err(e);
442 }
443 };
444 let ptr = unsafe { data.as_bytes_mut_ptr().add(abs_offset as usize) as *mut T };
446 unsafe { data.project(ptr) }
447 };
448
449 let lease = unsafe { crate::SegmentLease::new(borrows, borrow) };
451 Ok(crate::SegRefMut::new(inner, lease))
452 }
453
454 pub fn split_segments_mut<'a, T: crate::Pod, const N: usize>(
481 &'a self,
482 borrows: &'a mut SegmentBorrowRegistry,
483 ranges: [(u32, u32); N],
484 ) -> Result<crate::SegmentsMut<'a, T, N>, ProgramError> {
485 for (off, size) in ranges {
490 crate::write_policy::check_data_mutation(self.address(), off, size)?;
491 }
492 self.split_segments_mut_ungated::<T, N>(borrows, ranges)
493 }
494
495 pub(crate) fn split_segments_mut_ungated<'a, T: crate::Pod, const N: usize>(
500 &'a self,
501 borrows: &'a mut SegmentBorrowRegistry,
502 ranges: [(u32, u32); N],
503 ) -> Result<crate::SegmentsMut<'a, T, N>, ProgramError> {
504 self.check_writable()?;
505 let expected = core::mem::size_of::<T>() as u32;
506 let data_len = self.data_len();
507
508 let mut recs: [core::mem::MaybeUninit<crate::segment_borrow::SegmentBorrow>; N] =
519 unsafe { core::mem::MaybeUninit::uninit().assume_init() };
520 let mut offsets = [0usize; N];
521 let mut i = 0;
522 while i < N {
523 let (off, size) = ranges[i];
524 let in_bounds = match off.checked_add(size) {
525 Some(end) => end as usize <= data_len,
526 None => false,
527 };
528 if size != expected || !in_bounds {
529 unsafe { release_registered(borrows, &recs, i) };
531 return if size != expected {
532 ProgramError::err_invalid_argument()
533 } else {
534 ProgramError::err_data_too_small()
535 };
536 }
537 match borrows.register_leased_write(self.address(), off, size) {
538 Ok(b) => {
539 recs[i] = core::mem::MaybeUninit::new(b);
540 offsets[i] = off as usize;
541 }
542 Err(e) => {
543 unsafe { release_registered(borrows, &recs, i) };
545 return Err(e);
546 }
547 }
548 i += 1;
549 }
550
551 let data = match self.try_borrow_mut_ungated() {
557 Ok(d) => d,
558 Err(e) => {
559 unsafe { release_registered(borrows, &recs, N) };
561 return Err(e);
562 }
563 };
564
565 let reg_ptr = borrows as *mut SegmentBorrowRegistry;
570
571 let mut leases: [core::mem::MaybeUninit<crate::SegmentLease<'a>>; N] =
575 unsafe { core::mem::MaybeUninit::uninit().assume_init() };
576 let mut k = 0;
577 while k < N {
578 let lease = unsafe { crate::SegmentLease::from_raw(reg_ptr, recs[k].assume_init()) };
581 leases[k] = core::mem::MaybeUninit::new(lease);
582 k += 1;
583 }
584 let leases = unsafe {
586 let out = core::ptr::read(&leases as *const _ as *const [crate::SegmentLease<'a>; N]);
587 #[allow(clippy::forget_non_drop)]
590 core::mem::forget(leases);
591 out
592 };
593
594 Ok(crate::SegmentsMut::new(data, offsets, leases))
595 }
596
597 #[inline(always)]
618 pub fn segment_ref_const<'a, T: crate::Pod>(
619 &'a self,
620 borrows: &'a mut SegmentBorrowRegistry,
621 segment: crate::segment::Segment,
622 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
623 self.segment_ref::<T>(borrows, segment.offset, segment.size)
624 }
625
626 #[inline(always)]
629 pub fn segment_mut_const<'a, T: crate::Pod>(
630 &'a self,
631 borrows: &'a mut SegmentBorrowRegistry,
632 segment: crate::segment::Segment,
633 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
634 self.segment_mut::<T>(borrows, segment.offset, segment.size)
635 }
636
637 #[inline(always)]
652 pub fn segment_ref_typed<'a, T: crate::Pod, const OFFSET: u32>(
653 &'a self,
654 borrows: &'a mut SegmentBorrowRegistry,
655 _segment: crate::segment::TypedSegment<T, OFFSET>,
656 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
657 self.segment_ref::<T>(borrows, OFFSET, core::mem::size_of::<T>() as u32)
658 }
659
660 #[inline(always)]
663 pub fn segment_mut_typed<'a, T: crate::Pod, const OFFSET: u32>(
664 &'a self,
665 borrows: &'a mut SegmentBorrowRegistry,
666 _segment: crate::segment::TypedSegment<T, OFFSET>,
667 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
668 self.segment_mut::<T>(borrows, OFFSET, core::mem::size_of::<T>() as u32)
669 }
670
671 #[inline(always)]
692 pub fn load<T: LayoutContract + crate::Pod>(&self) -> Result<Ref<'_, T>, ProgramError> {
693 let data = self.try_borrow()?;
694 check_typed_projection::<T>(data.len(), T::TYPE_OFFSET)?;
695 T::validate_header(&data)?;
696 if data.len() < T::required_len() {
697 return ProgramError::err_data_too_small();
698 }
699 let ptr = unsafe { data.as_bytes_ptr().add(T::TYPE_OFFSET) as *const T };
701 Ok(unsafe { data.project(ptr) })
703 }
704
705 #[inline]
711 pub fn with<T, R, F>(&self, f: F) -> Result<R, ProgramError>
712 where
713 T: LayoutContract + crate::Pod,
714 F: FnOnce(&T) -> Result<R, ProgramError>,
715 {
716 let account = self.load::<T>()?;
717 f(&*account)
718 }
719
720 #[inline(always)]
732 pub fn load_mut<T: LayoutContract + crate::Pod>(&self) -> Result<RefMut<'_, T>, ProgramError> {
733 let mut data = self.try_borrow_mut()?;
734 check_typed_projection::<T>(data.len(), T::TYPE_OFFSET)?;
735 T::validate_header(&data)?;
736 if data.len() < T::required_len() {
737 return ProgramError::err_data_too_small();
738 }
739 #[cfg(feature = "touch-map")]
746 crate::segment_borrow::touch_log::record_account(
747 self.address(),
748 data.len() as u32,
749 crate::segment_borrow::AccessKind::Write,
750 );
751 let ptr = unsafe { data.as_bytes_mut_ptr().add(T::TYPE_OFFSET) as *mut T };
753 Ok(unsafe { data.project(ptr) })
755 }
756
757 #[inline]
762 pub fn with_mut<T, R, F>(&self, f: F) -> Result<R, ProgramError>
763 where
764 T: LayoutContract + crate::Pod,
765 F: FnOnce(&mut T) -> Result<R, ProgramError>,
766 {
767 let mut account = self.load_mut::<T>()?;
768 f(&mut *account)
769 }
770
771 #[inline(always)]
786 pub fn load_compact<T: crate::CompactLayout>(&self) -> Result<Ref<'_, T>, ProgramError> {
787 let data = self.try_borrow()?;
788 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
789 T::validate_compact(&data)?;
790 let ptr =
792 unsafe { data.as_bytes_ptr().add(crate::compact::COMPACT_BODY_OFFSET) as *const T };
793 Ok(unsafe { data.project(ptr) })
795 }
796
797 #[inline(always)]
799 pub fn load_compact_mut<T: crate::CompactLayout>(&self) -> Result<RefMut<'_, T>, ProgramError> {
800 let mut data = self.try_borrow_mut()?;
801 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
802 T::validate_compact(&data)?;
803 #[cfg(feature = "touch-map")]
805 crate::segment_borrow::touch_log::record_account(
806 self.address(),
807 data.len() as u32,
808 crate::segment_borrow::AccessKind::Write,
809 );
810 let ptr = unsafe {
812 data.as_bytes_mut_ptr()
813 .add(crate::compact::COMPACT_BODY_OFFSET) as *mut T
814 };
815 Ok(unsafe { data.project(ptr) })
817 }
818
819 #[inline]
821 pub fn with_compact<T, R, F>(&self, f: F) -> Result<R, ProgramError>
822 where
823 T: crate::CompactLayout,
824 F: FnOnce(&T) -> Result<R, ProgramError>,
825 {
826 let account = self.load_compact::<T>()?;
827 f(&*account)
828 }
829
830 #[inline]
832 pub fn with_compact_mut<T, R, F>(&self, f: F) -> Result<R, ProgramError>
833 where
834 T: crate::CompactLayout,
835 F: FnOnce(&mut T) -> Result<R, ProgramError>,
836 {
837 let mut account = self.load_compact_mut::<T>()?;
838 f(&mut *account)
839 }
840
841 #[inline(always)]
848 pub fn init_compact<T: crate::CompactLayout>(&self) -> ProgramResult {
849 self.check_writable()?;
850 let mut data = self.try_borrow_mut()?;
851 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
852 if data.len() < T::COMPACT_LEN {
853 return Err(ProgramError::AccountDataTooSmall);
854 }
855 if data.len() != T::COMPACT_LEN {
856 return Err(ProgramError::InvalidAccountData);
857 }
858 data[0] = T::DISC;
859 Ok(())
860 }
861
862 #[inline(always)]
880 pub fn load_compact_dynamic<T: crate::CompactDynamicLayout>(
881 &self,
882 ) -> Result<Ref<'_, T>, ProgramError> {
883 let data = self.try_borrow()?;
884 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
885 T::validate_compact_dynamic(&data)?;
886 let ptr =
891 unsafe { data.as_bytes_ptr().add(crate::compact::COMPACT_BODY_OFFSET) as *const T };
892 Ok(unsafe { data.project(ptr) })
894 }
895
896 #[inline(always)]
899 pub fn load_compact_dynamic_mut<T: crate::CompactDynamicLayout>(
900 &self,
901 ) -> Result<RefMut<'_, T>, ProgramError> {
902 let mut data = self.try_borrow_mut()?;
903 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
904 T::validate_compact_dynamic(&data)?;
905 let ptr = unsafe {
908 data.as_bytes_mut_ptr()
909 .add(crate::compact::COMPACT_BODY_OFFSET) as *mut T
910 };
911 Ok(unsafe { data.project(ptr) })
913 }
914
915 #[inline]
917 pub fn with_compact_dynamic<T, R, F>(&self, f: F) -> Result<R, ProgramError>
918 where
919 T: crate::CompactDynamicLayout,
920 F: FnOnce(&T) -> Result<R, ProgramError>,
921 {
922 let account = self.load_compact_dynamic::<T>()?;
923 f(&*account)
924 }
925
926 #[inline]
928 pub fn with_compact_dynamic_mut<T, R, F>(&self, f: F) -> Result<R, ProgramError>
929 where
930 T: crate::CompactDynamicLayout,
931 F: FnOnce(&mut T) -> Result<R, ProgramError>,
932 {
933 let mut account = self.load_compact_dynamic_mut::<T>()?;
934 f(&mut *account)
935 }
936
937 #[inline(always)]
946 pub fn init_compact_dynamic<T: crate::CompactDynamicLayout>(&self) -> ProgramResult {
947 self.check_writable()?;
948 let mut data = self.try_borrow_mut()?;
949 let head_end =
950 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
951 if T::TAIL_OFFSET < head_end {
952 return Err(ProgramError::InvalidAccountData);
953 }
954 let tail_end = T::TAIL_OFFSET
955 .checked_add(4)
956 .ok_or(ProgramError::ArithmeticOverflow)?;
957 if data.len() < T::MIN_LEN {
958 return Err(ProgramError::AccountDataTooSmall);
959 }
960 data[0] = T::DISC;
961 if data.len() >= tail_end {
963 data[T::TAIL_OFFSET..tail_end].copy_from_slice(&0u32.to_le_bytes());
964 }
965 Ok(())
966 }
967
968 #[inline(always)]
973 pub unsafe fn raw_ref<T: crate::Pod>(&self) -> Result<Ref<'_, T>, ProgramError> {
978 let data = self.try_borrow()?;
979 if core::mem::size_of::<T>() > data.len() {
980 return Err(ProgramError::AccountDataTooSmall);
981 }
982 let ptr = data.as_ptr() as *const T;
983 Ok(unsafe { data.project(ptr) })
985 }
986
987 #[inline(always)]
992 pub unsafe fn raw_mut<T: crate::Pod>(&self) -> Result<RefMut<'_, T>, ProgramError> {
997 self.check_writable()?;
998 let mut data = self.try_borrow_mut_ungated()?;
1003 if core::mem::size_of::<T>() > data.len() {
1004 return Err(ProgramError::AccountDataTooSmall);
1005 }
1006 let ptr = data.as_bytes_mut_ptr() as *mut T;
1007 Ok(unsafe { data.project(ptr) })
1009 }
1010
1011 #[inline(always)]
1028 pub fn load_cross_program<T: LayoutContract + crate::Pod>(
1029 &self,
1030 ) -> Result<Ref<'_, T>, ProgramError> {
1031 let data = self.try_borrow()?;
1032 check_typed_projection::<T>(data.len(), T::TYPE_OFFSET)?;
1033 T::validate_header(&data)?;
1034 if data.len() < T::required_len() {
1037 return ProgramError::err_data_too_small();
1038 }
1039 let ptr = unsafe { data.as_bytes_ptr().add(T::TYPE_OFFSET) as *const T };
1041 Ok(unsafe { data.project(ptr) })
1043 }
1044
1045 #[inline(always)]
1051 pub fn layout_info(&self) -> Option<crate::layout::LayoutInfo> {
1052 let data = self.try_borrow().ok()?;
1053 crate::layout::LayoutInfo::from_data(&data)
1054 }
1055
1056 #[inline(always)]
1058 pub fn fields<T: LayoutContract>() -> &'static [FieldInfo] {
1059 T::fields()
1060 }
1061
1062 #[inline]
1070 pub fn field<T: LayoutContract>(name: &str) -> Option<&'static FieldInfo> {
1071 <T as crate::field_map::FieldMap>::field_by_name(name)
1072 }
1073
1074 #[inline(always)]
1079 pub fn extension_range<T: LayoutContract>(
1080 &self,
1081 ) -> Result<core::ops::Range<usize>, ProgramError> {
1082 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
1083 let data_len = self.data_len();
1084 if data_len < offset {
1085 return Err(ProgramError::AccountDataTooSmall);
1086 }
1087 Ok(offset..data_len)
1088 }
1089
1090 #[inline(always)]
1092 pub fn extension_bytes<T: LayoutContract>(&self) -> Result<Ref<'_, [u8]>, ProgramError> {
1093 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
1094 let data = self.try_borrow()?;
1095 if data.len() < offset {
1096 return Err(ProgramError::AccountDataTooSmall);
1097 }
1098 Ok(data.slice_from(offset))
1099 }
1100
1101 #[inline(always)]
1103 pub fn extension_bytes_mut<T: LayoutContract>(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
1104 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
1105 let len = self.data_len();
1106 if len < offset {
1107 return Err(ProgramError::AccountDataTooSmall);
1108 }
1109 if len > offset {
1115 crate::write_policy::check_data_mutation(
1116 self.address(),
1117 offset as u32,
1118 (len - offset) as u32,
1119 )?;
1120 }
1121 let data = self.try_borrow_mut_ungated()?;
1122 Ok(data.slice_from(offset))
1123 }
1124
1125 #[inline]
1138 pub fn zero_range(&self, start: usize, len: usize) -> ProgramResult {
1139 if len == 0 {
1140 return Ok(());
1141 }
1142 let end = start
1143 .checked_add(len)
1144 .ok_or(ProgramError::ArithmeticOverflow)?;
1145 if end > self.data_len() {
1146 return Err(ProgramError::AccountDataTooSmall);
1147 }
1148 let offset_u32 = u32::try_from(start).map_err(|_| ProgramError::ArithmeticOverflow)?;
1149 let len_u32 = u32::try_from(len).map_err(|_| ProgramError::ArithmeticOverflow)?;
1150 crate::write_policy::check_data_mutation(self.address(), offset_u32, len_u32)?;
1151 let mut data = self.try_borrow_mut_ungated()?;
1152 for byte in data[start..end].iter_mut() {
1153 *byte = 0;
1154 }
1155 Ok(())
1156 }
1157
1158 #[inline]
1183 pub fn zero_appended(&self, previous_len: usize) -> ProgramResult {
1184 let len = self.data_len();
1185 if previous_len >= len {
1186 return Ok(());
1187 }
1188 crate::write_policy::check_account_transition(self.address())?;
1189 let mut data = self.try_borrow_mut_ungated()?;
1190 for byte in data[previous_len..len].iter_mut() {
1191 *byte = 0;
1192 }
1193 Ok(())
1194 }
1195
1196 #[inline(always)]
1201 pub fn init_layout<T: LayoutContract>(&self) -> ProgramResult {
1202 let mut data = self.try_borrow_mut()?;
1203 crate::layout::init_header::<T>(&mut data)
1204 }
1205
1206 #[inline(always)]
1210 pub fn require_signer(&self) -> ProgramResult {
1211 if self.is_signer() {
1212 Ok(())
1213 } else {
1214 ProgramError::err_missing_signer()
1215 }
1216 }
1217
1218 #[inline(always)]
1220 pub fn require_writable(&self) -> ProgramResult {
1221 if self.is_writable() {
1222 Ok(())
1223 } else {
1224 ProgramError::err_immutable()
1225 }
1226 }
1227
1228 #[inline(always)]
1230 pub fn require_owned_by(&self, program: &Address) -> ProgramResult {
1231 if self.owned_by(program) {
1232 Ok(())
1233 } else {
1234 ProgramError::err_incorrect_program()
1235 }
1236 }
1237
1238 #[inline(always)]
1240 pub fn require_payer(&self) -> ProgramResult {
1241 self.require_signer()?;
1242 self.require_writable()
1243 }
1244
1245 #[inline(always)]
1249 pub fn check_signer(&self) -> Result<&Self, ProgramError> {
1250 if self.is_signer() {
1251 Ok(self)
1252 } else {
1253 ProgramError::err_missing_signer()
1254 }
1255 }
1256
1257 #[inline(always)]
1259 pub fn check_writable(&self) -> Result<&Self, ProgramError> {
1260 if self.is_writable() {
1261 Ok(self)
1262 } else {
1263 ProgramError::err_immutable()
1264 }
1265 }
1266
1267 #[inline(always)]
1269 pub fn check_owned_by(&self, program: &Address) -> Result<&Self, ProgramError> {
1270 if self.owned_by(program) {
1271 Ok(self)
1272 } else {
1273 ProgramError::err_incorrect_program()
1274 }
1275 }
1276
1277 #[inline]
1284 pub fn check_owned_by_any(&self, programs: &[&Address]) -> Result<&Self, ProgramError> {
1285 if programs.iter().any(|program| self.owned_by(program)) {
1286 Ok(self)
1287 } else {
1288 ProgramError::err_incorrect_program()
1289 }
1290 }
1291
1292 #[inline(always)]
1294 pub fn check_disc(&self, expected: u8) -> Result<&Self, ProgramError> {
1295 if self.disc() == expected {
1296 Ok(self)
1297 } else {
1298 Err(ProgramError::InvalidAccountData)
1299 }
1300 }
1301
1302 #[inline(always)]
1304 pub fn check_has_data(&self) -> Result<&Self, ProgramError> {
1305 if !self.is_data_empty() {
1306 Ok(self)
1307 } else {
1308 Err(ProgramError::AccountDataTooSmall)
1309 }
1310 }
1311
1312 #[inline(always)]
1314 pub fn check_executable(&self) -> Result<&Self, ProgramError> {
1315 if self.executable() {
1316 Ok(self)
1317 } else {
1318 Err(ProgramError::InvalidArgument)
1319 }
1320 }
1321
1322 #[inline(always)]
1324 pub fn check_address(&self, expected: &Address) -> Result<&Self, ProgramError> {
1325 if address_eq(self.address(), expected) {
1326 Ok(self)
1327 } else {
1328 Err(ProgramError::InvalidArgument)
1329 }
1330 }
1331
1332 #[inline(always)]
1334 pub fn check_data_len(&self, min_len: usize) -> Result<&Self, ProgramError> {
1335 if self.data_len() >= min_len {
1336 Ok(self)
1337 } else {
1338 Err(ProgramError::AccountDataTooSmall)
1339 }
1340 }
1341
1342 #[inline(always)]
1344 pub fn check_version(&self, expected: u8) -> Result<&Self, ProgramError> {
1345 if self.version() == expected {
1346 Ok(self)
1347 } else {
1348 Err(ProgramError::InvalidAccountData)
1349 }
1350 }
1351
1352 #[inline(always)]
1354 pub fn check_layout<T: LayoutContract>(&self) -> Result<&Self, ProgramError> {
1355 let data = self.try_borrow()?;
1356 T::validate_header(&data)?;
1357 Ok(self)
1358 }
1359
1360 #[inline(always)]
1362 pub const fn proof(&self) -> crate::proof::AccountProof<'_> {
1363 crate::proof::AccountProof::new(self)
1364 }
1365
1366 #[inline(always)]
1370 pub fn disc(&self) -> u8 {
1371 native_boundary::disc(self.backend())
1372 }
1373
1374 #[inline(always)]
1376 pub fn version(&self) -> u8 {
1377 native_boundary::version(self.backend())
1378 }
1379
1380 #[inline(always)]
1382 pub fn layout_id(&self) -> Option<&[u8; 8]> {
1383 native_boundary::layout_id(self.backend())
1384 }
1385
1386 #[inline(always)]
1388 pub fn require_disc(&self, expected: u8) -> ProgramResult {
1389 if self.disc() == expected {
1390 Ok(())
1391 } else {
1392 Err(ProgramError::InvalidAccountData)
1393 }
1394 }
1395
1396 #[inline(always)]
1407 pub fn flags(&self) -> u8 {
1408 self.backend().flags()
1409 }
1410
1411 #[inline(always)]
1413 pub fn expect_flags(&self, required: u8) -> ProgramResult {
1414 if self.flags() & required == required {
1415 Ok(())
1416 } else {
1417 Err(ProgramError::InvalidArgument)
1418 }
1419 }
1420
1421 #[inline(always)]
1432 pub fn expect_signer_writable(&self, need_signer: bool, need_writable: bool) -> ProgramResult {
1433 if self
1439 .backend()
1440 .is_signer_writable(need_signer, need_writable)
1441 {
1442 return Ok(());
1443 }
1444 if need_signer {
1446 self.require_signer()?;
1447 }
1448 if need_writable {
1449 self.require_writable()?;
1450 }
1451 Ok(())
1454 }
1455
1456 #[inline]
1464 pub fn resize(&self, new_len: usize) -> ProgramResult {
1465 crate::write_policy::check_account_transition(self.address())?;
1469 native_boundary::resize(self.backend(), new_len)
1470 }
1471
1472 #[inline]
1474 pub fn resize_raw(&self, new_len: usize) -> ProgramResult {
1475 crate::write_policy::check_account_transition(self.address())?;
1477 native_boundary::resize_raw(self.backend(), new_len)
1478 }
1479
1480 #[inline(always)]
1487 pub unsafe fn assign(&self, new_owner: &Address) {
1488 unsafe {
1490 native_boundary::assign(self.backend(), new_owner);
1491 }
1492 }
1493
1494 #[inline]
1496 pub fn close(&self) -> ProgramResult {
1497 crate::write_policy::check_account_transition(self.address())?;
1500 native_boundary::close(self.backend())
1501 }
1502
1503 #[inline]
1531 pub fn close_to(&self, destination: &AccountView<'_>, program_id: &Address) -> ProgramResult {
1532 crate::write_policy::check_account_transition(self.address())?;
1536 self.require_writable()?;
1537 self.require_owned_by(program_id)?;
1538 destination.require_writable()?;
1539
1540 let lamports = self.lamports();
1541 let dest_lamports = destination.lamports();
1542 destination.try_set_lamports(
1543 dest_lamports
1544 .checked_add(lamports)
1545 .ok_or(ProgramError::ArithmeticOverflow)?,
1546 )?;
1547 self.try_set_lamports(0)?;
1548 native_boundary::zero_data(self.backend())?;
1549 Ok(())
1550 }
1551
1552 #[inline]
1567 pub fn close_to_unchecked(&self, destination: &AccountView<'_>) -> ProgramResult {
1568 crate::write_policy::check_account_transition(self.address())?;
1569 let lamports = self.lamports();
1570 let dest_lamports = destination.lamports();
1571 destination.try_set_lamports(
1572 dest_lamports
1573 .checked_add(lamports)
1574 .ok_or(ProgramError::ArithmeticOverflow)?,
1575 )?;
1576 self.try_set_lamports(0)?;
1577 native_boundary::zero_data(self.backend())?;
1578 Ok(())
1579 }
1580
1581 #[inline(always)]
1585 pub(crate) fn data_ptr_unchecked(&self) -> *mut u8 {
1586 self.backend().data_ptr_unchecked()
1587 }
1588
1589 #[inline(always)]
1591 pub(crate) fn account_ptr(&self) -> *const hopper_native::RuntimeAccount {
1592 self.backend().account_ptr()
1593 }
1594
1595 #[inline(always)]
1597 pub fn check_borrow(&self) -> Result<(), ProgramError> {
1598 borrow_registry::check_shared(self.address())?;
1599 self.backend().check_borrow().map_err(ProgramError::from)
1600 }
1601
1602 #[inline(always)]
1604 pub fn check_borrow_mut(&self) -> Result<(), ProgramError> {
1605 borrow_registry::check_mutable(self.address())?;
1606 self.backend()
1607 .check_borrow_mut()
1608 .map_err(ProgramError::from)
1609 }
1610
1611 #[inline(always)]
1617 pub unsafe fn borrow_unchecked(&self) -> &[u8] {
1618 unsafe { self.backend().borrow_unchecked() }
1620 }
1621
1622 #[allow(clippy::mut_from_ref)]
1633 #[inline(always)]
1634 pub unsafe fn borrow_unchecked_mut(&self) -> &mut [u8] {
1635 unsafe { self.backend().borrow_unchecked_mut() }
1638 }
1639
1640 #[inline(always)]
1646 pub unsafe fn resize_unchecked(&self, new_len: usize) {
1647 unsafe {
1649 self.backend().resize_unchecked(new_len);
1650 }
1651 }
1652
1653 #[inline(always)]
1659 pub unsafe fn close_unchecked(&self) {
1660 unsafe {
1662 self.backend().close_unchecked();
1663 }
1664 }
1665
1666 #[allow(dead_code)]
1670 #[inline(always)]
1671 pub(crate) fn as_backend(&self) -> &BackendAccountView<'_> {
1672 self.backend()
1673 }
1674}
1675
1676impl<'info> core::fmt::Debug for AccountView<'info> {
1677 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1678 f.debug_struct("AccountView")
1679 .field("address", self.address())
1680 .field("lamports", &self.lamports())
1681 .field("data_len", &self.data_len())
1682 .field("is_signer", &self.is_signer())
1683 .field("is_writable", &self.is_writable())
1684 .finish()
1685 }
1686}
1687
1688pub struct RemainingAccounts<'a> {
1692 accounts: &'a [AccountView<'a>],
1693 cursor: usize,
1694}
1695
1696impl<'a> RemainingAccounts<'a> {
1697 #[inline(always)]
1699 pub fn new(accounts: &'a [AccountView<'a>]) -> Self {
1700 Self {
1701 accounts,
1702 cursor: 0,
1703 }
1704 }
1705
1706 #[inline(always)]
1708 pub fn remaining(&self) -> usize {
1709 self.accounts.len() - self.cursor
1710 }
1711
1712 #[allow(clippy::should_implement_trait)]
1717 #[inline(always)]
1718 pub fn next(&mut self) -> Result<&'a AccountView<'a>, ProgramError> {
1719 if self.cursor >= self.accounts.len() {
1720 return Err(ProgramError::NotEnoughAccountKeys);
1721 }
1722 let account = &self.accounts[self.cursor];
1723 self.cursor += 1;
1724 Ok(account)
1725 }
1726
1727 #[inline(always)]
1729 pub fn next_signer(&mut self) -> Result<&'a AccountView<'a>, ProgramError> {
1730 let account = self.next()?;
1731 account.require_signer()?;
1732 Ok(account)
1733 }
1734
1735 #[inline(always)]
1737 pub fn next_writable(&mut self) -> Result<&'a AccountView<'a>, ProgramError> {
1738 let account = self.next()?;
1739 account.require_writable()?;
1740 Ok(account)
1741 }
1742
1743 #[inline(always)]
1745 pub fn next_owned_by(
1746 &mut self,
1747 program: &Address,
1748 ) -> Result<&'a AccountView<'a>, ProgramError> {
1749 let account = self.next()?;
1750 account.require_owned_by(program)?;
1751 Ok(account)
1752 }
1753}
1754
1755#[cfg(test)]
1756mod tests {
1757 use super::*;
1758 use crate::compact::CompactLayout;
1759 use crate::layout::HopperHeader;
1760
1761 use hopper_native::{
1762 AccountView as NativeAccountView, Address as NativeAddress, RuntimeAccount, NOT_BORROWED,
1763 };
1764
1765 #[repr(C)]
1766 #[derive(Clone, Copy, Debug, Default)]
1767 struct TestLayout {
1768 a: [u8; 8],
1769 b: [u8; 8],
1770 }
1771
1772 #[repr(C)]
1773 #[derive(Clone, Copy, Debug)]
1774 struct HeaderLayout {
1775 header: [u8; HopperHeader::SIZE],
1776 amount: [u8; 8],
1777 }
1778
1779 #[repr(C)]
1780 #[derive(Clone, Copy, Debug, Default)]
1781 struct EpochTwoLayout {
1782 amount: [u8; 8],
1783 }
1784
1785 unsafe impl crate::Zeroable for TestLayout {}
1786 unsafe impl crate::Zeroable for HeaderLayout {}
1787 unsafe impl crate::Zeroable for EpochTwoLayout {}
1788 unsafe impl crate::Pod for TestLayout {}
1789 unsafe impl crate::Pod for HeaderLayout {}
1790 unsafe impl crate::Pod for EpochTwoLayout {}
1791
1792 #[inline(always)]
1793 fn le_u64(v: u64) -> [u8; 8] {
1794 v.to_le_bytes()
1795 }
1796
1797 #[inline(always)]
1798 fn from_le_u64(bytes: [u8; 8]) -> u64 {
1799 u64::from_le_bytes(bytes)
1800 }
1801
1802 impl crate::field_map::FieldMap for TestLayout {
1803 const FIELDS: &'static [crate::field_map::FieldInfo] = &[
1804 crate::field_map::FieldInfo::new("a", HopperHeader::SIZE, 8),
1805 crate::field_map::FieldInfo::new("b", HopperHeader::SIZE + 8, 8),
1806 ];
1807 }
1808
1809 impl LayoutContract for TestLayout {
1810 const DISC: u8 = 7;
1811 const VERSION: u8 = 1;
1812 const LAYOUT_ID: [u8; 8] = [0xAB; 8];
1813 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1814 const EXTENSION_OFFSET: Option<usize> = Some(Self::SIZE);
1815 }
1816
1817 impl crate::field_map::FieldMap for HeaderLayout {
1818 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1819 "amount",
1820 HopperHeader::SIZE,
1821 8,
1822 )];
1823 }
1824
1825 impl LayoutContract for HeaderLayout {
1826 const DISC: u8 = 11;
1827 const VERSION: u8 = 2;
1828 const LAYOUT_ID: [u8; 8] = [0xCD; 8];
1829 const SIZE: usize = core::mem::size_of::<Self>();
1830 const TYPE_OFFSET: usize = 0;
1831 }
1832
1833 impl crate::field_map::FieldMap for EpochTwoLayout {
1834 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1835 "amount",
1836 HopperHeader::SIZE,
1837 8,
1838 )];
1839 }
1840
1841 impl LayoutContract for EpochTwoLayout {
1842 const DISC: u8 = 12;
1843 const VERSION: u8 = 1;
1844 const LAYOUT_ID: [u8; 8] = [0xEF; 8];
1845 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1846 const SCHEMA_EPOCH: u32 = 2;
1847 }
1848
1849 #[repr(C)]
1855 #[derive(Clone, Copy, Debug, Default)]
1856 struct LaxForeignLayout {
1857 amount: [u8; 8],
1858 }
1859 unsafe impl crate::Zeroable for LaxForeignLayout {}
1860 unsafe impl crate::Pod for LaxForeignLayout {}
1861 impl crate::field_map::FieldMap for LaxForeignLayout {
1862 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1863 "amount",
1864 HopperHeader::SIZE,
1865 8,
1866 )];
1867 }
1868 impl LayoutContract for LaxForeignLayout {
1869 const DISC: u8 = 0x5A;
1870 const VERSION: u8 = 1;
1871 const LAYOUT_ID: [u8; 8] = [0x5A; 8];
1872 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1873 fn validate_header(data: &[u8]) -> ProgramResult {
1875 if crate::layout::read_disc(data) != Some(Self::DISC) {
1876 return ProgramError::err_invalid_data();
1877 }
1878 Ok(())
1879 }
1880 }
1881
1882 #[test]
1883 fn load_cross_program_guards_length_even_with_lax_foreign_header() {
1884 let required = HopperHeader::SIZE + 8;
1887 assert_eq!(LaxForeignLayout::required_len(), required);
1888
1889 let (_short_backing, short) = make_account(required - 1, 60);
1893 {
1894 let mut d = short.try_borrow_mut().unwrap();
1895 d[0] = LaxForeignLayout::DISC;
1896 }
1897 assert!(matches!(
1898 short.load_cross_program::<LaxForeignLayout>(),
1899 Err(ProgramError::AccountDataTooSmall)
1900 ));
1901
1902 let (_ok_backing, ok) = make_account(required, 61);
1904 {
1905 let mut d = ok.try_borrow_mut().unwrap();
1906 d[0] = LaxForeignLayout::DISC;
1907 }
1908 let view = ok.load_cross_program::<LaxForeignLayout>().unwrap();
1909 assert_eq!(view.amount, [0u8; 8]);
1910 }
1911
1912 #[repr(transparent)]
1913 #[derive(Clone, Copy)]
1914 struct ForgedProjection<const OFFSET: usize>([u8; 8]);
1915 unsafe impl<const O: usize> crate::Zeroable for ForgedProjection<O> {}
1917 unsafe impl<const O: usize> crate::Pod for ForgedProjection<O> {}
1919 impl<const O: usize> crate::field_map::FieldMap for ForgedProjection<O> {
1920 const FIELDS: &'static [crate::field_map::FieldInfo] = &[];
1921 }
1922 impl<const O: usize> LayoutContract for ForgedProjection<O> {
1923 const DISC: u8 = 1;
1924 const VERSION: u8 = 1;
1925 const LAYOUT_ID: [u8; 8] = [0; 8];
1926 const SIZE: usize = 0;
1927 const TYPE_OFFSET: usize = O;
1928 fn required_len() -> usize {
1929 0
1930 }
1931 fn validate_header(_: &[u8]) -> ProgramResult {
1932 Ok(())
1933 }
1934 }
1935 impl<const O: usize> crate::CompactLayout for ForgedProjection<O> {
1936 const DISC: u8 = 1;
1937 const BODY_SIZE: usize = 0;
1938 const COMPACT_LEN: usize = 0;
1939 fn validate_compact(_: &[u8]) -> ProgramResult {
1940 Ok(())
1941 }
1942 }
1943 impl<const O: usize> crate::CompactDynamicLayout for ForgedProjection<O> {
1944 const DISC: u8 = 1;
1945 const MIN_LEN: usize = 0;
1946 const TAIL_OFFSET: usize = O;
1947 fn validate_compact_dynamic(_: &[u8]) -> ProgramResult {
1948 Ok(())
1949 }
1950 }
1951
1952 #[test]
1953 fn typed_loads_do_not_trust_overridden_sizing_and_validation() {
1954 for len in 0..24 {
1955 let (_backing, view) = make_account(len, 81);
1956 assert!(matches!(
1957 view.load::<ForgedProjection<16>>(),
1958 Err(ProgramError::AccountDataTooSmall)
1959 ));
1960 assert!(matches!(
1961 view.load_mut::<ForgedProjection<16>>(),
1962 Err(ProgramError::AccountDataTooSmall)
1963 ));
1964 assert!(matches!(
1965 view.load_cross_program::<ForgedProjection<16>>(),
1966 Err(ProgramError::AccountDataTooSmall)
1967 ));
1968 }
1969 let (_backing, view) = make_account(24, 82);
1970 assert_eq!(view.load::<ForgedProjection<16>>().unwrap().0, [0; 8]);
1971 assert!(matches!(
1972 view.load::<ForgedProjection<{ usize::MAX }>>(),
1973 Err(ProgramError::ArithmeticOverflow)
1974 ));
1975 }
1976
1977 #[test]
1978 fn compact_loads_recheck_actual_body_bounds() {
1979 for len in 0..9 {
1980 let (_backing, view) = make_account(len, 83);
1981 assert!(matches!(
1982 view.load_compact::<ForgedProjection<9>>(),
1983 Err(ProgramError::AccountDataTooSmall)
1984 ));
1985 assert!(matches!(
1986 view.load_compact_mut::<ForgedProjection<9>>(),
1987 Err(ProgramError::AccountDataTooSmall)
1988 ));
1989 assert!(matches!(
1990 view.load_compact_dynamic::<ForgedProjection<9>>(),
1991 Err(ProgramError::AccountDataTooSmall)
1992 ));
1993 assert!(matches!(
1994 view.load_compact_dynamic_mut::<ForgedProjection<9>>(),
1995 Err(ProgramError::AccountDataTooSmall)
1996 ));
1997 assert_eq!(
1998 view.init_compact::<ForgedProjection<9>>(),
1999 Err(ProgramError::AccountDataTooSmall)
2000 );
2001 assert_eq!(
2002 view.init_compact_dynamic::<ForgedProjection<9>>(),
2003 Err(ProgramError::AccountDataTooSmall)
2004 );
2005 }
2006 let (_backing, view) = make_account(9, 84);
2007 assert_eq!(
2008 view.load_compact::<ForgedProjection<9>>().unwrap().0,
2009 [0; 8]
2010 );
2011 assert_eq!(
2012 view.load_compact_dynamic::<ForgedProjection<9>>()
2013 .unwrap()
2014 .0,
2015 [0; 8]
2016 );
2017 }
2018
2019 #[test]
2020 fn compact_init_rejects_overlapping_or_overflowing_tail_before_writing() {
2021 let (_backing, view) = make_account(16, 85);
2022 assert_eq!(
2023 view.init_compact_dynamic::<ForgedProjection<0>>(),
2024 Err(ProgramError::InvalidAccountData)
2025 );
2026 assert_eq!(
2027 view.init_compact_dynamic::<ForgedProjection<{ usize::MAX }>>(),
2028 Err(ProgramError::ArithmeticOverflow)
2029 );
2030 assert_eq!(&*view.try_borrow().unwrap(), &[0; 16]);
2031 }
2032
2033 fn make_account(
2034 total_data_len: usize,
2035 address_byte: u8,
2036 ) -> (std::vec::Vec<u64>, AccountView<'static>) {
2037 let mut backing = std::vec![0u64; (RuntimeAccount::SIZE + total_data_len).div_ceil(8)];
2038 let raw = backing.as_mut_ptr() as *mut RuntimeAccount;
2039 unsafe {
2041 raw.write(RuntimeAccount {
2042 borrow_state: NOT_BORROWED,
2043 is_signer: 1,
2044 is_writable: 1,
2045 executable: 0,
2046 resize_delta: 0,
2047 address: NativeAddress::new_from_array([address_byte; 32]),
2048 owner: NativeAddress::new_from_array([2; 32]),
2049 lamports: 42,
2050 data_len: total_data_len as u64,
2051 });
2052 }
2053 let backend = unsafe { NativeAccountView::new_unchecked(raw) };
2055 let account = AccountView::from_backend(backend);
2056 (backing, account)
2057 }
2058
2059 fn make_flagged_account(
2063 is_signer: u8,
2064 is_writable: u8,
2065 ) -> (std::vec::Vec<u64>, AccountView<'static>) {
2066 let mut backing = std::vec![0u64; (RuntimeAccount::SIZE).div_ceil(8)];
2067 let raw = backing.as_mut_ptr() as *mut RuntimeAccount;
2068 unsafe {
2071 raw.write(RuntimeAccount {
2072 borrow_state: NOT_BORROWED,
2073 is_signer,
2074 is_writable,
2075 executable: 0,
2076 resize_delta: 0,
2077 address: NativeAddress::new_from_array([9; 32]),
2078 owner: NativeAddress::new_from_array([2; 32]),
2079 lamports: 0,
2080 data_len: 0,
2081 });
2082 }
2083 let backend = unsafe { NativeAccountView::new_unchecked(raw) };
2085 (backing, AccountView::from_backend(backend))
2086 }
2087
2088 #[test]
2089 fn expect_signer_writable_keeps_distinct_errors_and_passes_valid() {
2090 let (_b, both) = make_flagged_account(1, 1);
2092 assert!(both.expect_signer_writable(true, true).is_ok());
2093
2094 let (_b, no_signer) = make_flagged_account(0, 1);
2096 assert!(matches!(
2097 no_signer.expect_signer_writable(true, true),
2098 Err(ProgramError::MissingRequiredSignature)
2099 ));
2100
2101 let (_b, no_writable) = make_flagged_account(1, 0);
2103 assert!(matches!(
2104 no_writable.expect_signer_writable(true, true),
2105 Err(ProgramError::Immutable)
2106 ));
2107
2108 let (_b, signer_only) = make_flagged_account(1, 0);
2110 assert!(signer_only.expect_signer_writable(true, false).is_ok());
2111 let (_b, writable_only) = make_flagged_account(0, 1);
2112 assert!(writable_only.expect_signer_writable(false, true).is_ok());
2113
2114 let (_b, neither) = make_flagged_account(0, 0);
2116 assert!(neither.expect_signer_writable(false, false).is_ok());
2117
2118 assert!(matches!(
2120 neither.expect_signer_writable(true, false),
2121 Err(ProgramError::MissingRequiredSignature)
2122 ));
2123 assert!(matches!(
2125 neither.expect_signer_writable(false, true),
2126 Err(ProgramError::Immutable)
2127 ));
2128 }
2129
2130 #[test]
2131 fn load_mut_is_zero_copy_and_pointer_stable() {
2132 let (_backing, account) = make_account(TestLayout::SIZE + 8, 1);
2133
2134 {
2135 let mut data = account.try_borrow_mut().unwrap();
2136 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2137 data[HopperHeader::SIZE..HopperHeader::SIZE + 8].copy_from_slice(&10u64.to_le_bytes());
2138 data[HopperHeader::SIZE + 8..HopperHeader::SIZE + 16]
2139 .copy_from_slice(&20u64.to_le_bytes());
2140 data[TestLayout::SIZE..TestLayout::SIZE + 8].copy_from_slice(b"tailpass");
2141 }
2142
2143 let first_ptr = {
2144 let first = account.load::<TestLayout>().unwrap();
2145 assert_eq!(from_le_u64(first.a), 10);
2146 assert_eq!(from_le_u64(first.b), 20);
2147 first.as_ptr() as usize
2148 };
2149
2150 {
2151 let tail = account.extension_bytes::<TestLayout>().unwrap();
2152 assert_eq!(&tail[..8], b"tailpass");
2153 }
2154
2155 let mut second = account.load_mut::<TestLayout>().unwrap();
2156 let second_ptr = second.as_mut_ptr() as usize;
2157 second.b = le_u64(99);
2158 assert_eq!(first_ptr, second_ptr);
2159 drop(second);
2160
2161 let reread = account.load::<TestLayout>().unwrap();
2162 assert_eq!(from_le_u64(reread.a), 10);
2163 assert_eq!(from_le_u64(reread.b), 99);
2164 }
2165
2166 #[repr(C)]
2167 #[derive(Clone, Copy, Debug, Default)]
2168 struct CompactVault {
2169 authority: [u8; 32],
2170 balance: [u8; 8],
2171 }
2172 unsafe impl crate::Zeroable for CompactVault {}
2173 unsafe impl crate::Pod for CompactVault {}
2174 impl crate::CompactLayout for CompactVault {
2175 const DISC: u8 = 1;
2176 }
2177
2178 #[test]
2179 fn compact_load_uses_one_byte_header_and_body_at_offset_one() {
2180 assert_eq!(CompactVault::COMPACT_LEN, 1 + 40);
2183 let headered_len = HopperHeader::SIZE + CompactVault::BODY_SIZE;
2184 assert_eq!(
2185 headered_len - CompactVault::COMPACT_LEN,
2186 HopperHeader::SIZE - 1
2187 );
2188
2189 let (_backing, account) = make_account(CompactVault::COMPACT_LEN, 50);
2190
2191 account.init_compact::<CompactVault>().unwrap();
2192 {
2193 let data = account.try_borrow().unwrap();
2195 assert_eq!(data[0], 1);
2196 }
2197
2198 {
2199 let mut v = account.load_compact_mut::<CompactVault>().unwrap();
2200 v.authority = [9u8; 32];
2201 v.balance = 1234u64.to_le_bytes();
2202 }
2203
2204 let v = account.load_compact::<CompactVault>().unwrap();
2205 assert_eq!(v.authority, [9u8; 32]);
2206 assert_eq!(u64::from_le_bytes(v.balance), 1234);
2207
2208 let data = account.try_borrow().unwrap();
2210 let base = data.as_bytes_ptr() as usize;
2211 let body = (&*v) as *const CompactVault as usize;
2212 assert_eq!(body, base + 1);
2213 }
2214
2215 #[test]
2216 fn compact_load_rejects_wrong_disc() {
2217 let (_backing, account) = make_account(CompactVault::COMPACT_LEN, 51);
2218 {
2219 let mut data = account.try_borrow_mut().unwrap();
2220 data[0] = 2; }
2222 assert_eq!(
2223 account.load_compact::<CompactVault>().unwrap_err(),
2224 ProgramError::InvalidAccountData
2225 );
2226 }
2227
2228 #[test]
2229 fn compact_load_rejects_short_buffer() {
2230 let (_backing, account) = make_account(CompactVault::COMPACT_LEN - 1, 52);
2231 account
2232 .try_borrow_mut()
2233 .map(|mut d| d[0] = CompactVault::DISC)
2234 .unwrap();
2235 assert_eq!(
2236 account.load_compact::<CompactVault>().unwrap_err(),
2237 ProgramError::AccountDataTooSmall
2238 );
2239 }
2240
2241 #[test]
2242 fn compact_load_rejects_oversized_fixed_buffer() {
2243 let (_backing, account) = make_account(CompactVault::COMPACT_LEN + 1, 53);
2244 {
2245 let mut data = account.try_borrow_mut().unwrap();
2246 data[0] = CompactVault::DISC;
2247 }
2248 assert_eq!(
2249 account.load_compact::<CompactVault>().unwrap_err(),
2250 ProgramError::InvalidAccountData
2251 );
2252 assert_eq!(
2253 account.init_compact::<CompactVault>().unwrap_err(),
2254 ProgramError::InvalidAccountData
2255 );
2256 }
2257
2258 #[repr(C)]
2260 #[derive(Clone, Copy, Debug, Default)]
2261 struct CompactDynHead {
2262 owner: [u8; 32],
2263 count: [u8; 8],
2264 }
2265 unsafe impl crate::Zeroable for CompactDynHead {}
2266 unsafe impl crate::Pod for CompactDynHead {}
2267 impl crate::CompactDynamicLayout for CompactDynHead {
2268 const DISC: u8 = 9;
2269 }
2270
2271 #[test]
2272 fn compact_dynamic_loads_head_with_a_growable_tail() {
2273 use crate::CompactDynamicLayout;
2274 assert_eq!(CompactDynHead::FIXED_HEAD_SIZE, 40);
2275 assert_eq!(CompactDynHead::MIN_LEN, 41);
2276 assert_eq!(CompactDynHead::TAIL_OFFSET, 41);
2277
2278 let total = CompactDynHead::MIN_LEN + 4 + 16;
2280 let (_backing, account) = make_account(total, 70);
2281
2282 account.init_compact_dynamic::<CompactDynHead>().unwrap();
2284 {
2285 let data = account.try_borrow().unwrap();
2286 assert_eq!(data[0], 9);
2287 let prefix = u32::from_le_bytes(
2288 data[CompactDynHead::TAIL_OFFSET..CompactDynHead::TAIL_OFFSET + 4]
2289 .try_into()
2290 .unwrap(),
2291 );
2292 assert_eq!(prefix, 0);
2293 }
2294
2295 {
2298 let mut head = account
2299 .load_compact_dynamic_mut::<CompactDynHead>()
2300 .unwrap();
2301 head.owner = [7u8; 32];
2302 head.count = 5u64.to_le_bytes();
2303 }
2304 let head = account.load_compact_dynamic::<CompactDynHead>().unwrap();
2305 assert_eq!(head.owner, [7u8; 32]);
2306 assert_eq!(u64::from_le_bytes(head.count), 5);
2307
2308 let data = account.try_borrow().unwrap();
2310 let base = data.as_bytes_ptr() as usize;
2311 assert_eq!((&*head) as *const CompactDynHead as usize, base + 1);
2312 }
2313
2314 #[test]
2315 fn compact_dynamic_rejects_short_and_wrong_disc() {
2316 use crate::CompactDynamicLayout;
2317 let (_b1, short) = make_account(CompactDynHead::MIN_LEN - 1, 71);
2319 short
2320 .try_borrow_mut()
2321 .map(|mut d| d[0] = CompactDynHead::DISC)
2322 .unwrap();
2323 assert_eq!(
2324 short.load_compact_dynamic::<CompactDynHead>().unwrap_err(),
2325 ProgramError::AccountDataTooSmall
2326 );
2327
2328 let (_b2, bad) = make_account(CompactDynHead::MIN_LEN + 8, 72);
2330 bad.try_borrow_mut().map(|mut d| d[0] = 3).unwrap();
2331 assert_eq!(
2332 bad.load_compact_dynamic::<CompactDynHead>().unwrap_err(),
2333 ProgramError::InvalidAccountData
2334 );
2335 }
2336
2337 #[test]
2338 fn close_refuses_while_data_borrow_is_live() {
2339 let (_backing, account) = make_account(16, 90);
2343 {
2344 let _data = account.try_borrow().unwrap();
2345 assert_eq!(
2346 account.close().unwrap_err(),
2347 ProgramError::AccountBorrowFailed
2348 );
2349 }
2350 account.close().unwrap();
2352 assert_eq!(account.data_len(), 0);
2353 assert_eq!(account.lamports(), 0);
2354 }
2355
2356 #[test]
2357 fn check_owned_by_any_accepts_listed_owner_and_rejects_others() {
2358 let (_backing, account) = make_account(8, 80);
2360 let token = Address::new([2; 32]); let token_2022 = Address::new([9; 32]);
2362 let other = Address::new([3; 32]);
2363
2364 assert!(account.check_owned_by_any(&[&token_2022, &token]).is_ok());
2367 assert!(account.check_owned_by_any(&[&token]).is_ok());
2368
2369 assert!(account.check_owned_by_any(&[&token_2022, &other]).is_err());
2371
2372 assert!(account.check_owned_by_any(&[]).is_err());
2374 }
2375
2376 #[test]
2377 fn default_layout_accepts_legacy_zero_epoch() {
2378 let (_backing, account) = make_account(TestLayout::SIZE, 43);
2379 {
2380 let mut data = account.try_borrow_mut().unwrap();
2381 crate::layout::write_header_with_epoch(
2382 &mut data,
2383 TestLayout::DISC,
2384 TestLayout::VERSION,
2385 &TestLayout::LAYOUT_ID,
2386 0,
2387 )
2388 .unwrap();
2389 }
2390
2391 assert!(account.load::<TestLayout>().is_ok());
2392 }
2393
2394 #[test]
2395 fn init_header_stamps_layout_schema_epoch() {
2396 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 44);
2397 {
2398 let mut data = account.try_borrow_mut().unwrap();
2399 crate::layout::init_header::<EpochTwoLayout>(&mut data).unwrap();
2400 assert_eq!(crate::layout::read_schema_epoch(&data), Some(2));
2401 }
2402
2403 assert!(account.load::<EpochTwoLayout>().is_ok());
2404 }
2405
2406 #[test]
2407 fn typed_load_rejects_schema_epoch_mismatch() {
2408 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 45);
2409 {
2410 let mut data = account.try_borrow_mut().unwrap();
2411 crate::layout::write_header_with_epoch(
2412 &mut data,
2413 EpochTwoLayout::DISC,
2414 EpochTwoLayout::VERSION,
2415 &EpochTwoLayout::LAYOUT_ID,
2416 1,
2417 )
2418 .unwrap();
2419 }
2420
2421 assert_eq!(
2422 account.load::<EpochTwoLayout>().unwrap_err(),
2423 ProgramError::InvalidAccountData
2424 );
2425 }
2426
2427 #[test]
2428 fn layout_info_matches_checks_schema_epoch() {
2429 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 46);
2430 {
2431 let mut data = account.try_borrow_mut().unwrap();
2432 crate::layout::write_header_with_epoch(
2433 &mut data,
2434 EpochTwoLayout::DISC,
2435 EpochTwoLayout::VERSION,
2436 &EpochTwoLayout::LAYOUT_ID,
2437 1,
2438 )
2439 .unwrap();
2440 }
2441 assert!(!account.layout_info().unwrap().matches::<EpochTwoLayout>());
2442
2443 {
2444 let mut data = account.try_borrow_mut().unwrap();
2445 crate::layout::write_header_with_epoch(
2446 &mut data,
2447 EpochTwoLayout::DISC,
2448 EpochTwoLayout::VERSION,
2449 &EpochTwoLayout::LAYOUT_ID,
2450 EpochTwoLayout::SCHEMA_EPOCH,
2451 )
2452 .unwrap();
2453 }
2454 assert!(account.layout_info().unwrap().matches::<EpochTwoLayout>());
2455 }
2456
2457 #[test]
2458 fn typed_load_holds_borrow_until_drop() {
2459 let (_backing, account) = make_account(TestLayout::SIZE, 3);
2460
2461 {
2462 let mut data = account.try_borrow_mut().unwrap();
2463 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2464 }
2465
2466 let shared = account.load::<TestLayout>().unwrap();
2467 assert_eq!(
2468 account.load_mut::<TestLayout>().unwrap_err(),
2469 ProgramError::AccountBorrowFailed
2470 );
2471 drop(shared);
2472 assert!(account.load_mut::<TestLayout>().is_ok());
2473 }
2474
2475 #[test]
2476 fn duplicate_address_aliases_are_rejected_across_views() {
2477 let (_first_backing, first) = make_account(TestLayout::SIZE, 9);
2478 let (_second_backing, second) = make_account(TestLayout::SIZE, 9);
2479
2480 let first_shared = first.try_borrow().unwrap();
2481 let second_shared = second.try_borrow().unwrap();
2482 assert_eq!(
2483 second.try_borrow_mut().unwrap_err(),
2484 ProgramError::AccountBorrowFailed
2485 );
2486 drop(first_shared);
2487 drop(second_shared);
2488 assert!(second.try_borrow_mut().is_ok());
2489 }
2490
2491 #[test]
2492 fn load_rejects_wrong_disc_and_wrong_version() {
2493 let (_backing, account) = make_account(TestLayout::SIZE, 4);
2494
2495 {
2496 let mut data = account.try_borrow_mut().unwrap();
2497 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2498 }
2499
2500 {
2501 let mut data = account.try_borrow_mut().unwrap();
2502 data[0] = TestLayout::DISC.wrapping_add(1);
2503 }
2504 assert_eq!(
2505 account.load::<TestLayout>().unwrap_err(),
2506 ProgramError::InvalidAccountData
2507 );
2508
2509 {
2510 let mut data = account.try_borrow_mut().unwrap();
2511 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2512 data[1] = TestLayout::VERSION.wrapping_add(1);
2513 }
2514 assert_eq!(
2515 account.load::<TestLayout>().unwrap_err(),
2516 ProgramError::InvalidAccountData
2517 );
2518 }
2519
2520 #[test]
2521 fn load_rejects_undersized_layout_body() {
2522 let (_backing, account) = make_account(TestLayout::SIZE - 1, 5);
2523
2524 {
2525 let mut data = account.try_borrow_mut().unwrap();
2526 data[0] = TestLayout::DISC;
2527 data[1] = TestLayout::VERSION;
2528 data[4..12].copy_from_slice(&TestLayout::LAYOUT_ID);
2529 }
2530
2531 assert_eq!(
2532 account.load::<TestLayout>().unwrap_err(),
2533 ProgramError::AccountDataTooSmall
2534 );
2535 }
2536
2537 #[test]
2538 fn load_supports_header_inclusive_layouts() {
2539 let (_backing, account) = make_account(HeaderLayout::SIZE, 6);
2540
2541 {
2542 let mut data = account.try_borrow_mut().unwrap();
2543 crate::layout::init_header::<HeaderLayout>(&mut data).unwrap();
2544 }
2545
2546 {
2547 let mut layout = account.load_mut::<HeaderLayout>().unwrap();
2548 layout.amount = le_u64(55);
2549 }
2550
2551 let layout = account.load::<HeaderLayout>().unwrap();
2552 assert_eq!(layout.header[0], HeaderLayout::DISC);
2553 assert_eq!(layout.header[1], HeaderLayout::VERSION);
2554 assert_eq!(from_le_u64(layout.amount), 55);
2555 }
2556
2557 #[test]
2567 fn live_load_blocks_segment_mut() {
2568 let (_backing, account) = make_account(TestLayout::SIZE, 10);
2569 {
2570 let mut data = account.try_borrow_mut().unwrap();
2571 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2572 }
2573
2574 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2575 let _read_view = account.load::<TestLayout>().unwrap();
2576
2577 let err = account
2579 .segment_mut::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2580 .unwrap_err();
2581 assert_eq!(err, ProgramError::AccountBorrowFailed);
2582 }
2583
2584 #[test]
2585 fn live_load_mut_blocks_segment_ref() {
2586 let (_backing, account) = make_account(TestLayout::SIZE, 11);
2587 {
2588 let mut data = account.try_borrow_mut().unwrap();
2589 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2590 }
2591
2592 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2593 let _write_view = account.load_mut::<TestLayout>().unwrap();
2594
2595 let err = account
2598 .segment_ref::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2599 .unwrap_err();
2600 assert_eq!(err, ProgramError::AccountBorrowFailed);
2601 }
2602
2603 #[test]
2604 fn every_access_path_is_tracked() {
2605 let (_backing, account) = make_account(TestLayout::SIZE, 40);
2613 {
2614 let mut data = account.try_borrow_mut().unwrap();
2615 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2616 }
2617 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2618
2619 {
2621 let _r = account.try_borrow().unwrap();
2622 assert!(account.try_borrow_mut().is_err());
2623 }
2624 {
2626 let _w = account.try_borrow_mut().unwrap();
2627 assert!(account.try_borrow().is_err());
2628 }
2629 {
2631 let _v = account.load::<TestLayout>().unwrap();
2632 assert!(account.load_mut::<TestLayout>().is_err());
2633 }
2634 {
2636 let _v = account.load_mut::<TestLayout>().unwrap();
2637 assert!(account.load::<TestLayout>().is_err());
2638 }
2639 {
2641 let _r = unsafe { account.raw_ref::<[u8; 16]>() }.unwrap();
2643 assert!(account.load_mut::<TestLayout>().is_err());
2644 }
2645 {
2647 let _w = unsafe { account.raw_mut::<[u8; 16]>() }.unwrap();
2649 assert!(account.load::<TestLayout>().is_err());
2650 }
2651 {
2654 let _r = account
2655 .segment_ref::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2656 .unwrap();
2657 }
2663 assert_eq!(borrows.len(), 0);
2669 let _w = account
2670 .segment_mut::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2671 .unwrap();
2672 }
2673
2674 #[test]
2679 fn seg_lease_releases_on_drop_and_allows_reacquire() {
2680 let (_backing, account) = make_account(TestLayout::SIZE, 41);
2681 {
2682 let mut data = account.try_borrow_mut().unwrap();
2683 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2684 }
2685 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2686 const OFF: u32 = crate::layout::HopperHeader::SIZE as u32;
2687
2688 {
2689 let mut first = account
2690 .segment_mut::<[u8; 8]>(&mut borrows, OFF, 8)
2691 .unwrap();
2692 *first = le_u64(100);
2693 }
2694 assert_eq!(borrows.len(), 0);
2696 {
2699 let mut second = account
2700 .segment_mut::<[u8; 8]>(&mut borrows, OFF, 8)
2701 .unwrap();
2702 assert_eq!(from_le_u64(*second), 100);
2703 *second = le_u64(200);
2704 }
2705 assert_eq!(borrows.len(), 0);
2706 let read = account
2707 .segment_ref::<[u8; 8]>(&mut borrows, OFF, 8)
2708 .unwrap();
2709 assert_eq!(from_le_u64(*read), 200);
2710 }
2711
2712 #[test]
2716 fn seg_lease_still_rejects_simultaneous_overlap() {
2717 let (_backing, account) = make_account(TestLayout::SIZE, 42);
2718 {
2719 let mut data = account.try_borrow_mut().unwrap();
2720 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2721 }
2722 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2723 const OFF: u32 = crate::layout::HopperHeader::SIZE as u32;
2724
2725 let _first = account
2726 .segment_mut::<[u8; 8]>(&mut borrows, OFF, 8)
2727 .unwrap();
2728 drop(_first);
2735 assert_eq!(borrows.len(), 0);
2736 }
2737
2738 #[test]
2739 fn split_segments_mut_borrows_two_disjoint_ranges() {
2740 let (_backing, account) = make_account(TestLayout::SIZE, 43);
2741 {
2742 let mut data = account.try_borrow_mut().unwrap();
2743 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2744 }
2745 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2746 const A: u32 = HopperHeader::SIZE as u32; const B: u32 = HopperHeader::SIZE as u32 + 8; {
2750 let mut segs = account
2751 .split_segments_mut::<[u8; 8], 2>(&mut borrows, [(A, 8), (B, 8)])
2752 .unwrap();
2753 assert_eq!(segs.len(), 2);
2754 let [a, b] = segs.all_mut();
2756 *a = le_u64(111);
2757 *b = le_u64(222);
2758 }
2759 assert_eq!(borrows.len(), 0);
2761
2762 let a = account.segment_ref::<[u8; 8]>(&mut borrows, A, 8).unwrap();
2763 assert_eq!(from_le_u64(*a), 111);
2764 drop(a);
2765 let b = account.segment_ref::<[u8; 8]>(&mut borrows, B, 8).unwrap();
2766 assert_eq!(from_le_u64(*b), 222);
2767 }
2768
2769 #[test]
2770 fn split_segments_mut_rejects_overlap_and_rolls_back() {
2771 let (_backing, account) = make_account(TestLayout::SIZE, 44);
2772 {
2773 let mut data = account.try_borrow_mut().unwrap();
2774 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2775 }
2776 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2777 const A: u32 = HopperHeader::SIZE as u32;
2778
2779 let err = account
2782 .split_segments_mut::<[u8; 8], 2>(&mut borrows, [(A, 8), (A + 4, 8)])
2783 .unwrap_err();
2784 assert_eq!(err, ProgramError::AccountBorrowFailed);
2785 assert_eq!(borrows.len(), 0);
2786
2787 let err = account
2789 .split_segments_mut::<[u8; 8], 2>(&mut borrows, [(A, 8), (9_000, 8)])
2790 .unwrap_err();
2791 assert_eq!(err, ProgramError::AccountDataTooSmall);
2792 assert_eq!(borrows.len(), 0);
2793 }
2794
2795 #[test]
2796 fn typed_segment_api_round_trips() {
2797 use crate::segment::TypedSegment;
2798
2799 let (_backing, account) = make_account(TestLayout::SIZE, 22);
2800 {
2801 let mut data = account.try_borrow_mut().unwrap();
2802 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2803 }
2804
2805 const A_TYPED: TypedSegment<[u8; 8], { crate::layout::HopperHeader::SIZE as u32 }> =
2806 TypedSegment::new();
2807
2808 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2812 {
2813 let mut a = account
2814 .segment_mut_typed::<[u8; 8], { crate::layout::HopperHeader::SIZE as u32 }>(
2815 &mut borrows,
2816 A_TYPED,
2817 )
2818 .unwrap();
2819 *a = le_u64(1337);
2820 }
2821 assert_eq!(borrows.len(), 0);
2822
2823 let read = account
2824 .segment_ref_typed::<[u8; 8], { crate::layout::HopperHeader::SIZE as u32 }>(
2825 &mut borrows,
2826 A_TYPED,
2827 )
2828 .unwrap();
2829 assert_eq!(from_le_u64(*read), 1337);
2830 }
2831
2832 #[test]
2833 fn const_segment_api_matches_manual_offsets() {
2834 use crate::segment::Segment;
2835
2836 let (_backing, account) = make_account(TestLayout::SIZE, 20);
2837 {
2838 let mut data = account.try_borrow_mut().unwrap();
2839 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2840 }
2841
2842 const A_SEG: Segment = Segment::body(0, 8); let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2847 {
2848 let mut a = account
2849 .segment_mut_const::<[u8; 8]>(&mut borrows, A_SEG)
2850 .unwrap();
2851 *a = le_u64(7);
2852 }
2853 let read = account
2854 .segment_ref::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2855 .unwrap();
2856 assert_eq!(from_le_u64(*read), 7);
2857 }
2858
2859 #[test]
2860 fn load_after_segment_drop_succeeds() {
2861 let (_backing, account) = make_account(TestLayout::SIZE, 12);
2862 {
2863 let mut data = account.try_borrow_mut().unwrap();
2864 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2865 }
2866
2867 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2868 {
2869 let mut seg = account
2870 .segment_mut::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2871 .unwrap();
2872 *seg = le_u64(42);
2873 }
2874 let view = account.load::<TestLayout>().unwrap();
2876 assert_eq!(from_le_u64(view.a), 42);
2877 }
2878
2879 #[test]
2885 #[cfg(not(feature = "unguarded-raw-surfaces"))]
2886 fn zero_range_is_gated_over_exactly_the_cleared_bytes() {
2887 use crate::write_policy::{
2888 install_lamport_gate, write_policy_violation, WritePolicy, WriteRange,
2889 };
2890
2891 let (_b0, a0) = make_account(32, 70);
2892 let accounts = [a0];
2893 static TAIL: WritePolicy = WritePolicy::new(&[WriteRange::tail_from(0, 16)]);
2895
2896 {
2897 let mut data = accounts[0].try_borrow_mut().unwrap();
2898 for byte in data.iter_mut() {
2899 *byte = 0xAA;
2900 }
2901 }
2902
2903 let _gate = install_lamport_gate(&accounts, &TAIL);
2904
2905 assert!(accounts[0].zero_range(16, 16).is_ok());
2907 assert_eq!(
2909 accounts[0].zero_range(8, 16),
2910 Err(write_policy_violation(0)),
2911 );
2912 assert_eq!(accounts[0].zero_range(0, 8), Err(write_policy_violation(0)));
2914 assert!(accounts[0].zero_range(0, 0).is_ok());
2916 assert_eq!(
2918 accounts[0].zero_range(24, 16),
2919 Err(ProgramError::AccountDataTooSmall),
2920 );
2921
2922 drop(_gate);
2923 let data = accounts[0].try_borrow().unwrap();
2924 assert!(
2925 data[16..32].iter().all(|b| *b == 0),
2926 "the authorized range was actually cleared"
2927 );
2928 assert!(
2929 data[0..16].iter().all(|b| *b == 0xAA),
2930 "refused ranges left the head untouched"
2931 );
2932 }
2933
2934 #[test]
2942 #[cfg(not(feature = "unguarded-raw-surfaces"))]
2943 fn zero_appended_rides_the_transition_authority_not_the_byte_ranges() {
2944 use crate::write_policy::{
2945 install_lamport_gate, write_policy_violation, WritePolicy, WriteRange,
2946 };
2947
2948 let (_b0, a0) = make_account(32, 72);
2949 let (_bf, foreign) = make_account(32, 73);
2950 let accounts = [a0];
2951 static NARROW: WritePolicy = WritePolicy::new(&[WriteRange::new(0, 0, 8)]);
2954
2955 {
2956 let mut data = accounts[0].try_borrow_mut().unwrap();
2957 for byte in data.iter_mut() {
2958 *byte = 0xCC;
2959 }
2960 }
2961
2962 let _gate = install_lamport_gate(&accounts, &NARROW);
2963
2964 assert!(accounts[0].zero_appended(16).is_ok());
2968
2969 assert_eq!(
2972 foreign.zero_appended(16),
2973 Err(write_policy_violation(u8::MAX)),
2974 );
2975
2976 assert!(accounts[0].zero_appended(32).is_ok());
2981 assert!(accounts[0].zero_appended(64).is_ok());
2982
2983 drop(_gate);
2984 let data = accounts[0].try_borrow().unwrap();
2985 assert!(
2986 data[16..32].iter().all(|b| *b == 0),
2987 "the appended region was cleared"
2988 );
2989 assert!(
2990 data[0..16].iter().all(|b| *b == 0xCC),
2991 "the pre-existing body was untouched"
2992 );
2993 }
2994
2995 #[test]
3003 #[cfg(not(feature = "unguarded-raw-surfaces"))]
3004 fn extension_bytes_mut_is_governed_over_its_exact_range() {
3005 use crate::write_policy::{
3006 install_lamport_gate, write_policy_violation, WritePolicy, WriteRange,
3007 };
3008
3009 const EXT_LEN: usize = 8;
3010 let (_backing, account) = make_account(TestLayout::SIZE + EXT_LEN, 60);
3011 {
3012 let mut data = account.try_borrow_mut().unwrap();
3013 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
3014 }
3015 let accounts = [account];
3016
3017 {
3019 let ext = accounts[0].extension_bytes_mut::<TestLayout>().unwrap();
3020 assert_eq!(ext.len(), EXT_LEN);
3021 }
3022
3023 {
3027 static HEAD_ONLY: WritePolicy = WritePolicy::new(&[WriteRange::new(0, 0, 8)]);
3028 let _gate = install_lamport_gate(&accounts, &HEAD_ONLY);
3029 assert_eq!(
3030 accounts[0].extension_bytes_mut::<TestLayout>().map(|_| ()),
3031 Err(write_policy_violation(0)),
3032 );
3033 }
3034
3035 {
3038 static TAIL: WritePolicy =
3039 WritePolicy::new(&[WriteRange::tail_from(0, TestLayout::SIZE as u32)]);
3040 let _gate = install_lamport_gate(&accounts, &TAIL);
3041 let ext = accounts[0].extension_bytes_mut::<TestLayout>().unwrap();
3042 assert_eq!(ext.len(), EXT_LEN);
3043 }
3044
3045 {
3047 static WHOLE: WritePolicy = WritePolicy::new(&[WriteRange::whole_account(0)]);
3048 let _gate = install_lamport_gate(&accounts, &WHOLE);
3049 assert!(accounts[0].extension_bytes_mut::<TestLayout>().is_ok());
3050 }
3051
3052 let (_short_backing, short) = make_account(TestLayout::SIZE - 1, 61);
3056 assert_eq!(
3057 short.extension_bytes_mut::<TestLayout>().map(|_| ()),
3058 Err(ProgramError::AccountDataTooSmall),
3059 );
3060 }
3061}