nitrate_program/account_info.rs
1// Copyright (c) 2024 nifty-oss maintainers
2// Copyright (c) 2024 Magnetar Fields
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Data structures to represent account information.
17
18#![allow(clippy::missing_safety_doc)]
19
20use solana_program::{
21 entrypoint::MAX_PERMITTED_DATA_INCREASE, program_error::ProgramError,
22 program_memory::sol_memset, pubkey::Pubkey,
23};
24use std::{ptr::NonNull, slice::from_raw_parts_mut};
25
26/// Raw account data.
27///
28/// This data is wrapped in an `AccountInfo` struct, which provides safe access
29/// to the data.
30#[repr(C)]
31#[derive(Clone, Copy, Default)]
32pub(crate) struct Account {
33 /// Borrow state of the account data.
34 ///
35 /// 0) We reuse the duplicate flag for this. We set it to 0b0000_0000.
36 /// 1) We use the first four bits to track state of lamport borrow
37 /// 2) We use the second four bits to track state of data borrow
38 ///
39 /// 4 bit state: [1 bit mutable borrow flag | u3 immmutable borrow flag]
40 /// This gives us up to 7 immutable borrows. Note that does not mean 7
41 /// duplicate account infos, but rather 7 calls to borrow lamports or
42 /// borrow data across all duplicate account infos.
43 pub(crate) borrow_state: u8,
44
45 /// Indicates whether the transaction was signed by this account.
46 is_signer: u8,
47
48 /// Indicates whether the account is writable.
49 is_writable: u8,
50
51 /// Indicates whether this account represents a program.
52 executable: u8,
53
54 /// Account's original data length when it was serialized for the
55 /// current program invocation.
56 ///
57 /// The value of this field is lazily initialized to the current data length.
58 /// On first access, the original data length will be 0. The caller should
59 /// ensure that the original data length is set to the current data length for
60 /// subsequence access.
61 ///
62 /// The value of this field is currently only used for `realloc`.
63 original_data_len: [u8; 4],
64
65 /// Public key of the account
66 key: Pubkey,
67
68 /// Program that owns this account
69 owner: Pubkey,
70
71 /// The lamports in the account. Modifiable by programs.
72 lamports: u64,
73
74 /// Length of the data.
75 pub(crate) data_len: u64,
76}
77
78// Convenience macro to get the original data length from the account – the value will
79// be zero on first access.
80macro_rules! get_original_data_len {
81 ( $self:expr ) => {
82 unsafe { *(&(*$self).original_data_len as *const _ as *const u32) as usize }
83 };
84}
85
86// Convenience macro to set the original data length in the account.
87macro_rules! set_original_data_len {
88 ( $self:expr, $len:expr ) => {
89 unsafe {
90 *(&mut (*$self).original_data_len) = u32::to_le_bytes($len as u32);
91 }
92 };
93}
94
95/// Wrapper struct for an `Account`.
96///
97/// This struct provides safe access to the data in an `Account`. It is also
98/// used to track borrows of the account data and lamports, given that an
99/// account can be "shared" across multiple `AccountInfo` instances.
100#[repr(C)]
101#[derive(Clone, PartialEq, Eq)]
102pub struct AccountInfo {
103 /// Raw (pointer to) account data.
104 ///
105 /// Note that this is a pointer can be shared across multiple `AccountInfo`.
106 pub(crate) raw: *mut Account,
107}
108
109impl AccountInfo {
110 /// Public key of the account.
111 #[inline(always)]
112 pub fn key(&self) -> &Pubkey {
113 unsafe { &(*self.raw).key }
114 }
115
116 /// Program that owns this account.
117 #[inline(always)]
118 pub fn owner(&self) -> &Pubkey {
119 unsafe { &(*self.raw).owner }
120 }
121
122 /// Indicates whether the transaction was signed by this account.
123 #[inline(always)]
124 pub fn is_signer(&self) -> bool {
125 unsafe { (*self.raw).is_signer != 0 }
126 }
127
128 /// Indicates whether the account is writable.
129 #[inline(always)]
130 pub fn is_writable(&self) -> bool {
131 unsafe { (*self.raw).is_writable != 0 }
132 }
133
134 /// Indicates whether this account represents a program.
135 ///
136 /// Program accounts are always read-only.
137 #[inline(always)]
138 pub fn executable(&self) -> bool {
139 unsafe { (*self.raw).executable != 0 }
140 }
141
142 /// Returns the size of the data in the account.
143 #[inline(always)]
144 pub fn data_len(&self) -> usize {
145 unsafe { (*self.raw).data_len as usize }
146 }
147
148 /// Indicates whether the account data is empty.
149 ///
150 /// An account is considered empty if the data length is zero.
151 #[inline(always)]
152 pub fn data_is_empty(&self) -> bool {
153 self.data_len() == 0
154 }
155
156 /// Changes the owner of the account.
157 #[rustversion::attr(since(1.72), allow(invalid_reference_casting))]
158 pub fn assign(&self, new_owner: &Pubkey) {
159 unsafe {
160 std::ptr::write_volatile(
161 &(*self.raw).owner as *const Pubkey as *mut [u8; 32],
162 new_owner.to_bytes(),
163 );
164 }
165 }
166
167 /// Returns a read-only reference to the lamports in the account.
168 ///
169 /// # SAFETY
170 ///
171 /// This does not check or modify the 4-bit refcell. Useful when instruction
172 /// has verified non-duplicate accounts.
173 pub unsafe fn unchecked_borrow_lamports(&self) -> &u64 {
174 &(*self.raw).lamports
175 }
176
177 /// Returns a mutable reference to the lamports in the account.
178 ///
179 /// # SAFETY
180 ///
181 /// This does not check or modify the 4-bit refcell. Useful when instruction
182 /// has verified non-duplicate accounts.
183 #[allow(clippy::mut_from_ref)]
184 pub unsafe fn unchecked_borrow_mut_lamports(&self) -> &mut u64 {
185 &mut (*self.raw).lamports
186 }
187
188 /// Returns a read-only reference to the data in the account.
189 ///
190 /// # SAFETY
191 ///
192 /// This does not check or modify the 4-bit refcell. Useful when instruction
193 /// has verified non-duplicate accounts.
194 pub unsafe fn unchecked_borrow_data(&self) -> &[u8] {
195 core::slice::from_raw_parts(self.data_ptr(), self.data_len())
196 }
197
198 /// Returns a mutable reference to the data in the account.
199 ///
200 /// # SAFETY
201 ///
202 /// This does not check or modify the 4-bit refcell. Useful when instruction
203 /// has verified non-duplicate accounts.
204 #[allow(clippy::mut_from_ref)]
205 pub unsafe fn unchecked_borrow_mut_data(&self) -> &mut [u8] {
206 core::slice::from_raw_parts_mut(self.data_ptr(), self.data_len())
207 }
208
209 /// Tries to get a read-only reference to the lamport field, failing if the
210 /// field is already mutable borrowed or if 7 borrows already exist.
211 pub fn try_borrow_lamports(&self) -> Result<Ref<u64>, ProgramError> {
212 let borrow_state = unsafe { &mut (*self.raw).borrow_state };
213
214 // check if mutable borrow is already taken
215 if *borrow_state & 0b_1000_0000 != 0 {
216 return Err(ProgramError::AccountBorrowFailed);
217 }
218
219 // check if we have reached the max immutable borrow count
220 if *borrow_state & 0b_0111_0000 == 0b_0111_0000 {
221 return Err(ProgramError::AccountBorrowFailed);
222 }
223
224 // increment the immutable borrow count
225 *borrow_state += 1 << LAMPORTS_SHIFT;
226
227 // return the reference to lamports
228 Ok(Ref {
229 value: unsafe { &(*self.raw).lamports },
230 state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
231 borrow_shift: LAMPORTS_SHIFT,
232 })
233 }
234
235 /// Tries to get a read only reference to the lamport field, failing if the field
236 /// is already borrowed in any form.
237 pub fn try_borrow_mut_lamports(&self) -> Result<RefMut<u64>, ProgramError> {
238 let borrow_state = unsafe { &mut (*self.raw).borrow_state };
239
240 // check if any borrow (mutable or immutable) is already taken for lamports
241 if *borrow_state & 0b_1111_0000 != 0 {
242 return Err(ProgramError::AccountBorrowFailed);
243 }
244
245 // set the mutable lamport borrow flag
246 *borrow_state |= 0b_1000_0000;
247
248 // return the mutable reference to lamports
249 Ok(RefMut {
250 value: unsafe { &mut (*self.raw).lamports },
251 state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
252 borrow_mask: LAMPORTS_MASK,
253 })
254 }
255
256 /// Tries to get a read only reference to the data field, failing if the field
257 /// is already mutable borrowed or if 7 borrows already exist.
258 pub fn try_borrow_data(&self) -> Result<Ref<[u8]>, ProgramError> {
259 let borrow_state = unsafe { &mut (*self.raw).borrow_state };
260
261 // check if mutable data borrow is already taken (most significant bit
262 // of the data_borrow_state)
263 if *borrow_state & 0b_0000_1000 != 0 {
264 return Err(ProgramError::AccountBorrowFailed);
265 }
266
267 // check if we have reached the max immutable data borrow count (7)
268 if *borrow_state & 0b0111 == 0b0111 {
269 return Err(ProgramError::AccountBorrowFailed);
270 }
271
272 // increment the immutable data borrow count
273 *borrow_state += 1;
274
275 // return the reference to data
276 Ok(Ref {
277 value: unsafe { core::slice::from_raw_parts(self.data_ptr(), self.data_len()) },
278 state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
279 borrow_shift: DATA_SHIFT,
280 })
281 }
282
283 /// Tries to get a read only reference to the data field, failing if the field
284 /// is already borrowed in any form.
285 pub fn try_borrow_mut_data(&self) -> Result<RefMut<[u8]>, ProgramError> {
286 let borrow_state = unsafe { &mut (*self.raw).borrow_state };
287
288 // check if any borrow (mutable or immutable) is already taken for data
289 if *borrow_state & 0b_0000_1111 != 0 {
290 return Err(ProgramError::AccountBorrowFailed);
291 }
292
293 // set the mutable data borrow flag
294 *borrow_state |= 0b0000_1000;
295
296 // return the mutable reference to data
297 Ok(RefMut {
298 value: unsafe { from_raw_parts_mut(self.data_ptr(), self.data_len()) },
299 state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
300 borrow_mask: DATA_MASK,
301 })
302 }
303
304 /// Realloc the account's data and optionally zero-initialize the new
305 /// memory.
306 ///
307 /// Note: Account data can be increased within a single call by up to
308 /// `solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE` bytes.
309 ///
310 /// Note: Memory used to grow is already zero-initialized upon program
311 /// entrypoint and re-zeroing it wastes compute units. If within the same
312 /// call a program reallocs from larger to smaller and back to larger again
313 /// the new space could contain stale data. Pass `true` for `zero_init` in
314 /// this case, otherwise compute units will be wasted re-zero-initializing.
315 ///
316 /// # Safety
317 ///
318 /// This method makes assumptions about the layout and location of memory
319 /// referenced by `AccountInfo` fields. It should only be called for
320 /// instances of `AccountInfo` that were created by the runtime and received
321 /// in the `process_instruction` entrypoint of a program.
322 pub fn realloc(&self, new_len: usize, zero_init: bool) -> Result<(), ProgramError> {
323 let mut data = self.try_borrow_mut_data()?;
324 let current_len = data.len();
325
326 // return early if length hasn't changed
327 if new_len == current_len {
328 return Ok(());
329 }
330
331 let original_len = match get_original_data_len!(self.raw) {
332 len if len > 0 => len,
333 _ => {
334 set_original_data_len!(self.raw, current_len);
335 current_len
336 }
337 };
338
339 // return early if the length increase from the original serialized data
340 // length is too large and would result in an out of bounds allocation
341 if new_len.saturating_sub(original_len) > MAX_PERMITTED_DATA_INCREASE {
342 return Err(ProgramError::InvalidRealloc);
343 }
344
345 // realloc
346 unsafe {
347 let data_ptr = data.as_mut_ptr();
348 // set new length in the serialized data
349 *(data_ptr.offset(-8) as *mut u64) = new_len as u64;
350 // recreate the local slice with the new length
351 data.value = from_raw_parts_mut(data_ptr, new_len);
352 }
353
354 if zero_init {
355 let len_increase = new_len.saturating_sub(current_len);
356 if len_increase > 0 {
357 sol_memset(&mut data[original_len..], 0, len_increase);
358 }
359 }
360
361 Ok(())
362 }
363
364 /// Returns the memory address of the account data.
365 fn data_ptr(&self) -> *mut u8 {
366 unsafe { (self.raw as *const _ as *mut u8).add(std::mem::size_of::<Account>()) }
367 }
368}
369
370/// Bytes to shift to get to the borrow state of lamports.
371const LAMPORTS_SHIFT: u8 = 4;
372
373/// Bytes to shift to get to the borrow state of data.
374const DATA_SHIFT: u8 = 0;
375
376/// Reference to account data or lamports with checked borrow rules.
377pub struct Ref<'a, T: ?Sized> {
378 value: &'a T,
379 state: NonNull<u8>,
380 /// Indicates the type of borrow (lamports or data) by representing the
381 /// shift amount.
382 borrow_shift: u8,
383}
384
385impl<'a, T: ?Sized> core::ops::Deref for Ref<'a, T> {
386 type Target = T;
387 fn deref(&self) -> &Self::Target {
388 self.value
389 }
390}
391
392impl<'a, T: ?Sized> Drop for Ref<'a, T> {
393 // decrement the immutable borrow count
394 fn drop(&mut self) {
395 unsafe { *self.state.as_mut() -= 1 << self.borrow_shift };
396 }
397}
398
399/// Mask representing the mutable borrow flag for lamports.
400const LAMPORTS_MASK: u8 = 0b_0111_1111;
401
402/// Mask representing the mutable borrow flag for data.
403const DATA_MASK: u8 = 0b_1111_0111;
404
405/// Mutable reference to account data or lamports with checked borrow rules.
406pub struct RefMut<'a, T: ?Sized> {
407 value: &'a mut T,
408 state: NonNull<u8>,
409 /// Indicates the type of borrow (lamports or data) by representing the
410 /// mutable borrow mask.
411 borrow_mask: u8,
412}
413
414impl<'a, T: ?Sized> core::ops::Deref for RefMut<'a, T> {
415 type Target = T;
416 fn deref(&self) -> &Self::Target {
417 self.value
418 }
419}
420impl<'a, T: ?Sized> core::ops::DerefMut for RefMut<'a, T> {
421 fn deref_mut(&mut self) -> &mut <Self as core::ops::Deref>::Target {
422 self.value
423 }
424}
425
426impl<'a, T: ?Sized> Drop for RefMut<'a, T> {
427 // unset the mutable borrow flag
428 fn drop(&mut self) {
429 unsafe { *self.state.as_mut() &= self.borrow_mask };
430 }
431}