Explorar o código

lang: Support empty seeds constraint token (#853)

Alan O'Donnell %!s(int64=4) %!d(string=hai) anos
pai
achega
d774b456bf

+ 18 - 7
lang/syn/src/codegen/accounts/constraints.rs

@@ -287,24 +287,27 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
             if let Some(pair) = s.pop() {
                 s.push_value(pair.into_value());
             }
+            let maybe_seeds_plus_comma = (!s.is_empty()).then(|| {
+                quote! { #s, }
+            });
             let inner = match c.bump.as_ref() {
                 // Bump target not given. Use the canonical bump.
                 None => {
                     quote! {
                         [
-                            #s,
+                            #maybe_seeds_plus_comma
                             &[
                                 Pubkey::find_program_address(
                                     &[#s],
                                     program_id,
                                 ).1
-                            ]
+                            ][..]
                         ]
                     }
                 }
                 // Bump target given. Use it.
                 Some(b) => quote! {
-                    [#s, &[#b]]
+                    [#maybe_seeds_plus_comma &[#b][..]]
                 },
             };
             quote! {
@@ -317,7 +320,12 @@ 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 = &c.seeds;
+    let s = &mut c.seeds.clone();
+    // 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() {
+        s.push_value(pair.into_value());
+    }
 
     // If the bump is provided with init *and target*, then force it to be the
     // canonical bump.
@@ -336,25 +344,28 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
             }
         }
     } else {
+        let maybe_seeds_plus_comma = (!s.is_empty()).then(|| {
+            quote! { #s, }
+        });
         let seeds = match c.bump.as_ref() {
             // Bump target not given. Find it.
             None => {
                 quote! {
                     [
-                        #s,
+                        #maybe_seeds_plus_comma
                         &[
                             Pubkey::find_program_address(
                                 &[#s],
                                 program_id,
                             ).1
-                        ]
+                        ][..]
                     ]
                 }
             }
             // Bump target given. Use it.
             Some(b) => {
                 quote! {
-                    [#s, &[#b]]
+                    [#maybe_seeds_plus_comma &[#b][..]]
                 }
             }
         };

+ 14 - 0
tests/misc/programs/misc/src/context.rs

@@ -230,3 +230,17 @@ pub struct TestFetchAll<'info> {
     pub authority: Signer<'info>,
     pub system_program: Program<'info, System>,
 }
+
+#[derive(Accounts)]
+pub struct TestInitWithEmptySeeds<'info> {
+    #[account(init, seeds = [], bump, payer = authority, space = 8 + size_of::<Data>())]
+    pub pda: Account<'info, Data>,
+    pub authority: Signer<'info>,
+    pub system_program: Program<'info, System>,
+}
+
+#[derive(Accounts)]
+pub struct TestEmptySeedsConstraint<'info> {
+    #[account(seeds = [], bump)]
+    pub pda: AccountInfo<'info>,
+}

+ 8 - 0
tests/misc/programs/misc/src/lib.rs

@@ -176,4 +176,12 @@ pub mod misc {
         ctx.accounts.data.filterable = filterable;
         Ok(())
     }
+
+    pub fn test_init_with_empty_seeds(ctx: Context<TestInitWithEmptySeeds>) -> ProgramResult {
+        Ok(())
+    }
+
+    pub fn test_empty_seeds_constraint(ctx: Context<TestEmptySeedsConstraint>) -> ProgramResult {
+        Ok(())
+    }
 }

+ 30 - 0
tests/misc/tests/misc.js

@@ -770,4 +770,34 @@ describe("misc", () => {
     // results in 1 account.
     assert.equal(allAccountsFilteredByProgramFilters2.length, 1);
   });
+
+  it("Can use pdas with empty seeds", async () => {
+    const [pda, bump] = await PublicKey.findProgramAddress([], program.programId);
+
+    await program.rpc.testInitWithEmptySeeds({
+      accounts: {
+        pda: pda,
+        authority: program.provider.wallet.publicKey,
+        systemProgram: anchor.web3.SystemProgram.programId
+      }
+    });
+    await program.rpc.testEmptySeedsConstraint({
+      accounts: {
+        pda: pda
+      }
+    });
+
+    const [pda2, bump2] = await PublicKey.findProgramAddress(["non-empty"], program.programId);
+    await assert.rejects(
+      program.rpc.testEmptySeedsConstraint({
+        accounts: {
+          pda: pda2
+        }
+      }),
+      (err) => {
+        assert.equal(err.code, 146);
+        return true;
+      }
+    );
+  });
 });