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(always)]
47fn check_compact_init_len<T: crate::CompactLayout>(len: usize) -> ProgramResult {
48 if len != T::COMPACT_LEN
49 || len < crate::compact::COMPACT_BODY_OFFSET + core::mem::size_of::<T>()
50 {
51 return Err(compact_init_len_error::<T>(len));
52 }
53 Ok(())
54}
55
56#[cold]
57#[inline(never)]
58fn compact_init_len_error<T: crate::CompactLayout>(len: usize) -> ProgramError {
59 match check_typed_projection::<T>(len, crate::compact::COMPACT_BODY_OFFSET) {
60 Err(error) => error,
61 Ok(_) => crate::compact::compact_len_error(len, T::COMPACT_LEN),
62 }
63}
64
65#[inline]
73unsafe fn release_registered<const N: usize>(
74 reg: &mut SegmentBorrowRegistry,
75 recs: &[core::mem::MaybeUninit<crate::segment_borrow::SegmentBorrow>; N],
76 count: usize,
77) {
78 let mut j = 0;
79 while j < count {
80 unsafe {
82 reg.release(recs[j].assume_init_ref());
83 }
84 j += 1;
85 }
86}
87
88#[repr(transparent)]
102pub struct AccountView<'info> {
103 inner: BackendAccountView<'info>,
104}
105
106const _: () = {
107 assert!(
108 core::mem::size_of::<AccountView<'static>>()
109 == core::mem::size_of::<BackendAccountView<'static>>()
110 );
111 assert!(
112 core::mem::align_of::<AccountView<'static>>()
113 == core::mem::align_of::<BackendAccountView<'static>>()
114 );
115 assert!(!core::mem::needs_drop::<AccountView<'static>>());
116};
117
118#[cfg(target_os = "solana")]
121unsafe impl<'info> Send for AccountView<'info> {}
122#[cfg(target_os = "solana")]
123unsafe impl<'info> Sync for AccountView<'info> {}
124
125impl<'info> Clone for AccountView<'info> {
126 #[inline(always)]
127 fn clone(&self) -> Self {
128 Self::from_inner(self.backend().clone())
129 }
130}
131
132impl<'info> PartialEq for AccountView<'info> {
133 #[inline(always)]
134 fn eq(&self, other: &Self) -> bool {
135 self.backend() == other.backend()
136 }
137}
138
139impl<'info> Eq for AccountView<'info> {}
140
141impl<'info> AccountView<'info> {
142 #[inline(always)]
145 pub(crate) fn from_inner(inner: BackendAccountView<'info>) -> Self {
146 Self { inner }
147 }
148
149 #[inline(always)]
150 fn backend(&self) -> &BackendAccountView<'info> {
151 &self.inner
152 }
153
154 #[cfg(test)]
155 #[inline(always)]
156 pub(crate) fn from_backend(inner: BackendAccountView<'info>) -> Self {
157 Self::from_inner(inner)
158 }
159
160 #[inline(always)]
164 pub fn address(&self) -> &Address {
165 native_boundary::account_address(self.backend())
166 }
167
168 #[inline(always)]
175 pub unsafe fn owner(&self) -> &Address {
176 unsafe { native_boundary::account_owner(self.backend()) }
179 }
180
181 #[inline(always)]
183 pub fn read_owner(&self) -> Address {
184 native_boundary::read_owner(self.backend())
185 }
186
187 #[inline(always)]
189 pub fn owned_by(&self, program: &Address) -> bool {
190 native_boundary::owned_by(self.backend(), program)
191 }
192
193 #[inline(always)]
195 pub fn is_signer(&self) -> bool {
196 self.backend().is_signer()
197 }
198
199 #[inline(always)]
201 pub fn is_writable(&self) -> bool {
202 self.backend().is_writable()
203 }
204
205 #[inline(always)]
207 pub fn executable(&self) -> bool {
208 self.backend().executable()
209 }
210
211 #[inline(always)]
213 pub fn data_len(&self) -> usize {
214 self.backend().data_len()
215 }
216
217 #[inline(always)]
219 pub fn lamports(&self) -> u64 {
220 self.backend().lamports()
221 }
222
223 #[inline(always)]
225 pub fn is_data_empty(&self) -> bool {
226 self.data_len() == 0
227 }
228
229 #[inline(always)]
235 pub fn try_set_lamports(&self, lamports: u64) -> ProgramResult {
236 native_boundary::try_set_lamports(self.backend(), lamports)
237 }
238
239 #[inline(always)]
241 pub fn set_lamports(&self, lamports: u64) -> ProgramResult {
242 self.try_set_lamports(lamports)
243 }
244
245 #[inline(always)]
249 pub fn try_borrow(&self) -> Result<Ref<'_, [u8]>, ProgramError> {
250 let token = BorrowToken::shared(self.address())?;
251 match self.backend().try_borrow() {
252 Ok(data) => Ok(Ref::from_backend(data, token)),
253 Err(error) => {
254 drop(token);
255 Err(ProgramError::from(error))
256 }
257 }
258 }
259
260 #[inline(always)]
280 pub fn try_borrow_mut(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
281 let len = self.data_len();
282 if len > 0 {
283 crate::write_policy::check_data_mutation(self.address(), 0, len as u32)?;
284 }
285 self.try_borrow_mut_ungated()
286 }
287
288 #[inline(always)]
309 pub(crate) fn try_borrow_mut_ungated(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
310 let token = BorrowToken::mutable(self.address())?;
311 match self.backend().try_borrow_mut() {
312 Ok(data) => Ok(RefMut::from_backend(data, token)),
313 Err(error) => {
314 drop(token);
315 Err(ProgramError::from(error))
316 }
317 }
318 }
319
320 #[inline(always)]
343 pub fn segment_ref<'a, T: crate::Pod>(
344 &'a self,
345 borrows: &'a mut SegmentBorrowRegistry,
346 abs_offset: u32,
347 size: u32,
348 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
349 let expected_size = core::mem::size_of::<T>() as u32;
350 if size != expected_size {
351 return ProgramError::err_invalid_argument();
352 }
353
354 let end = abs_offset
355 .checked_add(size)
356 .ok_or(ProgramError::ArithmeticOverflow)?;
357 if end as usize > self.data_len() {
358 return ProgramError::err_data_too_small();
359 }
360
361 let borrow = borrows.register_leased_read(self.address(), abs_offset, size)?;
362
363 #[cfg(target_os = "solana")]
365 let inner: Ref<'_, T> = {
366 let native_ref = self.backend().segment_ref::<T>(abs_offset, size);
370 let native_ref = match native_ref {
371 Ok(nr) => nr,
372 Err(e) => {
373 borrows.release(&borrow);
377 return Err(ProgramError::from(e));
378 }
379 };
380 let (typed_ref, state_ptr) = native_ref.into_raw_parts();
381 Ref::from_segment(typed_ref as *const T, state_ptr)
382 };
383 #[cfg(not(target_os = "solana"))]
384 let inner: Ref<'_, T> = {
385 let data = match self.try_borrow() {
386 Ok(d) => d,
387 Err(e) => {
388 borrows.release(&borrow);
389 return Err(e);
390 }
391 };
392 let ptr = unsafe { data.as_bytes_ptr().add(abs_offset as usize) as *const T };
397 unsafe { data.project(ptr) }
398 };
399
400 let lease = unsafe { crate::SegmentLease::new(borrows, borrow) };
403 Ok(crate::SegRef::new(inner, lease))
404 }
405
406 #[inline(always)]
417 pub fn segment_mut<'a, T: crate::Pod>(
418 &'a self,
419 borrows: &'a mut SegmentBorrowRegistry,
420 abs_offset: u32,
421 size: u32,
422 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
423 crate::write_policy::check_data_mutation(self.address(), abs_offset, size)?;
424 self.segment_mut_ungated::<T>(borrows, abs_offset, size)
425 }
426
427 #[inline(always)]
432 pub(crate) fn segment_mut_ungated<'a, T: crate::Pod>(
433 &'a self,
434 borrows: &'a mut SegmentBorrowRegistry,
435 abs_offset: u32,
436 size: u32,
437 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
438 self.check_writable()?;
439
440 let expected_size = core::mem::size_of::<T>() as u32;
441 if size != expected_size {
442 return ProgramError::err_invalid_argument();
443 }
444
445 let end = abs_offset
446 .checked_add(size)
447 .ok_or(ProgramError::ArithmeticOverflow)?;
448 if end as usize > self.data_len() {
449 return ProgramError::err_data_too_small();
450 }
451
452 let borrow = borrows.register_leased_write(self.address(), abs_offset, size)?;
453
454 #[cfg(target_os = "solana")]
455 let inner: RefMut<'_, T> = {
456 let native_ref = self.backend().segment_mut::<T>(abs_offset, size);
459 let native_ref = match native_ref {
460 Ok(nr) => nr,
461 Err(e) => {
462 borrows.release(&borrow);
463 return Err(ProgramError::from(e));
464 }
465 };
466 let (typed_ref, state_ptr) = native_ref.into_raw_parts();
467 RefMut::from_segment(typed_ref as *mut T, state_ptr)
468 };
469 #[cfg(not(target_os = "solana"))]
470 let inner: RefMut<'_, T> = {
471 let mut data = match self.try_borrow_mut_ungated() {
472 Ok(d) => d,
473 Err(e) => {
474 borrows.release(&borrow);
475 return Err(e);
476 }
477 };
478 let ptr = unsafe { data.as_bytes_mut_ptr().add(abs_offset as usize) as *mut T };
483 unsafe { data.project(ptr) }
484 };
485
486 let lease = unsafe { crate::SegmentLease::new(borrows, borrow) };
489 Ok(crate::SegRefMut::new(inner, lease))
490 }
491
492 pub fn split_segments_mut<'a, T: crate::Pod, const N: usize>(
519 &'a self,
520 borrows: &'a mut SegmentBorrowRegistry,
521 ranges: [(u32, u32); N],
522 ) -> Result<crate::SegmentsMut<'a, T, N>, ProgramError> {
523 for (off, size) in ranges {
528 crate::write_policy::check_data_mutation(self.address(), off, size)?;
529 }
530 self.split_segments_mut_ungated::<T, N>(borrows, ranges)
531 }
532
533 pub(crate) fn split_segments_mut_ungated<'a, T: crate::Pod, const N: usize>(
538 &'a self,
539 borrows: &'a mut SegmentBorrowRegistry,
540 ranges: [(u32, u32); N],
541 ) -> Result<crate::SegmentsMut<'a, T, N>, ProgramError> {
542 self.check_writable()?;
543 let expected = core::mem::size_of::<T>() as u32;
544 let data_len = self.data_len();
545
546 let mut recs: [core::mem::MaybeUninit<crate::segment_borrow::SegmentBorrow>; N] =
557 unsafe { core::mem::MaybeUninit::uninit().assume_init() };
558 let mut offsets = [0usize; N];
559 let mut i = 0;
560 while i < N {
561 let (off, size) = ranges[i];
562 let in_bounds = match off.checked_add(size) {
563 Some(end) => end as usize <= data_len,
564 None => false,
565 };
566 if size != expected || !in_bounds {
567 unsafe { release_registered(borrows, &recs, i) };
569 return if size != expected {
570 ProgramError::err_invalid_argument()
571 } else {
572 ProgramError::err_data_too_small()
573 };
574 }
575 match borrows.register_leased_write(self.address(), off, size) {
576 Ok(b) => {
577 recs[i] = core::mem::MaybeUninit::new(b);
578 offsets[i] = off as usize;
579 }
580 Err(e) => {
581 unsafe { release_registered(borrows, &recs, i) };
583 return Err(e);
584 }
585 }
586 i += 1;
587 }
588
589 let data = match self.try_borrow_mut_ungated() {
595 Ok(d) => d,
596 Err(e) => {
597 unsafe { release_registered(borrows, &recs, N) };
599 return Err(e);
600 }
601 };
602
603 let reg_ptr = borrows as *mut SegmentBorrowRegistry;
608
609 let mut leases: [core::mem::MaybeUninit<crate::SegmentLease<'a>>; N] =
613 unsafe { core::mem::MaybeUninit::uninit().assume_init() };
614 let mut k = 0;
615 while k < N {
616 let lease = unsafe { crate::SegmentLease::from_raw(reg_ptr, recs[k].assume_init()) };
619 leases[k] = core::mem::MaybeUninit::new(lease);
620 k += 1;
621 }
622 let leases = unsafe {
624 let out = core::ptr::read(&leases as *const _ as *const [crate::SegmentLease<'a>; N]);
625 #[allow(clippy::forget_non_drop)]
628 core::mem::forget(leases);
629 out
630 };
631
632 Ok(crate::SegmentsMut::new(data, offsets, leases))
633 }
634
635 #[inline(always)]
656 pub fn segment_ref_const<'a, T: crate::Pod>(
657 &'a self,
658 borrows: &'a mut SegmentBorrowRegistry,
659 segment: crate::segment::Segment,
660 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
661 self.segment_ref::<T>(borrows, segment.offset, segment.size)
662 }
663
664 #[inline(always)]
667 pub fn segment_mut_const<'a, T: crate::Pod>(
668 &'a self,
669 borrows: &'a mut SegmentBorrowRegistry,
670 segment: crate::segment::Segment,
671 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
672 self.segment_mut::<T>(borrows, segment.offset, segment.size)
673 }
674
675 #[inline(always)]
690 pub fn segment_ref_typed<'a, T: crate::Pod, const OFFSET: u32>(
691 &'a self,
692 borrows: &'a mut SegmentBorrowRegistry,
693 _segment: crate::segment::TypedSegment<T, OFFSET>,
694 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
695 self.segment_ref::<T>(borrows, OFFSET, core::mem::size_of::<T>() as u32)
696 }
697
698 #[inline(always)]
701 pub fn segment_mut_typed<'a, T: crate::Pod, const OFFSET: u32>(
702 &'a self,
703 borrows: &'a mut SegmentBorrowRegistry,
704 _segment: crate::segment::TypedSegment<T, OFFSET>,
705 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
706 self.segment_mut::<T>(borrows, OFFSET, core::mem::size_of::<T>() as u32)
707 }
708
709 #[inline(always)]
730 pub fn load<T: LayoutContract + crate::Pod>(&self) -> Result<Ref<'_, T>, ProgramError> {
731 let data = self.try_borrow()?;
732 check_typed_projection::<T>(data.len(), T::TYPE_OFFSET)?;
733 T::validate_header(&data)?;
734 if data.len() < T::required_len() {
735 return ProgramError::err_data_too_small();
736 }
737 let ptr = unsafe { data.as_bytes_ptr().add(T::TYPE_OFFSET) as *const T };
740 Ok(unsafe { data.project(ptr) })
742 }
743
744 #[inline]
750 pub fn with<T, R, F>(&self, f: F) -> Result<R, ProgramError>
751 where
752 T: LayoutContract + crate::Pod,
753 F: FnOnce(&T) -> Result<R, ProgramError>,
754 {
755 let account = self.load::<T>()?;
756 f(&*account)
757 }
758
759 #[inline(always)]
771 pub fn load_mut<T: LayoutContract + crate::Pod>(&self) -> Result<RefMut<'_, T>, ProgramError> {
772 let mut data = self.try_borrow_mut()?;
773 check_typed_projection::<T>(data.len(), T::TYPE_OFFSET)?;
774 T::validate_header(&data)?;
775 if data.len() < T::required_len() {
776 return ProgramError::err_data_too_small();
777 }
778 #[cfg(feature = "touch-map")]
785 crate::segment_borrow::touch_log::record_account(
786 self.address(),
787 data.len() as u32,
788 crate::segment_borrow::AccessKind::Write,
789 );
790 let ptr = unsafe { data.as_bytes_mut_ptr().add(T::TYPE_OFFSET) as *mut T };
793 Ok(unsafe { data.project(ptr) })
795 }
796
797 #[inline]
802 pub fn with_mut<T, R, F>(&self, f: F) -> Result<R, ProgramError>
803 where
804 T: LayoutContract + crate::Pod,
805 F: FnOnce(&mut T) -> Result<R, ProgramError>,
806 {
807 let mut account = self.load_mut::<T>()?;
808 f(&mut *account)
809 }
810
811 #[inline(always)]
826 pub fn load_compact<T: crate::CompactLayout>(&self) -> Result<Ref<'_, T>, ProgramError> {
827 let data = self.try_borrow()?;
828 T::validate_compact(&data)?;
831 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
832 let ptr =
835 unsafe { data.as_bytes_ptr().add(crate::compact::COMPACT_BODY_OFFSET) as *const T };
836 Ok(unsafe { data.project(ptr) })
838 }
839
840 #[inline(always)]
842 pub fn load_compact_mut<T: crate::CompactLayout>(&self) -> Result<RefMut<'_, T>, ProgramError> {
843 let mut data = self.try_borrow_mut()?;
844 T::validate_compact(&data)?;
847 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
848 #[cfg(feature = "touch-map")]
850 crate::segment_borrow::touch_log::record_account(
851 self.address(),
852 data.len() as u32,
853 crate::segment_borrow::AccessKind::Write,
854 );
855 let ptr = unsafe {
858 data.as_bytes_mut_ptr()
859 .add(crate::compact::COMPACT_BODY_OFFSET) as *mut T
860 };
861 Ok(unsafe { data.project(ptr) })
863 }
864
865 #[inline]
867 pub fn with_compact<T, R, F>(&self, f: F) -> Result<R, ProgramError>
868 where
869 T: crate::CompactLayout,
870 F: FnOnce(&T) -> Result<R, ProgramError>,
871 {
872 let account = self.load_compact::<T>()?;
873 f(&*account)
874 }
875
876 #[inline]
878 pub fn with_compact_mut<T, R, F>(&self, f: F) -> Result<R, ProgramError>
879 where
880 T: crate::CompactLayout,
881 F: FnOnce(&mut T) -> Result<R, ProgramError>,
882 {
883 let mut account = self.load_compact_mut::<T>()?;
884 f(&mut *account)
885 }
886
887 #[inline(always)]
894 pub fn init_compact<T: crate::CompactLayout>(&self) -> ProgramResult {
895 self.check_writable()?;
896 let mut data = self.try_borrow_mut()?;
897 check_compact_init_len::<T>(data.len())?;
898 data[0] = T::DISC;
899 Ok(())
900 }
901
902 #[inline(always)]
919 pub fn init_compact_mut<T: crate::CompactLayout>(&self) -> Result<RefMut<'_, T>, ProgramError> {
920 self.check_writable()?;
921 let mut data = self.try_borrow_mut()?;
922 check_compact_init_len::<T>(data.len())?;
923 data[0] = T::DISC;
924 #[cfg(feature = "touch-map")]
926 crate::segment_borrow::touch_log::record_account(
927 self.address(),
928 data.len() as u32,
929 crate::segment_borrow::AccessKind::Write,
930 );
931 let ptr = unsafe {
934 data.as_bytes_mut_ptr()
935 .add(crate::compact::COMPACT_BODY_OFFSET) as *mut T
936 };
937 unsafe { ptr.write_bytes(0, 1) };
941 Ok(unsafe { data.project(ptr) })
944 }
945
946 #[inline(always)]
964 pub fn load_compact_dynamic<T: crate::CompactDynamicLayout>(
965 &self,
966 ) -> Result<Ref<'_, T>, ProgramError> {
967 let data = self.try_borrow()?;
968 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
969 T::validate_compact_dynamic(&data)?;
970 let ptr =
975 unsafe { data.as_bytes_ptr().add(crate::compact::COMPACT_BODY_OFFSET) as *const T };
976 Ok(unsafe { data.project(ptr) })
978 }
979
980 #[inline(always)]
983 pub fn load_compact_dynamic_mut<T: crate::CompactDynamicLayout>(
984 &self,
985 ) -> Result<RefMut<'_, T>, ProgramError> {
986 let mut data = self.try_borrow_mut()?;
987 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
988 T::validate_compact_dynamic(&data)?;
989 let ptr = unsafe {
992 data.as_bytes_mut_ptr()
993 .add(crate::compact::COMPACT_BODY_OFFSET) as *mut T
994 };
995 Ok(unsafe { data.project(ptr) })
997 }
998
999 #[inline]
1001 pub fn with_compact_dynamic<T, R, F>(&self, f: F) -> Result<R, ProgramError>
1002 where
1003 T: crate::CompactDynamicLayout,
1004 F: FnOnce(&T) -> Result<R, ProgramError>,
1005 {
1006 let account = self.load_compact_dynamic::<T>()?;
1007 f(&*account)
1008 }
1009
1010 #[inline]
1012 pub fn with_compact_dynamic_mut<T, R, F>(&self, f: F) -> Result<R, ProgramError>
1013 where
1014 T: crate::CompactDynamicLayout,
1015 F: FnOnce(&mut T) -> Result<R, ProgramError>,
1016 {
1017 let mut account = self.load_compact_dynamic_mut::<T>()?;
1018 f(&mut *account)
1019 }
1020
1021 #[inline(always)]
1030 pub fn init_compact_dynamic<T: crate::CompactDynamicLayout>(&self) -> ProgramResult {
1031 self.check_writable()?;
1032 let mut data = self.try_borrow_mut()?;
1033 let head_end =
1034 check_typed_projection::<T>(data.len(), crate::compact::COMPACT_BODY_OFFSET)?;
1035 if T::TAIL_OFFSET < head_end {
1036 return Err(ProgramError::InvalidAccountData);
1037 }
1038 let tail_end = T::TAIL_OFFSET
1039 .checked_add(4)
1040 .ok_or(ProgramError::ArithmeticOverflow)?;
1041 if data.len() < T::MIN_LEN {
1042 return Err(ProgramError::AccountDataTooSmall);
1043 }
1044 data[0] = T::DISC;
1045 if data.len() >= tail_end {
1047 data[T::TAIL_OFFSET..tail_end].copy_from_slice(&0u32.to_le_bytes());
1048 }
1049 Ok(())
1050 }
1051
1052 #[inline(always)]
1057 pub unsafe fn raw_ref<T: crate::Pod>(&self) -> Result<Ref<'_, T>, ProgramError> {
1062 let data = self.try_borrow()?;
1063 if core::mem::size_of::<T>() > data.len() {
1064 return Err(ProgramError::AccountDataTooSmall);
1065 }
1066 let ptr = data.as_ptr() as *const T;
1067 Ok(unsafe { data.project(ptr) })
1071 }
1072
1073 #[inline(always)]
1078 pub unsafe fn raw_mut<T: crate::Pod>(&self) -> Result<RefMut<'_, T>, ProgramError> {
1083 self.check_writable()?;
1084 let mut data = self.try_borrow_mut_ungated()?;
1089 if core::mem::size_of::<T>() > data.len() {
1090 return Err(ProgramError::AccountDataTooSmall);
1091 }
1092 let ptr = data.as_bytes_mut_ptr() as *mut T;
1093 Ok(unsafe { data.project(ptr) })
1097 }
1098
1099 #[inline(always)]
1116 pub fn load_cross_program<T: LayoutContract + crate::Pod>(
1117 &self,
1118 ) -> Result<Ref<'_, T>, ProgramError> {
1119 let data = self.try_borrow()?;
1120 check_typed_projection::<T>(data.len(), T::TYPE_OFFSET)?;
1121 T::validate_header(&data)?;
1122 if data.len() < T::required_len() {
1125 return ProgramError::err_data_too_small();
1126 }
1127 let ptr = unsafe { data.as_bytes_ptr().add(T::TYPE_OFFSET) as *const T };
1130 Ok(unsafe { data.project(ptr) })
1132 }
1133
1134 #[inline(always)]
1140 pub fn layout_info(&self) -> Option<crate::layout::LayoutInfo> {
1141 let data = self.try_borrow().ok()?;
1142 crate::layout::LayoutInfo::from_data(&data)
1143 }
1144
1145 #[inline(always)]
1147 pub fn fields<T: LayoutContract>() -> &'static [FieldInfo] {
1148 T::fields()
1149 }
1150
1151 #[inline]
1159 pub fn field<T: LayoutContract>(name: &str) -> Option<&'static FieldInfo> {
1160 <T as crate::field_map::FieldMap>::field_by_name(name)
1161 }
1162
1163 #[inline(always)]
1168 pub fn extension_range<T: LayoutContract>(
1169 &self,
1170 ) -> Result<core::ops::Range<usize>, ProgramError> {
1171 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
1172 let data_len = self.data_len();
1173 if data_len < offset {
1174 return Err(ProgramError::AccountDataTooSmall);
1175 }
1176 Ok(offset..data_len)
1177 }
1178
1179 #[inline(always)]
1181 pub fn extension_bytes<T: LayoutContract>(&self) -> Result<Ref<'_, [u8]>, ProgramError> {
1182 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
1183 let data = self.try_borrow()?;
1184 if data.len() < offset {
1185 return Err(ProgramError::AccountDataTooSmall);
1186 }
1187 Ok(data.slice_from(offset))
1188 }
1189
1190 #[inline(always)]
1192 pub fn extension_bytes_mut<T: LayoutContract>(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
1193 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
1194 let len = self.data_len();
1195 if len < offset {
1196 return Err(ProgramError::AccountDataTooSmall);
1197 }
1198 if len > offset {
1204 crate::write_policy::check_data_mutation(
1205 self.address(),
1206 offset as u32,
1207 (len - offset) as u32,
1208 )?;
1209 }
1210 let data = self.try_borrow_mut_ungated()?;
1211 Ok(data.slice_from(offset))
1212 }
1213
1214 #[inline]
1227 pub fn zero_range(&self, start: usize, len: usize) -> ProgramResult {
1228 if len == 0 {
1229 return Ok(());
1230 }
1231 let end = start
1232 .checked_add(len)
1233 .ok_or(ProgramError::ArithmeticOverflow)?;
1234 if end > self.data_len() {
1235 return Err(ProgramError::AccountDataTooSmall);
1236 }
1237 let offset_u32 = u32::try_from(start).map_err(|_| ProgramError::ArithmeticOverflow)?;
1238 let len_u32 = u32::try_from(len).map_err(|_| ProgramError::ArithmeticOverflow)?;
1239 crate::write_policy::check_data_mutation(self.address(), offset_u32, len_u32)?;
1240 let mut data = self.try_borrow_mut_ungated()?;
1241 for byte in data[start..end].iter_mut() {
1242 *byte = 0;
1243 }
1244 Ok(())
1245 }
1246
1247 #[inline]
1272 pub fn zero_appended(&self, previous_len: usize) -> ProgramResult {
1273 let len = self.data_len();
1274 if previous_len >= len {
1275 return Ok(());
1276 }
1277 crate::write_policy::check_account_transition(self.address())?;
1278 let mut data = self.try_borrow_mut_ungated()?;
1279 for byte in data[previous_len..len].iter_mut() {
1280 *byte = 0;
1281 }
1282 Ok(())
1283 }
1284
1285 #[inline(always)]
1290 pub fn init_layout<T: LayoutContract>(&self) -> ProgramResult {
1291 let mut data = self.try_borrow_mut()?;
1292 crate::layout::init_header::<T>(&mut data)
1293 }
1294
1295 #[inline(always)]
1299 pub fn require_signer(&self) -> ProgramResult {
1300 if self.is_signer() {
1301 Ok(())
1302 } else {
1303 ProgramError::err_missing_signer()
1304 }
1305 }
1306
1307 #[inline(always)]
1309 pub fn require_writable(&self) -> ProgramResult {
1310 if self.is_writable() {
1311 Ok(())
1312 } else {
1313 ProgramError::err_immutable()
1314 }
1315 }
1316
1317 #[inline(always)]
1319 pub fn require_owned_by(&self, program: &Address) -> ProgramResult {
1320 if self.owned_by(program) {
1321 Ok(())
1322 } else {
1323 ProgramError::err_incorrect_program()
1324 }
1325 }
1326
1327 #[inline(always)]
1329 pub fn require_payer(&self) -> ProgramResult {
1330 self.require_signer()?;
1331 self.require_writable()
1332 }
1333
1334 #[inline(always)]
1338 pub fn check_signer(&self) -> Result<&Self, ProgramError> {
1339 if self.is_signer() {
1340 Ok(self)
1341 } else {
1342 ProgramError::err_missing_signer()
1343 }
1344 }
1345
1346 #[inline(always)]
1348 pub fn check_writable(&self) -> Result<&Self, ProgramError> {
1349 if self.is_writable() {
1350 Ok(self)
1351 } else {
1352 ProgramError::err_immutable()
1353 }
1354 }
1355
1356 #[inline(always)]
1358 pub fn check_owned_by(&self, program: &Address) -> Result<&Self, ProgramError> {
1359 if self.owned_by(program) {
1360 Ok(self)
1361 } else {
1362 ProgramError::err_incorrect_program()
1363 }
1364 }
1365
1366 #[inline]
1373 pub fn check_owned_by_any(&self, programs: &[&Address]) -> Result<&Self, ProgramError> {
1374 if programs.iter().any(|program| self.owned_by(program)) {
1375 Ok(self)
1376 } else {
1377 ProgramError::err_incorrect_program()
1378 }
1379 }
1380
1381 #[inline(always)]
1383 pub fn check_disc(&self, expected: u8) -> Result<&Self, ProgramError> {
1384 if self.disc() == expected {
1385 Ok(self)
1386 } else {
1387 Err(ProgramError::InvalidAccountData)
1388 }
1389 }
1390
1391 #[inline(always)]
1393 pub fn check_has_data(&self) -> Result<&Self, ProgramError> {
1394 if !self.is_data_empty() {
1395 Ok(self)
1396 } else {
1397 Err(ProgramError::AccountDataTooSmall)
1398 }
1399 }
1400
1401 #[inline(always)]
1403 pub fn check_executable(&self) -> Result<&Self, ProgramError> {
1404 if self.executable() {
1405 Ok(self)
1406 } else {
1407 Err(ProgramError::InvalidArgument)
1408 }
1409 }
1410
1411 #[inline(always)]
1413 pub fn check_address(&self, expected: &Address) -> Result<&Self, ProgramError> {
1414 if address_eq(self.address(), expected) {
1415 Ok(self)
1416 } else {
1417 Err(ProgramError::InvalidArgument)
1418 }
1419 }
1420
1421 #[inline(always)]
1423 pub fn check_data_len(&self, min_len: usize) -> Result<&Self, ProgramError> {
1424 if self.data_len() >= min_len {
1425 Ok(self)
1426 } else {
1427 Err(ProgramError::AccountDataTooSmall)
1428 }
1429 }
1430
1431 #[inline(always)]
1433 pub fn check_version(&self, expected: u8) -> Result<&Self, ProgramError> {
1434 if self.version() == expected {
1435 Ok(self)
1436 } else {
1437 Err(ProgramError::InvalidAccountData)
1438 }
1439 }
1440
1441 #[inline(always)]
1443 pub fn check_layout<T: LayoutContract>(&self) -> Result<&Self, ProgramError> {
1444 let data = self.try_borrow()?;
1445 T::validate_header(&data)?;
1446 Ok(self)
1447 }
1448
1449 #[inline(always)]
1451 pub const fn proof(&self) -> crate::proof::AccountProof<'_> {
1452 crate::proof::AccountProof::new(self)
1453 }
1454
1455 #[inline(always)]
1459 pub fn disc(&self) -> u8 {
1460 native_boundary::disc(self.backend())
1461 }
1462
1463 #[inline(always)]
1465 pub fn version(&self) -> u8 {
1466 native_boundary::version(self.backend())
1467 }
1468
1469 #[inline(always)]
1473 pub fn layout_id(&self) -> Option<[u8; 8]> {
1474 native_boundary::layout_id(self.backend())
1475 }
1476
1477 #[inline(always)]
1479 pub fn require_disc(&self, expected: u8) -> ProgramResult {
1480 if self.disc() == expected {
1481 Ok(())
1482 } else {
1483 Err(ProgramError::InvalidAccountData)
1484 }
1485 }
1486
1487 #[inline(always)]
1498 pub fn flags(&self) -> u8 {
1499 self.backend().flags()
1500 }
1501
1502 #[inline(always)]
1504 pub fn expect_flags(&self, required: u8) -> ProgramResult {
1505 if self.flags() & required == required {
1506 Ok(())
1507 } else {
1508 Err(ProgramError::InvalidArgument)
1509 }
1510 }
1511
1512 #[inline(always)]
1523 pub fn expect_signer_writable(&self, need_signer: bool, need_writable: bool) -> ProgramResult {
1524 if self
1530 .backend()
1531 .is_signer_writable(need_signer, need_writable)
1532 {
1533 return Ok(());
1534 }
1535 if need_signer {
1537 self.require_signer()?;
1538 }
1539 if need_writable {
1540 self.require_writable()?;
1541 }
1542 Ok(())
1545 }
1546
1547 #[inline]
1555 pub fn resize(&self, new_len: usize) -> ProgramResult {
1556 crate::write_policy::check_account_transition(self.address())?;
1560 if new_len != self.data_len() {
1561 self.check_borrow_mut()?;
1562 }
1563 native_boundary::resize(self.backend(), new_len)
1564 }
1565
1566 #[inline]
1568 pub fn resize_raw(&self, new_len: usize) -> ProgramResult {
1569 crate::write_policy::check_account_transition(self.address())?;
1571 if new_len != self.data_len() {
1572 self.check_borrow_mut()?;
1573 }
1574 native_boundary::resize_raw(self.backend(), new_len)
1575 }
1576
1577 #[inline(always)]
1584 pub unsafe fn assign(&self, new_owner: &Address) {
1585 unsafe {
1588 native_boundary::assign(self.backend(), new_owner);
1589 }
1590 }
1591
1592 #[inline]
1594 pub fn close(&self) -> ProgramResult {
1595 crate::write_policy::check_account_transition(self.address())?;
1598 self.check_borrow_mut()?;
1599 native_boundary::close(self.backend())
1600 }
1601
1602 #[inline]
1628 pub fn close_to(&self, destination: &AccountView<'_>, program_id: &Address) -> ProgramResult {
1629 crate::write_policy::check_account_transition(self.address())?;
1633 self.require_writable()?;
1634 self.require_owned_by(program_id)?;
1635 destination.require_writable()?;
1636 self.close_to_preflighted(destination)
1637 }
1638
1639 #[inline]
1655 pub fn close_to_unchecked(&self, destination: &AccountView<'_>) -> ProgramResult {
1656 crate::write_policy::check_account_transition(self.address())?;
1657 self.close_to_preflighted(destination)
1658 }
1659
1660 #[inline]
1661 fn close_to_preflighted(&self, destination: &AccountView<'_>) -> ProgramResult {
1662 if crate::address::address_eq(self.address(), destination.address()) {
1663 return Err(ProgramError::InvalidArgument);
1664 }
1665 self.check_borrow_mut()?;
1666 self.require_writable()?;
1668 crate::write_policy::check_lamport_mutation(self.address())?;
1669 crate::write_policy::check_lamport_mutation(destination.address())?;
1670 let credited = destination
1671 .lamports()
1672 .checked_add(self.lamports())
1673 .ok_or(ProgramError::ArithmeticOverflow)?;
1674 native_boundary::zero_data(self.backend())?;
1677 self.try_set_lamports(0)?;
1678 destination.try_set_lamports(credited)?;
1679 Ok(())
1680 }
1681
1682 #[inline(always)]
1686 pub(crate) fn data_ptr_unchecked(&self) -> *mut u8 {
1687 self.backend().data_ptr_unchecked()
1688 }
1689
1690 #[inline(always)]
1694 pub fn header_word(&self) -> u32 {
1695 self.backend().header_word()
1696 }
1697
1698 #[inline(always)]
1700 pub(crate) fn account_ptr(&self) -> *const hopper_native::RuntimeAccount {
1701 self.backend().account_ptr()
1702 }
1703
1704 #[inline(always)]
1706 pub fn check_borrow(&self) -> Result<(), ProgramError> {
1707 borrow_registry::check_shared(self.address())?;
1708 self.backend().check_borrow().map_err(ProgramError::from)
1709 }
1710
1711 #[inline(always)]
1713 pub fn check_borrow_mut(&self) -> Result<(), ProgramError> {
1714 borrow_registry::check_mutable(self.address())?;
1715 self.backend()
1716 .check_borrow_mut()
1717 .map_err(ProgramError::from)
1718 }
1719
1720 #[inline(always)]
1726 pub unsafe fn borrow_unchecked(&self) -> &[u8] {
1727 unsafe { self.backend().borrow_unchecked() }
1730 }
1731
1732 #[allow(clippy::mut_from_ref)]
1743 #[inline(always)]
1744 pub unsafe fn borrow_unchecked_mut(&self) -> &mut [u8] {
1745 unsafe { self.backend().borrow_unchecked_mut() }
1748 }
1749
1750 #[inline(always)]
1756 pub unsafe fn resize_unchecked(&self, new_len: usize) {
1757 unsafe {
1760 self.backend().resize_unchecked(new_len);
1761 }
1762 }
1763
1764 #[inline(always)]
1770 pub unsafe fn close_unchecked(&self) {
1771 unsafe {
1774 self.backend().close_unchecked();
1775 }
1776 }
1777
1778 #[allow(dead_code)]
1782 #[inline(always)]
1783 pub(crate) fn as_backend(&self) -> &BackendAccountView<'_> {
1784 self.backend()
1785 }
1786}
1787
1788impl<'info> core::fmt::Debug for AccountView<'info> {
1789 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1790 f.debug_struct("AccountView")
1791 .field("address", self.address())
1792 .field("lamports", &self.lamports())
1793 .field("data_len", &self.data_len())
1794 .field("is_signer", &self.is_signer())
1795 .field("is_writable", &self.is_writable())
1796 .finish()
1797 }
1798}
1799
1800pub struct RemainingAccounts<'a> {
1804 accounts: &'a [AccountView<'a>],
1805 cursor: usize,
1806}
1807
1808impl<'a> RemainingAccounts<'a> {
1809 #[inline(always)]
1811 pub fn new(accounts: &'a [AccountView<'a>]) -> Self {
1812 Self {
1813 accounts,
1814 cursor: 0,
1815 }
1816 }
1817
1818 #[inline(always)]
1820 pub fn remaining(&self) -> usize {
1821 self.accounts.len() - self.cursor
1822 }
1823
1824 #[allow(clippy::should_implement_trait)]
1829 #[inline(always)]
1830 pub fn next(&mut self) -> Result<&'a AccountView<'a>, ProgramError> {
1831 if self.cursor >= self.accounts.len() {
1832 return Err(ProgramError::NotEnoughAccountKeys);
1833 }
1834 let account = &self.accounts[self.cursor];
1835 self.cursor += 1;
1836 Ok(account)
1837 }
1838
1839 #[inline(always)]
1841 pub fn next_signer(&mut self) -> Result<&'a AccountView<'a>, ProgramError> {
1842 let account = self.next()?;
1843 account.require_signer()?;
1844 Ok(account)
1845 }
1846
1847 #[inline(always)]
1849 pub fn next_writable(&mut self) -> Result<&'a AccountView<'a>, ProgramError> {
1850 let account = self.next()?;
1851 account.require_writable()?;
1852 Ok(account)
1853 }
1854
1855 #[inline(always)]
1857 pub fn next_owned_by(
1858 &mut self,
1859 program: &Address,
1860 ) -> Result<&'a AccountView<'a>, ProgramError> {
1861 let account = self.next()?;
1862 account.require_owned_by(program)?;
1863 Ok(account)
1864 }
1865}
1866
1867#[cfg(test)]
1868mod tests {
1869 use super::*;
1870 use crate::compact::CompactLayout;
1871 use crate::layout::HopperHeader;
1872
1873 use hopper_native::{
1874 AccountView as NativeAccountView, Address as NativeAddress, RuntimeAccount, NOT_BORROWED,
1875 };
1876
1877 #[repr(C)]
1878 #[derive(Clone, Copy, Debug, Default)]
1879 struct TestLayout {
1880 a: [u8; 8],
1881 b: [u8; 8],
1882 }
1883
1884 #[repr(C)]
1885 #[derive(Clone, Copy, Debug)]
1886 struct HeaderLayout {
1887 header: [u8; HopperHeader::SIZE],
1888 amount: [u8; 8],
1889 }
1890
1891 #[repr(C)]
1892 #[derive(Clone, Copy, Debug, Default)]
1893 struct EpochTwoLayout {
1894 amount: [u8; 8],
1895 }
1896
1897 unsafe impl crate::Zeroable for TestLayout {}
1898 unsafe impl crate::Zeroable for HeaderLayout {}
1899 unsafe impl crate::Zeroable for EpochTwoLayout {}
1900 unsafe impl crate::Pod for TestLayout {}
1901 unsafe impl crate::Pod for HeaderLayout {}
1902 unsafe impl crate::Pod for EpochTwoLayout {}
1903
1904 #[inline(always)]
1905 fn le_u64(v: u64) -> [u8; 8] {
1906 v.to_le_bytes()
1907 }
1908
1909 #[inline(always)]
1910 fn from_le_u64(bytes: [u8; 8]) -> u64 {
1911 u64::from_le_bytes(bytes)
1912 }
1913
1914 impl crate::field_map::FieldMap for TestLayout {
1915 const FIELDS: &'static [crate::field_map::FieldInfo] = &[
1916 crate::field_map::FieldInfo::new("a", HopperHeader::SIZE, 8),
1917 crate::field_map::FieldInfo::new("b", HopperHeader::SIZE + 8, 8),
1918 ];
1919 }
1920
1921 impl LayoutContract for TestLayout {
1922 const DISC: u8 = 7;
1923 const VERSION: u8 = 1;
1924 const LAYOUT_ID: [u8; 8] = [0xAB; 8];
1925 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1926 const EXTENSION_OFFSET: Option<usize> = Some(Self::SIZE);
1927 }
1928
1929 impl crate::field_map::FieldMap for HeaderLayout {
1930 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1931 "amount",
1932 HopperHeader::SIZE,
1933 8,
1934 )];
1935 }
1936
1937 impl LayoutContract for HeaderLayout {
1938 const DISC: u8 = 11;
1939 const VERSION: u8 = 2;
1940 const LAYOUT_ID: [u8; 8] = [0xCD; 8];
1941 const SIZE: usize = core::mem::size_of::<Self>();
1942 const TYPE_OFFSET: usize = 0;
1943 }
1944
1945 impl crate::field_map::FieldMap for EpochTwoLayout {
1946 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1947 "amount",
1948 HopperHeader::SIZE,
1949 8,
1950 )];
1951 }
1952
1953 impl LayoutContract for EpochTwoLayout {
1954 const DISC: u8 = 12;
1955 const VERSION: u8 = 1;
1956 const LAYOUT_ID: [u8; 8] = [0xEF; 8];
1957 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1958 const SCHEMA_EPOCH: u32 = 2;
1959 }
1960
1961 #[repr(C)]
1967 #[derive(Clone, Copy, Debug, Default)]
1968 struct LaxForeignLayout {
1969 amount: [u8; 8],
1970 }
1971 unsafe impl crate::Zeroable for LaxForeignLayout {}
1972 unsafe impl crate::Pod for LaxForeignLayout {}
1973 impl crate::field_map::FieldMap for LaxForeignLayout {
1974 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1975 "amount",
1976 HopperHeader::SIZE,
1977 8,
1978 )];
1979 }
1980 impl LayoutContract for LaxForeignLayout {
1981 const DISC: u8 = 0x5A;
1982 const VERSION: u8 = 1;
1983 const LAYOUT_ID: [u8; 8] = [0x5A; 8];
1984 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1985 fn validate_header(data: &[u8]) -> ProgramResult {
1987 if crate::layout::read_disc(data) != Some(Self::DISC) {
1988 return ProgramError::err_invalid_data();
1989 }
1990 Ok(())
1991 }
1992 }
1993
1994 #[test]
1995 fn load_cross_program_guards_length_even_with_lax_foreign_header() {
1996 let required = HopperHeader::SIZE + 8;
1999 assert_eq!(LaxForeignLayout::required_len(), required);
2000
2001 let (_short_backing, short) = make_account(required - 1, 60);
2005 {
2006 let mut d = short.try_borrow_mut().unwrap();
2007 d[0] = LaxForeignLayout::DISC;
2008 }
2009 assert!(matches!(
2010 short.load_cross_program::<LaxForeignLayout>(),
2011 Err(ProgramError::AccountDataTooSmall)
2012 ));
2013
2014 let (_ok_backing, ok) = make_account(required, 61);
2016 {
2017 let mut d = ok.try_borrow_mut().unwrap();
2018 d[0] = LaxForeignLayout::DISC;
2019 }
2020 let view = ok.load_cross_program::<LaxForeignLayout>().unwrap();
2021 assert_eq!(view.amount, [0u8; 8]);
2022 }
2023
2024 #[repr(transparent)]
2025 #[derive(Clone, Copy)]
2026 struct ForgedProjection<const OFFSET: usize>([u8; 8]);
2027 unsafe impl<const O: usize> crate::Zeroable for ForgedProjection<O> {}
2029 unsafe impl<const O: usize> crate::Pod for ForgedProjection<O> {}
2031 impl<const O: usize> crate::field_map::FieldMap for ForgedProjection<O> {
2032 const FIELDS: &'static [crate::field_map::FieldInfo] = &[];
2033 }
2034 impl<const O: usize> LayoutContract for ForgedProjection<O> {
2035 const DISC: u8 = 1;
2036 const VERSION: u8 = 1;
2037 const LAYOUT_ID: [u8; 8] = [0; 8];
2038 const SIZE: usize = 0;
2039 const TYPE_OFFSET: usize = O;
2040 fn required_len() -> usize {
2041 0
2042 }
2043 fn validate_header(_: &[u8]) -> ProgramResult {
2044 Ok(())
2045 }
2046 }
2047 impl<const O: usize> crate::CompactLayout for ForgedProjection<O> {
2048 const DISC: u8 = 1;
2049 const BODY_SIZE: usize = 0;
2050 const COMPACT_LEN: usize = 0;
2051 fn validate_compact(_: &[u8]) -> ProgramResult {
2052 Ok(())
2053 }
2054 }
2055 impl<const O: usize> crate::CompactDynamicLayout for ForgedProjection<O> {
2056 const DISC: u8 = 1;
2057 const MIN_LEN: usize = 0;
2058 const TAIL_OFFSET: usize = O;
2059 fn validate_compact_dynamic(_: &[u8]) -> ProgramResult {
2060 Ok(())
2061 }
2062 }
2063
2064 #[test]
2065 fn typed_loads_do_not_trust_overridden_sizing_and_validation() {
2066 for len in 0..24 {
2067 let (_backing, view) = make_account(len, 81);
2068 assert!(matches!(
2069 view.load::<ForgedProjection<16>>(),
2070 Err(ProgramError::AccountDataTooSmall)
2071 ));
2072 assert!(matches!(
2073 view.load_mut::<ForgedProjection<16>>(),
2074 Err(ProgramError::AccountDataTooSmall)
2075 ));
2076 assert!(matches!(
2077 view.load_cross_program::<ForgedProjection<16>>(),
2078 Err(ProgramError::AccountDataTooSmall)
2079 ));
2080 }
2081 let (_backing, view) = make_account(24, 82);
2082 assert_eq!(view.load::<ForgedProjection<16>>().unwrap().0, [0; 8]);
2083 assert!(matches!(
2084 view.load::<ForgedProjection<{ usize::MAX }>>(),
2085 Err(ProgramError::ArithmeticOverflow)
2086 ));
2087 }
2088
2089 #[test]
2090 fn compact_loads_recheck_actual_body_bounds() {
2091 for len in 0..9 {
2092 let (_backing, view) = make_account(len, 83);
2093 assert!(matches!(
2094 view.load_compact::<ForgedProjection<9>>(),
2095 Err(ProgramError::AccountDataTooSmall)
2096 ));
2097 assert!(matches!(
2098 view.load_compact_mut::<ForgedProjection<9>>(),
2099 Err(ProgramError::AccountDataTooSmall)
2100 ));
2101 assert!(matches!(
2102 view.load_compact_dynamic::<ForgedProjection<9>>(),
2103 Err(ProgramError::AccountDataTooSmall)
2104 ));
2105 assert!(matches!(
2106 view.load_compact_dynamic_mut::<ForgedProjection<9>>(),
2107 Err(ProgramError::AccountDataTooSmall)
2108 ));
2109 assert_eq!(
2110 view.init_compact::<ForgedProjection<9>>(),
2111 Err(ProgramError::AccountDataTooSmall)
2112 );
2113 assert_eq!(
2114 view.init_compact_dynamic::<ForgedProjection<9>>(),
2115 Err(ProgramError::AccountDataTooSmall)
2116 );
2117 }
2118 let (_backing, view) = make_account(9, 84);
2119 assert_eq!(
2120 view.load_compact::<ForgedProjection<9>>().unwrap().0,
2121 [0; 8]
2122 );
2123 assert_eq!(
2124 view.load_compact_dynamic::<ForgedProjection<9>>()
2125 .unwrap()
2126 .0,
2127 [0; 8]
2128 );
2129 }
2130
2131 #[test]
2132 fn compact_init_rejects_overlapping_or_overflowing_tail_before_writing() {
2133 let (_backing, view) = make_account(16, 85);
2134 assert_eq!(
2135 view.init_compact_dynamic::<ForgedProjection<0>>(),
2136 Err(ProgramError::InvalidAccountData)
2137 );
2138 assert_eq!(
2139 view.init_compact_dynamic::<ForgedProjection<{ usize::MAX }>>(),
2140 Err(ProgramError::ArithmeticOverflow)
2141 );
2142 assert_eq!(&*view.try_borrow().unwrap(), &[0; 16]);
2143 }
2144
2145 fn make_account(
2146 total_data_len: usize,
2147 address_byte: u8,
2148 ) -> (std::vec::Vec<u64>, AccountView<'static>) {
2149 let mut backing = std::vec![0u64; (RuntimeAccount::SIZE + total_data_len).div_ceil(8)];
2150 let raw = backing.as_mut_ptr() as *mut RuntimeAccount;
2151 unsafe {
2153 raw.write(RuntimeAccount {
2154 borrow_state: NOT_BORROWED,
2155 is_signer: 1,
2156 is_writable: 1,
2157 executable: 0,
2158 resize_delta: 0,
2159 address: NativeAddress::new_from_array([address_byte; 32]),
2160 owner: NativeAddress::new_from_array([2; 32]),
2161 lamports: 42,
2162 data_len: total_data_len as u64,
2163 });
2164 }
2165 let backend = unsafe { NativeAccountView::new_unchecked(raw) };
2167 let account = AccountView::from_backend(backend);
2168 (backing, account)
2169 }
2170
2171 fn make_flagged_account(
2175 is_signer: u8,
2176 is_writable: u8,
2177 ) -> (std::vec::Vec<u64>, AccountView<'static>) {
2178 let mut backing = std::vec![0u64; (RuntimeAccount::SIZE).div_ceil(8)];
2179 let raw = backing.as_mut_ptr() as *mut RuntimeAccount;
2180 unsafe {
2183 raw.write(RuntimeAccount {
2184 borrow_state: NOT_BORROWED,
2185 is_signer,
2186 is_writable,
2187 executable: 0,
2188 resize_delta: 0,
2189 address: NativeAddress::new_from_array([9; 32]),
2190 owner: NativeAddress::new_from_array([2; 32]),
2191 lamports: 0,
2192 data_len: 0,
2193 });
2194 }
2195 let backend = unsafe { NativeAccountView::new_unchecked(raw) };
2197 (backing, AccountView::from_backend(backend))
2198 }
2199
2200 #[test]
2201 fn expect_signer_writable_keeps_distinct_errors_and_passes_valid() {
2202 let (_b, both) = make_flagged_account(1, 1);
2204 assert!(both.expect_signer_writable(true, true).is_ok());
2205
2206 let (_b, no_signer) = make_flagged_account(0, 1);
2208 assert!(matches!(
2209 no_signer.expect_signer_writable(true, true),
2210 Err(ProgramError::MissingRequiredSignature)
2211 ));
2212
2213 let (_b, no_writable) = make_flagged_account(1, 0);
2215 assert!(matches!(
2216 no_writable.expect_signer_writable(true, true),
2217 Err(ProgramError::Immutable)
2218 ));
2219
2220 let (_b, signer_only) = make_flagged_account(1, 0);
2222 assert!(signer_only.expect_signer_writable(true, false).is_ok());
2223 let (_b, writable_only) = make_flagged_account(0, 1);
2224 assert!(writable_only.expect_signer_writable(false, true).is_ok());
2225
2226 let (_b, neither) = make_flagged_account(0, 0);
2228 assert!(neither.expect_signer_writable(false, false).is_ok());
2229
2230 assert!(matches!(
2232 neither.expect_signer_writable(true, false),
2233 Err(ProgramError::MissingRequiredSignature)
2234 ));
2235 assert!(matches!(
2237 neither.expect_signer_writable(false, true),
2238 Err(ProgramError::Immutable)
2239 ));
2240 }
2241
2242 #[test]
2243 fn load_mut_is_zero_copy_and_pointer_stable() {
2244 let (_backing, account) = make_account(TestLayout::SIZE + 8, 1);
2245
2246 {
2247 let mut data = account.try_borrow_mut().unwrap();
2248 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2249 data[HopperHeader::SIZE..HopperHeader::SIZE + 8].copy_from_slice(&10u64.to_le_bytes());
2250 data[HopperHeader::SIZE + 8..HopperHeader::SIZE + 16]
2251 .copy_from_slice(&20u64.to_le_bytes());
2252 data[TestLayout::SIZE..TestLayout::SIZE + 8].copy_from_slice(b"tailpass");
2253 }
2254
2255 let first_ptr = {
2256 let first = account.load::<TestLayout>().unwrap();
2257 assert_eq!(from_le_u64(first.a), 10);
2258 assert_eq!(from_le_u64(first.b), 20);
2259 first.as_ptr() as usize
2260 };
2261
2262 {
2263 let tail = account.extension_bytes::<TestLayout>().unwrap();
2264 assert_eq!(&tail[..8], b"tailpass");
2265 }
2266
2267 let mut second = account.load_mut::<TestLayout>().unwrap();
2268 let second_ptr = second.as_mut_ptr() as usize;
2269 second.b = le_u64(99);
2270 assert_eq!(first_ptr, second_ptr);
2271 drop(second);
2272
2273 let reread = account.load::<TestLayout>().unwrap();
2274 assert_eq!(from_le_u64(reread.a), 10);
2275 assert_eq!(from_le_u64(reread.b), 99);
2276 }
2277
2278 #[repr(C)]
2279 #[derive(Clone, Copy, Debug, Default)]
2280 struct CompactVault {
2281 authority: [u8; 32],
2282 balance: [u8; 8],
2283 }
2284 unsafe impl crate::Zeroable for CompactVault {}
2285 unsafe impl crate::Pod for CompactVault {}
2286 impl crate::CompactLayout for CompactVault {
2287 const DISC: u8 = 1;
2288 }
2289
2290 #[test]
2291 fn compact_load_uses_one_byte_header_and_body_at_offset_one() {
2292 assert_eq!(CompactVault::COMPACT_LEN, 1 + 40);
2295 let headered_len = HopperHeader::SIZE + CompactVault::BODY_SIZE;
2296 assert_eq!(
2297 headered_len - CompactVault::COMPACT_LEN,
2298 HopperHeader::SIZE - 1
2299 );
2300
2301 let (_backing, account) = make_account(CompactVault::COMPACT_LEN, 50);
2302
2303 account.init_compact::<CompactVault>().unwrap();
2304 {
2305 let data = account.try_borrow().unwrap();
2307 assert_eq!(data[0], 1);
2308 }
2309
2310 {
2311 let mut v = account.load_compact_mut::<CompactVault>().unwrap();
2312 v.authority = [9u8; 32];
2313 v.balance = 1234u64.to_le_bytes();
2314 }
2315
2316 let v = account.load_compact::<CompactVault>().unwrap();
2317 assert_eq!(v.authority, [9u8; 32]);
2318 assert_eq!(u64::from_le_bytes(v.balance), 1234);
2319
2320 let data = account.try_borrow().unwrap();
2322 let base = data.as_bytes_ptr() as usize;
2323 let body = (&*v) as *const CompactVault as usize;
2324 assert_eq!(body, base + 1);
2325 }
2326
2327 #[test]
2328 fn compact_load_rejects_wrong_disc() {
2329 let (_backing, account) = make_account(CompactVault::COMPACT_LEN, 51);
2330 {
2331 let mut data = account.try_borrow_mut().unwrap();
2332 data[0] = 2; }
2334 assert_eq!(
2335 account.load_compact::<CompactVault>().unwrap_err(),
2336 ProgramError::InvalidAccountData
2337 );
2338 }
2339
2340 #[test]
2341 fn compact_load_rejects_short_buffer() {
2342 let (_backing, account) = make_account(CompactVault::COMPACT_LEN - 1, 52);
2343 account
2344 .try_borrow_mut()
2345 .map(|mut d| d[0] = CompactVault::DISC)
2346 .unwrap();
2347 assert_eq!(
2348 account.load_compact::<CompactVault>().unwrap_err(),
2349 ProgramError::AccountDataTooSmall
2350 );
2351 }
2352
2353 #[test]
2354 fn compact_load_rejects_oversized_fixed_buffer() {
2355 let (_backing, account) = make_account(CompactVault::COMPACT_LEN + 1, 53);
2356 {
2357 let mut data = account.try_borrow_mut().unwrap();
2358 data[0] = CompactVault::DISC;
2359 }
2360 assert_eq!(
2361 account.load_compact::<CompactVault>().unwrap_err(),
2362 ProgramError::InvalidAccountData
2363 );
2364 assert_eq!(
2365 account.init_compact::<CompactVault>().unwrap_err(),
2366 ProgramError::InvalidAccountData
2367 );
2368 }
2369
2370 #[repr(C)]
2372 #[derive(Clone, Copy, Debug, Default)]
2373 struct CompactDynHead {
2374 owner: [u8; 32],
2375 count: [u8; 8],
2376 }
2377 unsafe impl crate::Zeroable for CompactDynHead {}
2378 unsafe impl crate::Pod for CompactDynHead {}
2379 impl crate::CompactDynamicLayout for CompactDynHead {
2380 const DISC: u8 = 9;
2381 }
2382
2383 #[test]
2384 fn compact_dynamic_loads_head_with_a_growable_tail() {
2385 use crate::CompactDynamicLayout;
2386 assert_eq!(CompactDynHead::FIXED_HEAD_SIZE, 40);
2387 assert_eq!(CompactDynHead::MIN_LEN, 41);
2388 assert_eq!(CompactDynHead::TAIL_OFFSET, 41);
2389
2390 let total = CompactDynHead::MIN_LEN + 4 + 16;
2392 let (_backing, account) = make_account(total, 70);
2393
2394 account.init_compact_dynamic::<CompactDynHead>().unwrap();
2396 {
2397 let data = account.try_borrow().unwrap();
2398 assert_eq!(data[0], 9);
2399 let prefix = u32::from_le_bytes(
2400 data[CompactDynHead::TAIL_OFFSET..CompactDynHead::TAIL_OFFSET + 4]
2401 .try_into()
2402 .unwrap(),
2403 );
2404 assert_eq!(prefix, 0);
2405 }
2406
2407 {
2410 let mut head = account
2411 .load_compact_dynamic_mut::<CompactDynHead>()
2412 .unwrap();
2413 head.owner = [7u8; 32];
2414 head.count = 5u64.to_le_bytes();
2415 }
2416 let head = account.load_compact_dynamic::<CompactDynHead>().unwrap();
2417 assert_eq!(head.owner, [7u8; 32]);
2418 assert_eq!(u64::from_le_bytes(head.count), 5);
2419
2420 let data = account.try_borrow().unwrap();
2422 let base = data.as_bytes_ptr() as usize;
2423 assert_eq!((&*head) as *const CompactDynHead as usize, base + 1);
2424 }
2425
2426 #[test]
2427 fn compact_dynamic_rejects_short_and_wrong_disc() {
2428 use crate::CompactDynamicLayout;
2429 let (_b1, short) = make_account(CompactDynHead::MIN_LEN - 1, 71);
2431 short
2432 .try_borrow_mut()
2433 .map(|mut d| d[0] = CompactDynHead::DISC)
2434 .unwrap();
2435 assert_eq!(
2436 short.load_compact_dynamic::<CompactDynHead>().unwrap_err(),
2437 ProgramError::AccountDataTooSmall
2438 );
2439
2440 let (_b2, bad) = make_account(CompactDynHead::MIN_LEN + 8, 72);
2442 bad.try_borrow_mut().map(|mut d| d[0] = 3).unwrap();
2443 assert_eq!(
2444 bad.load_compact_dynamic::<CompactDynHead>().unwrap_err(),
2445 ProgramError::InvalidAccountData
2446 );
2447 }
2448
2449 #[test]
2450 fn close_refuses_while_data_borrow_is_live() {
2451 let (_backing, account) = make_account(16, 90);
2455 {
2456 let _data = account.try_borrow().unwrap();
2457 assert_eq!(
2458 account.close().unwrap_err(),
2459 ProgramError::AccountBorrowFailed
2460 );
2461 }
2462 account.close().unwrap();
2464 assert_eq!(account.data_len(), 0);
2465 assert_eq!(account.lamports(), 0);
2466 }
2467
2468 #[test]
2469 fn close_to_refusal_preserves_source_and_recipient() {
2470 let (_source_backing, source) = make_account(16, 91);
2471 let (_dest_backing, destination) = make_account(16, 92);
2472 let before = (source.lamports(), destination.lamports());
2473 let borrowed = source.try_borrow().unwrap();
2474 assert_eq!(
2475 source.close_to(&destination, &Address::new([2; 32])),
2476 Err(ProgramError::AccountBorrowFailed)
2477 );
2478 assert_eq!((source.lamports(), destination.lamports()), before);
2479 assert_eq!(&*borrowed, &[0; 16]);
2480 }
2481
2482 #[test]
2483 fn close_to_rejects_the_same_account_as_recipient() {
2484 let (_backing, source) = make_account(16, 93);
2485 let before = source.lamports();
2486 assert_eq!(
2487 source.close_to(&source, &Address::new([2; 32])),
2488 Err(ProgramError::InvalidArgument)
2489 );
2490 assert_eq!(source.lamports(), before);
2491 assert_eq!(source.data_len(), 16);
2492 }
2493
2494 #[test]
2495 fn check_owned_by_any_accepts_listed_owner_and_rejects_others() {
2496 let (_backing, account) = make_account(8, 80);
2498 let token = Address::new([2; 32]); let token_2022 = Address::new([9; 32]);
2500 let other = Address::new([3; 32]);
2501
2502 assert!(account.check_owned_by_any(&[&token_2022, &token]).is_ok());
2505 assert!(account.check_owned_by_any(&[&token]).is_ok());
2506
2507 assert!(account.check_owned_by_any(&[&token_2022, &other]).is_err());
2509
2510 assert!(account.check_owned_by_any(&[]).is_err());
2512 }
2513
2514 #[test]
2515 fn default_layout_accepts_legacy_zero_epoch() {
2516 let (_backing, account) = make_account(TestLayout::SIZE, 43);
2517 {
2518 let mut data = account.try_borrow_mut().unwrap();
2519 crate::layout::write_header_with_epoch(
2520 &mut data,
2521 TestLayout::DISC,
2522 TestLayout::VERSION,
2523 &TestLayout::LAYOUT_ID,
2524 0,
2525 )
2526 .unwrap();
2527 }
2528
2529 assert!(account.load::<TestLayout>().is_ok());
2530 }
2531
2532 #[test]
2533 fn init_header_stamps_layout_schema_epoch() {
2534 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 44);
2535 {
2536 let mut data = account.try_borrow_mut().unwrap();
2537 crate::layout::init_header::<EpochTwoLayout>(&mut data).unwrap();
2538 assert_eq!(crate::layout::read_schema_epoch(&data), Some(2));
2539 }
2540
2541 assert!(account.load::<EpochTwoLayout>().is_ok());
2542 }
2543
2544 #[test]
2545 fn typed_load_rejects_schema_epoch_mismatch() {
2546 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 45);
2547 {
2548 let mut data = account.try_borrow_mut().unwrap();
2549 crate::layout::write_header_with_epoch(
2550 &mut data,
2551 EpochTwoLayout::DISC,
2552 EpochTwoLayout::VERSION,
2553 &EpochTwoLayout::LAYOUT_ID,
2554 1,
2555 )
2556 .unwrap();
2557 }
2558
2559 assert_eq!(
2560 account.load::<EpochTwoLayout>().unwrap_err(),
2561 ProgramError::InvalidAccountData
2562 );
2563 }
2564
2565 #[test]
2566 fn layout_info_matches_checks_schema_epoch() {
2567 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 46);
2568 {
2569 let mut data = account.try_borrow_mut().unwrap();
2570 crate::layout::write_header_with_epoch(
2571 &mut data,
2572 EpochTwoLayout::DISC,
2573 EpochTwoLayout::VERSION,
2574 &EpochTwoLayout::LAYOUT_ID,
2575 1,
2576 )
2577 .unwrap();
2578 }
2579 assert!(!account.layout_info().unwrap().matches::<EpochTwoLayout>());
2580
2581 {
2582 let mut data = account.try_borrow_mut().unwrap();
2583 crate::layout::write_header_with_epoch(
2584 &mut data,
2585 EpochTwoLayout::DISC,
2586 EpochTwoLayout::VERSION,
2587 &EpochTwoLayout::LAYOUT_ID,
2588 EpochTwoLayout::SCHEMA_EPOCH,
2589 )
2590 .unwrap();
2591 }
2592 assert!(account.layout_info().unwrap().matches::<EpochTwoLayout>());
2593 }
2594
2595 #[test]
2596 fn typed_load_holds_borrow_until_drop() {
2597 let (_backing, account) = make_account(TestLayout::SIZE, 3);
2598
2599 {
2600 let mut data = account.try_borrow_mut().unwrap();
2601 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2602 }
2603
2604 let shared = account.load::<TestLayout>().unwrap();
2605 assert_eq!(
2606 account.load_mut::<TestLayout>().unwrap_err(),
2607 ProgramError::AccountBorrowFailed
2608 );
2609 drop(shared);
2610 assert!(account.load_mut::<TestLayout>().is_ok());
2611 }
2612
2613 #[test]
2614 fn duplicate_address_aliases_are_rejected_across_views() {
2615 let (_first_backing, first) = make_account(TestLayout::SIZE, 9);
2616 let (_second_backing, second) = make_account(TestLayout::SIZE, 9);
2617
2618 let first_shared = first.try_borrow().unwrap();
2619 let second_shared = second.try_borrow().unwrap();
2620 assert_eq!(
2621 second.try_borrow_mut().unwrap_err(),
2622 ProgramError::AccountBorrowFailed
2623 );
2624 drop(first_shared);
2625 drop(second_shared);
2626 assert!(second.try_borrow_mut().is_ok());
2627 }
2628
2629 #[test]
2630 fn load_rejects_wrong_disc_and_wrong_version() {
2631 let (_backing, account) = make_account(TestLayout::SIZE, 4);
2632
2633 {
2634 let mut data = account.try_borrow_mut().unwrap();
2635 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2636 }
2637
2638 {
2639 let mut data = account.try_borrow_mut().unwrap();
2640 data[0] = TestLayout::DISC.wrapping_add(1);
2641 }
2642 assert_eq!(
2643 account.load::<TestLayout>().unwrap_err(),
2644 ProgramError::InvalidAccountData
2645 );
2646
2647 {
2648 let mut data = account.try_borrow_mut().unwrap();
2649 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2650 data[1] = TestLayout::VERSION.wrapping_add(1);
2651 }
2652 assert_eq!(
2653 account.load::<TestLayout>().unwrap_err(),
2654 ProgramError::InvalidAccountData
2655 );
2656 }
2657
2658 #[test]
2659 fn load_rejects_undersized_layout_body() {
2660 let (_backing, account) = make_account(TestLayout::SIZE - 1, 5);
2661
2662 {
2663 let mut data = account.try_borrow_mut().unwrap();
2664 data[0] = TestLayout::DISC;
2665 data[1] = TestLayout::VERSION;
2666 data[4..12].copy_from_slice(&TestLayout::LAYOUT_ID);
2667 }
2668
2669 assert_eq!(
2670 account.load::<TestLayout>().unwrap_err(),
2671 ProgramError::AccountDataTooSmall
2672 );
2673 }
2674
2675 #[test]
2676 fn load_supports_header_inclusive_layouts() {
2677 let (_backing, account) = make_account(HeaderLayout::SIZE, 6);
2678
2679 {
2680 let mut data = account.try_borrow_mut().unwrap();
2681 crate::layout::init_header::<HeaderLayout>(&mut data).unwrap();
2682 }
2683
2684 {
2685 let mut layout = account.load_mut::<HeaderLayout>().unwrap();
2686 layout.amount = le_u64(55);
2687 }
2688
2689 let layout = account.load::<HeaderLayout>().unwrap();
2690 assert_eq!(layout.header[0], HeaderLayout::DISC);
2691 assert_eq!(layout.header[1], HeaderLayout::VERSION);
2692 assert_eq!(from_le_u64(layout.amount), 55);
2693 }
2694
2695 #[test]
2705 fn live_load_blocks_segment_mut() {
2706 let (_backing, account) = make_account(TestLayout::SIZE, 10);
2707 {
2708 let mut data = account.try_borrow_mut().unwrap();
2709 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2710 }
2711
2712 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2713 let _read_view = account.load::<TestLayout>().unwrap();
2714
2715 let err = account
2717 .segment_mut::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2718 .unwrap_err();
2719 assert_eq!(err, ProgramError::AccountBorrowFailed);
2720 }
2721
2722 #[test]
2723 fn live_load_mut_blocks_segment_ref() {
2724 let (_backing, account) = make_account(TestLayout::SIZE, 11);
2725 {
2726 let mut data = account.try_borrow_mut().unwrap();
2727 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2728 }
2729
2730 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2731 let _write_view = account.load_mut::<TestLayout>().unwrap();
2732
2733 let err = account
2736 .segment_ref::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2737 .unwrap_err();
2738 assert_eq!(err, ProgramError::AccountBorrowFailed);
2739 }
2740
2741 #[test]
2742 fn every_access_path_is_tracked() {
2743 let (_backing, account) = make_account(TestLayout::SIZE, 40);
2751 {
2752 let mut data = account.try_borrow_mut().unwrap();
2753 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2754 }
2755 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2756
2757 {
2759 let _r = account.try_borrow().unwrap();
2760 assert!(account.try_borrow_mut().is_err());
2761 }
2762 {
2764 let _w = account.try_borrow_mut().unwrap();
2765 assert!(account.try_borrow().is_err());
2766 }
2767 {
2769 let _v = account.load::<TestLayout>().unwrap();
2770 assert!(account.load_mut::<TestLayout>().is_err());
2771 }
2772 {
2774 let _v = account.load_mut::<TestLayout>().unwrap();
2775 assert!(account.load::<TestLayout>().is_err());
2776 }
2777 {
2779 let _r = unsafe { account.raw_ref::<[u8; 16]>() }.unwrap();
2781 assert!(account.load_mut::<TestLayout>().is_err());
2782 }
2783 {
2785 let _w = unsafe { account.raw_mut::<[u8; 16]>() }.unwrap();
2787 assert!(account.load::<TestLayout>().is_err());
2788 }
2789 {
2792 let _r = account
2793 .segment_ref::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2794 .unwrap();
2795 }
2801 assert_eq!(borrows.len(), 0);
2807 let _w = account
2808 .segment_mut::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2809 .unwrap();
2810 }
2811
2812 #[test]
2817 fn seg_lease_releases_on_drop_and_allows_reacquire() {
2818 let (_backing, account) = make_account(TestLayout::SIZE, 41);
2819 {
2820 let mut data = account.try_borrow_mut().unwrap();
2821 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2822 }
2823 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2824 const OFF: u32 = crate::layout::HopperHeader::SIZE as u32;
2825
2826 {
2827 let mut first = account
2828 .segment_mut::<[u8; 8]>(&mut borrows, OFF, 8)
2829 .unwrap();
2830 *first = le_u64(100);
2831 }
2832 assert_eq!(borrows.len(), 0);
2834 {
2837 let mut second = account
2838 .segment_mut::<[u8; 8]>(&mut borrows, OFF, 8)
2839 .unwrap();
2840 assert_eq!(from_le_u64(*second), 100);
2841 *second = le_u64(200);
2842 }
2843 assert_eq!(borrows.len(), 0);
2844 let read = account
2845 .segment_ref::<[u8; 8]>(&mut borrows, OFF, 8)
2846 .unwrap();
2847 assert_eq!(from_le_u64(*read), 200);
2848 }
2849
2850 #[test]
2854 fn seg_lease_still_rejects_simultaneous_overlap() {
2855 let (_backing, account) = make_account(TestLayout::SIZE, 42);
2856 {
2857 let mut data = account.try_borrow_mut().unwrap();
2858 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2859 }
2860 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2861 const OFF: u32 = crate::layout::HopperHeader::SIZE as u32;
2862
2863 let _first = account
2864 .segment_mut::<[u8; 8]>(&mut borrows, OFF, 8)
2865 .unwrap();
2866 drop(_first);
2873 assert_eq!(borrows.len(), 0);
2874 }
2875
2876 #[test]
2877 fn split_segments_mut_borrows_two_disjoint_ranges() {
2878 let (_backing, account) = make_account(TestLayout::SIZE, 43);
2879 {
2880 let mut data = account.try_borrow_mut().unwrap();
2881 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2882 }
2883 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2884 const A: u32 = HopperHeader::SIZE as u32; const B: u32 = HopperHeader::SIZE as u32 + 8; {
2888 let mut segs = account
2889 .split_segments_mut::<[u8; 8], 2>(&mut borrows, [(A, 8), (B, 8)])
2890 .unwrap();
2891 assert_eq!(segs.len(), 2);
2892 let [a, b] = segs.all_mut();
2894 *a = le_u64(111);
2895 *b = le_u64(222);
2896 }
2897 assert_eq!(borrows.len(), 0);
2899
2900 let a = account.segment_ref::<[u8; 8]>(&mut borrows, A, 8).unwrap();
2901 assert_eq!(from_le_u64(*a), 111);
2902 drop(a);
2903 let b = account.segment_ref::<[u8; 8]>(&mut borrows, B, 8).unwrap();
2904 assert_eq!(from_le_u64(*b), 222);
2905 }
2906
2907 #[test]
2908 fn split_segments_mut_rejects_overlap_and_rolls_back() {
2909 let (_backing, account) = make_account(TestLayout::SIZE, 44);
2910 {
2911 let mut data = account.try_borrow_mut().unwrap();
2912 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2913 }
2914 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2915 const A: u32 = HopperHeader::SIZE as u32;
2916
2917 let err = account
2920 .split_segments_mut::<[u8; 8], 2>(&mut borrows, [(A, 8), (A + 4, 8)])
2921 .unwrap_err();
2922 assert_eq!(err, ProgramError::AccountBorrowFailed);
2923 assert_eq!(borrows.len(), 0);
2924
2925 let err = account
2927 .split_segments_mut::<[u8; 8], 2>(&mut borrows, [(A, 8), (9_000, 8)])
2928 .unwrap_err();
2929 assert_eq!(err, ProgramError::AccountDataTooSmall);
2930 assert_eq!(borrows.len(), 0);
2931 }
2932
2933 #[test]
2934 fn typed_segment_api_round_trips() {
2935 use crate::segment::TypedSegment;
2936
2937 let (_backing, account) = make_account(TestLayout::SIZE, 22);
2938 {
2939 let mut data = account.try_borrow_mut().unwrap();
2940 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2941 }
2942
2943 const A_TYPED: TypedSegment<[u8; 8], { crate::layout::HopperHeader::SIZE as u32 }> =
2944 TypedSegment::new();
2945
2946 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2950 {
2951 let mut a = account
2952 .segment_mut_typed::<[u8; 8], { crate::layout::HopperHeader::SIZE as u32 }>(
2953 &mut borrows,
2954 A_TYPED,
2955 )
2956 .unwrap();
2957 *a = le_u64(1337);
2958 }
2959 assert_eq!(borrows.len(), 0);
2960
2961 let read = account
2962 .segment_ref_typed::<[u8; 8], { crate::layout::HopperHeader::SIZE as u32 }>(
2963 &mut borrows,
2964 A_TYPED,
2965 )
2966 .unwrap();
2967 assert_eq!(from_le_u64(*read), 1337);
2968 }
2969
2970 #[test]
2971 fn const_segment_api_matches_manual_offsets() {
2972 use crate::segment::Segment;
2973
2974 let (_backing, account) = make_account(TestLayout::SIZE, 20);
2975 {
2976 let mut data = account.try_borrow_mut().unwrap();
2977 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
2978 }
2979
2980 const A_SEG: Segment = Segment::body(0, 8); let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
2985 {
2986 let mut a = account
2987 .segment_mut_const::<[u8; 8]>(&mut borrows, A_SEG)
2988 .unwrap();
2989 *a = le_u64(7);
2990 }
2991 let read = account
2992 .segment_ref::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
2993 .unwrap();
2994 assert_eq!(from_le_u64(*read), 7);
2995 }
2996
2997 #[test]
2998 fn load_after_segment_drop_succeeds() {
2999 let (_backing, account) = make_account(TestLayout::SIZE, 12);
3000 {
3001 let mut data = account.try_borrow_mut().unwrap();
3002 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
3003 }
3004
3005 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
3006 {
3007 let mut seg = account
3008 .segment_mut::<[u8; 8]>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
3009 .unwrap();
3010 *seg = le_u64(42);
3011 }
3012 let view = account.load::<TestLayout>().unwrap();
3014 assert_eq!(from_le_u64(view.a), 42);
3015 }
3016
3017 #[test]
3023 #[cfg(not(feature = "unguarded-raw-surfaces"))]
3024 fn zero_range_is_gated_over_exactly_the_cleared_bytes() {
3025 use crate::write_policy::{
3026 install_lamport_gate, write_policy_violation, WritePolicy, WriteRange,
3027 };
3028
3029 let (_b0, a0) = make_account(32, 70);
3030 let accounts = [a0];
3031 static TAIL: WritePolicy = WritePolicy::new(&[WriteRange::tail_from(0, 16)]);
3033
3034 {
3035 let mut data = accounts[0].try_borrow_mut().unwrap();
3036 for byte in data.iter_mut() {
3037 *byte = 0xAA;
3038 }
3039 }
3040
3041 let _gate = install_lamport_gate(&accounts, &TAIL);
3042
3043 assert!(accounts[0].zero_range(16, 16).is_ok());
3045 assert_eq!(
3047 accounts[0].zero_range(8, 16),
3048 Err(write_policy_violation(0)),
3049 );
3050 assert_eq!(accounts[0].zero_range(0, 8), Err(write_policy_violation(0)));
3052 assert!(accounts[0].zero_range(0, 0).is_ok());
3054 assert_eq!(
3056 accounts[0].zero_range(24, 16),
3057 Err(ProgramError::AccountDataTooSmall),
3058 );
3059
3060 drop(_gate);
3061 let data = accounts[0].try_borrow().unwrap();
3062 assert!(
3063 data[16..32].iter().all(|b| *b == 0),
3064 "the authorized range was actually cleared"
3065 );
3066 assert!(
3067 data[0..16].iter().all(|b| *b == 0xAA),
3068 "refused ranges left the head untouched"
3069 );
3070 }
3071
3072 #[test]
3080 #[cfg(not(feature = "unguarded-raw-surfaces"))]
3081 fn zero_appended_rides_the_transition_authority_not_the_byte_ranges() {
3082 use crate::write_policy::{
3083 install_lamport_gate, write_policy_violation, WritePolicy, WriteRange,
3084 };
3085
3086 let (_b0, a0) = make_account(32, 72);
3087 let (_bf, foreign) = make_account(32, 73);
3088 let accounts = [a0];
3089 static NARROW: WritePolicy = WritePolicy::new(&[WriteRange::new(0, 0, 8)]);
3092
3093 {
3094 let mut data = accounts[0].try_borrow_mut().unwrap();
3095 for byte in data.iter_mut() {
3096 *byte = 0xCC;
3097 }
3098 }
3099
3100 let _gate = install_lamport_gate(&accounts, &NARROW);
3101
3102 assert!(accounts[0].zero_appended(16).is_ok());
3106
3107 assert_eq!(
3110 foreign.zero_appended(16),
3111 Err(write_policy_violation(u8::MAX)),
3112 );
3113
3114 assert!(accounts[0].zero_appended(32).is_ok());
3119 assert!(accounts[0].zero_appended(64).is_ok());
3120
3121 drop(_gate);
3122 let data = accounts[0].try_borrow().unwrap();
3123 assert!(
3124 data[16..32].iter().all(|b| *b == 0),
3125 "the appended region was cleared"
3126 );
3127 assert!(
3128 data[0..16].iter().all(|b| *b == 0xCC),
3129 "the pre-existing body was untouched"
3130 );
3131 }
3132
3133 #[test]
3141 #[cfg(not(feature = "unguarded-raw-surfaces"))]
3142 fn extension_bytes_mut_is_governed_over_its_exact_range() {
3143 use crate::write_policy::{
3144 install_lamport_gate, write_policy_violation, WritePolicy, WriteRange,
3145 };
3146
3147 const EXT_LEN: usize = 8;
3148 let (_backing, account) = make_account(TestLayout::SIZE + EXT_LEN, 60);
3149 {
3150 let mut data = account.try_borrow_mut().unwrap();
3151 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
3152 }
3153 let accounts = [account];
3154
3155 {
3157 let ext = accounts[0].extension_bytes_mut::<TestLayout>().unwrap();
3158 assert_eq!(ext.len(), EXT_LEN);
3159 }
3160
3161 {
3165 static HEAD_ONLY: WritePolicy = WritePolicy::new(&[WriteRange::new(0, 0, 8)]);
3166 let _gate = install_lamport_gate(&accounts, &HEAD_ONLY);
3167 assert_eq!(
3168 accounts[0].extension_bytes_mut::<TestLayout>().map(|_| ()),
3169 Err(write_policy_violation(0)),
3170 );
3171 }
3172
3173 {
3176 static TAIL: WritePolicy =
3177 WritePolicy::new(&[WriteRange::tail_from(0, TestLayout::SIZE as u32)]);
3178 let _gate = install_lamport_gate(&accounts, &TAIL);
3179 let ext = accounts[0].extension_bytes_mut::<TestLayout>().unwrap();
3180 assert_eq!(ext.len(), EXT_LEN);
3181 }
3182
3183 {
3185 static WHOLE: WritePolicy = WritePolicy::new(&[WriteRange::whole_account(0)]);
3186 let _gate = install_lamport_gate(&accounts, &WHOLE);
3187 assert!(accounts[0].extension_bytes_mut::<TestLayout>().is_ok());
3188 }
3189
3190 let (_short_backing, short) = make_account(TestLayout::SIZE - 1, 61);
3194 assert_eq!(
3195 short.extension_bytes_mut::<TestLayout>().map(|_| ()),
3196 Err(ProgramError::AccountDataTooSmall),
3197 );
3198 }
3199}