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}