Browse Source

lang: fix missing ATA owner check (#1240)

Paul 3 years ago
parent
commit
e026e9e874
3 changed files with 109 additions and 53 deletions
  1. 1 0
      CHANGELOG.md
  2. 3 0
      lang/syn/src/codegen/accounts/constraints.rs
  3. 105 53
      tests/misc/tests/misc.js

+ 1 - 0
CHANGELOG.md

@@ -13,6 +13,7 @@ incremented for features.
 
 ### Fixes
 
+* lang: Add missing owner check when `associated_token::authority` is used ([#1240](https://github.com/project-serum/anchor/pull/1240)).
 * ts: Add type declarations for conditional `workspace` and `Wallet` exports ([#1137](https://github.com/project-serum/anchor/pull/1137)).
 * ts: Change commitment message `recent` to `processed` and `max` to `finalized` ([#1128](https://github.com/project-serum/anchor/pull/1128))
 * ts: fix `translateAddress` which currently leads to failing browser code. Now uses `PublicKey` constructor instead of prototype chain constructor name checking which doesn't work in the presence of code minifying/mangling([#1138](https://github.com/project-serum/anchor/pull/1138))

+ 3 - 0
lang/syn/src/codegen/accounts/constraints.rs

@@ -395,6 +395,9 @@ 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::__private::ErrorCode::ConstraintTokenOwner.into());
+        }
         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::__private::ErrorCode::ConstraintAssociated.into());

+ 105 - 53
tests/misc/tests/misc.js

@@ -614,65 +614,117 @@ describe("misc", () => {
     assert.equal(account2.idata, 3);
   });
 
-  let associatedToken = null;
-
-  it("Can create an associated token account", async () => {
-    associatedToken = await Token.getAssociatedTokenAddress(
-      ASSOCIATED_TOKEN_PROGRAM_ID,
-      TOKEN_PROGRAM_ID,
-      mint.publicKey,
-      program.provider.wallet.publicKey
+  describe("associated_token constraints", () => {
+    let associatedToken = null;
+    // apparently cannot await here so doing it in the 'it' statements
+    let client = Token.createMint(
+      program.provider.connection,
+      program.provider.wallet.payer,
+      program.provider.wallet.publicKey,
+      program.provider.wallet.publicKey,
+      9,
+      TOKEN_PROGRAM_ID
     );
 
-    await program.rpc.testInitAssociatedToken({
-      accounts: {
-        token: associatedToken,
-        mint: mint.publicKey,
-        payer: program.provider.wallet.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
-        systemProgram: anchor.web3.SystemProgram.programId,
-        tokenProgram: TOKEN_PROGRAM_ID,
-        associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
-      },
+    it("Can create an associated token account", async () => {
+      const localClient = await client;
+      associatedToken = await Token.getAssociatedTokenAddress(
+        ASSOCIATED_TOKEN_PROGRAM_ID,
+        TOKEN_PROGRAM_ID,
+        localClient.publicKey,
+        program.provider.wallet.publicKey
+      );
+
+      await program.rpc.testInitAssociatedToken({
+        accounts: {
+          token: associatedToken,
+          mint: localClient.publicKey,
+          payer: program.provider.wallet.publicKey,
+          rent: anchor.web3.SYSVAR_RENT_PUBKEY,
+          systemProgram: anchor.web3.SystemProgram.programId,
+          tokenProgram: TOKEN_PROGRAM_ID,
+          associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
+        },
+      });
+
+      const account = await localClient.getAccountInfo(associatedToken);
+      assert.ok(account.state === 1);
+      assert.ok(account.amount.toNumber() === 0);
+      assert.ok(account.isInitialized);
+      assert.ok(account.owner.equals(program.provider.wallet.publicKey));
+      assert.ok(account.mint.equals(localClient.publicKey));
     });
-    const client = new Token(
-      program.provider.connection,
-      mint.publicKey,
-      TOKEN_PROGRAM_ID,
-      program.provider.wallet.payer
-    );
-    const account = await client.getAccountInfo(associatedToken);
-    assert.ok(account.state === 1);
-    assert.ok(account.amount.toNumber() === 0);
-    assert.ok(account.isInitialized);
-    assert.ok(account.owner.equals(program.provider.wallet.publicKey));
-    assert.ok(account.mint.equals(mint.publicKey));
-  });
 
-  it("Can validate associated_token constraints", async () => {
-    await program.rpc.testValidateAssociatedToken({
-      accounts: {
-        token: associatedToken,
-        mint: mint.publicKey,
-        wallet: program.provider.wallet.publicKey,
-      },
+    it("Can validate associated_token constraints", async () => {
+      const localClient = await client;
+      await program.rpc.testValidateAssociatedToken({
+        accounts: {
+          token: associatedToken,
+          mint: localClient.publicKey,
+          wallet: program.provider.wallet.publicKey,
+        },
+      });
+
+      let otherMint = await Token.createMint(
+        program.provider.connection,
+        program.provider.wallet.payer,
+        program.provider.wallet.publicKey,
+        program.provider.wallet.publicKey,
+        9,
+        TOKEN_PROGRAM_ID
+      );
+
+      await assert.rejects(
+        async () => {
+          await program.rpc.testValidateAssociatedToken({
+            accounts: {
+              token: associatedToken,
+              mint: otherMint.publicKey,
+              wallet: program.provider.wallet.publicKey,
+            },
+          });
+        },
+        (err) => {
+          assert.equal(err.code, 2009);
+          return true;
+        }
+      );
     });
 
-    await assert.rejects(
-      async () => {
-        await program.rpc.testValidateAssociatedToken({
-          accounts: {
-            token: associatedToken,
-            mint: mint.publicKey,
-            wallet: anchor.web3.Keypair.generate().publicKey,
-          },
-        });
-      },
-      (err) => {
-        assert.equal(err.code, 2009);
-        return true;
-      }
-    );
+    it("associated_token constraints check do not allow authority change", async () => {
+      const localClient = await client;
+      await program.rpc.testValidateAssociatedToken({
+        accounts: {
+          token: associatedToken,
+          mint: localClient.publicKey,
+          wallet: program.provider.wallet.publicKey,
+        },
+      });
+
+      await localClient.setAuthority(
+        associatedToken,
+        anchor.web3.Keypair.generate().publicKey,
+        "AccountOwner",
+        program.provider.wallet.payer,
+        []
+      );
+
+      await assert.rejects(
+        async () => {
+          await program.rpc.testValidateAssociatedToken({
+            accounts: {
+              token: associatedToken,
+              mint: localClient.publicKey,
+              wallet: program.provider.wallet.publicKey,
+            },
+          });
+        },
+        (err) => {
+          assert.equal(err.code, 2015);
+          return true;
+        }
+      );
+    });
   });
 
   it("Can fetch all accounts of a given type", async () => {