diff --git a/examples/cashiers-check/programs/cashiers-check/src/lib.rs b/examples/cashiers-check/programs/cashiers-check/src/lib.rs index 4088b00b1..bc9a4947d 100644 --- a/examples/cashiers-check/programs/cashiers-check/src/lib.rs +++ b/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")] diff --git a/examples/chat/programs/chat/src/lib.rs b/examples/chat/programs/chat/src/lib.rs index e491b9bc9..05e29c86e 100644 --- a/examples/chat/programs/chat/src/lib.rs +++ b/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>, } diff --git a/examples/composite/programs/composite/src/lib.rs b/examples/composite/programs/composite/src/lib.rs index ee9f4f9d4..a448364e1 100644 --- a/examples/composite/programs/composite/src/lib.rs +++ b/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>, } diff --git a/examples/errors/programs/errors/src/lib.rs b/examples/errors/programs/errors/src/lib.rs index f3efd32fe..05cf759dc 100644 --- a/examples/errors/programs/errors/src/lib.rs +++ b/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>, } diff --git a/examples/escrow/programs/escrow/src/lib.rs b/examples/escrow/programs/escrow/src/lib.rs index 36a89ea97..e473d30eb 100644 --- a/examples/escrow/programs/escrow/src/lib.rs +++ b/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>, } diff --git a/examples/ido-pool/programs/ido-pool/src/lib.rs b/examples/ido-pool/programs/ido-pool/src/lib.rs index 814054389..a079a6187 100644 --- a/examples/ido-pool/programs/ido-pool/src/lib.rs +++ b/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( diff --git a/examples/lockup/programs/lockup/src/lib.rs b/examples/lockup/programs/lockup/src/lib.rs index bda9a88cd..63b65891e 100644 --- a/examples/lockup/programs/lockup/src/lib.rs +++ b/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>, diff --git a/examples/lockup/programs/registry/src/lib.rs b/examples/lockup/programs/registry/src/lib.rs index 78e04f1b8..2068f8550 100644 --- a/examples/lockup/programs/registry/src/lib.rs +++ b/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>, diff --git a/examples/misc/programs/misc/src/context.rs b/examples/misc/programs/misc/src/context.rs index 931a40cd4..7a1a2525f 100644 --- a/examples/misc/programs/misc/src/context.rs +++ b/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>, } diff --git a/examples/multisig/programs/multisig/src/lib.rs b/examples/multisig/programs/multisig/src/lib.rs index 23adeb18c..9942abf0c 100644 --- a/examples/multisig/programs/multisig/src/lib.rs +++ b/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)] diff --git a/examples/tutorial/basic-1/programs/basic-1/src/lib.rs b/examples/tutorial/basic-1/programs/basic-1/src/lib.rs index 1863addd2..62741ca26 100644 --- a/examples/tutorial/basic-1/programs/basic-1/src/lib.rs +++ b/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>, } diff --git a/examples/tutorial/basic-2/programs/basic-2/src/lib.rs b/examples/tutorial/basic-2/programs/basic-2/src/lib.rs index f0e8d963c..3a11aa54d 100644 --- a/examples/tutorial/basic-2/programs/basic-2/src/lib.rs +++ b/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>, } diff --git a/examples/tutorial/basic-3/programs/puppet/src/lib.rs b/examples/tutorial/basic-3/programs/puppet/src/lib.rs index 704ffba82..09762ec84 100644 --- a/examples/tutorial/basic-3/programs/puppet/src/lib.rs +++ b/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>, } diff --git a/examples/zero-copy/programs/zero-copy/src/lib.rs b/examples/zero-copy/programs/zero-copy/src/lib.rs index afac4e19e..04947a1a3 100644 --- a/examples/zero-copy/programs/zero-copy/src/lib.rs +++ b/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>, } diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index f9db2988a..21fab6f41 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/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 { let ConstraintGroup { init, + zeroed, mutable, signer, has_one, @@ -70,6 +66,9 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { 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 { 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; diff --git a/lang/syn/src/codegen/accounts/try_accounts.rs b/lang/syn/src/codegen/accounts/try_accounts.rs index 80a056620..f378ead7b 100644 --- a/lang/syn/src/codegen/accounts/try_accounts.rs +++ b/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)); diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 0041ee9b1..0e69da6d8 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -263,6 +263,7 @@ pub struct ErrorCode { #[derive(Debug, Default, Clone)] pub struct ConstraintGroup { init: Option, + zeroed: Option, mutable: Option, signer: Option, owner: Option, @@ -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), + Zeroed(Context), Mut(Context), Signer(Context), HasOne(Context), @@ -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 {} diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs index e4bc55bdd..6cea62da1 100644 --- a/lang/syn/src/parser/accounts/constraints.rs +++ b/lang/syn/src/parser/accounts/constraints.rs @@ -62,6 +62,7 @@ pub fn parse_token(stream: ParseStream) -> ParseResult { 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 { pub struct ConstraintGroupBuilder<'ty> { pub f_ty: Option<&'ty Ty>, pub init: Option>, + pub zeroed: Option>, pub mutable: Option>, pub signer: Option>, pub has_one: Vec>, @@ -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) -> 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) -> 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() {