Skip to content

Commit

Permalink
lang: Add AccountNotInitialized error and checks for it
Browse files Browse the repository at this point in the history
- Add `AccountInitialized` trait
    - For `AccountInfo` implement it directly for prevent clone
- Add `ErrorCode::AccountNotInitialized`
- Add check for it in `Account::try_from` & `Account::try_from_unchecked`
- Add check in `ProgramAccount::try_from` & `ProgramAccount::try_from_unchecked`
    - Yes this type deprecated, but until it is removed from the repository, it seems to me not superfluous to add this check to it
- Add `ErrorCode::StateNotInitialized`
- Add check in `State::try_from`
- Improve documentation for `AccountInitialized` trait
- Add `AccountNotInitialized` & `StateNotInitialized` to error.ts
  • Loading branch information
cyphersnake committed Nov 15, 2021
1 parent 94de51b commit 3596b62
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 5 deletions.
6 changes: 6 additions & 0 deletions lang/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Account<'a, T
/// Deserializes the given `info` into a `Account`.
#[inline(never)]
pub fn try_from(info: &AccountInfo<'a>) -> Result<Account<'a, T>, ProgramError> {
if info.not_initialized() {
return Err(ErrorCode::AccountNotInitialized.into());
}
if info.owner != &T::owner() {
return Err(ErrorCode::AccountNotProgramOwned.into());
}
Expand All @@ -34,6 +37,9 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Account<'a, T
/// possible.
#[inline(never)]
pub fn try_from_unchecked(info: &AccountInfo<'a>) -> Result<Account<'a, T>, ProgramError> {
if info.not_initialized() {
return Err(ErrorCode::AccountNotInitialized.into());
}
if info.owner != &T::owner() {
return Err(ErrorCode::AccountNotProgramOwned.into());
}
Expand Down
14 changes: 13 additions & 1 deletion lang/src/account_info.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::error::ErrorCode;
use crate::{Accounts, AccountsExit, Key, ToAccountInfo, ToAccountInfos, ToAccountMetas};
use crate::{
AccountInitialized, Accounts, AccountsExit, Key, ToAccountInfo, ToAccountInfos, ToAccountMetas,
};
use solana_program::account_info::AccountInfo;
use solana_program::entrypoint::ProgramResult;
use solana_program::instruction::AccountMeta;
use solana_program::program_error::ProgramError;
use solana_program::pubkey::Pubkey;
use solana_program::system_program;

impl<'info> Accounts<'info> for AccountInfo<'info> {
fn try_accounts(
Expand Down Expand Up @@ -56,3 +59,12 @@ impl<'info> Key for AccountInfo<'info> {
*self.key
}
}

impl<'info> AccountInitialized<'info> for AccountInfo<'info> {
fn initialized(&self) -> bool {
self.lamports() == 0 && system_program::ID.eq(self.owner)
}
fn not_initialized(&self) -> bool {
!self.initialized()
}
}
4 changes: 4 additions & 0 deletions lang/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,14 @@ pub enum ErrorCode {
AccountNotSigner,
#[msg("The given account is not owned by the system program")]
AccountNotSystemOwned,
#[msg("The program expected this account to be already initialized")]
AccountNotInitialized,

// State.
#[msg("The given state account does not have the correct address")]
StateInvalidAddress = 180,
#[msg("The given state account does not initialized")]
StateNotInitialized,

// Used for APIs that shouldn't be used anymore.
#[msg("The API being used is deprecated and should no longer be used")]
Expand Down
25 changes: 25 additions & 0 deletions lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,31 @@ pub trait ToAccountInfo<'info> {
fn to_account_info(&self) -> AccountInfo<'info>;
}

/// Allows you to check if a given account is initialized or not
pub trait AccountInitialized<'info>: ToAccountInfo<'info> {
/// Checks if account initialized by call
/// [`CreateAccount`](solana_program::system_instruction::SystemInstruction::CreateAccount)
/// or [CreateAccountWithSeed](`solana_program::system_instruction::SystemInstruction::CreateAccountWithSeed`)
/// solana system instructions
///
/// According to the [documentation](https://docs.solana.com/developing/programming-model/accounts#creating),
/// an uninitialized account has 0 [lamports](AccountInfo::lamports) and has a [system program](solana_program::system_program) [owner](AccountInfo::owner)
fn initialized(&self) -> bool {
!self.not_initialized()
}
/// Checks if account not initialized by call
/// [`CreateAccount`](solana_program::system_instruction::SystemInstruction::CreateAccount)
/// or [CreateAccountWithSeed](`solana_program::system_instruction::SystemInstruction::CreateAccountWithSeed`)
/// solana system instructions
///
/// According to the [documentation](https://docs.solana.com/developing/programming-model/accounts#creating),
/// an uninitialized account has 0 [lamports](AccountInfo::lamports) and has a [system program](solana_program::system_program) [owner](AccountInfo::owner)
fn not_initialized(&self) -> bool {
let account_info = self.to_account_info();
account_info.lamports() == 0 && system_program::ID.eq(account_info.owner)
}
}

/// A data structure that can be serialized and stored into account storage,
/// i.e. an
/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html#structfield.data)'s
Expand Down
10 changes: 8 additions & 2 deletions lang/src/program_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::error::ErrorCode;
#[allow(deprecated)]
use crate::CpiAccount;
use crate::{
AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key,
ToAccountInfo, ToAccountInfos, ToAccountMetas,
AccountDeserialize, AccountInitialized, AccountSerialize, Accounts, AccountsClose,
AccountsExit, Key, ToAccountInfo, ToAccountInfos, ToAccountMetas,
};
use solana_program::account_info::AccountInfo;
use solana_program::entrypoint::ProgramResult;
Expand Down Expand Up @@ -40,6 +40,9 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramAccount<'a, T>
program_id: &Pubkey,
info: &AccountInfo<'a>,
) -> Result<ProgramAccount<'a, T>, ProgramError> {
if info.not_initialized() {
return Err(ErrorCode::AccountNotInitialized.into());
}
if info.owner != program_id {
return Err(ErrorCode::AccountNotProgramOwned.into());
}
Expand All @@ -58,6 +61,9 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramAccount<'a, T>
program_id: &Pubkey,
info: &AccountInfo<'a>,
) -> Result<ProgramAccount<'a, T>, ProgramError> {
if info.not_initialized() {
return Err(ErrorCode::AccountNotInitialized.into());
}
if info.owner != program_id {
return Err(ErrorCode::AccountNotProgramOwned.into());
}
Expand Down
7 changes: 5 additions & 2 deletions lang/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::error::ErrorCode;
#[allow(deprecated)]
use crate::CpiAccount;
use crate::{
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, ToAccountInfo,
ToAccountInfos, ToAccountMetas,
AccountDeserialize, AccountInitialized, AccountSerialize, Accounts, AccountsExit, Key,
ToAccountInfo, ToAccountInfos, ToAccountMetas,
};
use solana_program::account_info::AccountInfo;
use solana_program::entrypoint::ProgramResult;
Expand Down Expand Up @@ -42,6 +42,9 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramState<'a, T> {
program_id: &Pubkey,
info: &AccountInfo<'a>,
) -> Result<ProgramState<'a, T>, ProgramError> {
if info.not_initialized() {
return Err(ErrorCode::StateNotInitialized.into());
}
if info.owner != program_id {
return Err(ErrorCode::AccountNotProgramOwned.into());
}
Expand Down
10 changes: 10 additions & 0 deletions ts/src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,11 @@ const LangErrorCode = {
InvalidProgramExecutable: 169,
AccountNotSigner: 170,
AccountNotSystemOwned: 171,
AccountNotInitialized: 172,

// State.
StateInvalidAddress: 180,
StateNotInitialized: 181,

// Used for APIs that shouldn't be used anymore.
Deprecated: 299,
Expand Down Expand Up @@ -175,12 +177,20 @@ const LangErrorMessage = new Map([
LangErrorCode.AccountNotSystemOwned,
"The given account is not owned by the system program",
],
[
LangErrorCode.AccountNotInitialized,
"The program expected this account to be already initialized",
],

// State.
[
LangErrorCode.StateInvalidAddress,
"The given state account does not have the correct address",
],
[
LangErrorCode.StateNotInitialized,
"The given state account does not initialized",
],

// Misc.
[
Expand Down

0 comments on commit 3596b62

Please sign in to comment.