add `.with_pubkeys` and `.with_values` to `syn/../constraints.rs` for better logging (#1627)
This commit is contained in:
parent
d871f39793
commit
7f8ca97aa8
|
@ -134,12 +134,21 @@ fn generate_constraint_composite(f: &CompositeField, c: &Constraint) -> proc_mac
|
||||||
fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2::TokenStream {
|
fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2::TokenStream {
|
||||||
let field = &f.ident;
|
let field = &f.ident;
|
||||||
let addr = &c.address;
|
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! {
|
quote! {
|
||||||
if #field.key() != #addr {
|
{
|
||||||
|
let actual = #field.key();
|
||||||
|
let expected = #addr;
|
||||||
|
if actual != expected {
|
||||||
return #error;
|
return #error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
|
pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
|
||||||
|
@ -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 {
|
pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::TokenStream {
|
||||||
let ident = &f.ident;
|
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! {
|
quote! {
|
||||||
if !#ident.to_account_info().is_writable {
|
if !#ident.to_account_info().is_writable {
|
||||||
return #error;
|
return #error;
|
||||||
|
@ -194,12 +203,21 @@ pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macr
|
||||||
Ty::AccountLoader(_) => quote! {#ident.load()?},
|
Ty::AccountLoader(_) => quote! {#ident.load()?},
|
||||||
_ => quote! {#ident},
|
_ => 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! {
|
quote! {
|
||||||
if #field.#target != #target.key() {
|
{
|
||||||
|
let my_key = #field.#target;
|
||||||
|
let target_key = #target.key();
|
||||||
|
if my_key != target_key {
|
||||||
return #error;
|
return #error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro2::TokenStream {
|
pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro2::TokenStream {
|
||||||
|
@ -213,7 +231,7 @@ pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro
|
||||||
Ty::CpiAccount(_) => quote! { #ident.to_account_info() },
|
Ty::CpiAccount(_) => quote! { #ident.to_account_info() },
|
||||||
_ => panic!("Invalid syntax: signer cannot be specified."),
|
_ => 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! {
|
quote! {
|
||||||
if !#info.is_signer {
|
if !#info.is_signer {
|
||||||
return #error;
|
return #error;
|
||||||
|
@ -245,7 +263,7 @@ pub fn generate_constraint_literal(
|
||||||
|
|
||||||
pub fn generate_constraint_raw(ident: &Ident, c: &ConstraintRaw) -> proc_macro2::TokenStream {
|
pub fn generate_constraint_raw(ident: &Ident, c: &ConstraintRaw) -> proc_macro2::TokenStream {
|
||||||
let raw = &c.raw;
|
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! {
|
quote! {
|
||||||
if !(#raw) {
|
if !(#raw) {
|
||||||
return #error;
|
return #error;
|
||||||
|
@ -256,12 +274,21 @@ pub fn generate_constraint_raw(ident: &Ident, c: &ConstraintRaw) -> proc_macro2:
|
||||||
pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2::TokenStream {
|
pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2::TokenStream {
|
||||||
let ident = &f.ident;
|
let ident = &f.ident;
|
||||||
let owner_address = &c.owner_address;
|
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! {
|
quote! {
|
||||||
if #ident.as_ref().owner != &#owner_address {
|
{
|
||||||
|
let my_owner = #ident.as_ref().owner;
|
||||||
|
let owner_address = #owner_address;
|
||||||
|
if my_owner != &owner_address {
|
||||||
return #error;
|
return #error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_constraint_rent_exempt(
|
pub fn generate_constraint_rent_exempt(
|
||||||
|
@ -374,10 +401,10 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
||||||
let pa: #ty_decl = #from_account_info;
|
let pa: #ty_decl = #from_account_info;
|
||||||
if #if_needed {
|
if #if_needed {
|
||||||
if pa.mint != #mint.key() {
|
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() {
|
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
|
pa
|
||||||
|
@ -409,10 +436,10 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
||||||
let pa: #ty_decl = #from_account_info;
|
let pa: #ty_decl = #from_account_info;
|
||||||
if #if_needed {
|
if #if_needed {
|
||||||
if pa.mint != #mint.key() {
|
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() {
|
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()) {
|
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));
|
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority).with_account_name(#name_str));
|
||||||
}
|
}
|
||||||
if pa.decimals != #decimals {
|
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
|
pa
|
||||||
|
@ -525,11 +552,11 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
||||||
// Assert the account was created correctly.
|
// Assert the account was created correctly.
|
||||||
if #if_needed {
|
if #if_needed {
|
||||||
if space != actual_field.data_len() {
|
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 {
|
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();
|
let b = c.bump.as_ref().unwrap();
|
||||||
quote! {
|
quote! {
|
||||||
if #name.key() != __pda_address {
|
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 {
|
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 {
|
else if c.is_init {
|
||||||
quote! {
|
quote! {
|
||||||
if #name.key() != __pda_address {
|
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.
|
// Check it.
|
||||||
if #name.key() != __pda_address {
|
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 wallet_address = &c.wallet;
|
||||||
let spl_token_mint_address = &c.mint;
|
let spl_token_mint_address = &c.mint;
|
||||||
quote! {
|
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 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)));
|
||||||
}
|
}
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -743,14 +775,26 @@ fn generate_custom_error(
|
||||||
account_name: &Ident,
|
account_name: &Ident,
|
||||||
custom_error: &Option<Expr>,
|
custom_error: &Option<Expr>,
|
||||||
error: proc_macro2::TokenStream,
|
error: proc_macro2::TokenStream,
|
||||||
|
compared_values: &Option<&(proc_macro2::TokenStream, proc_macro2::TokenStream)>,
|
||||||
) -> proc_macro2::TokenStream {
|
) -> proc_macro2::TokenStream {
|
||||||
let account_name = account_name.to_string();
|
let account_name = account_name.to_string();
|
||||||
match custom_error {
|
let mut error = match custom_error {
|
||||||
Some(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 => {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ const assert = require("assert");
|
||||||
const anchor = require("@project-serum/anchor");
|
const anchor = require("@project-serum/anchor");
|
||||||
const { Account, Transaction, TransactionInstruction } = anchor.web3;
|
const { Account, Transaction, TransactionInstruction } = anchor.web3;
|
||||||
const { TOKEN_PROGRAM_ID, Token } = require("@solana/spl-token");
|
const { TOKEN_PROGRAM_ID, Token } = require("@solana/spl-token");
|
||||||
|
const { Keypair } = require("@solana/web3.js");
|
||||||
|
|
||||||
// sleep to allow logs to come in
|
// sleep to allow logs to come in
|
||||||
const sleep = (ms) =>
|
const sleep = (ms) =>
|
||||||
|
@ -171,14 +172,15 @@ describe("errors", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Emits a has one error", async () => {
|
it("Emits a has one error", async () => {
|
||||||
|
await withLogTest(async () => {
|
||||||
try {
|
try {
|
||||||
const account = new Account();
|
const account = new Keypair();
|
||||||
const tx = await program.rpc.hasOneError({
|
const tx = await program.rpc.hasOneError({
|
||||||
accounts: {
|
accounts: {
|
||||||
myAccount: account.publicKey,
|
myAccount: account.publicKey,
|
||||||
owner: anchor.web3.SYSVAR_RENT_PUBKEY,
|
owner: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||||
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
|
||||||
},
|
},
|
||||||
|
// this initializes the account.owner variable with Pubkey::default
|
||||||
instructions: [
|
instructions: [
|
||||||
await program.account.hasOneAccount.createInstruction(account),
|
await program.account.hasOneAccount.createInstruction(account),
|
||||||
],
|
],
|
||||||
|
@ -191,6 +193,13 @@ describe("errors", () => {
|
||||||
assert.equal(err.msg, errMsg);
|
assert.equal(err.msg, errMsg);
|
||||||
assert.equal(err.code, 2001);
|
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
|
// This test uses a raw transaction and provider instead of a program
|
||||||
|
|
Loading…
Reference in New Issue