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
46unsafe impl Send for AccountView {}
49unsafe impl Sync for AccountView {}
50
51impl AccountView {
52 #[cfg(test)]
53 #[inline(always)]
54 pub(crate) fn from_backend(inner: BackendAccountView) -> Self {
55 Self { inner }
56 }
57
58 #[inline(always)]
62 pub fn address(&self) -> &Address {
63 compat::account_address(&self.inner)
64 }
65
66 #[inline(always)]
73 pub unsafe fn owner(&self) -> &Address {
74 unsafe { compat::account_owner(&self.inner) }
76 }
77
78 #[inline(always)]
80 pub fn read_owner(&self) -> Address {
81 compat::read_owner(&self.inner)
82 }
83
84 #[inline(always)]
86 pub fn owned_by(&self, program: &Address) -> bool {
87 compat::owned_by(&self.inner, program)
88 }
89
90 #[inline(always)]
92 pub fn is_signer(&self) -> bool {
93 self.inner.is_signer()
94 }
95
96 #[inline(always)]
98 pub fn is_writable(&self) -> bool {
99 self.inner.is_writable()
100 }
101
102 #[inline(always)]
104 pub fn executable(&self) -> bool {
105 self.inner.executable()
106 }
107
108 #[inline(always)]
110 pub fn data_len(&self) -> usize {
111 self.inner.data_len()
112 }
113
114 #[inline(always)]
116 pub fn lamports(&self) -> u64 {
117 self.inner.lamports()
118 }
119
120 #[inline(always)]
122 pub fn is_data_empty(&self) -> bool {
123 self.data_len() == 0
124 }
125
126 #[inline(always)]
128 pub fn set_lamports(&self, lamports: u64) {
129 self.inner.set_lamports(lamports);
130 }
131
132 #[inline(always)]
136 pub fn try_borrow(&self) -> Result<Ref<'_, [u8]>, ProgramError> {
137 let token = BorrowToken::shared(self.address())?;
138 match self.inner.try_borrow() {
139 Ok(data) => Ok(Ref::from_backend(data, token)),
140 Err(error) => {
141 drop(token);
142 Err(ProgramError::from(error))
143 }
144 }
145 }
146
147 #[inline(always)]
149 pub fn try_borrow_mut(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
150 let token = BorrowToken::mutable(self.address())?;
151 match self.inner.try_borrow_mut() {
152 Ok(data) => Ok(RefMut::from_backend(data, token)),
153 Err(error) => {
154 drop(token);
155 Err(ProgramError::from(error))
156 }
157 }
158 }
159
160 #[inline(always)]
183 pub fn segment_ref<'a, T: crate::Pod>(
184 &'a self,
185 borrows: &'a mut SegmentBorrowRegistry,
186 abs_offset: u32,
187 size: u32,
188 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
189 let expected_size = core::mem::size_of::<T>() as u32;
190 if size != expected_size {
191 return ProgramError::err_invalid_argument();
192 }
193
194 let end = abs_offset
195 .checked_add(size)
196 .ok_or(ProgramError::ArithmeticOverflow)?;
197 if end as usize > self.data_len() {
198 return ProgramError::err_data_too_small();
199 }
200
201 let borrow = borrows.register_leased_read(self.address(), abs_offset, size)?;
202
203 #[cfg(target_os = "solana")]
205 let inner: Ref<'_, T> = {
206 let native_ref = unsafe { self.inner.segment_ref_unchecked::<T>(abs_offset) };
208 let native_ref = match native_ref {
209 Ok(nr) => nr,
210 Err(e) => {
211 borrows.release(&borrow);
215 return Err(ProgramError::from(e));
216 }
217 };
218 let (typed_ref, state_ptr) = native_ref.into_raw_parts();
219 Ref::from_segment(typed_ref as *const T, state_ptr)
220 };
221 #[cfg(not(target_os = "solana"))]
222 let inner: Ref<'_, T> = {
223 let data = match self.try_borrow() {
224 Ok(d) => d,
225 Err(e) => {
226 borrows.release(&borrow);
227 return Err(e);
228 }
229 };
230 let ptr = unsafe { data.as_bytes_ptr().add(abs_offset as usize) as *const T };
232 unsafe { data.project(ptr) }
233 };
234
235 let lease = unsafe { crate::SegmentLease::new(borrows, borrow) };
238 Ok(crate::SegRef::new(inner, lease))
239 }
240
241 #[inline(always)]
246 pub fn segment_mut<'a, T: crate::Pod>(
247 &'a self,
248 borrows: &'a mut SegmentBorrowRegistry,
249 abs_offset: u32,
250 size: u32,
251 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
252 self.check_writable()?;
253
254 let expected_size = core::mem::size_of::<T>() as u32;
255 if size != expected_size {
256 return ProgramError::err_invalid_argument();
257 }
258
259 let end = abs_offset
260 .checked_add(size)
261 .ok_or(ProgramError::ArithmeticOverflow)?;
262 if end as usize > self.data_len() {
263 return ProgramError::err_data_too_small();
264 }
265
266 let borrow = borrows.register_leased_write(self.address(), abs_offset, size)?;
267
268 #[cfg(target_os = "solana")]
269 let inner: RefMut<'_, T> = {
270 let native_ref = unsafe { self.inner.segment_mut_unchecked::<T>(abs_offset) };
272 let native_ref = match native_ref {
273 Ok(nr) => nr,
274 Err(e) => {
275 borrows.release(&borrow);
276 return Err(ProgramError::from(e));
277 }
278 };
279 let (typed_ref, state_ptr) = native_ref.into_raw_parts();
280 RefMut::from_segment(typed_ref as *mut T, state_ptr)
281 };
282 #[cfg(not(target_os = "solana"))]
283 let inner: RefMut<'_, T> = {
284 let mut data = match self.try_borrow_mut() {
285 Ok(d) => d,
286 Err(e) => {
287 borrows.release(&borrow);
288 return Err(e);
289 }
290 };
291 let ptr = unsafe { data.as_bytes_mut_ptr().add(abs_offset as usize) as *mut T };
293 unsafe { data.project(ptr) }
294 };
295
296 let lease = unsafe { crate::SegmentLease::new(borrows, borrow) };
298 Ok(crate::SegRefMut::new(inner, lease))
299 }
300
301 #[inline(always)]
322 pub fn segment_ref_const<'a, T: crate::Pod>(
323 &'a self,
324 borrows: &'a mut SegmentBorrowRegistry,
325 segment: crate::segment::Segment,
326 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
327 self.segment_ref::<T>(borrows, segment.offset, segment.size)
328 }
329
330 #[inline(always)]
333 pub fn segment_mut_const<'a, T: crate::Pod>(
334 &'a self,
335 borrows: &'a mut SegmentBorrowRegistry,
336 segment: crate::segment::Segment,
337 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
338 self.segment_mut::<T>(borrows, segment.offset, segment.size)
339 }
340
341 #[inline(always)]
356 pub fn segment_ref_typed<'a, T: crate::Pod, const OFFSET: u32>(
357 &'a self,
358 borrows: &'a mut SegmentBorrowRegistry,
359 _segment: crate::segment::TypedSegment<T, OFFSET>,
360 ) -> Result<crate::SegRef<'a, T>, ProgramError> {
361 self.segment_ref::<T>(borrows, OFFSET, core::mem::size_of::<T>() as u32)
362 }
363
364 #[inline(always)]
367 pub fn segment_mut_typed<'a, T: crate::Pod, const OFFSET: u32>(
368 &'a self,
369 borrows: &'a mut SegmentBorrowRegistry,
370 _segment: crate::segment::TypedSegment<T, OFFSET>,
371 ) -> Result<crate::SegRefMut<'a, T>, ProgramError> {
372 self.segment_mut::<T>(borrows, OFFSET, core::mem::size_of::<T>() as u32)
373 }
374
375 #[inline(always)]
396 pub fn load<T: LayoutContract>(&self) -> Result<Ref<'_, T>, ProgramError> {
397 let data = self.try_borrow()?;
398 T::validate_header(&data)?;
399 if data.len() < T::required_len() {
400 return ProgramError::err_data_too_small();
401 }
402 let ptr = unsafe { data.as_bytes_ptr().add(T::TYPE_OFFSET) as *const T };
404 Ok(unsafe { data.project(ptr) })
406 }
407
408 #[inline(always)]
420 pub fn load_mut<T: LayoutContract>(&self) -> Result<RefMut<'_, T>, ProgramError> {
421 let mut data = self.try_borrow_mut()?;
422 T::validate_header(&data)?;
423 if data.len() < T::required_len() {
424 return ProgramError::err_data_too_small();
425 }
426 let ptr = unsafe { data.as_bytes_mut_ptr().add(T::TYPE_OFFSET) as *mut T };
428 Ok(unsafe { data.project(ptr) })
430 }
431
432 #[inline(always)]
437 pub unsafe fn raw_ref<T: crate::Pod>(&self) -> Result<Ref<'_, T>, ProgramError> {
442 let data = self.try_borrow()?;
443 if core::mem::size_of::<T>() > data.len() {
444 return Err(ProgramError::AccountDataTooSmall);
445 }
446 let ptr = data.as_ptr() as *const T;
447 Ok(unsafe { data.project(ptr) })
449 }
450
451 #[inline(always)]
456 pub unsafe fn raw_mut<T: crate::Pod>(&self) -> Result<RefMut<'_, T>, ProgramError> {
461 self.check_writable()?;
462 let mut data = self.try_borrow_mut()?;
463 if core::mem::size_of::<T>() > data.len() {
464 return Err(ProgramError::AccountDataTooSmall);
465 }
466 let ptr = data.as_bytes_mut_ptr() as *mut T;
467 Ok(unsafe { data.project(ptr) })
469 }
470
471 #[inline(always)]
487 pub fn load_cross_program<T: LayoutContract>(&self) -> Result<Ref<'_, T>, ProgramError> {
488 let data = self.try_borrow()?;
489 if data.len() < T::required_len() {
490 return Err(ProgramError::AccountDataTooSmall);
491 }
492 T::check_disc(&data)?;
493 if let Some(id) = crate::layout::read_layout_id(&data) {
494 if *id != T::LAYOUT_ID {
495 return Err(ProgramError::InvalidAccountData);
496 }
497 } else {
498 return Err(ProgramError::AccountDataTooSmall);
499 }
500 let ptr = unsafe { data.as_bytes_ptr().add(T::TYPE_OFFSET) as *const T };
502 Ok(unsafe { data.project(ptr) })
504 }
505
506 #[inline(always)]
512 pub fn layout_info(&self) -> Option<crate::layout::LayoutInfo> {
513 let data = self.try_borrow().ok()?;
514 crate::layout::LayoutInfo::from_data(&data)
515 }
516
517 #[inline(always)]
519 pub fn fields<T: LayoutContract>() -> &'static [FieldInfo] {
520 T::fields()
521 }
522
523 #[inline]
531 pub fn field<T: LayoutContract>(name: &str) -> Option<&'static FieldInfo> {
532 <T as crate::field_map::FieldMap>::field_by_name(name)
533 }
534
535 #[inline(always)]
540 pub fn extension_range<T: LayoutContract>(
541 &self,
542 ) -> Result<core::ops::Range<usize>, ProgramError> {
543 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
544 let data_len = self.data_len();
545 if data_len < offset {
546 return Err(ProgramError::AccountDataTooSmall);
547 }
548 Ok(offset..data_len)
549 }
550
551 #[inline(always)]
553 pub fn extension_bytes<T: LayoutContract>(&self) -> Result<Ref<'_, [u8]>, ProgramError> {
554 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
555 let data = self.try_borrow()?;
556 if data.len() < offset {
557 return Err(ProgramError::AccountDataTooSmall);
558 }
559 Ok(data.slice_from(offset))
560 }
561
562 #[inline(always)]
564 pub fn extension_bytes_mut<T: LayoutContract>(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
565 let offset = T::EXTENSION_OFFSET.ok_or(ProgramError::InvalidArgument)?;
566 let data = self.try_borrow_mut()?;
567 if data.len() < offset {
568 return Err(ProgramError::AccountDataTooSmall);
569 }
570 Ok(data.slice_from(offset))
571 }
572
573 #[inline(always)]
578 pub fn init_layout<T: LayoutContract>(&self) -> ProgramResult {
579 let mut data = self.try_borrow_mut()?;
580 crate::layout::init_header::<T>(&mut data)
581 }
582
583 #[inline(always)]
587 pub fn require_signer(&self) -> ProgramResult {
588 if self.is_signer() {
589 Ok(())
590 } else {
591 ProgramError::err_missing_signer()
592 }
593 }
594
595 #[inline(always)]
597 pub fn require_writable(&self) -> ProgramResult {
598 if self.is_writable() {
599 Ok(())
600 } else {
601 ProgramError::err_immutable()
602 }
603 }
604
605 #[inline(always)]
607 pub fn require_owned_by(&self, program: &Address) -> ProgramResult {
608 if self.owned_by(program) {
609 Ok(())
610 } else {
611 ProgramError::err_incorrect_program()
612 }
613 }
614
615 #[inline(always)]
617 pub fn require_payer(&self) -> ProgramResult {
618 self.require_signer()?;
619 self.require_writable()
620 }
621
622 #[inline(always)]
626 pub fn check_signer(&self) -> Result<&Self, ProgramError> {
627 if self.is_signer() {
628 Ok(self)
629 } else {
630 ProgramError::err_missing_signer()
631 }
632 }
633
634 #[inline(always)]
636 pub fn check_writable(&self) -> Result<&Self, ProgramError> {
637 if self.is_writable() {
638 Ok(self)
639 } else {
640 ProgramError::err_immutable()
641 }
642 }
643
644 #[inline(always)]
646 pub fn check_owned_by(&self, program: &Address) -> Result<&Self, ProgramError> {
647 if self.owned_by(program) {
648 Ok(self)
649 } else {
650 ProgramError::err_incorrect_program()
651 }
652 }
653
654 #[inline(always)]
656 pub fn check_disc(&self, expected: u8) -> Result<&Self, ProgramError> {
657 if self.disc() == expected {
658 Ok(self)
659 } else {
660 Err(ProgramError::InvalidAccountData)
661 }
662 }
663
664 #[inline(always)]
666 pub fn check_has_data(&self) -> Result<&Self, ProgramError> {
667 if !self.is_data_empty() {
668 Ok(self)
669 } else {
670 Err(ProgramError::AccountDataTooSmall)
671 }
672 }
673
674 #[inline(always)]
676 pub fn check_executable(&self) -> Result<&Self, ProgramError> {
677 if self.executable() {
678 Ok(self)
679 } else {
680 Err(ProgramError::InvalidArgument)
681 }
682 }
683
684 #[inline(always)]
686 pub fn check_address(&self, expected: &Address) -> Result<&Self, ProgramError> {
687 if address_eq(self.address(), expected) {
688 Ok(self)
689 } else {
690 Err(ProgramError::InvalidArgument)
691 }
692 }
693
694 #[inline(always)]
696 pub fn check_data_len(&self, min_len: usize) -> Result<&Self, ProgramError> {
697 if self.data_len() >= min_len {
698 Ok(self)
699 } else {
700 Err(ProgramError::AccountDataTooSmall)
701 }
702 }
703
704 #[inline(always)]
706 pub fn check_version(&self, expected: u8) -> Result<&Self, ProgramError> {
707 if self.version() == expected {
708 Ok(self)
709 } else {
710 Err(ProgramError::InvalidAccountData)
711 }
712 }
713
714 #[inline(always)]
716 pub fn check_layout<T: LayoutContract>(&self) -> Result<&Self, ProgramError> {
717 let data = self.try_borrow()?;
718 T::validate_header(&data)?;
719 Ok(self)
720 }
721
722 #[inline(always)]
726 pub fn disc(&self) -> u8 {
727 compat::disc(&self.inner)
728 }
729
730 #[inline(always)]
732 pub fn version(&self) -> u8 {
733 compat::version(&self.inner)
734 }
735
736 #[inline(always)]
738 pub fn layout_id(&self) -> Option<&[u8; 8]> {
739 compat::layout_id(&self.inner)
740 }
741
742 #[inline(always)]
744 pub fn require_disc(&self, expected: u8) -> ProgramResult {
745 if self.disc() == expected {
746 Ok(())
747 } else {
748 Err(ProgramError::InvalidAccountData)
749 }
750 }
751
752 #[inline(always)]
759 pub fn flags(&self) -> u8 {
760 let mut f: u8 = 0;
761 if self.is_signer() {
762 f |= 0b0001;
763 }
764 if self.is_writable() {
765 f |= 0b0010;
766 }
767 if self.executable() {
768 f |= 0b0100;
769 }
770 if !self.is_data_empty() {
771 f |= 0b1000;
772 }
773 f
774 }
775
776 #[inline(always)]
778 pub fn expect_flags(&self, required: u8) -> ProgramResult {
779 if self.flags() & required == required {
780 Ok(())
781 } else {
782 Err(ProgramError::InvalidArgument)
783 }
784 }
785
786 #[inline]
790 pub fn resize(&self, new_len: usize) -> ProgramResult {
791 self.inner.resize(new_len).map_err(ProgramError::from)
792 }
793
794 #[inline(always)]
801 pub unsafe fn assign(&self, new_owner: &Address) {
802 unsafe {
804 compat::assign(&self.inner, new_owner);
805 }
806 }
807
808 #[inline]
810 pub fn close(&self) -> ProgramResult {
811 compat::close(&self.inner)
812 }
813
814 #[inline]
843 pub fn close_to(&self, destination: &AccountView, program_id: &Address) -> ProgramResult {
844 self.require_writable()?;
845 self.require_owned_by(program_id)?;
846 destination.require_writable()?;
847
848 let lamports = self.lamports();
849 let dest_lamports = destination.lamports();
850 destination.set_lamports(
851 dest_lamports
852 .checked_add(lamports)
853 .ok_or(ProgramError::ArithmeticOverflow)?,
854 );
855 self.set_lamports(0);
856 compat::zero_data(&self.inner)?;
857 Ok(())
858 }
859
860 #[inline]
867 pub fn close_to_unchecked(&self, destination: &AccountView) -> ProgramResult {
868 let lamports = self.lamports();
869 let dest_lamports = destination.lamports();
870 destination.set_lamports(
871 dest_lamports
872 .checked_add(lamports)
873 .ok_or(ProgramError::ArithmeticOverflow)?,
874 );
875 self.set_lamports(0);
876 compat::zero_data(&self.inner)?;
877 Ok(())
878 }
879
880 #[cfg(feature = "hopper-native-backend")]
884 #[inline(always)]
885 pub(crate) fn data_ptr_unchecked(&self) -> *mut u8 {
886 self.inner.data_ptr_unchecked()
887 }
888
889 #[cfg(feature = "hopper-native-backend")]
891 #[inline(always)]
892 pub(crate) fn account_ptr(&self) -> *const hopper_native::RuntimeAccount {
893 self.inner.account_ptr()
894 }
895
896 #[inline(always)]
898 pub fn check_borrow(&self) -> Result<(), ProgramError> {
899 borrow_registry::check_shared(self.address())?;
900 self.inner.check_borrow().map_err(ProgramError::from)
901 }
902
903 #[inline(always)]
905 pub fn check_borrow_mut(&self) -> Result<(), ProgramError> {
906 borrow_registry::check_mutable(self.address())?;
907 self.inner.check_borrow_mut().map_err(ProgramError::from)
908 }
909
910 #[inline(always)]
916 pub unsafe fn borrow_unchecked(&self) -> &[u8] {
917 unsafe { self.inner.borrow_unchecked() }
919 }
920
921 #[inline(always)]
927 pub unsafe fn borrow_unchecked_mut(&self) -> &mut [u8] {
928 unsafe { self.inner.borrow_unchecked_mut() }
930 }
931
932 #[cfg(feature = "hopper-native-backend")]
938 #[inline(always)]
939 pub unsafe fn resize_unchecked(&self, new_len: usize) {
940 unsafe {
942 self.inner.resize_unchecked(new_len);
943 }
944 }
945
946 #[inline(always)]
952 pub unsafe fn close_unchecked(&self) {
953 unsafe {
955 self.inner.close_unchecked();
956 }
957 }
958
959 #[cfg(feature = "solana-program-backend")]
963 #[inline(always)]
964 pub(crate) fn as_backend(&self) -> &BackendAccountView {
965 &self.inner
966 }
967}
968
969impl core::fmt::Debug for AccountView {
970 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
971 f.debug_struct("AccountView")
972 .field("address", self.address())
973 .field("lamports", &self.lamports())
974 .field("data_len", &self.data_len())
975 .field("is_signer", &self.is_signer())
976 .field("is_writable", &self.is_writable())
977 .finish()
978 }
979}
980
981pub struct RemainingAccounts<'a> {
985 accounts: &'a [AccountView],
986 cursor: usize,
987}
988
989impl<'a> RemainingAccounts<'a> {
990 #[inline(always)]
992 pub fn new(accounts: &'a [AccountView]) -> Self {
993 Self {
994 accounts,
995 cursor: 0,
996 }
997 }
998
999 #[inline(always)]
1001 pub fn remaining(&self) -> usize {
1002 self.accounts.len() - self.cursor
1003 }
1004
1005 #[inline(always)]
1007 pub fn next(&mut self) -> Result<&'a AccountView, ProgramError> {
1008 if self.cursor >= self.accounts.len() {
1009 return Err(ProgramError::NotEnoughAccountKeys);
1010 }
1011 let account = &self.accounts[self.cursor];
1012 self.cursor += 1;
1013 Ok(account)
1014 }
1015
1016 #[inline(always)]
1018 pub fn next_signer(&mut self) -> Result<&'a AccountView, ProgramError> {
1019 let account = self.next()?;
1020 account.require_signer()?;
1021 Ok(account)
1022 }
1023
1024 #[inline(always)]
1026 pub fn next_writable(&mut self) -> Result<&'a AccountView, ProgramError> {
1027 let account = self.next()?;
1028 account.require_writable()?;
1029 Ok(account)
1030 }
1031
1032 #[inline(always)]
1034 pub fn next_owned_by(&mut self, program: &Address) -> Result<&'a AccountView, ProgramError> {
1035 let account = self.next()?;
1036 account.require_owned_by(program)?;
1037 Ok(account)
1038 }
1039}
1040
1041#[cfg(all(test, feature = "hopper-native-backend"))]
1042mod tests {
1043 use super::*;
1044 use crate::layout::HopperHeader;
1045
1046 use hopper_native::{
1047 AccountView as NativeAccountView, Address as NativeAddress, RuntimeAccount, NOT_BORROWED,
1048 };
1049
1050 #[repr(C)]
1051 #[derive(Clone, Copy, Debug, Default)]
1052 struct TestLayout {
1053 a: u64,
1054 b: u64,
1055 }
1056
1057 #[repr(C)]
1058 #[derive(Clone, Copy, Debug)]
1059 struct HeaderLayout {
1060 header: HopperHeader,
1061 amount: u64,
1062 }
1063
1064 impl crate::field_map::FieldMap for TestLayout {
1065 const FIELDS: &'static [crate::field_map::FieldInfo] = &[
1066 crate::field_map::FieldInfo::new("a", HopperHeader::SIZE, 8),
1067 crate::field_map::FieldInfo::new("b", HopperHeader::SIZE + 8, 8),
1068 ];
1069 }
1070
1071 impl LayoutContract for TestLayout {
1072 const DISC: u8 = 7;
1073 const VERSION: u8 = 1;
1074 const LAYOUT_ID: [u8; 8] = [0xAB; 8];
1075 const SIZE: usize = HopperHeader::SIZE + core::mem::size_of::<Self>();
1076 const EXTENSION_OFFSET: Option<usize> = Some(Self::SIZE);
1077 }
1078
1079 impl crate::field_map::FieldMap for HeaderLayout {
1080 const FIELDS: &'static [crate::field_map::FieldInfo] = &[crate::field_map::FieldInfo::new(
1081 "amount",
1082 HopperHeader::SIZE,
1083 8,
1084 )];
1085 }
1086
1087 impl LayoutContract for HeaderLayout {
1088 const DISC: u8 = 11;
1089 const VERSION: u8 = 2;
1090 const LAYOUT_ID: [u8; 8] = [0xCD; 8];
1091 const SIZE: usize = core::mem::size_of::<Self>();
1092 const TYPE_OFFSET: usize = 0;
1093 }
1094
1095 fn make_account(total_data_len: usize, address_byte: u8) -> (std::vec::Vec<u8>, AccountView) {
1096 let mut backing = std::vec![0u8; RuntimeAccount::SIZE + total_data_len];
1097 let raw = backing.as_mut_ptr() as *mut RuntimeAccount;
1098 unsafe {
1100 raw.write(RuntimeAccount {
1101 borrow_state: NOT_BORROWED,
1102 is_signer: 1,
1103 is_writable: 1,
1104 executable: 0,
1105 resize_delta: 0,
1106 address: NativeAddress::new_from_array([address_byte; 32]),
1107 owner: NativeAddress::new_from_array([2; 32]),
1108 lamports: 42,
1109 data_len: total_data_len as u64,
1110 });
1111 }
1112 let backend = unsafe { NativeAccountView::new_unchecked(raw) };
1114 let account = AccountView::from_backend(backend);
1115 (backing, account)
1116 }
1117
1118 #[test]
1119 fn load_mut_is_zero_copy_and_pointer_stable() {
1120 let (_backing, account) = make_account(TestLayout::SIZE + 8, 1);
1121
1122 {
1123 let mut data = account.try_borrow_mut().unwrap();
1124 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1125 data[HopperHeader::SIZE..HopperHeader::SIZE + 8].copy_from_slice(&10u64.to_le_bytes());
1126 data[HopperHeader::SIZE + 8..HopperHeader::SIZE + 16]
1127 .copy_from_slice(&20u64.to_le_bytes());
1128 data[TestLayout::SIZE..TestLayout::SIZE + 8].copy_from_slice(b"tailpass");
1129 }
1130
1131 let first_ptr = {
1132 let first = account.load::<TestLayout>().unwrap();
1133 assert_eq!(first.a, 10);
1134 assert_eq!(first.b, 20);
1135 first.as_ptr() as usize
1136 };
1137
1138 {
1139 let tail = account.extension_bytes::<TestLayout>().unwrap();
1140 assert_eq!(&tail[..8], b"tailpass");
1141 }
1142
1143 let mut second = account.load_mut::<TestLayout>().unwrap();
1144 let second_ptr = second.as_mut_ptr() as usize;
1145 second.b = 99;
1146 assert_eq!(first_ptr, second_ptr);
1147 drop(second);
1148
1149 let reread = account.load::<TestLayout>().unwrap();
1150 assert_eq!(reread.a, 10);
1151 assert_eq!(reread.b, 99);
1152 }
1153
1154 #[test]
1155 fn typed_load_holds_borrow_until_drop() {
1156 let (_backing, account) = make_account(TestLayout::SIZE, 3);
1157
1158 {
1159 let mut data = account.try_borrow_mut().unwrap();
1160 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1161 }
1162
1163 let shared = account.load::<TestLayout>().unwrap();
1164 assert_eq!(
1165 account.load_mut::<TestLayout>().unwrap_err(),
1166 ProgramError::AccountBorrowFailed
1167 );
1168 drop(shared);
1169 assert!(account.load_mut::<TestLayout>().is_ok());
1170 }
1171
1172 #[test]
1173 fn duplicate_address_aliases_are_rejected_across_views() {
1174 let (_first_backing, first) = make_account(TestLayout::SIZE, 9);
1175 let (_second_backing, second) = make_account(TestLayout::SIZE, 9);
1176
1177 let first_shared = first.try_borrow().unwrap();
1178 let second_shared = second.try_borrow().unwrap();
1179 assert_eq!(
1180 second.try_borrow_mut().unwrap_err(),
1181 ProgramError::AccountBorrowFailed
1182 );
1183 drop(first_shared);
1184 drop(second_shared);
1185 assert!(second.try_borrow_mut().is_ok());
1186 }
1187
1188 #[test]
1189 fn load_rejects_wrong_disc_and_wrong_version() {
1190 let (_backing, account) = make_account(TestLayout::SIZE, 4);
1191
1192 {
1193 let mut data = account.try_borrow_mut().unwrap();
1194 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1195 }
1196
1197 {
1198 let mut data = account.try_borrow_mut().unwrap();
1199 data[0] = TestLayout::DISC.wrapping_add(1);
1200 }
1201 assert_eq!(
1202 account.load::<TestLayout>().unwrap_err(),
1203 ProgramError::InvalidAccountData
1204 );
1205
1206 {
1207 let mut data = account.try_borrow_mut().unwrap();
1208 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1209 data[1] = TestLayout::VERSION.wrapping_add(1);
1210 }
1211 assert_eq!(
1212 account.load::<TestLayout>().unwrap_err(),
1213 ProgramError::InvalidAccountData
1214 );
1215 }
1216
1217 #[test]
1218 fn load_rejects_undersized_layout_body() {
1219 let (_backing, account) = make_account(TestLayout::SIZE - 1, 5);
1220
1221 {
1222 let mut data = account.try_borrow_mut().unwrap();
1223 data[0] = TestLayout::DISC;
1224 data[1] = TestLayout::VERSION;
1225 data[4..12].copy_from_slice(&TestLayout::LAYOUT_ID);
1226 }
1227
1228 assert_eq!(
1229 account.load::<TestLayout>().unwrap_err(),
1230 ProgramError::AccountDataTooSmall
1231 );
1232 }
1233
1234 #[test]
1235 fn load_supports_header_inclusive_layouts() {
1236 let (_backing, account) = make_account(HeaderLayout::SIZE, 6);
1237
1238 {
1239 let mut data = account.try_borrow_mut().unwrap();
1240 crate::layout::init_header::<HeaderLayout>(&mut data).unwrap();
1241 }
1242
1243 {
1244 let mut layout = account.load_mut::<HeaderLayout>().unwrap();
1245 layout.amount = 55;
1246 }
1247
1248 let layout = account.load::<HeaderLayout>().unwrap();
1249 assert_eq!(layout.header.disc, HeaderLayout::DISC);
1250 assert_eq!(layout.header.version, HeaderLayout::VERSION);
1251 assert_eq!(layout.amount, 55);
1252 }
1253
1254 #[test]
1264 fn live_load_blocks_segment_mut() {
1265 let (_backing, account) = make_account(TestLayout::SIZE, 10);
1266 {
1267 let mut data = account.try_borrow_mut().unwrap();
1268 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1269 }
1270
1271 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1272 let _read_view = account.load::<TestLayout>().unwrap();
1273
1274 let err = account
1276 .segment_mut::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1277 .unwrap_err();
1278 assert_eq!(err, ProgramError::AccountBorrowFailed);
1279 }
1280
1281 #[test]
1282 fn live_load_mut_blocks_segment_ref() {
1283 let (_backing, account) = make_account(TestLayout::SIZE, 11);
1284 {
1285 let mut data = account.try_borrow_mut().unwrap();
1286 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1287 }
1288
1289 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1290 let _write_view = account.load_mut::<TestLayout>().unwrap();
1291
1292 let err = account
1295 .segment_ref::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1296 .unwrap_err();
1297 assert_eq!(err, ProgramError::AccountBorrowFailed);
1298 }
1299
1300 #[test]
1301 fn every_access_path_is_tracked() {
1302 let (_backing, account) = make_account(TestLayout::SIZE, 40);
1310 {
1311 let mut data = account.try_borrow_mut().unwrap();
1312 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1313 }
1314 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1315
1316 {
1318 let _r = account.try_borrow().unwrap();
1319 assert!(account.try_borrow_mut().is_err());
1320 }
1321 {
1323 let _w = account.try_borrow_mut().unwrap();
1324 assert!(account.try_borrow().is_err());
1325 }
1326 {
1328 let _v = account.load::<TestLayout>().unwrap();
1329 assert!(account.load_mut::<TestLayout>().is_err());
1330 }
1331 {
1333 let _v = account.load_mut::<TestLayout>().unwrap();
1334 assert!(account.load::<TestLayout>().is_err());
1335 }
1336 {
1338 let _r = unsafe { account.raw_ref::<[u8; 16]>() }.unwrap();
1340 assert!(account.load_mut::<TestLayout>().is_err());
1341 }
1342 {
1344 let _w = unsafe { account.raw_mut::<[u8; 16]>() }.unwrap();
1346 assert!(account.load::<TestLayout>().is_err());
1347 }
1348 {
1351 let _r = account
1352 .segment_ref::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1353 .unwrap();
1354 }
1360 assert_eq!(borrows.len(), 0);
1366 let _w = account
1367 .segment_mut::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1368 .unwrap();
1369 }
1370
1371 #[test]
1376 fn seg_lease_releases_on_drop_and_allows_reacquire() {
1377 let (_backing, account) = make_account(TestLayout::SIZE, 41);
1378 {
1379 let mut data = account.try_borrow_mut().unwrap();
1380 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1381 }
1382 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1383 const OFF: u32 = crate::layout::HopperHeader::SIZE as u32;
1384
1385 {
1386 let mut first = account.segment_mut::<u64>(&mut borrows, OFF, 8).unwrap();
1387 *first = 100;
1388 }
1389 assert_eq!(borrows.len(), 0);
1391 {
1394 let mut second = account.segment_mut::<u64>(&mut borrows, OFF, 8).unwrap();
1395 assert_eq!(*second, 100);
1396 *second = 200;
1397 }
1398 assert_eq!(borrows.len(), 0);
1399 let read = account.segment_ref::<u64>(&mut borrows, OFF, 8).unwrap();
1400 assert_eq!(*read, 200);
1401 }
1402
1403 #[test]
1407 fn seg_lease_still_rejects_simultaneous_overlap() {
1408 let (_backing, account) = make_account(TestLayout::SIZE, 42);
1409 {
1410 let mut data = account.try_borrow_mut().unwrap();
1411 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1412 }
1413 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1414 const OFF: u32 = crate::layout::HopperHeader::SIZE as u32;
1415
1416 let _first = account.segment_mut::<u64>(&mut borrows, OFF, 8).unwrap();
1417 drop(_first);
1424 assert_eq!(borrows.len(), 0);
1425 }
1426
1427 #[test]
1428 fn typed_segment_api_round_trips() {
1429 use crate::segment::TypedSegment;
1430
1431 let (_backing, account) = make_account(TestLayout::SIZE, 22);
1432 {
1433 let mut data = account.try_borrow_mut().unwrap();
1434 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1435 }
1436
1437 const A_TYPED: TypedSegment<u64, { crate::layout::HopperHeader::SIZE as u32 }> =
1438 TypedSegment::new();
1439
1440 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1444 {
1445 let mut a = account
1446 .segment_mut_typed::<u64, { crate::layout::HopperHeader::SIZE as u32 }>(
1447 &mut borrows,
1448 A_TYPED,
1449 )
1450 .unwrap();
1451 *a = 1337;
1452 }
1453 assert_eq!(borrows.len(), 0);
1454
1455 let read = account
1456 .segment_ref_typed::<u64, { crate::layout::HopperHeader::SIZE as u32 }>(
1457 &mut borrows,
1458 A_TYPED,
1459 )
1460 .unwrap();
1461 assert_eq!(*read, 1337);
1462 }
1463
1464 #[test]
1465 fn const_segment_api_matches_manual_offsets() {
1466 use crate::segment::Segment;
1467
1468 let (_backing, account) = make_account(TestLayout::SIZE, 20);
1469 {
1470 let mut data = account.try_borrow_mut().unwrap();
1471 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1472 }
1473
1474 const A_SEG: Segment = Segment::body(0, 8); let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1479 {
1480 let mut a = account
1481 .segment_mut_const::<u64>(&mut borrows, A_SEG)
1482 .unwrap();
1483 *a = 7;
1484 }
1485 let read = account
1486 .segment_ref::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1487 .unwrap();
1488 assert_eq!(*read, 7);
1489 }
1490
1491 #[test]
1492 fn load_after_segment_drop_succeeds() {
1493 let (_backing, account) = make_account(TestLayout::SIZE, 12);
1494 {
1495 let mut data = account.try_borrow_mut().unwrap();
1496 crate::layout::init_header::<TestLayout>(&mut data).unwrap();
1497 }
1498
1499 let mut borrows = crate::segment_borrow::SegmentBorrowRegistry::new();
1500 {
1501 let mut seg = account
1502 .segment_mut::<u64>(&mut borrows, crate::layout::HopperHeader::SIZE as u32, 8)
1503 .unwrap();
1504 *seg = 42;
1505 }
1506 let view = account.load::<TestLayout>().unwrap();
1508 assert_eq!(view.a, 42);
1509 }
1510}