hopper_core/account/
header.rs1use hopper_runtime::error::ProgramError;
20
21pub const HEADER_LEN: usize = 16;
23
24pub const HEADER_FORMAT: u8 = 1;
26
27const DISC_OFFSET: usize = 0;
29const VERSION_OFFSET: usize = 1;
30const FLAGS_OFFSET: usize = 2;
31const LAYOUT_ID_OFFSET: usize = 4;
32const RESERVED_OFFSET: usize = 12;
33
34#[derive(Clone, Copy, PartialEq, Eq)]
36#[repr(C)]
37pub struct AccountHeader {
38 pub disc: u8,
39 pub version: u8,
40 pub flags: [u8; 2],
41 pub layout_id: [u8; 8],
42 pub reserved: [u8; 4],
43}
44
45const _: () = assert!(core::mem::size_of::<AccountHeader>() == HEADER_LEN);
46const _: () = assert!(core::mem::align_of::<AccountHeader>() == 1);
47
48#[cfg(feature = "hopper-native-backend")]
52unsafe impl ::hopper_runtime::__hopper_native::bytemuck::Zeroable for AccountHeader {}
53#[cfg(feature = "hopper-native-backend")]
54unsafe impl ::hopper_runtime::__hopper_native::bytemuck::Pod for AccountHeader {}
55
56unsafe impl super::Pod for AccountHeader {}
58unsafe impl ::hopper_runtime::__sealed::HopperZeroCopySealed for AccountHeader {}
60
61impl super::FixedLayout for AccountHeader {
62 const SIZE: usize = HEADER_LEN;
63}
64
65impl AccountHeader {
66 #[inline(always)]
68 pub const fn new(disc: u8, version: u8, flags: u16, layout_id: [u8; 8]) -> Self {
69 Self {
70 disc,
71 version,
72 flags: flags.to_le_bytes(),
73 layout_id,
74 reserved: [0; 4],
75 }
76 }
77
78 #[inline(always)]
80 pub const fn flags_u16(&self) -> u16 {
81 u16::from_le_bytes(self.flags)
82 }
83}
84
85#[inline(always)]
90pub fn write_header(
91 data: &mut [u8],
92 disc: u8,
93 version: u8,
94 layout_id: &[u8; 8],
95) -> Result<(), ProgramError> {
96 if data.len() < HEADER_LEN {
97 return Err(ProgramError::AccountDataTooSmall);
98 }
99 data[DISC_OFFSET] = disc;
100 data[VERSION_OFFSET] = version;
101 data[FLAGS_OFFSET..FLAGS_OFFSET + 2].copy_from_slice(&0u16.to_le_bytes());
102 data[LAYOUT_ID_OFFSET..LAYOUT_ID_OFFSET + 8].copy_from_slice(layout_id);
103 data[RESERVED_OFFSET..RESERVED_OFFSET + 4].copy_from_slice(&[0u8; 4]);
104 Ok(())
105}
106
107#[inline(always)]
109pub fn check_header(
110 data: &[u8],
111 expected_disc: u8,
112 min_version: u8,
113 layout_id: &[u8; 8],
114) -> Result<(), ProgramError> {
115 if data.len() < HEADER_LEN {
116 return Err(ProgramError::AccountDataTooSmall);
117 }
118 if data[DISC_OFFSET] != expected_disc {
119 return Err(ProgramError::InvalidAccountData);
120 }
121 if data[VERSION_OFFSET] < min_version {
122 return Err(ProgramError::InvalidAccountData);
123 }
124 if data[LAYOUT_ID_OFFSET..LAYOUT_ID_OFFSET + 8] != *layout_id {
125 return Err(ProgramError::InvalidAccountData);
126 }
127 Ok(())
128}
129
130#[inline(always)]
136pub fn read_discriminator(data: &[u8]) -> Result<u8, ProgramError> {
137 data.first()
138 .copied()
139 .ok_or(ProgramError::AccountDataTooSmall)
140}
141
142#[inline(always)]
144pub fn read_version(data: &[u8]) -> Result<u8, ProgramError> {
145 if data.len() < 2 {
146 return Err(ProgramError::AccountDataTooSmall);
147 }
148 Ok(data[VERSION_OFFSET])
149}
150
151#[inline(always)]
153pub fn read_header_flags(data: &[u8]) -> Result<u16, ProgramError> {
154 if data.len() < 4 {
155 return Err(ProgramError::AccountDataTooSmall);
156 }
157 Ok(u16::from_le_bytes([
158 data[FLAGS_OFFSET],
159 data[FLAGS_OFFSET + 1],
160 ]))
161}
162
163#[inline(always)]
165pub fn read_layout_id(data: &[u8]) -> Result<[u8; 8], ProgramError> {
166 if data.len() < 12 {
167 return Err(ProgramError::AccountDataTooSmall);
168 }
169 let mut id = [0u8; 8];
170 id.copy_from_slice(&data[LAYOUT_ID_OFFSET..LAYOUT_ID_OFFSET + 8]);
171 Ok(id)
172}