| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- //! Data structures to represent account information.
- #![allow(clippy::missing_safety_doc)]
- use std::{ptr::NonNull, slice::from_raw_parts_mut};
- use crate::{
- program_error::ProgramError, pubkey::Pubkey, syscalls::sol_memset_, MAX_PERMITTED_DATA_INCREASE,
- };
- /// Raw account data.
- ///
- /// This data is wrapped in an `AccountInfo` struct, which provides safe access
- /// to the data.
- #[repr(C)]
- #[derive(Clone, Copy, Default)]
- pub(crate) struct Account {
- /// Borrow state of the account data.
- ///
- /// 0) We reuse the duplicate flag for this. We set it to 0b0000_0000.
- /// 1) We use the first four bits to track state of lamport borrow
- /// 2) We use the second four bits to track state of data borrow
- ///
- /// 4 bit state: [1 bit mutable borrow flag | u3 immmutable borrow flag]
- /// This gives us up to 7 immutable borrows. Note that does not mean 7
- /// duplicate account infos, but rather 7 calls to borrow lamports or
- /// borrow data across all duplicate account infos.
- pub(crate) borrow_state: u8,
- /// Indicates whether the transaction was signed by this account.
- is_signer: u8,
- /// Indicates whether the account is writable.
- is_writable: u8,
- /// Indicates whether this account represents a program.
- executable: u8,
- /// Account's original data length when it was serialized for the
- /// current program invocation.
- ///
- /// The value of this field is lazily initialized to the current data length.
- /// On first access, the original data length will be 0. The caller should
- /// ensure that the original data length is set to the current data length for
- /// subsequence access.
- ///
- /// The value of this field is currently only used for `realloc`.
- original_data_len: [u8; 4],
- /// Public key of the account
- key: Pubkey,
- /// Program that owns this account
- owner: Pubkey,
- /// The lamports in the account. Modifiable by programs.
- lamports: u64,
- /// Length of the data.
- pub(crate) data_len: u64,
- }
- // Convenience macro to get the original data length from the account – the value will
- // be zero on first access.
- macro_rules! get_original_data_len {
- ( $self:expr ) => {
- unsafe { *(&(*$self).original_data_len as *const _ as *const u32) as usize }
- };
- }
- // Convenience macro to set the original data length in the account.
- macro_rules! set_original_data_len {
- ( $self:expr, $len:expr ) => {
- unsafe {
- *(&mut (*$self).original_data_len) = u32::to_le_bytes($len as u32);
- }
- };
- }
- /// Wrapper struct for an `Account`.
- ///
- /// This struct provides safe access to the data in an `Account`. It is also
- /// used to track borrows of the account data and lamports, given that an
- /// account can be "shared" across multiple `AccountInfo` instances.
- #[repr(C)]
- #[derive(Clone, PartialEq, Eq)]
- pub struct AccountInfo {
- /// Raw (pointer to) account data.
- ///
- /// Note that this is a pointer can be shared across multiple `AccountInfo`.
- pub(crate) raw: *mut Account,
- }
- impl AccountInfo {
- /// Public key of the account.
- #[inline(always)]
- pub fn key(&self) -> &Pubkey {
- unsafe { &(*self.raw).key }
- }
- /// Program that owns this account.
- #[inline(always)]
- pub fn owner(&self) -> &Pubkey {
- unsafe { &(*self.raw).owner }
- }
- /// Indicates whether the transaction was signed by this account.
- #[inline(always)]
- pub fn is_signer(&self) -> bool {
- unsafe { (*self.raw).is_signer != 0 }
- }
- /// Indicates whether the account is writable.
- #[inline(always)]
- pub fn is_writable(&self) -> bool {
- unsafe { (*self.raw).is_writable != 0 }
- }
- /// Indicates whether this account represents a program.
- ///
- /// Program accounts are always read-only.
- #[inline(always)]
- pub fn executable(&self) -> bool {
- unsafe { (*self.raw).executable != 0 }
- }
- /// Returns the size of the data in the account.
- #[inline(always)]
- pub fn data_len(&self) -> usize {
- unsafe { (*self.raw).data_len as usize }
- }
- /// Indicates whether the account data is empty.
- ///
- /// An account is considered empty if the data length is zero.
- #[inline(always)]
- pub fn data_is_empty(&self) -> bool {
- self.data_len() == 0
- }
- /// Changes the owner of the account.
- #[allow(invalid_reference_casting)]
- pub fn assign(&self, new_owner: &Pubkey) {
- unsafe {
- std::ptr::write_volatile(&(*self.raw).owner as *const _ as *mut Pubkey, *new_owner);
- }
- }
- /// Returns a read-only reference to the lamports in the account.
- ///
- /// # SAFETY
- ///
- /// This does not check or modify the 4-bit refcell. Useful when instruction
- /// has verified non-duplicate accounts.
- pub unsafe fn unchecked_borrow_lamports(&self) -> &u64 {
- &(*self.raw).lamports
- }
- /// Returns a mutable reference to the lamports in the account.
- ///
- /// # SAFETY
- ///
- /// This does not check or modify the 4-bit refcell. Useful when instruction
- /// has verified non-duplicate accounts.
- #[allow(clippy::mut_from_ref)]
- pub unsafe fn unchecked_borrow_mut_lamports(&self) -> &mut u64 {
- &mut (*self.raw).lamports
- }
- /// Returns a read-only reference to the data in the account.
- ///
- /// # SAFETY
- ///
- /// This does not check or modify the 4-bit refcell. Useful when instruction
- /// has verified non-duplicate accounts.
- pub unsafe fn unchecked_borrow_data(&self) -> &[u8] {
- core::slice::from_raw_parts(self.data_ptr(), self.data_len())
- }
- /// Returns a mutable reference to the data in the account.
- ///
- /// # SAFETY
- ///
- /// This does not check or modify the 4-bit refcell. Useful when instruction
- /// has verified non-duplicate accounts.
- #[allow(clippy::mut_from_ref)]
- pub unsafe fn unchecked_borrow_mut_data(&self) -> &mut [u8] {
- core::slice::from_raw_parts_mut(self.data_ptr(), self.data_len())
- }
- /// Tries to get a read-only reference to the lamport field, failing if the
- /// field is already mutable borrowed or if 7 borrows already exist.
- pub fn try_borrow_lamports(&self) -> Result<Ref<u64>, ProgramError> {
- let borrow_state = unsafe { &mut (*self.raw).borrow_state };
- // check if mutable borrow is already taken
- if *borrow_state & 0b_1000_0000 != 0 {
- return Err(ProgramError::AccountBorrowFailed);
- }
- // check if we have reached the max immutable borrow count
- if *borrow_state & 0b_0111_0000 == 0b_0111_0000 {
- return Err(ProgramError::AccountBorrowFailed);
- }
- // increment the immutable borrow count
- *borrow_state += 1 << LAMPORTS_SHIFT;
- // return the reference to lamports
- Ok(Ref {
- value: unsafe { &(*self.raw).lamports },
- state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
- borrow_shift: LAMPORTS_SHIFT,
- })
- }
- /// Tries to get a read only reference to the lamport field, failing if the field
- /// is already borrowed in any form.
- pub fn try_borrow_mut_lamports(&self) -> Result<RefMut<u64>, ProgramError> {
- let borrow_state = unsafe { &mut (*self.raw).borrow_state };
- // check if any borrow (mutable or immutable) is already taken for lamports
- if *borrow_state & 0b_1111_0000 != 0 {
- return Err(ProgramError::AccountBorrowFailed);
- }
- // set the mutable lamport borrow flag
- *borrow_state |= 0b_1000_0000;
- // return the mutable reference to lamports
- Ok(RefMut {
- value: unsafe { &mut (*self.raw).lamports },
- state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
- borrow_mask: LAMPORTS_MASK,
- })
- }
- /// Tries to get a read only reference to the data field, failing if the field
- /// is already mutable borrowed or if 7 borrows already exist.
- pub fn try_borrow_data(&self) -> Result<Ref<[u8]>, ProgramError> {
- let borrow_state = unsafe { &mut (*self.raw).borrow_state };
- // check if mutable data borrow is already taken (most significant bit
- // of the data_borrow_state)
- if *borrow_state & 0b_0000_1000 != 0 {
- return Err(ProgramError::AccountBorrowFailed);
- }
- // check if we have reached the max immutable data borrow count (7)
- if *borrow_state & 0b0111 == 0b0111 {
- return Err(ProgramError::AccountBorrowFailed);
- }
- // increment the immutable data borrow count
- *borrow_state += 1;
- // return the reference to data
- Ok(Ref {
- value: unsafe { core::slice::from_raw_parts(self.data_ptr(), self.data_len()) },
- state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
- borrow_shift: DATA_SHIFT,
- })
- }
- /// Tries to get a read only reference to the data field, failing if the field
- /// is already borrowed in any form.
- pub fn try_borrow_mut_data(&self) -> Result<RefMut<[u8]>, ProgramError> {
- let borrow_state = unsafe { &mut (*self.raw).borrow_state };
- // check if any borrow (mutable or immutable) is already taken for data
- if *borrow_state & 0b_0000_1111 != 0 {
- return Err(ProgramError::AccountBorrowFailed);
- }
- // set the mutable data borrow flag
- *borrow_state |= 0b0000_1000;
- // return the mutable reference to data
- Ok(RefMut {
- value: unsafe { from_raw_parts_mut(self.data_ptr(), self.data_len()) },
- state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
- borrow_mask: DATA_MASK,
- })
- }
- /// Realloc the account's data and optionally zero-initialize the new
- /// memory.
- ///
- /// Note: Account data can be increased within a single call by up to
- /// `solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE` bytes.
- ///
- /// Note: Memory used to grow is already zero-initialized upon program
- /// entrypoint and re-zeroing it wastes compute units. If within the same
- /// call a program reallocs from larger to smaller and back to larger again
- /// the new space could contain stale data. Pass `true` for `zero_init` in
- /// this case, otherwise compute units will be wasted re-zero-initializing.
- ///
- /// # Safety
- ///
- /// This method makes assumptions about the layout and location of memory
- /// referenced by `AccountInfo` fields. It should only be called for
- /// instances of `AccountInfo` that were created by the runtime and received
- /// in the `process_instruction` entrypoint of a program.
- pub fn realloc(&self, new_len: usize, zero_init: bool) -> Result<(), ProgramError> {
- let mut data = self.try_borrow_mut_data()?;
- let current_len = data.len();
- // return early if length hasn't changed
- if new_len == current_len {
- return Ok(());
- }
- let original_len = match get_original_data_len!(self.raw) {
- len if len > 0 => len,
- _ => {
- set_original_data_len!(self.raw, current_len);
- current_len
- }
- };
- // return early if the length increase from the original serialized data
- // length is too large and would result in an out of bounds allocation
- if new_len.saturating_sub(original_len) > MAX_PERMITTED_DATA_INCREASE {
- return Err(ProgramError::InvalidRealloc);
- }
- // realloc
- unsafe {
- let data_ptr = data.as_mut_ptr();
- // set new length in the serialized data
- *(data_ptr.offset(-8) as *mut u64) = new_len as u64;
- // recreate the local slice with the new length
- data.value = from_raw_parts_mut(data_ptr, new_len);
- }
- if zero_init {
- let len_increase = new_len.saturating_sub(current_len);
- if len_increase > 0 {
- unsafe {
- sol_memset_(
- &mut data[original_len..] as *mut _ as *mut u8,
- 0,
- len_increase as u64,
- );
- }
- }
- }
- Ok(())
- }
- /// Returns the memory address of the account data.
- fn data_ptr(&self) -> *mut u8 {
- unsafe { (self.raw as *const _ as *mut u8).add(std::mem::size_of::<Account>()) }
- }
- }
- /// Bytes to shift to get to the borrow state of lamports.
- const LAMPORTS_SHIFT: u8 = 4;
- /// Bytes to shift to get to the borrow state of data.
- const DATA_SHIFT: u8 = 0;
- /// Reference to account data or lamports with checked borrow rules.
- pub struct Ref<'a, T: ?Sized> {
- value: &'a T,
- state: NonNull<u8>,
- /// Indicates the type of borrow (lamports or data) by representing the
- /// shift amount.
- borrow_shift: u8,
- }
- impl<'a, T: ?Sized> core::ops::Deref for Ref<'a, T> {
- type Target = T;
- fn deref(&self) -> &Self::Target {
- self.value
- }
- }
- impl<'a, T: ?Sized> Drop for Ref<'a, T> {
- // decrement the immutable borrow count
- fn drop(&mut self) {
- unsafe { *self.state.as_mut() -= 1 << self.borrow_shift };
- }
- }
- /// Mask representing the mutable borrow flag for lamports.
- const LAMPORTS_MASK: u8 = 0b_0111_1111;
- /// Mask representing the mutable borrow flag for data.
- const DATA_MASK: u8 = 0b_1111_0111;
- /// Mutable reference to account data or lamports with checked borrow rules.
- pub struct RefMut<'a, T: ?Sized> {
- value: &'a mut T,
- state: NonNull<u8>,
- /// Indicates the type of borrow (lamports or data) by representing the
- /// mutable borrow mask.
- borrow_mask: u8,
- }
- impl<'a, T: ?Sized> core::ops::Deref for RefMut<'a, T> {
- type Target = T;
- fn deref(&self) -> &Self::Target {
- self.value
- }
- }
- impl<'a, T: ?Sized> core::ops::DerefMut for RefMut<'a, T> {
- fn deref_mut(&mut self) -> &mut <Self as core::ops::Deref>::Target {
- self.value
- }
- }
- impl<'a, T: ?Sized> Drop for RefMut<'a, T> {
- // unset the mutable borrow flag
- fn drop(&mut self) {
- unsafe { *self.state.as_mut() &= self.borrow_mask };
- }
- }
|