hopper_core/check/
trust.rs1use hopper_runtime::error::ProgramError;
20use hopper_runtime::{AccountView, Address, Ref};
21
22#[derive(Clone, Copy, PartialEq, Eq)]
24pub enum TrustLevel {
25 Strict,
27 Compatible,
29 Observational,
31}
32
33#[derive(Clone, Copy)]
35pub struct TrustFlags {
36 pub reject_closed: bool,
38 pub require_immutable: bool,
40 pub min_version: u8,
42}
43
44impl TrustFlags {
45 #[inline(always)]
47 pub const fn default() -> Self {
48 Self {
49 reject_closed: true,
50 require_immutable: false,
51 min_version: 0,
52 }
53 }
54
55 #[inline(always)]
57 pub const fn paranoid() -> Self {
58 Self {
59 reject_closed: true,
60 require_immutable: true,
61 min_version: 0,
62 }
63 }
64}
65
66pub struct TrustProfile<'a> {
71 pub owner: &'a Address,
73 pub layout_id: &'a [u8; 8],
75 pub size: usize,
77 pub level: TrustLevel,
79 pub flags: TrustFlags,
81}
82
83impl<'a> TrustProfile<'a> {
84 #[inline(always)]
86 pub const fn strict(owner: &'a Address, layout_id: &'a [u8; 8], size: usize) -> Self {
87 Self {
88 owner,
89 layout_id,
90 size,
91 level: TrustLevel::Strict,
92 flags: TrustFlags::default(),
93 }
94 }
95
96 #[inline(always)]
98 pub const fn compatible(owner: &'a Address, layout_id: &'a [u8; 8], min_size: usize) -> Self {
99 Self {
100 owner,
101 layout_id,
102 size: min_size,
103 level: TrustLevel::Compatible,
104 flags: TrustFlags::default(),
105 }
106 }
107
108 #[inline(always)]
110 pub const fn observational(layout_id: &'a [u8; 8]) -> Self {
111 const ZERO_ADDR: Address = Address::new_from_array([0u8; 32]);
114 Self {
115 owner: &ZERO_ADDR,
116 layout_id,
117 size: 0,
118 level: TrustLevel::Observational,
119 flags: TrustFlags {
120 reject_closed: false,
121 require_immutable: false,
122 min_version: 0,
123 },
124 }
125 }
126
127 #[inline(always)]
133 pub const fn read_only(owner: &'a Address, layout_id: &'a [u8; 8], min_size: usize) -> Self {
134 Self {
135 owner,
136 layout_id,
137 size: min_size,
138 level: TrustLevel::Compatible,
139 flags: TrustFlags {
140 reject_closed: true,
141 require_immutable: true,
142 min_version: 0,
143 },
144 }
145 }
146
147 #[inline(always)]
149 pub const fn with_min_version(mut self, v: u8) -> Self {
150 self.flags.min_version = v;
151 self
152 }
153
154 #[inline(always)]
156 pub const fn require_immutable(mut self) -> Self {
157 self.flags.require_immutable = true;
158 self
159 }
160
161 #[inline]
165 pub fn load(&self, account: &'a AccountView) -> Result<Ref<'a, [u8]>, ProgramError> {
166 if self.flags.require_immutable && account.is_writable() {
168 return Err(ProgramError::InvalidAccountData);
169 }
170
171 match self.level {
172 TrustLevel::Strict => self.load_strict(account),
173 TrustLevel::Compatible => self.load_compatible(account),
174 TrustLevel::Observational => self.load_observational(account),
175 }
176 }
177
178 #[inline]
179 fn load_strict(&self, account: &'a AccountView) -> Result<Ref<'a, [u8]>, ProgramError> {
180 if !account.owned_by(self.owner) {
182 return Err(ProgramError::IncorrectProgramId);
183 }
184 let data = account.try_borrow()?;
185 if data.len() != self.size {
187 return Err(ProgramError::AccountDataTooSmall);
188 }
189 self.check_layout_id(&data)?;
191 if self.flags.reject_closed {
193 self.check_not_closed(&data)?;
194 }
195 if self.flags.min_version > 0 {
197 self.check_min_version(&data)?;
198 }
199 Ok(data)
200 }
201
202 #[inline]
203 fn load_compatible(&self, account: &'a AccountView) -> Result<Ref<'a, [u8]>, ProgramError> {
204 if !account.owned_by(self.owner) {
205 return Err(ProgramError::IncorrectProgramId);
206 }
207 let data = account.try_borrow()?;
208 if data.len() < self.size {
210 return Err(ProgramError::AccountDataTooSmall);
211 }
212 self.check_layout_id(&data)?;
213 if self.flags.reject_closed {
214 self.check_not_closed(&data)?;
215 }
216 if self.flags.min_version > 0 {
217 self.check_min_version(&data)?;
218 }
219 Ok(data)
220 }
221
222 #[inline]
223 fn load_observational(&self, account: &'a AccountView) -> Result<Ref<'a, [u8]>, ProgramError> {
224 let data = account.try_borrow()?;
225 if data.len() < crate::account::HEADER_LEN {
226 return Err(ProgramError::AccountDataTooSmall);
227 }
228 self.check_layout_id(&data)?;
229 Ok(data)
230 }
231
232 #[inline(always)]
234 fn check_layout_id(&self, data: &[u8]) -> Result<(), ProgramError> {
235 if data.len() < 12 {
236 return Err(ProgramError::AccountDataTooSmall);
237 }
238 if data[4..12] != *self.layout_id {
239 return Err(ProgramError::InvalidAccountData);
240 }
241 Ok(())
242 }
243
244 #[inline(always)]
246 fn check_not_closed(&self, data: &[u8]) -> Result<(), ProgramError> {
247 if !data.is_empty() && data[0] == crate::account::CLOSE_SENTINEL {
248 return Err(ProgramError::InvalidAccountData);
249 }
250 Ok(())
251 }
252
253 #[inline(always)]
255 fn check_min_version(&self, data: &[u8]) -> Result<(), ProgramError> {
256 if data.len() < 2 {
257 return Err(ProgramError::AccountDataTooSmall);
258 }
259 if data[1] < self.flags.min_version {
260 return Err(ProgramError::InvalidAccountData);
261 }
262 Ok(())
263 }
264}
265
266#[inline]
270pub fn load_foreign_with_profile<'a, T: crate::account::Pod + crate::account::FixedLayout>(
271 account: &'a AccountView,
272 profile: &TrustProfile<'a>,
273) -> Result<crate::account::VerifiedAccount<'a, T>, ProgramError> {
274 let data = profile.load(account)?;
275 crate::account::VerifiedAccount::from_ref(data)
276}