Browse Source

lang: Add zero attribute (#513)

Armani Ferrante 4 years ago
parent
commit
2604a442fd

+ 1 - 1
examples/cashiers-check/programs/cashiers-check/src/lib.rs

@@ -83,7 +83,7 @@ pub mod cashiers_check {
 #[derive(Accounts)]
 pub struct CreateCheck<'info> {
     // Check being created.
-    #[account(init)]
+    #[account(zero)]
     check: ProgramAccount<'info, Check>,
     // Check's token vault.
     #[account(mut, "&vault.owner == check_signer.key")]

+ 1 - 1
examples/chat/programs/chat/src/lib.rs

@@ -53,7 +53,7 @@ pub struct CreateUser<'info> {
 
 #[derive(Accounts)]
 pub struct CreateChatRoom<'info> {
-    #[account(init)]
+    #[account(zero)]
     chat_room: Loader<'info, ChatRoom>,
 }
 

+ 2 - 2
examples/composite/programs/composite/src/lib.rs

@@ -27,9 +27,9 @@ mod composite {
 
 #[derive(Accounts)]
 pub struct Initialize<'info> {
-    #[account(init)]
+    #[account(zero)]
     pub dummy_a: ProgramAccount<'info, DummyA>,
-    #[account(init)]
+    #[account(zero)]
     pub dummy_b: ProgramAccount<'info, DummyB>,
 }
 

+ 1 - 1
examples/errors/programs/errors/src/lib.rs

@@ -43,7 +43,7 @@ pub struct MutError<'info> {
 
 #[derive(Accounts)]
 pub struct HasOneError<'info> {
-    #[account(init, has_one = owner)]
+    #[account(zero, has_one = owner)]
     my_account: ProgramAccount<'info, HasOneAccount>,
     owner: AccountInfo<'info>,
 }

+ 1 - 1
examples/escrow/programs/escrow/src/lib.rs

@@ -106,7 +106,7 @@ pub struct InitializeEscrow<'info> {
     )]
     pub initializer_deposit_token_account: CpiAccount<'info, TokenAccount>,
     pub initializer_receive_token_account: CpiAccount<'info, TokenAccount>,
-    #[account(init)]
+    #[account(zero)]
     pub escrow_account: ProgramAccount<'info, EscrowAccount>,
     pub token_program: AccountInfo<'info>,
 }

+ 1 - 1
examples/ido-pool/programs/ido-pool/src/lib.rs

@@ -191,7 +191,7 @@ pub mod ido_pool {
 
 #[derive(Accounts)]
 pub struct InitializePool<'info> {
-    #[account(init)]
+    #[account(zero)]
     pub pool_account: ProgramAccount<'info, PoolAccount>,
     pub pool_signer: AccountInfo<'info>,
     #[account(

+ 1 - 1
examples/lockup/programs/lockup/src/lib.rs

@@ -207,7 +207,7 @@ pub struct Auth<'info> {
 #[derive(Accounts)]
 pub struct CreateVesting<'info> {
     // Vesting.
-    #[account(init)]
+    #[account(zero)]
     vesting: ProgramAccount<'info, Vesting>,
     #[account(mut)]
     vault: CpiAccount<'info, TokenAccount>,

+ 5 - 5
examples/lockup/programs/registry/src/lib.rs

@@ -556,9 +556,9 @@ mod registry {
 
 #[derive(Accounts)]
 pub struct Initialize<'info> {
-    #[account(init)]
+    #[account(zero)]
     registrar: ProgramAccount<'info, Registrar>,
-    #[account(init)]
+    #[account(zero)]
     reward_event_q: ProgramAccount<'info, RewardQueue>,
     #[account("pool_mint.decimals == 0")]
     pool_mint: CpiAccount<'info, Mint>,
@@ -595,7 +595,7 @@ pub struct CreateMember<'info> {
     // Stake instance.
     registrar: ProgramAccount<'info, Registrar>,
     // Member.
-    #[account(init)]
+    #[account(zero)]
     member: ProgramAccount<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
@@ -790,7 +790,7 @@ pub struct StartUnstake<'info> {
     pool_mint: AccountInfo<'info>,
 
     // Member.
-    #[account(init)]
+    #[account(zero)]
     pending_withdrawal: ProgramAccount<'info, PendingWithdrawal>,
     #[account(has_one = beneficiary, has_one = registrar)]
     member: ProgramAccount<'info, Member>,
@@ -924,7 +924,7 @@ pub struct DropReward<'info> {
     reward_event_q: ProgramAccount<'info, RewardQueue>,
     pool_mint: CpiAccount<'info, Mint>,
     // Vendor.
-    #[account(init)]
+    #[account(zero)]
     vendor: ProgramAccount<'info, RewardVendor>,
     #[account(mut)]
     vendor_vault: CpiAccount<'info, TokenAccount>,

+ 3 - 3
examples/misc/programs/misc/src/context.rs

@@ -75,7 +75,7 @@ pub struct RemainingAccounts {}
 
 #[derive(Accounts)]
 pub struct Initialize<'info> {
-    #[account(init)]
+    #[account(zero)]
     pub data: ProgramAccount<'info, Data>,
 }
 
@@ -111,13 +111,13 @@ pub struct TestClose<'info> {
 
 #[derive(Accounts)]
 pub struct TestU16<'info> {
-    #[account(init)]
+    #[account(zero)]
     pub my_account: ProgramAccount<'info, DataU16>,
 }
 
 #[derive(Accounts)]
 pub struct TestI16<'info> {
-    #[account(init)]
+    #[account(zero)]
     pub data: ProgramAccount<'info, DataI16>,
 }
 

+ 2 - 2
examples/multisig/programs/multisig/src/lib.rs

@@ -164,14 +164,14 @@ pub mod multisig {
 
 #[derive(Accounts)]
 pub struct CreateMultisig<'info> {
-    #[account(init)]
+    #[account(zero)]
     multisig: ProgramAccount<'info, Multisig>,
 }
 
 #[derive(Accounts)]
 pub struct CreateTransaction<'info> {
     multisig: ProgramAccount<'info, Multisig>,
-    #[account(init)]
+    #[account(zero)]
     transaction: ProgramAccount<'info, Transaction>,
     // One of the owners. Checked in the handler.
     #[account(signer)]

+ 1 - 1
examples/tutorial/basic-1/programs/basic-1/src/lib.rs

@@ -19,7 +19,7 @@ mod basic_1 {
 
 #[derive(Accounts)]
 pub struct Initialize<'info> {
-    #[account(init)]
+    #[account(zero)]
     pub my_account: ProgramAccount<'info, MyAccount>,
 }
 

+ 1 - 1
examples/tutorial/basic-2/programs/basic-2/src/lib.rs

@@ -24,7 +24,7 @@ mod basic_2 {
 
 #[derive(Accounts)]
 pub struct Create<'info> {
-    #[account(init)]
+    #[account(zero)]
     pub counter: ProgramAccount<'info, Counter>,
 }
 

+ 1 - 1
examples/tutorial/basic-3/programs/puppet/src/lib.rs

@@ -16,7 +16,7 @@ pub mod puppet {
 
 #[derive(Accounts)]
 pub struct Initialize<'info> {
-    #[account(init)]
+    #[account(zero)]
     pub puppet: ProgramAccount<'info, Puppet>,
 }
 

+ 2 - 2
examples/zero-copy/programs/zero-copy/src/lib.rs

@@ -109,7 +109,7 @@ pub struct SetEvent<'info> {
 
 #[derive(Accounts)]
 pub struct CreateFoo<'info> {
-    #[account(init)]
+    #[account(zero)]
     foo: Loader<'info, Foo>,
     #[account(signer)]
     authority: AccountInfo<'info>,
@@ -165,7 +165,7 @@ pub struct UpdateBar<'info> {
 
 #[derive(Accounts)]
 pub struct CreateLargeAccount<'info> {
-    #[account(init)]
+    #[account(zero)]
     event_q: Loader<'info, EventQ>,
 }
 

+ 12 - 6
lang/syn/src/codegen/accounts/constraints.rs

@@ -1,9 +1,4 @@
-use crate::{
-    CompositeField, Constraint, ConstraintAddress, ConstraintClose, ConstraintExecutable,
-    ConstraintGroup, ConstraintHasOne, ConstraintInit, ConstraintLiteral, ConstraintMut,
-    ConstraintOwner, ConstraintRaw, ConstraintRentExempt, ConstraintSeedsGroup, ConstraintSigner,
-    ConstraintState, Field, PdaKind, Ty,
-};
+use crate::*;
 use proc_macro2_diagnostics::SpanDiagnosticExt;
 use quote::quote;
 use syn::Expr;
@@ -48,6 +43,7 @@ pub fn generate_composite(f: &CompositeField) -> proc_macro2::TokenStream {
 pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
     let ConstraintGroup {
         init,
+        zeroed,
         mutable,
         signer,
         has_one,
@@ -70,6 +66,9 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
     if let Some(c) = init {
         constraints.push(Constraint::Init(c));
     }
+    if let Some(c) = zeroed {
+        constraints.push(Constraint::Zeroed(c));
+    }
     if let Some(c) = mutable {
         constraints.push(Constraint::Mut(c));
     }
@@ -103,6 +102,7 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
 fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
     match c {
         Constraint::Init(c) => generate_constraint_init(f, c),
+        Constraint::Zeroed(c) => generate_constraint_zeroed(f, c),
         Constraint::Mut(c) => generate_constraint_mut(f, c),
         Constraint::HasOne(c) => generate_constraint_has_one(f, c),
         Constraint::Signer(c) => generate_constraint_signer(f, c),
@@ -140,6 +140,12 @@ pub fn generate_constraint_init(_f: &Field, _c: &ConstraintInit) -> proc_macro2:
     quote! {}
 }
 
+pub fn generate_constraint_zeroed(_f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream {
+    // No constraint. The zero discriminator is checked in `try_accounts_init`
+    // currently.
+    quote! {}
+}
+
 pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2::TokenStream {
     let field = &f.ident;
     let target = &c.sol_dest;

+ 1 - 1
lang/syn/src/codegen/accounts/try_accounts.rs

@@ -38,7 +38,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
                         }
                     } else {
                         let name = typed_ident(f);
-                        match f.constraints.is_init() {
+                        match f.constraints.is_init() || f.constraints.is_zeroed() {
                             false => quote! {
                                 #[cfg(feature = "anchor-debug")]
                                 ::solana_program::log::sol_log(stringify!(#name));

+ 10 - 0
lang/syn/src/lib.rs

@@ -263,6 +263,7 @@ pub struct ErrorCode {
 #[derive(Debug, Default, Clone)]
 pub struct ConstraintGroup {
     init: Option<ConstraintInit>,
+    zeroed: Option<ConstraintZeroed>,
     mutable: Option<ConstraintMut>,
     signer: Option<ConstraintSigner>,
     owner: Option<ConstraintOwner>,
@@ -282,6 +283,10 @@ impl ConstraintGroup {
         self.init.is_some()
     }
 
+    pub fn is_zeroed(&self) -> bool {
+        self.zeroed.is_some()
+    }
+
     pub fn is_mutable(&self) -> bool {
         self.mutable.is_some()
     }
@@ -302,6 +307,7 @@ impl ConstraintGroup {
 #[derive(Debug)]
 pub enum Constraint {
     Init(ConstraintInit),
+    Zeroed(ConstraintZeroed),
     Mut(ConstraintMut),
     Signer(ConstraintSigner),
     HasOne(ConstraintHasOne),
@@ -321,6 +327,7 @@ pub enum Constraint {
 #[derive(Debug)]
 pub enum ConstraintToken {
     Init(Context<ConstraintInit>),
+    Zeroed(Context<ConstraintZeroed>),
     Mut(Context<ConstraintMut>),
     Signer(Context<ConstraintSigner>),
     HasOne(Context<ConstraintHasOne>),
@@ -351,6 +358,9 @@ impl Parse for ConstraintToken {
 #[derive(Debug, Clone)]
 pub struct ConstraintInit {}
 
+#[derive(Debug, Clone)]
+pub struct ConstraintZeroed {}
+
 #[derive(Debug, Clone)]
 pub struct ConstraintMut {}
 

+ 41 - 1
lang/syn/src/parser/accounts/constraints.rs

@@ -62,6 +62,7 @@ pub fn parse_token(stream: ParseStream) -> ParseResult<ConstraintToken> {
 
     let c = match kw.as_str() {
         "init" => ConstraintToken::Init(Context::new(ident.span(), ConstraintInit {})),
+        "zero" => ConstraintToken::Zeroed(Context::new(ident.span(), ConstraintZeroed {})),
         "mut" => ConstraintToken::Mut(Context::new(ident.span(), ConstraintMut {})),
         "signer" => ConstraintToken::Signer(Context::new(ident.span(), ConstraintSigner {})),
         "executable" => {
@@ -229,6 +230,7 @@ pub fn parse_token(stream: ParseStream) -> ParseResult<ConstraintToken> {
 pub struct ConstraintGroupBuilder<'ty> {
     pub f_ty: Option<&'ty Ty>,
     pub init: Option<Context<ConstraintInit>>,
+    pub zeroed: Option<Context<ConstraintZeroed>>,
     pub mutable: Option<Context<ConstraintMut>>,
     pub signer: Option<Context<ConstraintSigner>>,
     pub has_one: Vec<Context<ConstraintHasOne>>,
@@ -255,6 +257,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
         Self {
             f_ty,
             init: None,
+            zeroed: None,
             mutable: None,
             signer: None,
             has_one: Vec::new(),
@@ -290,6 +293,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
                     .mutable
                     .replace(Context::new(i.span(), ConstraintMut {})),
             };
+            // Rent exempt if not explicitly skipped.
             if self.rent_exempt.is_none() {
                 self.rent_exempt
                     .replace(Context::new(i.span(), ConstraintRentExempt::Enforce));
@@ -297,6 +301,25 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
         }
 
         // Seeds.
+        if let Some(z) = &self.zeroed {
+            match self.mutable {
+                Some(m) => {
+                    return Err(ParseError::new(
+                        m.span(),
+                        "mut cannot be provided with zeroed",
+                    ))
+                }
+                None => self
+                    .mutable
+                    .replace(Context::new(z.span(), ConstraintMut {})),
+            };
+            // Rent exempt if not explicitly skipped.
+            if self.rent_exempt.is_none() {
+                self.rent_exempt
+                    .replace(Context::new(z.span(), ConstraintRentExempt::Enforce));
+            }
+        }
+
         if let Some(i) = &self.seeds {
             if self.init.is_some() && self.payer.is_none() {
                 return Err(ParseError::new(
@@ -365,6 +388,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
         let ConstraintGroupBuilder {
             f_ty: _,
             init,
+            zeroed,
             mutable,
             signer,
             has_one,
@@ -413,6 +437,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
         let is_init = init.is_some();
         Ok(ConstraintGroup {
             init: into_inner!(init),
+            zeroed: into_inner!(zeroed),
             mutable: into_inner!(mutable),
             signer: into_inner!(signer),
             has_one: into_inner_vec!(has_one),
@@ -469,6 +494,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
     pub fn add(&mut self, c: ConstraintToken) -> ParseResult<()> {
         match c {
             ConstraintToken::Init(c) => self.add_init(c),
+            ConstraintToken::Zeroed(c) => self.add_zeroed(c),
             ConstraintToken::Mut(c) => self.add_mut(c),
             ConstraintToken::Signer(c) => self.add_signer(c),
             ConstraintToken::HasOne(c) => self.add_has_one(c),
@@ -495,10 +521,24 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
         if self.init.is_some() {
             return Err(ParseError::new(c.span(), "init already provided"));
         }
+        if self.zeroed.is_some() {
+            return Err(ParseError::new(c.span(), "zeroed already provided"));
+        }
         self.init.replace(c);
         Ok(())
     }
 
+    fn add_zeroed(&mut self, c: Context<ConstraintZeroed>) -> ParseResult<()> {
+        if self.zeroed.is_some() {
+            return Err(ParseError::new(c.span(), "zeroed already provided"));
+        }
+        if self.init.is_some() {
+            return Err(ParseError::new(c.span(), "init already provided"));
+        }
+        self.zeroed.replace(c);
+        Ok(())
+    }
+
     fn add_close(&mut self, c: Context<ConstraintClose>) -> ParseResult<()> {
         if !matches!(self.f_ty, Some(Ty::ProgramAccount(_)))
             && !matches!(self.f_ty, Some(Ty::Loader(_)))
@@ -700,7 +740,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
         if self.seeds.is_none() {
             return Err(ParseError::new(
                 c.span(),
-                "associated or seeds must be provided before space",
+                "init must be provided before space",
             ));
         }
         if self.space.is_some() {