add `.with_pubkeys` and `.with_values` to `syn/../constraints.rs` for better logging (#1627)

This commit is contained in:
Paul 2022-03-16 18:23:28 -04:00 committed by GitHub
parent d871f39793
commit 7f8ca97aa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 52 deletions

View File

@ -134,10 +134,19 @@ fn generate_constraint_composite(f: &CompositeField, c: &Constraint) -> proc_mac
fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2::TokenStream {
let field = &f.ident;
let addr = &c.address;
let error = generate_custom_error(field, &c.error, quote! { ConstraintAddress });
let error = generate_custom_error(
field,
&c.error,
quote! { ConstraintAddress },
&Some(&(quote! { actual }, quote! { expected })),
);
quote! {
if #field.key() != #addr {
return #error;
{
let actual = #field.key();
let expected = #addr;
if actual != expected {
return #error;
}
}
}
}
@ -178,7 +187,7 @@ pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2:
pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::TokenStream {
let ident = &f.ident;
let error = generate_custom_error(ident, &c.error, quote! { ConstraintMut });
let error = generate_custom_error(ident, &c.error, quote! { ConstraintMut }, &None);
quote! {
if !#ident.to_account_info().is_writable {
return #error;
@ -194,10 +203,19 @@ pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macr
Ty::AccountLoader(_) => quote! {#ident.load()?},
_ => quote! {#ident},
};
let error = generate_custom_error(ident, &c.error, quote! { ConstraintHasOne });
let error = generate_custom_error(
ident,
&c.error,
quote! { ConstraintHasOne },
&Some(&(quote! { my_key }, quote! { target_key })),
);
quote! {
if #field.#target != #target.key() {
return #error;
{
let my_key = #field.#target;
let target_key = #target.key();
if my_key != target_key {
return #error;
}
}
}
}
@ -213,7 +231,7 @@ pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro
Ty::CpiAccount(_) => quote! { #ident.to_account_info() },
_ => panic!("Invalid syntax: signer cannot be specified."),
};
let error = generate_custom_error(ident, &c.error, quote! { ConstraintSigner });
let error = generate_custom_error(ident, &c.error, quote! { ConstraintSigner }, &None);
quote! {
if !#info.is_signer {
return #error;
@ -245,7 +263,7 @@ pub fn generate_constraint_literal(
pub fn generate_constraint_raw(ident: &Ident, c: &ConstraintRaw) -> proc_macro2::TokenStream {
let raw = &c.raw;
let error = generate_custom_error(ident, &c.error, quote! { ConstraintRaw });
let error = generate_custom_error(ident, &c.error, quote! { ConstraintRaw }, &None);
quote! {
if !(#raw) {
return #error;
@ -256,10 +274,19 @@ pub fn generate_constraint_raw(ident: &Ident, c: &ConstraintRaw) -> proc_macro2:
pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2::TokenStream {
let ident = &f.ident;
let owner_address = &c.owner_address;
let error = generate_custom_error(ident, &c.error, quote! { ConstraintOwner });
let error = generate_custom_error(
ident,
&c.error,
quote! { ConstraintOwner },
&Some(&(quote! { *my_owner }, quote! { owner_address })),
);
quote! {
if #ident.as_ref().owner != &#owner_address {
return #error;
{
let my_owner = #ident.as_ref().owner;
let owner_address = #owner_address;
if my_owner != &owner_address {
return #error;
}
}
}
}
@ -374,10 +401,10 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
let pa: #ty_decl = #from_account_info;
if #if_needed {
if pa.mint != #mint.key() {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenMint).with_account_name(#name_str));
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenMint).with_account_name(#name_str).with_pubkeys((pa.mint, #mint.key())));
}
if pa.owner != #owner.key() {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str));
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str).with_pubkeys((pa.owner, #owner.key())));
}
}
pa
@ -409,10 +436,10 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
let pa: #ty_decl = #from_account_info;
if #if_needed {
if pa.mint != #mint.key() {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenMint).with_account_name(#name_str));
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenMint).with_account_name(#name_str).with_pubkeys((pa.mint, #mint.key())));
}
if pa.owner != #owner.key() {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str));
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str).with_pubkeys((pa.owner, #owner.key())));
}
if pa.key() != anchor_spl::associated_token::get_associated_token_address(&#owner.key(), &#mint.key()) {
@ -471,7 +498,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority).with_account_name(#name_str));
}
if pa.decimals != #decimals {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintDecimals).with_account_name(#name_str));
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintDecimals).with_account_name(#name_str).with_values((pa.decimals, #decimals)));
}
}
pa
@ -525,11 +552,11 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
// Assert the account was created correctly.
if #if_needed {
if space != actual_field.data_len() {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSpace).with_account_name(#name_str));
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSpace).with_account_name(#name_str).with_values((space, actual_field.data_len())));
}
if actual_owner != #owner {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintOwner).with_account_name(#name_str));
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintOwner).with_account_name(#name_str).with_pubkeys((*actual_owner, *#owner)));
}
{
@ -577,10 +604,10 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
let b = c.bump.as_ref().unwrap();
quote! {
if #name.key() != __pda_address {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str));
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#name.key(), __pda_address)));
}
if __bump != #b {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str));
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_values((__bump, #b)));
}
}
}
@ -592,7 +619,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
else if c.is_init {
quote! {
if #name.key() != __pda_address {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str));
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#name.key(), __pda_address)));
}
}
}
@ -625,7 +652,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
// Check it.
if #name.key() != __pda_address {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str));
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#name.key(), __pda_address)));
}
}
}
@ -640,12 +667,17 @@ fn generate_constraint_associated_token(
let wallet_address = &c.wallet;
let spl_token_mint_address = &c.mint;
quote! {
if #name.owner != #wallet_address.key() {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str));
}
let __associated_token_address = anchor_spl::associated_token::get_associated_token_address(&#wallet_address.key(), &#spl_token_mint_address.key());
if #name.key() != __associated_token_address {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAssociated).with_account_name(#name_str));
{
let my_owner = #name.owner;
let wallet_address = #wallet_address.key();
if my_owner != wallet_address {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str).with_pubkeys((my_owner, wallet_address)));
}
let __associated_token_address = anchor_spl::associated_token::get_associated_token_address(&wallet_address, &#spl_token_mint_address.key());
let my_key = #name.key();
if my_key != __associated_token_address {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAssociated).with_account_name(#name_str).with_pubkeys((my_key, __associated_token_address)));
}
}
}
}
@ -743,14 +775,26 @@ fn generate_custom_error(
account_name: &Ident,
custom_error: &Option<Expr>,
error: proc_macro2::TokenStream,
compared_values: &Option<&(proc_macro2::TokenStream, proc_macro2::TokenStream)>,
) -> proc_macro2::TokenStream {
let account_name = account_name.to_string();
match custom_error {
let mut error = match custom_error {
Some(error) => {
quote! { Err(anchor_lang::error::Error::from(#error).with_account_name(#account_name)) }
quote! { anchor_lang::error::Error::from(#error).with_account_name(#account_name) }
}
None => {
quote! { Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::#error).with_account_name(#account_name)) }
quote! { anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::#error).with_account_name(#account_name) }
}
};
let compared_values = match compared_values {
Some((left, right)) => quote! { .with_pubkeys((#left, #right)) },
None => quote! {},
};
error.extend(compared_values);
quote! {
Err(#error)
}
}

View File

@ -2,6 +2,7 @@ const assert = require("assert");
const anchor = require("@project-serum/anchor");
const { Account, Transaction, TransactionInstruction } = anchor.web3;
const { TOKEN_PROGRAM_ID, Token } = require("@solana/spl-token");
const { Keypair } = require("@solana/web3.js");
// sleep to allow logs to come in
const sleep = (ms) =>
@ -171,26 +172,34 @@ describe("errors", () => {
});
it("Emits a has one error", async () => {
try {
const account = new Account();
const tx = await program.rpc.hasOneError({
accounts: {
myAccount: account.publicKey,
owner: anchor.web3.SYSVAR_RENT_PUBKEY,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
},
instructions: [
await program.account.hasOneAccount.createInstruction(account),
],
signers: [account],
});
assert.ok(false);
} catch (err) {
const errMsg = "A has_one constraint was violated";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 2001);
}
await withLogTest(async () => {
try {
const account = new Keypair();
const tx = await program.rpc.hasOneError({
accounts: {
myAccount: account.publicKey,
owner: anchor.web3.SYSVAR_RENT_PUBKEY,
},
// this initializes the account.owner variable with Pubkey::default
instructions: [
await program.account.hasOneAccount.createInstruction(account),
],
signers: [account],
});
assert.ok(false);
} catch (err) {
const errMsg = "A has_one constraint was violated";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 2001);
}
}, [
"Program log: AnchorError caused by account: my_account. Error Code: ConstraintHasOne. Error Number: 2001. Error Message: A has one constraint was violated.",
"Program log: Left:",
"Program log: 11111111111111111111111111111111",
"Program log: Right:",
"Program log: SysvarRent111111111111111111111111111111111",
]);
});
// This test uses a raw transaction and provider instead of a program