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 `ErrorCode::AccountNotInitialized`
- Add check 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 ea0f24e commit b28e40a
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 12 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
2 changes: 2 additions & 0 deletions lang/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ pub enum ErrorCode {
// 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
18 changes: 10 additions & 8 deletions lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,22 +156,24 @@ pub trait ToAccountInfo<'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
/// [`solana_program::system_instruction::SystemInstruction::CreateAccount`]
/// or [`solana_program::system_instruction::SystemInstruction::CreateAccountWithSeed`]
/// 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 and has a system program owner
/// an uninitialized account has 0 [lamports](AccountInfo::lamports) and has a [system program](solana_program::system_program) [owner](AccountInfo::owner)
fn initialized(&self) -> bool {
let account_info = self.to_account_info();
account_info.lamports() == 0 && system_program::ID.eq(account_info.owner)
}
/// Checks if account initialized by call
/// [`solana_program::system_instruction::SystemInstruction::CreateAccount`]
/// or [`solana_program::system_instruction::SystemInstruction::CreateAccountWithSeed`]
/// 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 and has a system program owner
/// 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 {
!self.initialized()
}
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: 180,

// 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 b28e40a

Please sign in to comment.