use crate::{clock::Epoch, instruction::InstructionError, pubkey::Pubkey}; use std::{ cell::{Ref, RefCell, RefMut}, cmp, fmt, iter::FromIterator, rc::Rc, }; /// An Account with data that is stored on chain #[repr(C)] #[frozen_abi(digest = "AVG6bXqVUippoYRAE8e1ewHeZi1UvFx5r5gKspFSEbxM")] #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Default, AbiExample)] #[serde(rename_all = "camelCase")] pub struct Account { /// lamports in the account pub lamports: u64, /// data held in this account #[serde(with = "serde_bytes")] pub data: Vec, /// the program that owns this account. If executable, the program that loads this account. pub owner: Pubkey, /// this account's data contains a loaded program (and is now read-only) pub executable: bool, /// the epoch at which this account will next owe rent pub rent_epoch: Epoch, } impl fmt::Debug for Account { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let data_len = cmp::min(64, self.data.len()); let data_str = if data_len > 0 { format!(" data: {}", hex::encode(self.data[..data_len].to_vec())) } else { "".to_string() }; write!( f, "Account {{ lamports: {} data.len: {} owner: {} executable: {} rent_epoch: {}{} }}", self.lamports, self.data.len(), self.owner, self.executable, self.rent_epoch, data_str, ) } } impl Account { pub fn new(lamports: u64, space: usize, owner: &Pubkey) -> Self { Self { lamports, data: vec![0u8; space], owner: *owner, ..Self::default() } } pub fn new_ref(lamports: u64, space: usize, owner: &Pubkey) -> Rc> { Rc::new(RefCell::new(Self::new(lamports, space, owner))) } pub fn new_data( lamports: u64, state: &T, owner: &Pubkey, ) -> Result { let data = bincode::serialize(state)?; Ok(Self { lamports, data, owner: *owner, ..Self::default() }) } pub fn new_ref_data( lamports: u64, state: &T, owner: &Pubkey, ) -> Result, bincode::Error> { Ok(RefCell::new(Self::new_data(lamports, state, owner)?)) } pub fn new_data_with_space( lamports: u64, state: &T, space: usize, owner: &Pubkey, ) -> Result { let mut account = Self::new(lamports, space, owner); account.serialize_data(state)?; Ok(account) } pub fn new_ref_data_with_space( lamports: u64, state: &T, space: usize, owner: &Pubkey, ) -> Result, bincode::Error> { Ok(RefCell::new(Self::new_data_with_space( lamports, state, space, owner, )?)) } pub fn deserialize_data(&self) -> Result { bincode::deserialize(&self.data) } pub fn serialize_data(&mut self, state: &T) -> Result<(), bincode::Error> { if bincode::serialized_size(state)? > self.data.len() as u64 { return Err(Box::new(bincode::ErrorKind::SizeLimit)); } bincode::serialize_into(&mut self.data[..], state) } } #[repr(C)] #[derive(Debug)] pub struct KeyedAccount<'a> { is_signer: bool, // Transaction was signed by this account's key is_writable: bool, key: &'a Pubkey, pub account: &'a RefCell, } impl<'a> KeyedAccount<'a> { pub fn signer_key(&self) -> Option<&Pubkey> { if self.is_signer { Some(self.key) } else { None } } pub fn unsigned_key(&self) -> &Pubkey { self.key } pub fn is_writable(&self) -> bool { self.is_writable } pub fn lamports(&self) -> Result { Ok(self.try_borrow()?.lamports) } pub fn data_len(&self) -> Result { Ok(self.try_borrow()?.data.len()) } pub fn data_is_empty(&self) -> Result { Ok(self.try_borrow()?.data.is_empty()) } pub fn owner(&self) -> Result { Ok(self.try_borrow()?.owner) } pub fn executable(&self) -> Result { Ok(self.try_borrow()?.executable) } pub fn rent_epoch(&self) -> Result { Ok(self.try_borrow()?.rent_epoch) } pub fn try_account_ref(&'a self) -> Result, InstructionError> { self.try_borrow() } pub fn try_account_ref_mut(&'a self) -> Result, InstructionError> { self.try_borrow_mut() } fn try_borrow(&self) -> Result, InstructionError> { self.account .try_borrow() .map_err(|_| InstructionError::AccountBorrowFailed) } fn try_borrow_mut(&self) -> Result, InstructionError> { self.account .try_borrow_mut() .map_err(|_| InstructionError::AccountBorrowFailed) } pub fn new(key: &'a Pubkey, is_signer: bool, account: &'a RefCell) -> Self { Self { is_signer, is_writable: true, key, account, } } pub fn new_readonly(key: &'a Pubkey, is_signer: bool, account: &'a RefCell) -> Self { Self { is_signer, is_writable: false, key, account, } } } impl<'a> PartialEq for KeyedAccount<'a> { fn eq(&self, other: &Self) -> bool { self.key == other.key } } impl<'a> From<(&'a Pubkey, &'a RefCell)> for KeyedAccount<'a> { fn from((key, account): (&'a Pubkey, &'a RefCell)) -> Self { Self { is_signer: false, is_writable: true, key, account, } } } impl<'a> From<(&'a Pubkey, bool, &'a RefCell)> for KeyedAccount<'a> { fn from((key, is_signer, account): (&'a Pubkey, bool, &'a RefCell)) -> Self { Self { is_signer, is_writable: true, key, account, } } } impl<'a> From<&'a (&'a Pubkey, &'a RefCell)> for KeyedAccount<'a> { fn from((key, account): &'a (&'a Pubkey, &'a RefCell)) -> Self { Self { is_signer: false, is_writable: true, key, account, } } } pub fn create_keyed_accounts<'a>( accounts: &'a [(&'a Pubkey, &'a RefCell)], ) -> Vec> { accounts.iter().map(Into::into).collect() } pub fn create_keyed_is_signer_accounts<'a>( accounts: &'a [(&'a Pubkey, bool, &'a RefCell)], ) -> Vec> { accounts .iter() .map(|(key, is_signer, account)| KeyedAccount { is_signer: *is_signer, is_writable: false, key, account, }) .collect() } pub fn create_keyed_readonly_accounts( accounts: &[(Pubkey, RefCell)], ) -> Vec { accounts .iter() .map(|(key, account)| KeyedAccount { is_signer: false, is_writable: false, key, account, }) .collect() } /// Return all the signers from a set of KeyedAccounts pub fn get_signers(keyed_accounts: &[KeyedAccount]) -> A where A: FromIterator, { keyed_accounts .iter() .filter_map(|keyed_account| keyed_account.signer_key()) .cloned() .collect::() } /// Return the next KeyedAccount or a NotEnoughAccountKeys error pub fn next_keyed_account<'a, 'b, I: Iterator>>( iter: &mut I, ) -> Result { iter.next().ok_or(InstructionError::NotEnoughAccountKeys) } /// Return true if the first keyed_account is executable, used to determine if /// the loader should call a program's 'main' pub fn is_executable(keyed_accounts: &[KeyedAccount]) -> Result { Ok(!keyed_accounts.is_empty() && keyed_accounts[0].executable()?) }