Răsfoiți Sursa

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

Paul 3 ani în urmă
părinte
comite
7f8ca97aa8
2 a modificat fișierele cu 105 adăugiri și 52 ștergeri
  1. 76 32
      lang/syn/src/codegen/accounts/constraints.rs
  2. 29 20
      tests/errors/tests/errors.js

+ 76 - 32
lang/syn/src/codegen/accounts/constraints.rs

@@ -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)
     }
 }

+ 29 - 20
tests/errors/tests/errors.js

@@ -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