hopper_core/virtual_state/
mod.rs1use crate::account::{FixedLayout, Pod};
48use hopper_runtime::{error::ProgramError, AccountView, Address, Ref, RefMut};
49
50#[derive(Clone, Copy)]
54pub struct VirtualSlot {
55 pub account_index: u8,
57 pub require_owned: bool,
59 pub require_writable: bool,
61}
62
63impl VirtualSlot {
64 #[inline(always)]
66 pub const fn read_only(account_index: u8) -> Self {
67 Self {
68 account_index,
69 require_owned: true,
70 require_writable: false,
71 }
72 }
73
74 #[inline(always)]
76 pub const fn writable(account_index: u8) -> Self {
77 Self {
78 account_index,
79 require_owned: true,
80 require_writable: true,
81 }
82 }
83
84 #[inline(always)]
86 pub const fn foreign(account_index: u8) -> Self {
87 Self {
88 account_index,
89 require_owned: false,
90 require_writable: false,
91 }
92 }
93}
94
95pub struct VirtualState<const N: usize> {
101 slots: [VirtualSlot; N],
102 count: usize,
103}
104
105impl<const N: usize> VirtualState<N> {
106 #[inline(always)]
108 pub const fn new() -> Self {
109 Self {
110 slots: [VirtualSlot {
111 account_index: 0,
112 require_owned: false,
113 require_writable: false,
114 }; N],
115 count: 0,
116 }
117 }
118
119 #[inline(always)]
121 pub const fn map(mut self, slot: usize, account_index: u8) -> Self {
122 assert!(slot < N, "slot index out of bounds");
123 self.slots[slot] = VirtualSlot::read_only(account_index);
124 if slot >= self.count {
125 self.count = slot + 1;
126 }
127 self
128 }
129
130 #[inline(always)]
132 pub const fn map_mut(mut self, slot: usize, account_index: u8) -> Self {
133 assert!(slot < N, "slot index out of bounds");
134 self.slots[slot] = VirtualSlot::writable(account_index);
135 if slot >= self.count {
136 self.count = slot + 1;
137 }
138 self
139 }
140
141 #[inline(always)]
143 pub const fn map_foreign(mut self, slot: usize, account_index: u8) -> Self {
144 assert!(slot < N, "slot index out of bounds");
145 self.slots[slot] = VirtualSlot::foreign(account_index);
146 if slot >= self.count {
147 self.count = slot + 1;
148 }
149 self
150 }
151
152 #[inline(always)]
156 pub const fn set_slot(mut self, slot: usize, vs: VirtualSlot) -> Self {
157 assert!(slot < N, "slot index out of bounds");
158 self.slots[slot] = vs;
159 if slot >= self.count {
160 self.count = slot + 1;
161 }
162 self
163 }
164
165 #[inline(always)]
167 pub const fn slot_count(&self) -> usize {
168 self.count
169 }
170
171 #[inline]
175 pub fn validate(
176 &self,
177 accounts: &[AccountView],
178 program_id: &Address,
179 ) -> Result<(), ProgramError> {
180 let mut i = 0;
181 while i < self.count {
182 let slot = &self.slots[i];
183 let idx = slot.account_index as usize;
184 if idx >= accounts.len() {
185 return Err(ProgramError::NotEnoughAccountKeys);
186 }
187 let acc = &accounts[idx];
188
189 if slot.require_owned {
190 crate::check::check_owner(acc, program_id)?;
191 }
192 if slot.require_writable {
193 crate::check::check_writable(acc)?;
194 }
195 i += 1;
196 }
197 Ok(())
198 }
199
200 #[inline]
202 pub fn overlay<'a, T: Pod + FixedLayout>(
203 &self,
204 accounts: &'a [AccountView],
205 slot: usize,
206 ) -> Result<Ref<'a, T>, ProgramError> {
207 if slot >= self.count {
208 return Err(ProgramError::InvalidArgument);
209 }
210 let idx = self.slots[slot].account_index as usize;
211 if idx >= accounts.len() {
212 return Err(ProgramError::NotEnoughAccountKeys);
213 }
214 let acc = &accounts[idx];
215 unsafe { acc.raw_ref::<T>() }
218 }
219
220 #[inline]
228 #[allow(clippy::mut_from_ref)]
229 pub fn overlay_mut<'a, T: Pod + FixedLayout>(
230 &self,
231 accounts: &'a [AccountView],
232 slot: usize,
233 ) -> Result<RefMut<'a, T>, ProgramError> {
234 if slot >= self.count {
235 return Err(ProgramError::InvalidArgument);
236 }
237 let vs = &self.slots[slot];
238 if !vs.require_writable {
239 return Err(ProgramError::InvalidArgument);
240 }
241 let idx = vs.account_index as usize;
242 if idx >= accounts.len() {
243 return Err(ProgramError::NotEnoughAccountKeys);
244 }
245 let acc = &accounts[idx];
246 unsafe { acc.raw_mut::<T>() }
249 }
250
251 #[inline]
253 pub fn data<'a>(
254 &self,
255 accounts: &'a [AccountView],
256 slot: usize,
257 ) -> Result<Ref<'a, [u8]>, ProgramError> {
258 if slot >= self.count {
259 return Err(ProgramError::InvalidArgument);
260 }
261 let idx = self.slots[slot].account_index as usize;
262 if idx >= accounts.len() {
263 return Err(ProgramError::NotEnoughAccountKeys);
264 }
265 accounts[idx].try_borrow()
266 }
267
268 #[inline]
270 pub fn account<'a>(
271 &self,
272 accounts: &'a [AccountView],
273 slot: usize,
274 ) -> Result<&'a AccountView, ProgramError> {
275 if slot >= self.count {
276 return Err(ProgramError::InvalidArgument);
277 }
278 let idx = self.slots[slot].account_index as usize;
279 accounts.get(idx).ok_or(ProgramError::NotEnoughAccountKeys)
280 }
281}
282
283impl<const N: usize> Default for VirtualState<N> {
284 fn default() -> Self {
285 Self::new()
286 }
287}
288
289pub struct ShardedAccess<'a, const SHARDS: usize> {
298 accounts: &'a [AccountView],
299 shard_indices: [u8; SHARDS],
300 shard_count: usize,
301}
302
303impl<'a, const SHARDS: usize> ShardedAccess<'a, SHARDS> {
304 #[inline]
306 pub fn new(accounts: &'a [AccountView], shard_indices: &[u8]) -> Result<Self, ProgramError> {
307 if shard_indices.len() > SHARDS {
308 return Err(ProgramError::InvalidArgument);
309 }
310 let mut indices = [0u8; SHARDS];
311 let mut i = 0;
312 while i < shard_indices.len() {
313 if shard_indices[i] as usize >= accounts.len() {
314 return Err(ProgramError::NotEnoughAccountKeys);
315 }
316 indices[i] = shard_indices[i];
317 i += 1;
318 }
319 Ok(Self {
320 accounts,
321 shard_indices: indices,
322 shard_count: shard_indices.len(),
323 })
324 }
325
326 #[inline(always)]
328 pub fn shard_for_key(&self, key: &[u8]) -> usize {
329 let mut hash: u32 = 0x811c_9dc5;
331 let mut i = 0;
332 while i < key.len() {
333 hash ^= key[i] as u32;
334 hash = hash.wrapping_mul(0x0100_0193);
335 i += 1;
336 }
337 (hash as usize) % self.shard_count
338 }
339
340 #[inline]
342 pub fn shard_account(&self, shard: usize) -> Result<&'a AccountView, ProgramError> {
343 if shard >= self.shard_count {
344 return Err(ProgramError::InvalidArgument);
345 }
346 let idx = self.shard_indices[shard] as usize;
347 self.accounts
348 .get(idx)
349 .ok_or(ProgramError::NotEnoughAccountKeys)
350 }
351
352 #[inline]
354 pub fn data_for_key(&self, key: &[u8]) -> Result<Ref<'a, [u8]>, ProgramError> {
355 let shard = self.shard_for_key(key);
356 let acc = self.shard_account(shard)?;
357 acc.try_borrow()
358 }
359
360 #[inline(always)]
362 pub fn shard_count(&self) -> usize {
363 self.shard_count
364 }
365}