From 72a3533699e87f760d32cdbe8c6d35fa724d05fa Mon Sep 17 00:00:00 2001 From: Alan O'Donnell Date: Sat, 11 Dec 2021 11:40:37 +0000 Subject: [PATCH 01/17] Add program_id constraint for PDAs. --- lang/syn/src/codegen/accounts/constraints.rs | 15 +++++-- lang/syn/src/lib.rs | 9 ++++- lang/syn/src/parser/accounts/constraints.rs | 41 ++++++++++++++++++++ tests/misc/programs/misc/src/context.rs | 5 +++ tests/misc/programs/misc/src/lib.rs | 5 +++ tests/misc/tests/misc.js | 33 ++++++++++++++++ 6 files changed, 104 insertions(+), 4 deletions(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index dcb203d500..3353e86524 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -327,6 +327,15 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream { let name = &f.ident; let s = &mut c.seeds.clone(); + + let deriving_program_id = c + .program_id + .clone() + // If they specified a program_id to use when deriving the PDA, use it + .and_then(|id| Some(quote! { #id })) + // otherwise fall back to the current program's program_id + .unwrap_or(quote! { program_id }); + // If the seeds came with a trailing comma, we need to chop it off // before we interpolate them below. if let Some(pair) = s.pop() { @@ -340,7 +349,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2 quote! { let (__program_signer, __bump) = anchor_lang::solana_program::pubkey::Pubkey::find_program_address( &[#s], - program_id, + &#deriving_program_id, ); if #name.key() != __program_signer { return Err(anchor_lang::__private::ErrorCode::ConstraintSeeds.into()); @@ -362,7 +371,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2 &[ Pubkey::find_program_address( &[#s], - program_id, + &#deriving_program_id, ).1 ][..] ] @@ -378,7 +387,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2 quote! { let __program_signer = Pubkey::create_program_address( &#seeds[..], - program_id, + &#deriving_program_id, ).map_err(|_| anchor_lang::__private::ErrorCode::ConstraintSeeds)?; if #name.key() != __program_signer { return Err(anchor_lang::__private::ErrorCode::ConstraintSeeds.into()); diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 038d22d3e4..0ea1029f90 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -610,6 +610,7 @@ pub enum ConstraintToken { MintFreezeAuthority(Context), MintDecimals(Context), Bump(Context), + ProgramId(Context), } impl Parse for ConstraintToken { @@ -687,7 +688,8 @@ pub struct ConstraintInitGroup { pub struct ConstraintSeedsGroup { pub is_init: bool, pub seeds: Punctuated, - pub bump: Option, // None => bump was given without a target. + pub bump: Option, // None => bump was given without a target. + pub program_id: Option, // None => use the current program's program_id } #[derive(Debug, Clone)] @@ -771,6 +773,11 @@ pub struct ConstraintTokenBump { bump: Option, } +#[derive(Debug, Clone)] +pub struct ConstraintTokenProgramId { + program_id: Expr, +} + #[derive(Debug, Clone)] pub struct ConstraintAssociatedToken { pub wallet: Expr, diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs index 3915f003dc..d1fc93369b 100644 --- a/lang/syn/src/parser/accounts/constraints.rs +++ b/lang/syn/src/parser/accounts/constraints.rs @@ -182,6 +182,15 @@ pub fn parse_token(stream: ParseStream) -> ParseResult { }; ConstraintToken::Bump(Context::new(ident.span(), ConstraintTokenBump { bump })) } + "program_id" => { + stream.parse::()?; + ConstraintToken::ProgramId(Context::new( + ident.span(), + ConstraintTokenProgramId { + program_id: stream.parse()?, + }, + )) + } _ => { stream.parse::()?; let span = ident @@ -308,6 +317,7 @@ pub struct ConstraintGroupBuilder<'ty> { pub mint_freeze_authority: Option>, pub mint_decimals: Option>, pub bump: Option>, + pub program_id: Option>, } impl<'ty> ConstraintGroupBuilder<'ty> { @@ -338,6 +348,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { mint_freeze_authority: None, mint_decimals: None, bump: None, + program_id: None, } } @@ -494,6 +505,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { mint_freeze_authority, mint_decimals, bump, + program_id, } = self; // Converts Option> -> Option. @@ -519,6 +531,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { bump: into_inner!(bump) .map(|b| b.bump) .expect("bump must be provided with seeds"), + program_id: into_inner!(program_id).map(|id| id.program_id), }); let associated_token = match (associated_token_mint, associated_token_authority) { (Some(mint), Some(auth)) => Some(ConstraintAssociatedToken { @@ -620,6 +633,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { ConstraintToken::MintFreezeAuthority(c) => self.add_mint_freeze_authority(c), ConstraintToken::MintDecimals(c) => self.add_mint_decimals(c), ConstraintToken::Bump(c) => self.add_bump(c), + ConstraintToken::ProgramId(c) => self.add_program_id(c), } } @@ -725,6 +739,33 @@ impl<'ty> ConstraintGroupBuilder<'ty> { Ok(()) } + fn add_program_id(&mut self, c: Context) -> ParseResult<()> { + if self.program_id.is_some() { + return Err(ParseError::new(c.span(), "program_id already provided")); + } + if self.seeds.is_none() { + return Err(ParseError::new( + c.span(), + "seeds must be provided before program_id", + )); + } + if let Some(ref init) = self.init { + if init.if_needed { + return Err(ParseError::new( + c.span(), + "program_id cannot be used with init_if_needed", + )); + } else { + return Err(ParseError::new( + c.span(), + "program_id cannot be used with init", + )); + } + } + self.program_id.replace(c); + Ok(()) + } + fn add_token_authority(&mut self, c: Context) -> ParseResult<()> { if self.token_authority.is_some() { return Err(ParseError::new( diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index d40479e86e..7d080f545f 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -379,3 +379,8 @@ pub struct InitIfNeededChecksRentExemption<'info> { pub system_program: Program<'info, System> } +#[instruction(bump: u8)] +pub struct TestProgramIdConstraint<'info> { + #[account(seeds = [b"seed"], bump, program_id = anchor_spl::associated_token::ID)] + associated_token_account: AccountInfo<'info>, +} diff --git a/tests/misc/programs/misc/src/lib.rs b/tests/misc/programs/misc/src/lib.rs index 7cc271a1cf..6bf3ec6409 100644 --- a/tests/misc/programs/misc/src/lib.rs +++ b/tests/misc/programs/misc/src/lib.rs @@ -266,6 +266,11 @@ pub mod misc { } pub fn init_if_needed_checks_rent_exemption(_ctx: Context) -> ProgramResult { + + pub fn test_program_id_constraint( + _ctx: Context, + _bump: u8, + ) -> ProgramResult { Ok(()) } } diff --git a/tests/misc/tests/misc.js b/tests/misc/tests/misc.js index cc8d457781..cfe605c2ca 100644 --- a/tests/misc/tests/misc.js +++ b/tests/misc/tests/misc.js @@ -1528,4 +1528,37 @@ describe("misc", () => { assert.equal("A rent exempt constraint was violated", err.msg); } }); + + it("Can validate PDAs derived from other program ids", async () => { + const wrongAddress = anchor.web3.Keypair.generate().publicKey; + try { + await program.rpc.testProgramIdConstraint(123, { + accounts: { + associatedTokenAccount: wrongAddress, + } + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2006); + } + + const [wrongProgramIdPDA, wrongBump] = await anchor.web3.PublicKey.findProgramAddress(["seed"], program.programId); + try { + await program.rpc.testProgramIdConstraint(wrongBump, { + accounts: { + associatedTokenAccount: wrongProgramIdPDA, + } + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2006); + } + + const [rightProgramIdPDA, rightBump] = await anchor.web3.PublicKey.findProgramAddress(["seed"], ASSOCIATED_TOKEN_PROGRAM_ID); + await program.rpc.testProgramIdConstraint(rightBump, { + accounts: { + associatedTokenAccount: rightProgramIdPDA, + } + }); + }); }); From d0e9705c178c18fb232f04392ae200e351445a56 Mon Sep 17 00:00:00 2001 From: Alan O'Donnell Date: Mon, 27 Dec 2021 11:11:46 -0500 Subject: [PATCH 02/17] Clippy made a good point. --- lang/syn/src/codegen/accounts/constraints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 3353e86524..79845c01a3 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -332,7 +332,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2 .program_id .clone() // If they specified a program_id to use when deriving the PDA, use it - .and_then(|id| Some(quote! { #id })) + .map(|id| quote! { #id }) // otherwise fall back to the current program's program_id .unwrap_or(quote! { program_id }); From 619efcd43e6aff6ff1872a940e842d3ef7a3a29d Mon Sep 17 00:00:00 2001 From: Alan O'Donnell Date: Mon, 27 Dec 2021 11:46:12 -0500 Subject: [PATCH 03/17] Run prettier. --- tests/misc/tests/misc.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/misc/tests/misc.js b/tests/misc/tests/misc.js index cfe605c2ca..bae2e4233e 100644 --- a/tests/misc/tests/misc.js +++ b/tests/misc/tests/misc.js @@ -1535,30 +1535,38 @@ describe("misc", () => { await program.rpc.testProgramIdConstraint(123, { accounts: { associatedTokenAccount: wrongAddress, - } + }, }); assert.ok(false); } catch (err) { assert.equal(err.code, 2006); } - const [wrongProgramIdPDA, wrongBump] = await anchor.web3.PublicKey.findProgramAddress(["seed"], program.programId); + const [wrongProgramIdPDA, wrongBump] = + await anchor.web3.PublicKey.findProgramAddress( + ["seed"], + program.programId + ); try { await program.rpc.testProgramIdConstraint(wrongBump, { accounts: { associatedTokenAccount: wrongProgramIdPDA, - } + }, }); assert.ok(false); } catch (err) { assert.equal(err.code, 2006); } - const [rightProgramIdPDA, rightBump] = await anchor.web3.PublicKey.findProgramAddress(["seed"], ASSOCIATED_TOKEN_PROGRAM_ID); + const [rightProgramIdPDA, rightBump] = + await anchor.web3.PublicKey.findProgramAddress( + ["seed"], + ASSOCIATED_TOKEN_PROGRAM_ID + ); await program.rpc.testProgramIdConstraint(rightBump, { accounts: { associatedTokenAccount: rightProgramIdPDA, - } + }, }); }); }); From b95197970f766b351a30af11aea1fe0b582d1bf7 Mon Sep 17 00:00:00 2001 From: Alan O'Donnell Date: Mon, 27 Dec 2021 14:23:13 -0500 Subject: [PATCH 04/17] Rename program_id constraint to program_seed. --- lang/syn/src/codegen/accounts/constraints.rs | 6 ++-- lang/syn/src/lib.rs | 10 +++--- lang/syn/src/parser/accounts/constraints.rs | 32 ++++++++++---------- tests/misc/package.json | 3 ++ tests/misc/programs/misc/src/context.rs | 2 +- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 79845c01a3..21bf6a1ece 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -329,10 +329,10 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2 let s = &mut c.seeds.clone(); let deriving_program_id = c - .program_id + .program_seed .clone() - // If they specified a program_id to use when deriving the PDA, use it - .map(|id| quote! { #id }) + // If they specified a program_seed to use when deriving the PDA, use it + .map(|program_id| quote! { #program_id }) // otherwise fall back to the current program's program_id .unwrap_or(quote! { program_id }); diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 0ea1029f90..a9431d3163 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -610,7 +610,7 @@ pub enum ConstraintToken { MintFreezeAuthority(Context), MintDecimals(Context), Bump(Context), - ProgramId(Context), + ProgramSeed(Context), } impl Parse for ConstraintToken { @@ -688,8 +688,8 @@ pub struct ConstraintInitGroup { pub struct ConstraintSeedsGroup { pub is_init: bool, pub seeds: Punctuated, - pub bump: Option, // None => bump was given without a target. - pub program_id: Option, // None => use the current program's program_id + pub bump: Option, // None => bump was given without a target. + pub program_seed: Option, // None => use the current program's program_id } #[derive(Debug, Clone)] @@ -774,8 +774,8 @@ pub struct ConstraintTokenBump { } #[derive(Debug, Clone)] -pub struct ConstraintTokenProgramId { - program_id: Expr, +pub struct ConstraintTokenProgramSeed { + program_seed: Expr, } #[derive(Debug, Clone)] diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs index d1fc93369b..ba4200bd00 100644 --- a/lang/syn/src/parser/accounts/constraints.rs +++ b/lang/syn/src/parser/accounts/constraints.rs @@ -182,12 +182,12 @@ pub fn parse_token(stream: ParseStream) -> ParseResult { }; ConstraintToken::Bump(Context::new(ident.span(), ConstraintTokenBump { bump })) } - "program_id" => { + "program_seed" => { stream.parse::()?; - ConstraintToken::ProgramId(Context::new( + ConstraintToken::ProgramSeed(Context::new( ident.span(), - ConstraintTokenProgramId { - program_id: stream.parse()?, + ConstraintTokenProgramSeed { + program_seed: stream.parse()?, }, )) } @@ -317,7 +317,7 @@ pub struct ConstraintGroupBuilder<'ty> { pub mint_freeze_authority: Option>, pub mint_decimals: Option>, pub bump: Option>, - pub program_id: Option>, + pub program_seed: Option>, } impl<'ty> ConstraintGroupBuilder<'ty> { @@ -348,7 +348,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { mint_freeze_authority: None, mint_decimals: None, bump: None, - program_id: None, + program_seed: None, } } @@ -505,7 +505,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { mint_freeze_authority, mint_decimals, bump, - program_id, + program_seed, } = self; // Converts Option> -> Option. @@ -531,7 +531,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { bump: into_inner!(bump) .map(|b| b.bump) .expect("bump must be provided with seeds"), - program_id: into_inner!(program_id).map(|id| id.program_id), + program_seed: into_inner!(program_seed).map(|id| id.program_seed), }); let associated_token = match (associated_token_mint, associated_token_authority) { (Some(mint), Some(auth)) => Some(ConstraintAssociatedToken { @@ -633,7 +633,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { ConstraintToken::MintFreezeAuthority(c) => self.add_mint_freeze_authority(c), ConstraintToken::MintDecimals(c) => self.add_mint_decimals(c), ConstraintToken::Bump(c) => self.add_bump(c), - ConstraintToken::ProgramId(c) => self.add_program_id(c), + ConstraintToken::ProgramSeed(c) => self.add_program_seed(c), } } @@ -739,30 +739,30 @@ impl<'ty> ConstraintGroupBuilder<'ty> { Ok(()) } - fn add_program_id(&mut self, c: Context) -> ParseResult<()> { - if self.program_id.is_some() { - return Err(ParseError::new(c.span(), "program_id already provided")); + fn add_program_seed(&mut self, c: Context) -> ParseResult<()> { + if self.program_seed.is_some() { + return Err(ParseError::new(c.span(), "program_seed already provided")); } if self.seeds.is_none() { return Err(ParseError::new( c.span(), - "seeds must be provided before program_id", + "seeds must be provided before program_seed", )); } if let Some(ref init) = self.init { if init.if_needed { return Err(ParseError::new( c.span(), - "program_id cannot be used with init_if_needed", + "program_seed cannot be used with init_if_needed", )); } else { return Err(ParseError::new( c.span(), - "program_id cannot be used with init", + "program_seed cannot be used with init", )); } } - self.program_id.replace(c); + self.program_seed.replace(c); Ok(()) } diff --git a/tests/misc/package.json b/tests/misc/package.json index 9a73969ab9..de4b4f5bc3 100644 --- a/tests/misc/package.json +++ b/tests/misc/package.json @@ -15,5 +15,8 @@ }, "scripts": { "test": "anchor test" + }, + "dependencies": { + "mocha": "^9.1.3" } } diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 7d080f545f..1f565f9690 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -381,6 +381,6 @@ pub struct InitIfNeededChecksRentExemption<'info> { #[instruction(bump: u8)] pub struct TestProgramIdConstraint<'info> { - #[account(seeds = [b"seed"], bump, program_id = anchor_spl::associated_token::ID)] + #[account(seeds = [b"seed"], bump, program_seed = anchor_spl::associated_token::ID)] associated_token_account: AccountInfo<'info>, } From 3a6acef182c5db5fc0e15bef06a1d881cae4b6e0 Mon Sep 17 00:00:00 2001 From: Alan O'Donnell Date: Mon, 27 Dec 2021 14:39:10 -0500 Subject: [PATCH 05/17] Slightly fancier program_seed test. --- tests/misc/programs/misc/src/context.rs | 3 +++ tests/misc/tests/misc.js | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 1f565f9690..124cf5b3b1 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -383,4 +383,7 @@ pub struct InitIfNeededChecksRentExemption<'info> { pub struct TestProgramIdConstraint<'info> { #[account(seeds = [b"seed"], bump, program_seed = anchor_spl::associated_token::ID)] associated_token_account: AccountInfo<'info>, + + #[account(seeds = [b"seed"], bump, program_seed = crate::ID)] + other_account: AccountInfo<'info>, } diff --git a/tests/misc/tests/misc.js b/tests/misc/tests/misc.js index bae2e4233e..d8a954e303 100644 --- a/tests/misc/tests/misc.js +++ b/tests/misc/tests/misc.js @@ -1530,11 +1530,16 @@ describe("misc", () => { }); it("Can validate PDAs derived from other program ids", async () => { + const [ourPda, ourPdaBump] = await anchor.web3.PublicKey.findProgramAddress( + [Buffer.from("seed")], + program.programId + ); const wrongAddress = anchor.web3.Keypair.generate().publicKey; try { await program.rpc.testProgramIdConstraint(123, { accounts: { associatedTokenAccount: wrongAddress, + otherAccount: ourPda, }, }); assert.ok(false); @@ -1551,6 +1556,7 @@ describe("misc", () => { await program.rpc.testProgramIdConstraint(wrongBump, { accounts: { associatedTokenAccount: wrongProgramIdPDA, + otherAccount: ourPda, }, }); assert.ok(false); @@ -1566,6 +1572,7 @@ describe("misc", () => { await program.rpc.testProgramIdConstraint(rightBump, { accounts: { associatedTokenAccount: rightProgramIdPDA, + otherAccount: ourPda, }, }); }); From 676b934de2051b53a9c66455442c1742e0f321a1 Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Fri, 7 Jan 2022 17:21:57 +0100 Subject: [PATCH 06/17] docs: update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5576afc777..3330032368 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,12 @@ incremented for features. ## [Unreleased] -#### Fixes +### Fixes + +### Features *lang: Improved error msgs when required programs are missing when using the `init` constraint([#1257](https://github.com/project-serum/anchor/pull/1257)) +*lang: Add seeds::program constraint for specifying which program_id to use when deriving PDAs.([#1197](https://github.com/project-serum/anchor/pull/1197)) ## [0.20.0] - 2022-01-06 From 7acab8216b4c42f00073021d636a10c8dbb96292 Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Fri, 7 Jan 2022 17:48:46 +0100 Subject: [PATCH 07/17] fixing rebase errors --- tests/misc/programs/misc/src/context.rs | 5 +++-- tests/misc/tests/misc.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 124cf5b3b1..2ca6887196 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -379,11 +379,12 @@ pub struct InitIfNeededChecksRentExemption<'info> { pub system_program: Program<'info, System> } +#[derive(Accounts)] #[instruction(bump: u8)] pub struct TestProgramIdConstraint<'info> { - #[account(seeds = [b"seed"], bump, program_seed = anchor_spl::associated_token::ID)] + #[account(seeds = [b"seed"], bump = bump, program_seed = anchor_spl::associated_token::ID)] associated_token_account: AccountInfo<'info>, - #[account(seeds = [b"seed"], bump, program_seed = crate::ID)] + #[account(seeds = [b"seed"], bump = bump, program_seed = crate::ID)] other_account: AccountInfo<'info>, } diff --git a/tests/misc/tests/misc.js b/tests/misc/tests/misc.js index d8a954e303..052daea824 100644 --- a/tests/misc/tests/misc.js +++ b/tests/misc/tests/misc.js @@ -1528,7 +1528,7 @@ describe("misc", () => { assert.equal("A rent exempt constraint was violated", err.msg); } }); - + it("Can validate PDAs derived from other program ids", async () => { const [ourPda, ourPdaBump] = await anchor.web3.PublicKey.findProgramAddress( [Buffer.from("seed")], From 746733cdff937dc516e0876fe2e9353701c03621 Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Fri, 7 Jan 2022 20:25:21 +0100 Subject: [PATCH 08/17] examples: update yarn.lock --- examples/tutorial/yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/tutorial/yarn.lock b/examples/tutorial/yarn.lock index e47e8a3522..b199202596 100644 --- a/examples/tutorial/yarn.lock +++ b/examples/tutorial/yarn.lock @@ -30,10 +30,10 @@ "@ethersproject/logger" "^5.5.0" hash.js "1.1.7" -"@project-serum/anchor@^0.19.0": - version "0.19.0" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.19.0.tgz#79f1fbe7c3134860ccbfe458a0e09daf79644885" - integrity sha512-cs0LBmJOrL9eJ8MRNqitnzbpCT5QEzVdJmiIjfNV5YaGn1K9vISR7DtISj3Bdl3KBdLqii4CTw1mpHdi8iXUCg== +"@project-serum/anchor@^0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.20.0.tgz#547f5c0ff7e66809fa7118b2e3abd8087b5ec519" + integrity sha512-p1KOiqGBIbNsopMrSVoPwgxR1iPffsdjMNCOysahTPL9whX2CLX9HQCdopHjYaGl7+SdHRuXml6Wahk/wUmC8g== dependencies: "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" From b00c81512e1345d752ebcb189205227d4d290943 Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Fri, 7 Jan 2022 20:27:08 +0100 Subject: [PATCH 09/17] tests: clarify test --- tests/misc/programs/misc/src/context.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 2ca6887196..35df264a17 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -382,6 +382,8 @@ pub struct InitIfNeededChecksRentExemption<'info> { #[derive(Accounts)] #[instruction(bump: u8)] pub struct TestProgramIdConstraint<'info> { + // not a real associated token account + // just deriving like this for testing purposes #[account(seeds = [b"seed"], bump = bump, program_seed = anchor_spl::associated_token::ID)] associated_token_account: AccountInfo<'info>, From 867f15618d9b2080ab1b456d964c9935973f77ff Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Fri, 7 Jan 2022 22:27:47 +0100 Subject: [PATCH 10/17] lang: rename to seeds::program, finish tests --- lang/syn/src/codegen/accounts/constraints.rs | 2 +- lang/syn/src/lib.rs | 4 +- lang/syn/src/parser/accounts/constraints.rs | 66 ++++++---- tests/misc/programs/misc/src/context.rs | 21 +++- tests/misc/programs/misc/src/lib.rs | 9 ++ tests/misc/tests/misc.js | 125 +++++++++++++------ 6 files changed, 157 insertions(+), 70 deletions(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 21bf6a1ece..b7b0089fc7 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -331,7 +331,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2 let deriving_program_id = c .program_seed .clone() - // If they specified a program_seed to use when deriving the PDA, use it + // If they specified a seeds::program to use when deriving the PDA, use it .map(|program_id| quote! { #program_id }) // otherwise fall back to the current program's program_id .unwrap_or(quote! { program_id }); diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index a9431d3163..c0bc448a3d 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -610,7 +610,7 @@ pub enum ConstraintToken { MintFreezeAuthority(Context), MintDecimals(Context), Bump(Context), - ProgramSeed(Context), + ProgramSeed(Context), } impl Parse for ConstraintToken { @@ -774,7 +774,7 @@ pub struct ConstraintTokenBump { } #[derive(Debug, Clone)] -pub struct ConstraintTokenProgramSeed { +pub struct ConstraintProgramSeed { program_seed: Expr, } diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs index ba4200bd00..90fc98d8d5 100644 --- a/lang/syn/src/parser/accounts/constraints.rs +++ b/lang/syn/src/parser/accounts/constraints.rs @@ -182,14 +182,42 @@ pub fn parse_token(stream: ParseStream) -> ParseResult { }; ConstraintToken::Bump(Context::new(ident.span(), ConstraintTokenBump { bump })) } - "program_seed" => { - stream.parse::()?; - ConstraintToken::ProgramSeed(Context::new( - ident.span(), - ConstraintTokenProgramSeed { - program_seed: stream.parse()?, - }, - )) + "seeds" => { + if stream.peek(Token![:]) { + stream.parse::()?; + stream.parse::()?; + let kw = stream.call(Ident::parse_any)?.to_string(); + stream.parse::()?; + + let span = ident + .span() + .join(stream.span()) + .unwrap_or_else(|| ident.span()); + + match kw.as_str() { + "program" => ConstraintToken::ProgramSeed(Context::new( + span, + ConstraintProgramSeed { + program_seed: stream.parse()?, + }, + )), + _ => return Err(ParseError::new(ident.span(), "Invalid attribute")), + } + } else { + stream.parse::()?; + let span = ident + .span() + .join(stream.span()) + .unwrap_or_else(|| ident.span()); + let seeds; + let bracket = bracketed!(seeds in stream); + ConstraintToken::Seeds(Context::new( + span.join(bracket.span).unwrap_or(span), + ConstraintSeeds { + seeds: seeds.parse_terminated(Expr::parse)?, + }, + )) + } } _ => { stream.parse::()?; @@ -243,16 +271,6 @@ pub fn parse_token(stream: ParseStream) -> ParseResult { space: stream.parse()?, }, )), - "seeds" => { - let seeds; - let bracket = bracketed!(seeds in stream); - ConstraintToken::Seeds(Context::new( - span.join(bracket.span).unwrap_or(span), - ConstraintSeeds { - seeds: seeds.parse_terminated(Expr::parse)?, - }, - )) - } "constraint" => ConstraintToken::Raw(Context::new( span, ConstraintRaw { @@ -317,7 +335,7 @@ pub struct ConstraintGroupBuilder<'ty> { pub mint_freeze_authority: Option>, pub mint_decimals: Option>, pub bump: Option>, - pub program_seed: Option>, + pub program_seed: Option>, } impl<'ty> ConstraintGroupBuilder<'ty> { @@ -739,26 +757,26 @@ impl<'ty> ConstraintGroupBuilder<'ty> { Ok(()) } - fn add_program_seed(&mut self, c: Context) -> ParseResult<()> { + fn add_program_seed(&mut self, c: Context) -> ParseResult<()> { if self.program_seed.is_some() { - return Err(ParseError::new(c.span(), "program_seed already provided")); + return Err(ParseError::new(c.span(), "seeds::program already provided")); } if self.seeds.is_none() { return Err(ParseError::new( c.span(), - "seeds must be provided before program_seed", + "seeds must be provided before seeds::program", )); } if let Some(ref init) = self.init { if init.if_needed { return Err(ParseError::new( c.span(), - "program_seed cannot be used with init_if_needed", + "seeds::program cannot be used with init_if_needed", )); } else { return Err(ParseError::new( c.span(), - "program_seed cannot be used with init", + "seeds::program cannot be used with init", )); } } diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 35df264a17..64ec51d5b2 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -380,13 +380,24 @@ pub struct InitIfNeededChecksRentExemption<'info> { } #[derive(Accounts)] -#[instruction(bump: u8)] +#[instruction(bump: u8, second_bump: u8)] pub struct TestProgramIdConstraint<'info> { // not a real associated token account // just deriving like this for testing purposes - #[account(seeds = [b"seed"], bump = bump, program_seed = anchor_spl::associated_token::ID)] - associated_token_account: AccountInfo<'info>, + #[account(seeds = [b"seed"], bump = bump, seeds::program = anchor_spl::associated_token::ID)] + first: AccountInfo<'info>, + + #[account(seeds = [b"seed"], bump = second_bump, seeds::program = crate::ID)] + second: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestProgramIdConstraintUsingFindPda<'info> { + // not a real associated token account + // just deriving like this for testing purposes + #[account(seeds = [b"seed"], bump, seeds::program = anchor_spl::associated_token::ID)] + first: AccountInfo<'info>, - #[account(seeds = [b"seed"], bump = bump, program_seed = crate::ID)] - other_account: AccountInfo<'info>, + #[account(seeds = [b"seed"], bump, seeds::program = crate::ID)] + second: AccountInfo<'info>, } diff --git a/tests/misc/programs/misc/src/lib.rs b/tests/misc/programs/misc/src/lib.rs index 6bf3ec6409..9b7df57aec 100644 --- a/tests/misc/programs/misc/src/lib.rs +++ b/tests/misc/programs/misc/src/lib.rs @@ -266,10 +266,19 @@ pub mod misc { } pub fn init_if_needed_checks_rent_exemption(_ctx: Context) -> ProgramResult { + Ok(()) + } pub fn test_program_id_constraint( _ctx: Context, _bump: u8, + _second_bump: u8 + ) -> ProgramResult { + Ok(()) + } + + pub fn test_program_id_constraint_find_pda( + _ctx: Context, ) -> ProgramResult { Ok(()) } diff --git a/tests/misc/tests/misc.js b/tests/misc/tests/misc.js index 052daea824..63a5629d7b 100644 --- a/tests/misc/tests/misc.js +++ b/tests/misc/tests/misc.js @@ -1529,51 +1529,100 @@ describe("misc", () => { } }); - it("Can validate PDAs derived from other program ids", async () => { - const [ourPda, ourPdaBump] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from("seed")], - program.programId - ); - const wrongAddress = anchor.web3.Keypair.generate().publicKey; - try { - await program.rpc.testProgramIdConstraint(123, { - accounts: { - associatedTokenAccount: wrongAddress, - otherAccount: ourPda, - }, - }); - assert.ok(false); - } catch (err) { - assert.equal(err.code, 2006); - } - - const [wrongProgramIdPDA, wrongBump] = + describe("Can validate PDAs derived from other program ids", () => { + it("With bumps using create_program_address", async () => { + const [firstPDA, firstBump] = await anchor.web3.PublicKey.findProgramAddress( - ["seed"], + [anchor.utils.bytes.utf8.encode("seed")], + ASSOCIATED_TOKEN_PROGRAM_ID + ); + const [secondPDA, secondBump] = await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], program.programId ); - try { - await program.rpc.testProgramIdConstraint(wrongBump, { + + // correct bump but wrong address + const wrongAddress = anchor.web3.Keypair.generate().publicKey; + try { + await program.rpc.testProgramIdConstraint(firstBump, secondBump, { + accounts: { + first: wrongAddress, + second: secondPDA, + }, + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2006); + } + + // matching bump seed for wrong address but derived from wrong program + try { + await program.rpc.testProgramIdConstraint(secondBump, secondBump, { + accounts: { + first: secondPDA, + second: secondPDA, + }, + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2006); + } + + // correct inputs should lead to successful tx + await program.rpc.testProgramIdConstraint(firstBump, secondBump, { accounts: { - associatedTokenAccount: wrongProgramIdPDA, - otherAccount: ourPda, + first: firstPDA, + second: secondPDA, }, }); - assert.ok(false); - } catch (err) { - assert.equal(err.code, 2006); - } + }); - const [rightProgramIdPDA, rightBump] = - await anchor.web3.PublicKey.findProgramAddress( - ["seed"], + it("With bumps using find_program_address", async () => { + const firstPDA = + (await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], ASSOCIATED_TOKEN_PROGRAM_ID - ); - await program.rpc.testProgramIdConstraint(rightBump, { - accounts: { - associatedTokenAccount: rightProgramIdPDA, - otherAccount: ourPda, - }, + ))[0]; + const secondPDA = (await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + program.programId + ))[0]; + + // random wrong address + const wrongAddress = anchor.web3.Keypair.generate().publicKey; + try { + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: wrongAddress, + second: secondPDA, + }, + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2006); + } + + // same seeds but derived from wrong program + try { + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: secondPDA, + second: secondPDA, + }, + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2006); + } + + // correct inputs should lead to successful tx + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: firstPDA, + second: secondPDA, + }, + }); }); - }); + }) + }); From 4fb094b0407c57c9da98d406344ff6ad75fb3d6a Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Fri, 7 Jan 2022 22:37:01 +0100 Subject: [PATCH 11/17] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3330032368..8abefd5832 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ incremented for features. ### Features *lang: Improved error msgs when required programs are missing when using the `init` constraint([#1257](https://github.com/project-serum/anchor/pull/1257)) -*lang: Add seeds::program constraint for specifying which program_id to use when deriving PDAs.([#1197](https://github.com/project-serum/anchor/pull/1197)) +*lang: Add `seeds::program` constraint for specifying which program_id to use when deriving PDAs.([#1197](https://github.com/project-serum/anchor/pull/1197)) ## [0.20.0] - 2022-01-06 From 4379b7a0303cd90c11a3689a83f858290dec1825 Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Fri, 7 Jan 2022 22:48:52 +0100 Subject: [PATCH 12/17] docs: add seeds::program to reference --- lang/derive/accounts/src/lib.rs | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/lang/derive/accounts/src/lib.rs b/lang/derive/accounts/src/lib.rs index 14830feb2c..5f9b7032d0 100644 --- a/lang/derive/accounts/src/lib.rs +++ b/lang/derive/accounts/src/lib.rs @@ -228,21 +228,42 @@ use syn::parse_macro_input; /// /// /// #[account(seeds = <seeds>, bump)]

-/// #[account(seeds = <seeds>, bump = <expr>)] +/// #[account(seeds = <seeds>, bump, seeds::program = <expr>)]

+/// #[account(seeds = <seeds>, bump = <expr>)]

+/// #[account(seeds = <seeds>, bump = <expr>, seeds::program = <expr>)]

/// /// /// Checks that given account is a PDA derived from the currently executing program, /// the seeds, and if provided, the bump. If not provided, anchor uses the canonical -/// bump. Will be adjusted in the future to allow PDA to be derived from other programs.
+/// bump.
+/// Add seeds::program = <expr> to derive the PDA from a different +/// program than the currently executing one.
/// This constraint behaves slightly differently when used with init. /// See its description. ///

/// Example: ///

-/// #[account(seeds = [b"example_seed], bump)]
-/// pub canonical_pda: AccountInfo<'info>,
-/// #[account(seeds = [b"other_seed], bump = 142)]
-/// pub arbitrary_pda: AccountInfo<'info>
+/// #[derive(Accounts)]
+/// #[instruction(first_bump: u8, second_bump: u8)]
+/// pub struct Example {
+///     #[account(seeds = [b"example_seed], bump)]
+///     pub canonical_pda: AccountInfo<'info>,
+///     #[account(
+///         seeds = [b"example_seed],
+///         bump,
+///         seeds::program = other_program.key()
+///     )]
+///     pub canonical_pda_two: AccountInfo<'info>,
+///     #[account(seeds = [b"other_seed], bump = first_bump)]
+///     pub arbitrary_pda: AccountInfo<'info>
+///     #[account(
+///         seeds = [b"other_seed],
+///         bump = second_bump,
+///         seeds::program = other_program.key()
+///     )]
+///     pub arbitrary_pda_two: AccountInfo<'info>,
+///     pub other_program: Program<'info, OtherProgram>
+/// }
 ///                 
/// /// From 5abfcea10bc0c3f6550a5091bfb2189528cc52e2 Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Fri, 7 Jan 2022 22:52:05 +0100 Subject: [PATCH 13/17] docs: more seeds::program reference --- lang/derive/accounts/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lang/derive/accounts/src/lib.rs b/lang/derive/accounts/src/lib.rs index 5f9b7032d0..20066a54a3 100644 --- a/lang/derive/accounts/src/lib.rs +++ b/lang/derive/accounts/src/lib.rs @@ -159,7 +159,9 @@ use syn::parse_macro_input; /// you can pass it in as instruction data and set the bump value like shown in the example, /// using the instruction_data attribute. /// Anchor will then check that the bump returned by find_program_address equals -/// the bump in the instruction data. +/// the bump in the instruction data.
+/// seeds::program cannot be used together with init because the creation of an +/// account requires its signature which for PDAs only the currently executing program can provide. /// /// /// Example: From f11b787237fb197a056da3c5d30cfadc0ef50856 Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Fri, 7 Jan 2022 22:52:50 +0100 Subject: [PATCH 14/17] linting --- tests/misc/tests/misc.js | 53 +++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/tests/misc/tests/misc.js b/tests/misc/tests/misc.js index 63a5629d7b..854b43f1a2 100644 --- a/tests/misc/tests/misc.js +++ b/tests/misc/tests/misc.js @@ -1532,15 +1532,16 @@ describe("misc", () => { describe("Can validate PDAs derived from other program ids", () => { it("With bumps using create_program_address", async () => { const [firstPDA, firstBump] = - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - ASSOCIATED_TOKEN_PROGRAM_ID - ); - const [secondPDA, secondBump] = await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - program.programId - ); - + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + ASSOCIATED_TOKEN_PROGRAM_ID + ); + const [secondPDA, secondBump] = + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + program.programId + ); + // correct bump but wrong address const wrongAddress = anchor.web3.Keypair.generate().publicKey; try { @@ -1554,7 +1555,7 @@ describe("misc", () => { } catch (err) { assert.equal(err.code, 2006); } - + // matching bump seed for wrong address but derived from wrong program try { await program.rpc.testProgramIdConstraint(secondBump, secondBump, { @@ -1567,7 +1568,7 @@ describe("misc", () => { } catch (err) { assert.equal(err.code, 2006); } - + // correct inputs should lead to successful tx await program.rpc.testProgramIdConstraint(firstBump, secondBump, { accounts: { @@ -1578,16 +1579,19 @@ describe("misc", () => { }); it("With bumps using find_program_address", async () => { - const firstPDA = - (await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - ASSOCIATED_TOKEN_PROGRAM_ID - ))[0]; - const secondPDA = (await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - program.programId - ))[0]; - + const firstPDA = ( + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + ASSOCIATED_TOKEN_PROGRAM_ID + ) + )[0]; + const secondPDA = ( + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + program.programId + ) + )[0]; + // random wrong address const wrongAddress = anchor.web3.Keypair.generate().publicKey; try { @@ -1601,7 +1605,7 @@ describe("misc", () => { } catch (err) { assert.equal(err.code, 2006); } - + // same seeds but derived from wrong program try { await program.rpc.testProgramIdConstraintFindPda({ @@ -1614,7 +1618,7 @@ describe("misc", () => { } catch (err) { assert.equal(err.code, 2006); } - + // correct inputs should lead to successful tx await program.rpc.testProgramIdConstraintFindPda({ accounts: { @@ -1623,6 +1627,5 @@ describe("misc", () => { }, }); }); - }) - + }); }); From 8988f48d91cab68cf74f9f5c0e7a413cdb082c0e Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Sat, 8 Jan 2022 11:51:08 +0100 Subject: [PATCH 15/17] docs: fix changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8abefd5832..33ce2d60b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,8 @@ incremented for features. ### Features -*lang: Improved error msgs when required programs are missing when using the `init` constraint([#1257](https://github.com/project-serum/anchor/pull/1257)) -*lang: Add `seeds::program` constraint for specifying which program_id to use when deriving PDAs.([#1197](https://github.com/project-serum/anchor/pull/1197)) +* lang: Improved error msgs when required programs are missing when using the `init` constraint([#1257](https://github.com/project-serum/anchor/pull/1257)) +* lang: Add `seeds::program` constraint for specifying which program_id to use when deriving PDAs.([#1197](https://github.com/project-serum/anchor/pull/1197)) ## [0.20.0] - 2022-01-06 From 0ee3e57485ad2e78410a8c5510300e19a2090859 Mon Sep 17 00:00:00 2001 From: Armani Ferrante Date: Tue, 11 Jan 2022 10:50:39 -0500 Subject: [PATCH 16/17] Update lang/syn/src/codegen/accounts/constraints.rs --- lang/syn/src/codegen/accounts/constraints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index b7b0089fc7..a2a3e68ce6 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -331,7 +331,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2 let deriving_program_id = c .program_seed .clone() - // If they specified a seeds::program to use when deriving the PDA, use it + // If they specified a seeds::program to use when deriving the PDA, use it. .map(|program_id| quote! { #program_id }) // otherwise fall back to the current program's program_id .unwrap_or(quote! { program_id }); From 0b2a583602526c4ba096d116b19ad186930ebbfe Mon Sep 17 00:00:00 2001 From: Armani Ferrante Date: Tue, 11 Jan 2022 10:50:45 -0500 Subject: [PATCH 17/17] Update lang/syn/src/codegen/accounts/constraints.rs --- lang/syn/src/codegen/accounts/constraints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index a2a3e68ce6..34a77a378e 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -333,7 +333,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2 .clone() // If they specified a seeds::program to use when deriving the PDA, use it. .map(|program_id| quote! { #program_id }) - // otherwise fall back to the current program's program_id + // Otherwise fall back to the current program's program_id. .unwrap_or(quote! { program_id }); // If the seeds came with a trailing comma, we need to chop it off