1use crate::account::AccountView;
26use crate::address::Address;
27use crate::error::ProgramError;
28
29pub struct LazyContext<'a, 'info> {
36 inner: &'a mut hopper_native::LazyContext<'info>,
37}
38
39impl<'a, 'info> LazyContext<'a, 'info> {
40 #[inline(always)]
44 pub fn from_native(inner: &'a mut hopper_native::LazyContext<'info>) -> Self {
45 Self { inner }
46 }
47
48 #[inline(always)]
51 pub fn instruction_data(&self) -> &[u8] {
52 self.inner.instruction_data()
53 }
54
55 #[inline(always)]
57 pub fn program_id(&self) -> &Address {
58 let native: &hopper_native::Address = self.inner.program_id();
59 unsafe { &*(native as *const hopper_native::Address as *const Address) }
63 }
64
65 #[inline(always)]
67 pub fn total_accounts(&self) -> usize {
68 self.inner.total_accounts()
69 }
70
71 #[inline(always)]
73 pub fn parsed_count(&self) -> usize {
74 self.inner.parsed_count()
75 }
76
77 #[inline(always)]
79 pub fn remaining(&self) -> usize {
80 self.inner.remaining()
81 }
82
83 #[inline(always)]
85 pub fn next_account(&mut self) -> Result<AccountView<'info>, ProgramError> {
86 self.inner
87 .next_account()
88 .map(AccountView::from_inner)
89 .map_err(ProgramError::from)
90 }
91
92 #[inline(always)]
94 pub fn next_signer(&mut self) -> Result<AccountView<'info>, ProgramError> {
95 self.inner
96 .next_signer()
97 .map(AccountView::from_inner)
98 .map_err(ProgramError::from)
99 }
100
101 #[inline(always)]
103 pub fn next_writable(&mut self) -> Result<AccountView<'info>, ProgramError> {
104 self.inner
105 .next_writable()
106 .map(AccountView::from_inner)
107 .map_err(ProgramError::from)
108 }
109
110 #[inline(always)]
113 pub fn next_payer(&mut self) -> Result<AccountView<'info>, ProgramError> {
114 self.inner
115 .next_payer()
116 .map(AccountView::from_inner)
117 .map_err(ProgramError::from)
118 }
119
120 #[inline(always)]
122 pub fn next_owned_by(&mut self, program: &Address) -> Result<AccountView<'info>, ProgramError> {
123 let native = unsafe { &*(program as *const Address as *const hopper_native::Address) };
125 self.inner
126 .next_owned_by(native)
127 .map(AccountView::from_inner)
128 .map_err(ProgramError::from)
129 }
130
131 #[inline(always)]
133 pub fn skip(&mut self, n: usize) -> Result<(), ProgramError> {
134 self.inner.skip(n).map_err(ProgramError::from)
135 }
136
137 #[inline(always)]
139 pub fn drain_remaining(&mut self) -> Result<&[AccountView<'info>], ProgramError> {
140 match self.inner.drain_remaining() {
141 Ok(native) => {
142 let views = unsafe {
146 core::slice::from_raw_parts(
147 native.as_ptr() as *const AccountView<'info>,
148 native.len(),
149 )
150 };
151 Ok(views)
152 }
153 Err(e) => Err(ProgramError::from(e)),
154 }
155 }
156
157 #[inline(always)]
159 pub fn get(&self, index: usize) -> Option<&AccountView<'info>> {
160 self.inner.get(index).map(|native| {
161 unsafe {
163 &*(native as *const hopper_native::AccountView<'info> as *const AccountView<'info>)
164 }
165 })
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 extern crate std;
172 use super::*;
173
174 fn build_frame(
179 count: u64,
180 data_len: usize,
181 ix_data: &[u8],
182 program_id: [u8; 32],
183 ) -> std::vec::Vec<u8> {
184 use hopper_native::RuntimeAccount;
185 let mut buf: std::vec::Vec<u8> = std::vec::Vec::new();
186 buf.extend_from_slice(&count.to_le_bytes());
187 for i in 0..count {
188 let start = buf.len();
189 let mut header = [0u8; core::mem::size_of::<RuntimeAccount>()];
191 header[0] = 0xFF;
192 let acct = RuntimeAccount {
195 borrow_state: 0xFF,
196 is_signer: (i == 0) as u8,
197 is_writable: 1,
198 executable: 0,
199 resize_delta: 0,
200 address: hopper_native::Address::new_from_array([i as u8 + 1; 32]),
201 owner: hopper_native::Address::new_from_array([9; 32]),
202 lamports: 5,
203 data_len: data_len as u64,
204 };
205 let bytes = unsafe {
207 core::slice::from_raw_parts(
208 &acct as *const RuntimeAccount as *const u8,
209 core::mem::size_of::<RuntimeAccount>(),
210 )
211 };
212 header.copy_from_slice(bytes);
213 header[0] = 0xFF;
214 buf.extend_from_slice(&header);
215 buf.extend_from_slice(&std::vec![0u8; data_len]);
216 buf.extend_from_slice(&std::vec![0u8; 10 * 1024]);
218 let advanced = buf.len() - start;
219 let pad = (8 - (advanced % 8)) % 8;
220 buf.extend_from_slice(&std::vec![0u8; pad]);
221 buf.extend_from_slice(&u64::MAX.to_le_bytes());
223 }
224 buf.extend_from_slice(&(ix_data.len() as u64).to_le_bytes());
225 buf.extend_from_slice(ix_data);
226 buf.extend_from_slice(&program_id);
227 buf
228 }
229
230 #[test]
231 fn bridge_yields_runtime_types_end_to_end() {
232 let mut frame = build_frame(2, 8, &[7, 1, 2], [42; 32]);
233 let mut native = unsafe { hopper_native::lazy::lazy_deserialize(frame.as_mut_ptr()) };
236 let mut ctx = LazyContext::from_native(&mut native);
237
238 assert_eq!(ctx.total_accounts(), 2);
239 assert_eq!(ctx.remaining(), 2);
240 assert_eq!(ctx.instruction_data(), &[7, 1, 2]);
241 assert_eq!(ctx.program_id(), &Address::new([42u8; 32]));
242
243 let first = ctx.next_signer().expect("first is a signer");
245 assert_eq!(first.address(), &Address::new([1u8; 32]));
246 assert!(first.is_signer());
247 let second = ctx.next_account().expect("second parses");
248 assert_eq!(second.address(), &Address::new([2u8; 32]));
249 assert_eq!(ctx.parsed_count(), 2);
250 assert_eq!(ctx.remaining(), 0);
251
252 assert_eq!(ctx.get(0).unwrap().address(), &Address::new([1u8; 32]));
254 assert!(ctx.get(2).is_none());
255
256 assert_eq!(
258 ctx.next_account().unwrap_err(),
259 ProgramError::NotEnoughAccountKeys,
260 "exhaustion maps through the layout-twin glue"
261 );
262 }
263
264 #[test]
265 fn signer_requirement_maps_to_the_runtime_error() {
266 let mut frame = build_frame(2, 8, &[0], [1; 32]);
267 let mut native = unsafe { hopper_native::lazy::lazy_deserialize(frame.as_mut_ptr()) };
269 let mut ctx = LazyContext::from_native(&mut native);
270 ctx.skip(1).expect("skip the signer");
271 assert_eq!(
272 ctx.next_signer().unwrap_err(),
273 ProgramError::MissingRequiredSignature
274 );
275 }
276
277 #[test]
278 fn drain_remaining_casts_the_whole_slice() {
279 let mut frame = build_frame(3, 4, &[0], [1; 32]);
280 let mut native = unsafe { hopper_native::lazy::lazy_deserialize(frame.as_mut_ptr()) };
282 let mut ctx = LazyContext::from_native(&mut native);
283 let _first = ctx.next_account().unwrap();
284 let rest = ctx.drain_remaining().expect("drains");
285 assert_eq!(rest.len(), 2);
286 assert_eq!(rest[0].address(), &Address::new([2u8; 32]));
287 assert_eq!(rest[1].address(), &Address::new([3u8; 32]));
288 }
289}