From e026e9e8741fdd17117570a9d54be58fefda8230 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 5 Jan 2022 17:09:57 +0100 Subject: [PATCH] lang: fix missing ATA owner check (#1240) --- CHANGELOG.md | 1 + lang/syn/src/codegen/accounts/constraints.rs | 3 + tests/misc/tests/misc.js | 160 ++++++++++++------- 3 files changed, 110 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67193f9cb..afec43e3b 100644 --- a/CHANGELOG.md +++ b/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)) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index b85eefe10..0d90d82f1 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/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()); diff --git a/tests/misc/tests/misc.js b/tests/misc/tests/misc.js index afd53c073..19535a186 100644 --- a/tests/misc/tests/misc.js +++ b/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 - ); - - 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, - }, - }); - const client = new Token( + 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, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer + program.provider.wallet.payer, + program.provider.wallet.publicKey, + program.provider.wallet.publicKey, + 9, + TOKEN_PROGRAM_ID ); - 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 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)); }); - 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("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; + } + ); + }); + + 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 () => {