From 2cbd973a6d2d2d4e5661f6a6d2cbdf2ab9a9e753 Mon Sep 17 00:00:00 2001 From: Mikhail Gorbachev Date: Mon, 15 Nov 2021 16:35:52 +0300 Subject: [PATCH] lang: Add `AccountNotInitialized` error --- CHANGELOG.md | 6 +++++- lang/src/account.rs | 6 ++++++ lang/src/error.rs | 2 ++ tests/errors/programs/errors/src/lib.rs | 14 +++++++++++++- tests/errors/tests/errors.js | 15 +++++++++++++++ ts/src/error.ts | 5 +++++ 6 files changed, 46 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82c9bf6117..a5ec6774a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,11 @@ incremented for features. ### Fixes -lang: Add `deprecated` attribute to `ProgramAccount` ([#1014](https://github.com/project-serum/anchor/pull/1014)). +* lang: Add `deprecated` attribute to `ProgramAccount` ([#1014](https://github.com/project-serum/anchor/pull/1014)). + +### Features + +* lang: Add `ErrorCode::AccountNotInitialized` error to separate the situation when the account has the wrong owner from when it does not exist (#[1024](https://github.com/project-serum/anchor/pull/1024)) ## [0.18.2] - 2021-11-14 diff --git a/lang/src/account.rs b/lang/src/account.rs index ba0c763365..183aacc072 100644 --- a/lang/src/account.rs +++ b/lang/src/account.rs @@ -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, ProgramError> { + if info.owner == &system_program::ID && info.lamports() == 0 { + return Err(ErrorCode::AccountNotInitialized.into()); + } if info.owner != &T::owner() { return Err(ErrorCode::AccountNotProgramOwned.into()); } @@ -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, ProgramError> { + if info.owner == &system_program::ID && info.lamports() == 0 { + return Err(ErrorCode::AccountNotInitialized.into()); + } if info.owner != &T::owner() { return Err(ErrorCode::AccountNotProgramOwned.into()); } diff --git a/lang/src/error.rs b/lang/src/error.rs index 539ec272de..93086d2038 100644 --- a/lang/src/error.rs +++ b/lang/src/error.rs @@ -74,6 +74,8 @@ 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")] diff --git a/tests/errors/programs/errors/src/lib.rs b/tests/errors/programs/errors/src/lib.rs index 1c13cd9783..7270987c5e 100644 --- a/tests/errors/programs/errors/src/lib.rs +++ b/tests/errors/programs/errors/src/lib.rs @@ -36,6 +36,10 @@ mod errors { pub fn raw_custom_error(_ctx: Context) -> Result<()> { Ok(()) } + + pub fn account_not_initialized_error(_ctx: Context) -> Result<()> { + Ok(()) + } } #[derive(Accounts)] @@ -68,7 +72,15 @@ pub struct HasOneAccount { #[derive(Accounts)] pub struct RawCustomError<'info> { #[account(constraint = *my_account.key == ID @ MyError::HelloCustom)] - my_account: AccountInfo<'info> + my_account: AccountInfo<'info>, +} + +#[account] +pub struct AnyAccount {} + +#[derive(Accounts)] +pub struct AccountNotInitializedError<'info> { + not_initialized_account: Account<'info, AnyAccount>, } #[error] diff --git a/tests/errors/tests/errors.js b/tests/errors/tests/errors.js index b5d6248b1b..dcd371e2eb 100644 --- a/tests/errors/tests/errors.js +++ b/tests/errors/tests/errors.js @@ -128,4 +128,19 @@ describe("errors", () => { assert.equal(err.code, 300 + 125); } }); + + it("Emits a account not initialized error", async () => { + try { + const tx = await program.rpc.accountNotInitializedError({ + accounts: { + notInitializedAccount: (new anchor.web3.Keypair()).publicKey + }, + }); + assert.ok(false); + } catch (err) { + const errMsg = + "Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0xac"; + assert.equal(err.toString(), errMsg); + } + }); }); diff --git a/ts/src/error.ts b/ts/src/error.ts index 932ce3415e..b27a31926d 100644 --- a/ts/src/error.ts +++ b/ts/src/error.ts @@ -89,6 +89,7 @@ const LangErrorCode = { InvalidProgramExecutable: 169, AccountNotSigner: 170, AccountNotSystemOwned: 171, + AccountNotInitialized: 172, // State. StateInvalidAddress: 180, @@ -175,6 +176,10 @@ 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. [