1use crate::address::{address_eq, Address};
18use crate::borrow::{Ref, RefMut};
19use crate::borrow_registry::{self, BorrowToken};
20use crate::compat::{self, BackendAccountView};
21use crate::error::ProgramError;
22use crate::field_map::FieldInfo;
23use crate::layout::LayoutContract;
24use crate::segment_borrow::SegmentBorrowRegistry;
25use crate::ProgramResult;
26
27#[repr(transparent)]
41#[derive(Clone, PartialEq, Eq)]
42pub struct AccountView {
43 inner: BackendAccountView,
44}
45
46const _: () = {
47 assert!(core::mem::size_of::<AccountView>() == core::mem::size_of::<BackendAccountView>());
48 assert!(core::mem::align_of::<AccountView>() == core::mem::align_of::<BackendAccountView>());
49 #[cfg(not(feature = "solana-program-backend"))]
50 assert!(!core::mem::needs_drop::<AccountView>());
51};
52
53unsafe impl Send for AccountView {}
56unsafe impl Sync for AccountView {}
57
58impl AccountView {
59 #[cfg(test)]
60 #[inline(always)]
61 pub(crate) fn from_backend(inner: BackendAccountView) -> Self {
62 Self { inner }
63 }
64
65 #[inline(always)]
69 pub fn address(&self) -> &Address {
70 compat::account_address(&self.inner)
71 }
72
73 #[inline(always)]
80 pub unsafe fn owner(&self) -> &Address {
81 unsafe { compat::account_owner(&self.inner) }
83 }
84
85 #[inline(always)]
87 pub fn read_owner(&self) -> Address {
88 compat::read_owner(&self.inner)
89 }
90
91 #[inline(always)]
93 pub fn owned_by(&self, program: &Address) -> bool {
94 compat::owned_by(&self.inner, program)
95 }
96
97 #[inline(always)]
99 pub fn is_signer(&self) -> bool {
100 self.inner.is_signer()
101 }
102
103 #[inline(always)]
105 pub fn is_writable(&self) -> bool {
106 self.inner.is_writable()
107 }
108
109 #[inline(always)]
111 pub fn executable(&self) -> bool {
112 self.inner.executable()
113 }
114
115 #[inline(always)]
117 pub fn data_len(&self) -> usize {
118 self.inner.data_len()
119 }
120
121 #[inline(always)]
123 pub fn lamports(&self) -> u64 {
124 self.inner.lamports()
125 }
126
127 #[inline(always)]
129 pub fn is_data_empty(&self) -> bool {
130 self.data_len() == 0
131 }
132
133 #[inline(always)]
135 pub fn set_lamports(&self, lamports: u64) {
136 self.inner.set_lamports(lamports);
137 }
138
139 #[inline(always)]
143 pub fn try_borrow(&self) -> Result<Ref<'_, [u8]>, ProgramError> {
144 let token = BorrowToken::shared(self.address())?;
145 match self.inner.try_borrow() {
146 Ok(data) => Ok(Ref::from_backend(data, token)),
147 Err(error) => {
148 drop(token);
149 Err(ProgramError::from(error))
150 }
151 }
152 }
153
154 #[inline(always)]
156 pub fn try_borrow_mut(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
157 let token = BorrowToken::mutable(self.address())?;
158 match self.inner.try_borrow_mut() {
159 Ok(data) => Ok(RefMut::from_backend(data, token)),
160 Err(error) => {
161 drop(token);
162 Err(ProgramError::from(error))
163 }
164 }
165 }
166
167 #[inline(always)]
190 pub fn segment_ref<'a, T: crate::Pod>(
191 &'a self,
192 borrows: &'a mut SegmentBorrowRegistry,
193 abs_offset: u32,
194 size: u32,
195 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
196 let expected_size = core::mem::size_of::<T>() as u32;
197 if size != expected_size {
198 return ProgramError::err_invalid_argument();
199 }
200
201 let end = abs_offset
202 .checked_add(size)
203 .ok_or(ProgramError::ArithmeticOverflow)?;
204 if end as usize > self.data_len() {
205 return ProgramError::err_data_too_small();
206 }
207
208 let borrow = borrows.register_leased_read(self.address(), abs_offset, size)?;
209
210 #[cfg(target_os = "solana")]
212 let inner: Ref<'_, T> = {
213 let native_ref = unsafe { self.inner.segment_ref_unchecked::<T>(abs_offset) };
215 let native_ref = match native_ref {
216 Ok(nr) => nr,
217 Err(e) => {
218 borrows.release(&borrow);
222 return Err(ProgramError::from(e));
223 }
224 };
225 let (typed_ref, state_ptr) = native_ref.into_raw_parts();
226 Ref::from_segment(typed_ref as *const T, state_ptr)
227 };
228 #[cfg(not(target_os = "solana"))]
229 let inner: Ref<'_, T> = {
230 let data = match self.try_borrow() {
231 Ok(d) => d,
232 Err(e) => {
233 borrows.release(&borrow);
234 return Err(e);
235 }
236 };
237 let ptr = unsafe { data.as_bytes_ptr().add(abs_offset as usize) as *const T };
239 unsafe { data.project(ptr) }
240 };
241
242 let lease = unsafe { crate::SegmentLease::new(borrows, borrow) };
245 Ok(crate::SegRef::new(inner, lease))
246 }
247
248 #[inline(always)]
253 pub fn segment_mut<'a, T: crate::Pod>(
254 &'a self,
255 borrows: &'a mut SegmentBorrowRegistry,
256 abs_offset: u32,
257 size: u32,
258 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
259 self.check_writable()?;
260
261 let expected_size = core::mem::size_of::<T>() as u32;
262 if size != expected_size {
263 return ProgramError::err_invalid_argument();
264 }
265
266 let end = abs_offset
267 .checked_add(size)
268 .ok_or(ProgramError::ArithmeticOverflow)?;
269 if end as usize > self.data_len() {
270 return ProgramError::err_data_too_small();
271 }
272
273 let borrow = borrows.register_leased_write(self.address(), abs_offset, size)?;
274
275 #[cfg(target_os = "solana")]
276 let inner: RefMut<'_, T> = {
277 let native_ref = unsafe { self.inner.segment_mut_unchecked::<T>(abs_offset) };
279 let native_ref = match native_ref {
280 Ok(nr) => nr,
281 Err(e) => {
282 borrows.release(&borrow);
283 return Err(ProgramError::from(e));
284 }
285 };
286 let (typed_ref, state_ptr) = native_ref.into_raw_parts();
287 RefMut::from_segment(typed_ref as *mut T, state_ptr)
288 };
289 #[cfg(not(target_os = "solana"))]
290 let inner: RefMut<'_, T> = {
291 let mut data = match self.try_borrow_mut() {
292 Ok(d) => d,
293 Err(e) => {
294 borrows.release(&borrow);
295 return Err(e);
296 }
297 };
298 let ptr = unsafe { data.as_bytes_mut_ptr().add(abs_offset as usize) as *mut T };
300 unsafe { data.project(ptr) }
301 };
302
303 let lease = unsafe { crate::SegmentLease::new(borrows, borrow) };
305 Ok(crate::SegRefMut::new(inner, lease))
306 }
307
308 #[inline(always)]
329 pub fn segment_ref_const<'a, T: crate::Pod>(
330 &'a self,
331 borrows: &'a mut SegmentBorrowRegistry,
332 segment: crate::segment::Segment,
333 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
334 self.segment_ref::<T>(borrows, segment.offset, segment.size)
335 }
336
337 #[inline(always)]
340 pub fn segment_mut_const<'a, T: crate::Pod>(
341 &'a self,
342 borrows: &'a mut SegmentBorrowRegistry,
343 segment: crate::segment::Segment,
344 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
345 self.segment_mut::<T>(borrows, segment.offset, segment.size)
346 }
347
348 #[inline(always)]
363 pub fn segment_ref_typed<'a, T: crate::Pod, const OFFSET: u32>(
364 &'a self,
365 borrows: &'a mut SegmentBorrowRegistry,
366 _segment: crate::segment::TypedSegment<T, OFFSET>,
367 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
368 self.segment_ref::<T>(borrows, OFFSET, core::mem::size_of::<T>() as u32)
369 }
370
371 #[inline(always)]
374 pub fn segment_mut_typed<'a, T: crate::Pod, const OFFSET: u32>(
375 &'a self,
376 borrows: &'a mut SegmentBorrowRegistry,
377 _segment: crate::segment::TypedSegment<T, OFFSET>,
378 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
379 self.segment_mut::<T>(borrows, OFFSET, core::mem::size_of::<T>() as u32)
380 }
381
382 #[inline(always)]
403 pub fn load<T: LayoutContract>(&self) -> Result<Ref<'_, T>, ProgramError> {
404 let data = self.try_borrow()?;
405 T::validate_header(&data)?;
406 if data.len() < T::required_len() {
407 return ProgramError::err_data_too_small();
408 }
409 let ptr = unsafe { data.as_bytes_ptr().add(T::TYPE_OFFSET) as *const T };
411 Ok(unsafe { data.project(ptr) })
413 }
414
415 #[inline]
421 pub fn with<T, R, F>(&self, f: F) -> Result<R, ProgramError>
422 where
423 T: LayoutContract,
424 F: FnOnce(&T) -> Result<R, ProgramError>,
425 {
426 let account = self.load::<T>()?;
427 f(&*account)
428 }
429
430 #[inline(always)]
442 pub fn load_mut<T: LayoutContract>(&self) -> Result<RefMut<'_, T>, ProgramError> {
443 let mut data = self.try_borrow_mut()?;
444 T::validate_header(&data)?;
445 if data.len() < T::required_len() {
446 return ProgramError::err_data_too_small();
447 }
448 let ptr = unsafe { data.as_bytes_mut_ptr().add(T::TYPE_OFFSET) as *mut T };
450 Ok(unsafe { data.project(ptr) })
452 }
453
454 #[inline]
459 pub fn with_mut<T, R, F>(&self, f: F) -> Result<R, ProgramError>
460 where
461 T: LayoutContract,
462 F: FnOnce(&mut T) -> Result<R, ProgramError>,
463 {
464 let mut account = self.load_mut::<T>()?;
465 f(&mut *account)
466 }
467
468 #[inline(always)]
473 pub unsafe fn raw_ref<T: crate::Pod>(&self) -> Result<Ref<'_, T>, ProgramError> {
478 let data = self.try_borrow()?;
479 if core::mem::size_of::<T>() > data.len() {
480 return Err(ProgramError::AccountDataTooSmall);
481 }
482 let ptr = data.as_ptr() as *const T;
483 Ok(unsafe { data.project(ptr) })
485 }
486
487 #[inline(always)]
492 pub unsafe fn raw_mut<T: crate::Pod>(&self) -> Result<RefMut<'_, T>, ProgramError> {
497 self.check_writable()?;
498 let mut data = self.try_borrow_mut()?;
499 if core::mem::size_of::<T>() > data.len() {
500 return Err(ProgramError::AccountDataTooSmall);
501 }
502 let ptr = data.as_bytes_mut_ptr() as *mut T;
503 Ok(unsafe { data.project(ptr) })
505 }
506
507 #[inline(always)]
524 pub fn load_cross_program<T: LayoutContract>(&self) -> Result<Ref<'_, T>, ProgramError> {
525 let data = self.try_borrow()?;
526 T::validate_header(&data)?;
527 let ptr = unsafe { data.as_bytes_ptr().add(T::TYPE_OFFSET) as *const T };
529 Ok(unsafe { data.project(ptr) })
531 }
532
533 #[inline(always)]
539 pub fn layout_info(&self) -> Option<crate::layout::LayoutInfo> {
540 let data = self.try_borrow().ok()?;
541 crate::layout::LayoutInfo::from_data(&data)
542 }
543
544 #[inline(always)]
546 pub fn fields<T: LayoutContract>() -> &'static [FieldInfo] {
547 T::fields()
548 }
549
550 #[inline]
558 pub fn field<T: LayoutContract>(name: &str) -> Option<&'static FieldInfo> {
559 <T as crate::field_map::FieldMap>::field_by_name(name)
560 }
561
562 #[inline(always)]
567 pub fn extension_range<T: LayoutContract>(
568 &self,
569 ) -> Result<core::ops::Range<usize>, ProgramError> {
570 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
571 let data_len = self.data_len();
572 if data_len < offset {
573 return Err(ProgramError::AccountDataTooSmall);
574 }
575 Ok(offset..data_len)
576 }
577
578 #[inline(always)]
580 pub fn extension_bytes<T: LayoutContract>(&self) -> Result<Ref<'_, [u8]>, ProgramError> {
581 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
582 let data = self.try_borrow()?;
583 if data.len() < offset {
584 return Err(ProgramError::AccountDataTooSmall);
585 }
586 Ok(data.slice_from(offset))
587 }
588
589 #[inline(always)]
591 pub fn extension_bytes_mut<T: LayoutContract>(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
592 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
593 let data = self.try_borrow_mut()?;
594 if data.len() < offset {
595 return Err(ProgramError::AccountDataTooSmall);
596 }
597 Ok(data.slice_from(offset))
598 }
599
600 #[inline(always)]
605 pub fn init_layout<T: LayoutContract>(&self) -> ProgramResult {
606 let mut data = self.try_borrow_mut()?;
607 crate::layout::init_header::<T>(&mut data)
608 }
609
610 #[inline(always)]
614 pub fn require_signer(&self) -> ProgramResult {
615 if self.is_signer() {
616 Ok(())
617 } else {
618 ProgramError::err_missing_signer()
619 }
620 }
621
622 #[inline(always)]
624 pub fn require_writable(&self) -> ProgramResult {
625 if self.is_writable() {
626 Ok(())
627 } else {
628 ProgramError::err_immutable()
629 }
630 }
631
632 #[inline(always)]
634 pub fn require_owned_by(&self, program: &Address) -> ProgramResult {
635 if self.owned_by(program) {
636 Ok(())
637 } else {
638 ProgramError::err_incorrect_program()
639 }
640 }
641
642 #[inline(always)]
644 pub fn require_payer(&self) -> ProgramResult {
645 self.require_signer()?;
646 self.require_writable()
647 }
648
649 #[inline(always)]
653 pub fn check_signer(&self) -> Result<&Self, ProgramError> {
654 if self.is_signer() {
655 Ok(self)
656 } else {
657 ProgramError::err_missing_signer()
658 }
659 }
660
661 #[inline(always)]
663 pub fn check_writable(&self) -> Result<&Self, ProgramError> {
664 if self.is_writable() {
665 Ok(self)
666 } else {
667 ProgramError::err_immutable()
668 }
669 }
670
671 #[inline(always)]
673 pub fn check_owned_by(&self, program: &Address) -> Result<&Self, ProgramError> {
674 if self.owned_by(program) {
675 Ok(self)
676 } else {
677 ProgramError::err_incorrect_program()
678 }
679 }
680
681 #[inline(always)]
683 pub fn check_disc(&self, expected: u8) -> Result<&Self, ProgramError> {
684 if self.disc() == expected {
685 Ok(self)
686 } else {
687 Err(ProgramError::InvalidAccountData)
688 }
689 }
690
691 #[inline(always)]
693 pub fn check_has_data(&self) -> Result<&Self, ProgramError> {
694 if !self.is_data_empty() {
695 Ok(self)
696 } else {
697 Err(ProgramError::AccountDataTooSmall)
698 }
699 }
700
701 #[inline(always)]
703 pub fn check_executable(&self) -> Result<&Self, ProgramError> {
704 if self.executable() {
705 Ok(self)
706 } else {
707 Err(ProgramError::InvalidArgument)
708 }
709 }
710
711 #[inline(always)]
713 pub fn check_address(&self, expected: &Address) -> Result<&Self, ProgramError> {
714 if address_eq(self.address(), expected) {
715 Ok(self)
716 } else {
717 Err(ProgramError::InvalidArgument)
718 }
719 }
720
721 #[inline(always)]
723 pub fn check_data_len(&self, min_len: usize) -> Result<&Self, ProgramError> {
724 if self.data_len() >= min_len {
725 Ok(self)
726 } else {
727 Err(ProgramError::AccountDataTooSmall)
728 }
729 }
730
731 #[inline(always)]
733 pub fn check_version(&self, expected: u8) -> Result<&Self, ProgramError> {
734 if self.version() == expected {
735 Ok(self)
736 } else {
737 Err(ProgramError::InvalidAccountData)
738 }
739 }
740
741 #[inline(always)]
743 pub fn check_layout<T: LayoutContract>(&self) -> Result<&Self, ProgramError> {
744 let data = self.try_borrow()?;
745 T::validate_header(&data)?;
746 Ok(self)
747 }
748
749 #[inline(always)]
751 pub const fn proof(&self) -> crate::proof::AccountProof<'_> {
752 crate::proof::AccountProof::new(self)
753 }
754
755 #[inline(always)]
759 pub fn disc(&self) -> u8 {
760 compat::disc(&self.inner)
761 }
762
763 #[inline(always)]
765 pub fn version(&self) -> u8 {
766 compat::version(&self.inner)
767 }
768
769 #[inline(always)]
771 pub fn layout_id(&self) -> Option<&[u8; 8]> {
772 compat::layout_id(&self.inner)
773 }
774
775 #[inline(always)]
777 pub fn require_disc(&self, expected: u8) -> ProgramResult {
778 if self.disc() == expected {
779 Ok(())
780 } else {
781 Err(ProgramError::InvalidAccountData)
782 }
783 }
784
785 #[inline(always)]
792 pub fn flags(&self) -> u8 {
793 let mut f: u8 = 0;
794 if self.is_signer() {
795 f |= 0b0001;
796 }
797 if self.is_writable() {
798 f |= 0b0010;
799 }
800 if self.executable() {
801 f |= 0b0100;
802 }
803 if !self.is_data_empty() {
804 f |= 0b1000;
805 }
806 f
807 }
808
809 #[inline(always)]
811 pub fn expect_flags(&self, required: u8) -> ProgramResult {
812 if self.flags() & required == required {
813 Ok(())
814 } else {
815 Err(ProgramError::InvalidArgument)
816 }
817 }
818
819 #[inline]
823 pub fn resize(&self, new_len: usize) -> ProgramResult {
824 self.inner.resize(new_len).map_err(ProgramError::from)
825 }
826
827 #[inline(always)]
834 pub unsafe fn assign(&self, new_owner: &Address) {
835 unsafe {
837 compat::assign(&self.inner, new_owner);
838 }
839 }
840
841 #[inline]
843 pub fn close(&self) -> ProgramResult {
844 compat::close(&self.inner)
845 }
846
847 #[inline]
876 pub fn close_to(&self, destination: &AccountView, program_id: &Address) -> ProgramResult {
877 self.require_writable()?;
878 self.require_owned_by(program_id)?;
879 destination.require_writable()?;
880
881 let lamports = self.lamports();
882 let dest_lamports = destination.lamports();
883 destination.set_lamports(
884 dest_lamports
885 .checked_add(lamports)
886 .ok_or(ProgramError::ArithmeticOverflow)?,
887 );
888 self.set_lamports(0);
889 compat::zero_data(&self.inner)?;
890 Ok(())
891 }
892
893 #[inline]
900 pub fn close_to_unchecked(&self, destination: &AccountView) -> ProgramResult {
901 let lamports = self.lamports();
902 let dest_lamports = destination.lamports();
903 destination.set_lamports(
904 dest_lamports
905 .checked_add(lamports)
906 .ok_or(ProgramError::ArithmeticOverflow)?,
907 );
908 self.set_lamports(0);
909 compat::zero_data(&self.inner)?;
910 Ok(())
911 }
912
913 #[cfg(feature = "hopper-native-backend")]
917 #[inline(always)]
918 pub(crate) fn data_ptr_unchecked(&self) -> *mut u8 {
919 self.inner.data_ptr_unchecked()
920 }
921
922 #[cfg(feature = "hopper-native-backend")]
924 #[inline(always)]
925 pub(crate) fn account_ptr(&self) -> *const hopper_native::RuntimeAccount {
926 self.inner.account_ptr()
927 }
928
929 #[inline(always)]
931 pub fn check_borrow(&self) -> Result<(), ProgramError> {
932 borrow_registry::check_shared(self.address())?;
933 self.inner.check_borrow().map_err(ProgramError::from)
934 }
935
936 #[inline(always)]
938 pub fn check_borrow_mut(&self) -> Result<(), ProgramError> {
939 borrow_registry::check_mutable(self.address())?;
940 self.inner.check_borrow_mut().map_err(ProgramError::from)
941 }
942
943 #[inline(always)]
949 pub unsafe fn borrow_unchecked(&self) -> &[u8] {
950 unsafe { self.inner.borrow_unchecked() }
952 }
953
954 #[inline(always)]
960 pub unsafe fn borrow_unchecked_mut(&self) -> &mut [u8] {
961 unsafe { self.inner.borrow_unchecked_mut() }
963 }
964
965 #[cfg(feature = "hopper-native-backend")]
971 #[inline(always)]
972 pub unsafe fn resize_unchecked(&self, new_len: usize) {
973 unsafe {
975 self.inner.resize_unchecked(new_len);
976 }
977 }
978
979 #[inline(always)]
985 pub unsafe fn close_unchecked(&self) {
986 unsafe {
988 self.inner.close_unchecked();
989 }
990 }
991
992 #[cfg(any(feature = "hopper-native-backend", feature = "solana-program-backend"))]
996 #[allow(dead_code)]
997 #[inline(always)]
998 pub(crate) fn as_backend(&self) -> &BackendAccountView {
999 &self.inner
1000 }
1001}
1002
1003impl core::fmt::Debug for AccountView {
1004 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1005 f.debug_struct("AccountView")
1006 .field("address", self.address())
1007 .field("lamports", &self.lamports())
1008 .field("data_len", &self.data_len())
1009 .field("is_signer", &self.is_signer())
1010 .field("is_writable", &self.is_writable())
1011 .finish()
1012 }
1013}
1014
1015pub struct RemainingAccounts<'a> {
1019 accounts: &'a [AccountView],
1020 cursor: usize,
1021}
1022
1023impl<'a> RemainingAccounts<'a> {
1024 #[inline(always)]
1026 pub fn new(accounts: &'a [AccountView]) -> Self {
1027 Self {
1028 accounts,
1029 cursor: 0,
1030 }
1031 }
1032
1033 #[inline(always)]
1035 pub fn remaining(&self) -> usize {
1036 self.accounts.len() - self.cursor
1037 }
1038
1039 #[inline(always)]
1041 pub fn next(&mut self) -> Result<&'a AccountView, ProgramError> {
1042 if self.cursor >= self.accounts.len() {
1043 return Err(ProgramError::NotEnoughAccountKeys);
1044 }
1045 let account = &self.accounts[self.cursor];
1046 self.cursor += 1;
1047 Ok(account)
1048 }
1049
1050 #[inline(always)]
1052 pub fn next_signer(&mut self) -> Result<&'a AccountView, ProgramError> {
1053 let account = self.next()?;
1054 account.require_signer()?;
1055 Ok(account)
1056 }
1057
1058 #[inline(always)]
1060 pub fn next_writable(&mut self) -> Result<&'a AccountView, ProgramError> {
1061 let account = self.next()?;
1062 account.require_writable()?;
1063 Ok(account)
1064 }
1065
1066 #[inline(always)]
1068 pub fn next_owned_by(&mut self, program: &Address) -> Result<&'a AccountView, ProgramError> {
1069 let account = self.next()?;
1070 account.require_owned_by(program)?;
1071 Ok(account)
1072 }
1073}
1074
1075#[cfg(all(test, feature = "hopper-native-backend"))]
1076mod tests {
1077 use super::*;
1078 use crate::layout::HopperHeader;
1079
1080 use hopper_native::{
1081 AccountView as NativeAccountView, Address as NativeAddress, RuntimeAccount, NOT_BORROWED,
1082 };
1083
1084 #[repr(C)]
1085 #[derive(Clone, Copy, Debug, Default)]
1086 struct TestLayout {
1087 a: u64,
1088 b: u64,
1089 }
1090
1091 #[repr(C)]
1092 #[derive(Clone, Copy, Debug)]
1093 struct HeaderLayout {
1094 header: HopperHeader,
1095 amount: u64,
1096 }
1097
1098 #[repr(C)]
1099 #[derive(Clone, Copy, Debug, Default)]
1100 struct EpochTwoLayout {
1101 amount: u64,
1102 }
1103
1104 impl crate::field_map::FieldMap for TestLayout {
1105 const FIELDS: &'static [crate::field_map::FieldInfo] = &[
1106 crate::field_map::FieldInfo::new("a", HopperHeader::SIZE, 8),
1107 crate::field_map::FieldInfo::new("b", HopperHeader::SIZE + 8, 8),
1108 ];
1109 }
1110
1111 impl LayoutContract for TestLayout {
1112 const DISC: u8 = 7;
1113 const VERSION: u8 = 1;
1114 const LAYOUT_ID: [u8; 8] = [0xAB; 8];
1115 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1116 const EXTENSION_OFFSET: Option<usize> = Some(Self::SIZE);
1117 }
1118
1119 impl crate::field_map::FieldMap for HeaderLayout {
1120 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1121 "amount",
1122 HopperHeader::SIZE,
1123 8,
1124 )];
1125 }
1126
1127 impl LayoutContract for HeaderLayout {
1128 const DISC: u8 = 11;
1129 const VERSION: u8 = 2;
1130 const LAYOUT_ID: [u8; 8] = [0xCD; 8];
1131 const SIZE: usize = core::mem::size_of::<Self>();
1132 const TYPE_OFFSET: usize = 0;
1133 }
1134
1135 impl crate::field_map::FieldMap for EpochTwoLayout {
1136 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1137 "amount",
1138 HopperHeader::SIZE,
1139 8,
1140 )];
1141 }
1142
1143 impl LayoutContract for EpochTwoLayout {
1144 const DISC: u8 = 12;
1145 const VERSION: u8 = 1;
1146 const LAYOUT_ID: [u8; 8] = [0xEF; 8];
1147 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1148 const SCHEMA_EPOCH: u32 = 2;
1149 }
1150
1151 fn make_account(total_data_len: usize, address_byte: u8) -> (std::vec::Vec<u8>, AccountView) {
1152 let mut backing = std::vec![0u8; RuntimeAccount::SIZE + total_data_len];
1153 let raw = backing.as_mut_ptr() as *mut RuntimeAccount;
1154 unsafe {
1156 raw.write(RuntimeAccount {
1157 borrow_state: NOT_BORROWED,
1158 is_signer: 1,
1159 is_writable: 1,
1160 executable: 0,
1161 resize_delta: 0,
1162 address: NativeAddress::new_from_array([address_byte; 32]),
1163 owner: NativeAddress::new_from_array([2; 32]),
1164 lamports: 42,
1165 data_len: total_data_len as u64,
1166 });
1167 }
1168 let backend = unsafe { NativeAccountView::new_unchecked(raw) };
1170 let account = AccountView::from_backend(backend);
1171 (backing, account)
1172 }
1173
1174 #[test]
1175 fn load_mut_is_zero_copy_and_pointer_stable() {
1176 let (_backing, account) = make_account(TestLayout::SIZE + 8, 1);
1177
1178 {
1179 let mut data = account.try_borrow_mut().unwrap();
1180 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1181 data[HopperHeader::SIZE..HopperHeader::SIZE + 8].copy_from_slice(&10u64.to_le_bytes());
1182 data[HopperHeader::SIZE + 8..HopperHeader::SIZE + 16]
1183 .copy_from_slice(&20u64.to_le_bytes());
1184 data[TestLayout::SIZE..TestLayout::SIZE + 8].copy_from_slice(b"tailpass");
1185 }
1186
1187 let first_ptr = {
1188 let first = account.load::<TestLayout>().unwrap();
1189 assert_eq!(first.a, 10);
1190 assert_eq!(first.b, 20);
1191 first.as_ptr() as usize
1192 };
1193
1194 {
1195 let tail = account.extension_bytes::<TestLayout>().unwrap();
1196 assert_eq!(&tail[..8], b"tailpass");
1197 }
1198
1199 let mut second = account.load_mut::<TestLayout>().unwrap();
1200 let second_ptr = second.as_mut_ptr() as usize;
1201 second.b = 99;
1202 assert_eq!(first_ptr, second_ptr);
1203 drop(second);
1204
1205 let reread = account.load::<TestLayout>().unwrap();
1206 assert_eq!(reread.a, 10);
1207 assert_eq!(reread.b, 99);
1208 }
1209
1210 #[test]
1211 fn default_layout_accepts_legacy_zero_epoch() {
1212 let (_backing, account) = make_account(TestLayout::SIZE, 43);
1213 {
1214 let mut data = account.try_borrow_mut().unwrap();
1215 crate::layout::write_header_with_epoch(
1216 &mut data,
1217 TestLayout::DISC,
1218 TestLayout::VERSION,
1219 &TestLayout::LAYOUT_ID,
1220 0,
1221 )
1222 .unwrap();
1223 }
1224
1225 assert!(account.load::<TestLayout>().is_ok());
1226 }
1227
1228 #[test]
1229 fn init_header_stamps_layout_schema_epoch() {
1230 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 44);
1231 {
1232 let mut data = account.try_borrow_mut().unwrap();
1233 crate::layout::init_header::<EpochTwoLayout>(&mut data).unwrap();
1234 assert_eq!(crate::layout::read_schema_epoch(&data), Some(2));
1235 }
1236
1237 assert!(account.load::<EpochTwoLayout>().is_ok());
1238 }
1239
1240 #[test]
1241 fn typed_load_rejects_schema_epoch_mismatch() {
1242 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 45);
1243 {
1244 let mut data = account.try_borrow_mut().unwrap();
1245 crate::layout::write_header_with_epoch(
1246 &mut data,
1247 EpochTwoLayout::DISC,
1248 EpochTwoLayout::VERSION,
1249 &EpochTwoLayout::LAYOUT_ID,
1250 1,
1251 )
1252 .unwrap();
1253 }
1254
1255 assert_eq!(
1256 account.load::<EpochTwoLayout>().unwrap_err(),
1257 ProgramError::InvalidAccountData
1258 );
1259 }
1260
1261 #[test]
1262 fn layout_info_matches_checks_schema_epoch() {
1263 let (_backing, account) = make_account(EpochTwoLayout::SIZE, 46);
1264 {
1265 let mut data = account.try_borrow_mut().unwrap();
1266 crate::layout::write_header_with_epoch(
1267 &mut data,
1268 EpochTwoLayout::DISC,
1269 EpochTwoLayout::VERSION,
1270 &EpochTwoLayout::LAYOUT_ID,
1271 1,
1272 )
1273 .unwrap();
1274 }
1275 assert!(!account.layout_info().unwrap().matches::<EpochTwoLayout>());
1276
1277 {
1278 let mut data = account.try_borrow_mut().unwrap();
1279 crate::layout::write_header_with_epoch(
1280 &mut data,
1281 EpochTwoLayout::DISC,
1282 EpochTwoLayout::VERSION,
1283 &EpochTwoLayout::LAYOUT_ID,
1284 EpochTwoLayout::SCHEMA_EPOCH,
1285 )
1286 .unwrap();
1287 }
1288 assert!(account.layout_info().unwrap().matches::<EpochTwoLayout>());
1289 }
1290
1291 #[test]
1292 fn typed_load_holds_borrow_until_drop() {
1293 let (_backing, account) = make_account(TestLayout::SIZE, 3);
1294
1295 {
1296 let mut data = account.try_borrow_mut().unwrap();
1297 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1298 }
1299
1300 let shared = account.load::<TestLayout>().unwrap();
1301 assert_eq!(
1302 account.load_mut::<TestLayout>().unwrap_err(),
1303 ProgramError::AccountBorrowFailed
1304 );
1305 drop(shared);
1306 assert!(account.load_mut::<TestLayout>().is_ok());
1307 }
1308
1309 #[test]
1310 fn duplicate_address_aliases_are_rejected_across_views() {
1311 let (_first_backing, first) = make_account(TestLayout::SIZE, 9);
1312 let (_second_backing, second) = make_account(TestLayout::SIZE, 9);
1313
1314 let first_shared = first.try_borrow().unwrap();
1315 let second_shared = second.try_borrow().unwrap();
1316 assert_eq!(
1317 second.try_borrow_mut().unwrap_err(),
1318 ProgramError::AccountBorrowFailed
1319 );
1320 drop(first_shared);
1321 drop(second_shared);
1322 assert!(second.try_borrow_mut().is_ok());
1323 }
1324
1325 #[test]
1326 fn load_rejects_wrong_disc_and_wrong_version() {
1327 let (_backing, account) = make_account(TestLayout::SIZE, 4);
1328
1329 {
1330 let mut data = account.try_borrow_mut().unwrap();
1331 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1332 }
1333
1334 {
1335 let mut data = account.try_borrow_mut().unwrap();
1336 data[0] = TestLayout::DISC.wrapping_add(1);
1337 }
1338 assert_eq!(
1339 account.load::<TestLayout>().unwrap_err(),
1340 ProgramError::InvalidAccountData
1341 );
1342
1343 {
1344 let mut data = account.try_borrow_mut().unwrap();
1345 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1346 data[1] = TestLayout::VERSION.wrapping_add(1);
1347 }
1348 assert_eq!(
1349 account.load::<TestLayout>().unwrap_err(),
1350 ProgramError::InvalidAccountData
1351 );
1352 }
1353
1354 #[test]
1355 fn load_rejects_undersized_layout_body() {
1356 let (_backing, account) = make_account(TestLayout::SIZE - 1, 5);
1357
1358 {
1359 let mut data = account.try_borrow_mut().unwrap();
1360 data[0] = TestLayout::DISC;
1361 data[1] = TestLayout::VERSION;
1362 data[4..12].copy_from_slice(&TestLayout::LAYOUT_ID);
1363 }
1364
1365 assert_eq!(
1366 account.load::<TestLayout>().unwrap_err(),
1367 ProgramError::AccountDataTooSmall
1368 );
1369 }
1370
1371 #[test]
1372 fn load_supports_header_inclusive_layouts() {
1373 let (_backing, account) = make_account(HeaderLayout::SIZE, 6);
1374
1375 {
1376 let mut data = account.try_borrow_mut().unwrap();
1377 crate::layout::init_header::<HeaderLayout>(&mut data).unwrap();
1378 }
1379
1380 {
1381 let mut layout = account.load_mut::<HeaderLayout>().unwrap();
1382 layout.amount = 55;
1383 }
1384
1385 let layout = account.load::<HeaderLayout>().unwrap();
1386 assert_eq!(layout.header.disc, HeaderLayout::DISC);
1387 assert_eq!(layout.header.version, HeaderLayout::VERSION);
1388 assert_eq!(layout.amount, 55);
1389 }
1390
1391 #[test]
1401 fn live_load_blocks_segment_mut() {
1402 let (_backing, account) = make_account(TestLayout::SIZE, 10);
1403 {
1404 let mut data = account.try_borrow_mut().unwrap();
1405 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1406 }
1407
1408 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1409 let _read_view = account.load::<TestLayout>().unwrap();
1410
1411 let err = account
1413 .segment_mut::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1414 .unwrap_err();
1415 assert_eq!(err, ProgramError::AccountBorrowFailed);
1416 }
1417
1418 #[test]
1419 fn live_load_mut_blocks_segment_ref() {
1420 let (_backing, account) = make_account(TestLayout::SIZE, 11);
1421 {
1422 let mut data = account.try_borrow_mut().unwrap();
1423 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1424 }
1425
1426 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1427 let _write_view = account.load_mut::<TestLayout>().unwrap();
1428
1429 let err = account
1432 .segment_ref::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1433 .unwrap_err();
1434 assert_eq!(err, ProgramError::AccountBorrowFailed);
1435 }
1436
1437 #[test]
1438 fn every_access_path_is_tracked() {
1439 let (_backing, account) = make_account(TestLayout::SIZE, 40);
1447 {
1448 let mut data = account.try_borrow_mut().unwrap();
1449 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1450 }
1451 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1452
1453 {
1455 let _r = account.try_borrow().unwrap();
1456 assert!(account.try_borrow_mut().is_err());
1457 }
1458 {
1460 let _w = account.try_borrow_mut().unwrap();
1461 assert!(account.try_borrow().is_err());
1462 }
1463 {
1465 let _v = account.load::<TestLayout>().unwrap();
1466 assert!(account.load_mut::<TestLayout>().is_err());
1467 }
1468 {
1470 let _v = account.load_mut::<TestLayout>().unwrap();
1471 assert!(account.load::<TestLayout>().is_err());
1472 }
1473 {
1475 let _r = unsafe { account.raw_ref::<[u8; 16]>() }.unwrap();
1477 assert!(account.load_mut::<TestLayout>().is_err());
1478 }
1479 {
1481 let _w = unsafe { account.raw_mut::<[u8; 16]>() }.unwrap();
1483 assert!(account.load::<TestLayout>().is_err());
1484 }
1485 {
1488 let _r = account
1489 .segment_ref::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1490 .unwrap();
1491 }
1497 assert_eq!(borrows.len(), 0);
1503 let _w = account
1504 .segment_mut::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1505 .unwrap();
1506 }
1507
1508 #[test]
1513 fn seg_lease_releases_on_drop_and_allows_reacquire() {
1514 let (_backing, account) = make_account(TestLayout::SIZE, 41);
1515 {
1516 let mut data = account.try_borrow_mut().unwrap();
1517 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1518 }
1519 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1520 const OFF: u32 = crate::layout::HopperHeader::SIZE as u32;
1521
1522 {
1523 let mut first = account.segment_mut::<u64>(&mut borrows, OFF, 8).unwrap();
1524 *first = 100;
1525 }
1526 assert_eq!(borrows.len(), 0);
1528 {
1531 let mut second = account.segment_mut::<u64>(&mut borrows, OFF, 8).unwrap();
1532 assert_eq!(*second, 100);
1533 *second = 200;
1534 }
1535 assert_eq!(borrows.len(), 0);
1536 let read = account.segment_ref::<u64>(&mut borrows, OFF, 8).unwrap();
1537 assert_eq!(*read, 200);
1538 }
1539
1540 #[test]
1544 fn seg_lease_still_rejects_simultaneous_overlap() {
1545 let (_backing, account) = make_account(TestLayout::SIZE, 42);
1546 {
1547 let mut data = account.try_borrow_mut().unwrap();
1548 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1549 }
1550 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1551 const OFF: u32 = crate::layout::HopperHeader::SIZE as u32;
1552
1553 let _first = account.segment_mut::<u64>(&mut borrows, OFF, 8).unwrap();
1554 drop(_first);
1561 assert_eq!(borrows.len(), 0);
1562 }
1563
1564 #[test]
1565 fn typed_segment_api_round_trips() {
1566 use crate::segment::TypedSegment;
1567
1568 let (_backing, account) = make_account(TestLayout::SIZE, 22);
1569 {
1570 let mut data = account.try_borrow_mut().unwrap();
1571 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1572 }
1573
1574 const A_TYPED: TypedSegment<u64, { crate::layout::HopperHeader::SIZE as u32 }> =
1575 TypedSegment::new();
1576
1577 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1581 {
1582 let mut a = account
1583 .segment_mut_typed::<u64, { crate::layout::HopperHeader::SIZE as u32 }>(
1584 &mut borrows,
1585 A_TYPED,
1586 )
1587 .unwrap();
1588 *a = 1337;
1589 }
1590 assert_eq!(borrows.len(), 0);
1591
1592 let read = account
1593 .segment_ref_typed::<u64, { crate::layout::HopperHeader::SIZE as u32 }>(
1594 &mut borrows,
1595 A_TYPED,
1596 )
1597 .unwrap();
1598 assert_eq!(*read, 1337);
1599 }
1600
1601 #[test]
1602 fn const_segment_api_matches_manual_offsets() {
1603 use crate::segment::Segment;
1604
1605 let (_backing, account) = make_account(TestLayout::SIZE, 20);
1606 {
1607 let mut data = account.try_borrow_mut().unwrap();
1608 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1609 }
1610
1611 const A_SEG: Segment = Segment::body(0, 8); let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1616 {
1617 let mut a = account
1618 .segment_mut_const::<u64>(&mut borrows, A_SEG)
1619 .unwrap();
1620 *a = 7;
1621 }
1622 let read = account
1623 .segment_ref::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1624 .unwrap();
1625 assert_eq!(*read, 7);
1626 }
1627
1628 #[test]
1629 fn load_after_segment_drop_succeeds() {
1630 let (_backing, account) = make_account(TestLayout::SIZE, 12);
1631 {
1632 let mut data = account.try_borrow_mut().unwrap();
1633 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1634 }
1635
1636 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1637 {
1638 let mut seg = account
1639 .segment_mut::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1640 .unwrap();
1641 *seg = 42;
1642 }
1643 let view = account.load::<TestLayout>().unwrap();
1645 assert_eq!(view.a, 42);
1646 }
1647}