Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lang: Framework defined error codes #354

Merged
merged 7 commits into from
Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ incremented for features.

* lang: Allows one to use `remaining_accounts` with `CpiContext` by implementing the `ToAccountMetas` trait on `CpiContext` ([#351](https://github.com/project-serum/anchor/pull/351/files)).

### Breaking

* lang, ts: Framework defined error codes are introduced, reserving error codes 0-300 for Anchor, and 300 and up for user defined error codes ([#354](https://github.com/project-serum/anchor/pull/354)).

## [0.7.0] - 2021-05-31

### Features
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/chat/tests/chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ describe("chat", () => {
assert.ok(msg.from.equals(user));
assert.ok(data.startsWith(messages[idx]));
} else {
assert.ok(new anchor.web3.PublicKey());
assert.ok(anchor.web3.PublicKey.default);
assert.ok(
JSON.stringify(msg.data) === JSON.stringify(new Array(280).fill(0))
);
Expand Down
38 changes: 38 additions & 0 deletions examples/errors/programs/errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use anchor_lang::prelude::*;
#[program]
mod errors {
use super::*;

pub fn hello(_ctx: Context<Hello>) -> Result<()> {
Err(MyError::Hello.into())
}
Expand All @@ -17,11 +18,48 @@ mod errors {
pub fn hello_next(_ctx: Context<Hello>) -> Result<()> {
Err(MyError::HelloNext.into())
}

pub fn mut_error(_ctx: Context<MutError>) -> Result<()> {
Ok(())
}

pub fn belongs_to_error(_ctx: Context<BelongsToError>) -> Result<()> {
Ok(())
}

pub fn signer_error(_ctx: Context<SignerError>) -> Result<()> {
Ok(())
}
}

#[derive(Accounts)]
pub struct Hello {}

#[derive(Accounts)]
pub struct MutError<'info> {
#[account(mut)]
my_account: AccountInfo<'info>,
}

#[derive(Accounts)]
pub struct BelongsToError<'info> {
#[account(init, belongs_to = owner)]
my_account: ProgramAccount<'info, BelongsToAccount>,
owner: AccountInfo<'info>,
rent: Sysvar<'info, Rent>,
}

#[derive(Accounts)]
pub struct SignerError<'info> {
#[account(signer)]
my_account: AccountInfo<'info>,
}

#[account]
pub struct BelongsToAccount {
owner: Pubkey,
}

#[error]
pub enum MyError {
#[msg("This is an error message clients will automatically display")]
Expand Down
75 changes: 72 additions & 3 deletions examples/errors/tests/errors.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const assert = require("assert");
const anchor = require('@project-serum/anchor');
const { Account, Transaction, TransactionInstruction } = anchor.web3;

describe("errors", () => {
// Configure the client to use the local cluster.
Expand All @@ -16,7 +17,7 @@ describe("errors", () => {
"This is an error message clients will automatically display";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 100);
assert.equal(err.code, 300);
}
});

Expand All @@ -28,7 +29,7 @@ describe("errors", () => {
const errMsg = "HelloNoMsg";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 100 + 123);
assert.equal(err.code, 300 + 123);
}
});

Expand All @@ -40,7 +41,75 @@ describe("errors", () => {
const errMsg = "HelloNext";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 100 + 124);
assert.equal(err.code, 300 + 124);
}
});

it("Emits a mut error", async () => {
try {
const tx = await program.rpc.mutError({
accounts: {
myAccount: anchor.web3.SYSVAR_RENT_PUBKEY,
},
});
assert.ok(false);
} catch (err) {
const errMsg = "A mut constraint was violated";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 140);
}
});

it("Emits a belongs to error", async () => {
try {
const account = new Account();
const tx = await program.rpc.belongsToError({
accounts: {
myAccount: account.publicKey,
owner: anchor.web3.SYSVAR_RENT_PUBKEY,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
},
instructions: [
await program.account.belongsToAccount.createInstruction(account),
],
signers: [account],
});
assert.ok(false);
} catch (err) {
const errMsg = "A belongs_to constraint was violated";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 141);
}
});

// This test uses a raw transaction and provider instead of a program
// instance since the client won't allow one to send a transaction
// with an invalid signer account.
it("Emits a signer error", async () => {
try {
const account = new Account();
const tx = new Transaction();
tx.add(
new TransactionInstruction({
keys: [
{
pubkey: anchor.web3.SYSVAR_RENT_PUBKEY,
isWritable: false,
isSigner: false,
},
],
programId: program.programId,
data: program.coder.instruction.encode("signer_error", {}),
})
);
await program.provider.send(tx);
assert.ok(false);
} catch (err) {
const errMsg =
"Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x8e";
assert.equal(err.toString(), errMsg);
}
});
});
2 changes: 1 addition & 1 deletion examples/lockup/migrations/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module.exports = async function (provider) {
});

// Delete the default whitelist entries.
const defaultEntry = { programId: new anchor.web3.PublicKey() };
const defaultEntry = { programId: new anchor.web3.PublicKey.default };
await lockup.state.rpc.whitelistDelete(defaultEntry, {
accounts: {
authority: provider.wallet.publicKey,
Expand Down
14 changes: 7 additions & 7 deletions examples/lockup/tests/lockup.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ describe("Lockup and Registry", () => {
assert.ok(lockupAccount.authority.equals(provider.wallet.publicKey));
assert.ok(lockupAccount.whitelist.length === WHITELIST_SIZE);
lockupAccount.whitelist.forEach((e) => {
assert.ok(e.programId.equals(new anchor.web3.PublicKey()));
assert.ok(e.programId.equals(anchor.web3.PublicKey.default));
});
});

it("Deletes the default whitelisted addresses", async () => {
const defaultEntry = { programId: new anchor.web3.PublicKey() };
const defaultEntry = { programId: anchor.web3.PublicKey.default };
await lockup.state.rpc.whitelistDelete(defaultEntry, {
accounts: {
authority: provider.wallet.publicKey,
Expand Down Expand Up @@ -116,7 +116,7 @@ describe("Lockup and Registry", () => {
await lockup.state.rpc.whitelistAdd(e, { accounts });
},
(err) => {
assert.equal(err.code, 108);
assert.equal(err.code, 308);
assert.equal(err.msg, "Whitelist is full");
return true;
}
Expand Down Expand Up @@ -216,7 +216,7 @@ describe("Lockup and Registry", () => {
});
},
(err) => {
assert.equal(err.code, 107);
assert.equal(err.code, 307);
assert.equal(err.msg, "Insufficient withdrawal balance.");
return true;
}
Expand Down Expand Up @@ -389,7 +389,7 @@ describe("Lockup and Registry", () => {

assert.ok(memberAccount.registrar.equals(registrar.publicKey));
assert.ok(memberAccount.beneficiary.equals(provider.wallet.publicKey));
assert.ok(memberAccount.metadata.equals(new anchor.web3.PublicKey()));
assert.ok(memberAccount.metadata.equals(anchor.web3.PublicKey.default));
assert.equal(
JSON.stringify(memberAccount.balances),
JSON.stringify(balances)
Expand Down Expand Up @@ -781,7 +781,7 @@ describe("Lockup and Registry", () => {
(err) => {
// Solana doesn't propagate errors across CPI. So we receive the registry's error code,
// not the lockup's.
const errorCode = "custom program error: 0x78";
const errorCode = "custom program error: 0x140";
assert.ok(err.toString().split(errorCode).length === 2);
return true;
}
Expand Down Expand Up @@ -863,7 +863,7 @@ describe("Lockup and Registry", () => {
await tryEndUnstake();
},
(err) => {
assert.equal(err.code, 109);
assert.equal(err.code, 309);
assert.equal(err.msg, "The unstake timelock has not yet expired.");
return true;
}
Expand Down
14 changes: 7 additions & 7 deletions examples/zero-copy/tests/zero-copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ describe("zero-copy", () => {
assert.ok(state.authority.equals(program.provider.wallet.publicKey));
assert.ok(state.events.length === 250);
state.events.forEach((event, idx) => {
assert.ok(event.from.equals(new PublicKey()));
assert.ok(event.from.equals(PublicKey.default));
assert.ok(event.data.toNumber() === 0);
});
});

it("Updates zero copy state", async () => {
let event = {
from: new PublicKey(),
from: PublicKey.default,
data: new BN(1234),
};
await program.state.rpc.setEvent(5, event, {
Expand All @@ -44,7 +44,7 @@ describe("zero-copy", () => {
assert.ok(event.from.equals(event.from));
assert.ok(event.data.eq(event.data));
} else {
assert.ok(event.from.equals(new PublicKey()));
assert.ok(event.from.equals(PublicKey.default));
assert.ok(event.data.toNumber() === 0);
}
});
Expand Down Expand Up @@ -175,7 +175,7 @@ describe("zero-copy", () => {
const account = await program.account.eventQ.fetch(eventQ.publicKey);
assert.ok(account.events.length === 25000);
account.events.forEach((event) => {
assert.ok(event.from.equals(new PublicKey()));
assert.ok(event.from.equals(PublicKey.default));
assert.ok(event.data.toNumber() === 0);
});
});
Expand All @@ -196,7 +196,7 @@ describe("zero-copy", () => {
assert.ok(event.from.equals(program.provider.wallet.publicKey));
assert.ok(event.data.toNumber() === 48);
} else {
assert.ok(event.from.equals(new PublicKey()));
assert.ok(event.from.equals(PublicKey.default));
assert.ok(event.data.toNumber() === 0);
}
});
Expand All @@ -219,7 +219,7 @@ describe("zero-copy", () => {
assert.ok(event.from.equals(program.provider.wallet.publicKey));
assert.ok(event.data.toNumber() === 1234);
} else {
assert.ok(event.from.equals(new PublicKey()));
assert.ok(event.from.equals(PublicKey.default));
assert.ok(event.data.toNumber() === 0);
}
});
Expand All @@ -245,7 +245,7 @@ describe("zero-copy", () => {
assert.ok(event.from.equals(program.provider.wallet.publicKey));
assert.ok(event.data.toNumber() === 99);
} else {
assert.ok(event.from.equals(new PublicKey()));
assert.ok(event.from.equals(PublicKey.default));
assert.ok(event.data.toNumber() === 0);
}
});
Expand Down
20 changes: 10 additions & 10 deletions lang/attribute/account/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,11 @@ pub fn account(
impl anchor_lang::AccountDeserialize for #account_name {
fn try_deserialize(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
if buf.len() < #discriminator.len() {
return Err(ProgramError::AccountDataTooSmall);
return Err(anchor_lang::__private::ErrorCode::AccountDiscriminatorNotFound.into());
}
let given_disc = &buf[..8];
if &#discriminator != given_disc {
return Err(ProgramError::InvalidInstructionData);
return Err(anchor_lang::__private::ErrorCode::AccountDiscriminatorMismatch.into());
}
Self::try_deserialize_unchecked(buf)
}
Expand All @@ -144,32 +144,32 @@ pub fn account(

impl anchor_lang::AccountSerialize for #account_name {
fn try_serialize<W: std::io::Write>(&self, writer: &mut W) -> std::result::Result<(), ProgramError> {
writer.write_all(&#discriminator).map_err(|_| ProgramError::InvalidAccountData)?;
writer.write_all(&#discriminator).map_err(|_| anchor_lang::__private::ErrorCode::AccountDidNotSerialize)?;
AnchorSerialize::serialize(
self,
writer
)
.map_err(|_| ProgramError::InvalidAccountData)?;
.map_err(|_| anchor_lang::__private::ErrorCode::AccountDidNotSerialize)?;
Ok(())
}
}

impl anchor_lang::AccountDeserialize for #account_name {
fn try_deserialize(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
if buf.len() < #discriminator.len() {
return Err(ProgramError::AccountDataTooSmall);
return Err(anchor_lang::__private::ErrorCode::AccountDiscriminatorNotFound.into());
}
let given_disc = &buf[..8];
if &#discriminator != given_disc {
return Err(ProgramError::InvalidInstructionData);
return Err(anchor_lang::__private::ErrorCode::AccountDiscriminatorMismatch.into());
}
Self::try_deserialize_unchecked(buf)
}

fn try_deserialize_unchecked(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
let mut data: &[u8] = &buf[8..];
AnchorDeserialize::deserialize(&mut data)
.map_err(|_| ProgramError::InvalidAccountData)
.map_err(|_| anchor_lang::__private::ErrorCode::AccountDidNotDeserialize.into())
}
}

Expand Down Expand Up @@ -327,8 +327,8 @@ pub fn zero_copy(
let account_strct = parse_macro_input!(item as syn::ItemStruct);

proc_macro::TokenStream::from(quote! {
#[derive(anchor_lang::__private::ZeroCopyAccessor, Copy, Clone)]
#[repr(packed)]
#account_strct
#[derive(anchor_lang::__private::ZeroCopyAccessor, Copy, Clone)]
#[repr(packed)]
#account_strct
})
}
Loading