Browse Source

lang: remove default space calc (#1519)

Paul 3 years ago
parent
commit
c8d8cacd22

+ 4 - 0
CHANGELOG.md

@@ -31,6 +31,10 @@ incremented for features.
 
 * cli: Fix rust template ([#1488](https://github.com/project-serum/anchor/pull/1488)).
 
+### Breaking
+
+* lang: Remove space calculation using [`#[derive(Default)]`] (https://github.com/project-serum/anchor/pull/1519).
+
 ## [0.22.0] - 2022-02-20
 
 ### Features

+ 8 - 18
lang/derive/accounts/src/lib.rs

@@ -89,7 +89,6 @@ use syn::parse_macro_input;
 ///         </tr>
 ///         <tr>
 ///             <td>
-///                 <code>#[account(init, payer = &lt;target_account&gt;)]</code><br><br>
 ///                 <code>#[account(init, payer = &lt;target_account&gt;, space = &lt;num_bytes&gt;)]</code>
 ///             </td>
 ///             <td>
@@ -110,14 +109,12 @@ use syn::parse_macro_input;
 ///                         and be called <code>system_program</code>.
 ///                     </li>
 ///                     <li>
-///                         Requires that the <code>space</code> constraint is specified
-///                         or, if creating an <code>Account</code> type, the <code>T</code> of <code>Account</code>
-///                         to implement the rust std <code>Default</code> trait.<br>
+///                         Requires that the <code>space</code> constraint is specified.
 ///                         When using the <code>space</code> constraint, one must remember to add 8 to it
-///                         which is the size of the account discriminator.<br>
-///                         The given number is the size of the account in bytes, so accounts that hold
-///                         a variable number of items such as a <code>Vec</code> should use the <code>space</code>
-///                         constraint instead of using the <code>Default</code> trait and allocate sufficient space for all items that may
+///                         which is the size of the account discriminator. This only has to be done
+///                         for accounts owned by anchor programs.<br>
+///                         The given space number is the size of the account in bytes, so accounts that hold
+///                         a variable number of items such as a <code>Vec</code> should allocate sufficient space for all items that may
 ///                         be added to the data structure because account size is fixed. Check out the <a href = "https://borsh.io/" target = "_blank" rel = "noopener noreferrer">borsh library</a>
 ///                         (which anchor uses under the hood for serialization) specification to learn how much
 ///                         space different data structures require.
@@ -126,20 +123,13 @@ use syn::parse_macro_input;
 ///                 Example:
 ///                 <pre>
 /// #[account]
-/// #[derive(Default)]
 /// pub struct MyData {
 /// &nbsp;&nbsp;&nbsp;&nbsp;pub data: u64
 /// }&#10;
-/// #[account]
-/// pub struct OtherData {
-/// &nbsp;&nbsp;&nbsp;&nbsp;pub data: u64
-/// }&#10;
 /// #[derive(Accounts)]
 /// pub struct Initialize<'info> {
-/// &nbsp;&nbsp;&nbsp;&nbsp;#[account(init, payer = payer)]
-/// &nbsp;&nbsp;&nbsp;&nbsp;pub data_account: Account<'info, MyData>,
 /// &nbsp;&nbsp;&nbsp;&nbsp;#[account(init, payer = payer, space = 8 + 8)]
-/// &nbsp;&nbsp;&nbsp;&nbsp;pub data_account_two: Account<'info, OtherData>,
+/// &nbsp;&nbsp;&nbsp;&nbsp;pub data_account_two: Account<'info, MyData>,
 /// &nbsp;&nbsp;&nbsp;&nbsp;#[account(mut)]
 /// &nbsp;&nbsp;&nbsp;&nbsp;pub payer: Signer<'info>,
 /// &nbsp;&nbsp;&nbsp;&nbsp;pub system_program: Program<'info, System>,
@@ -172,7 +162,7 @@ use syn::parse_macro_input;
 /// #[instruction(bump: u8)]
 /// pub struct Initialize<'info> {
 /// &nbsp;&nbsp;&nbsp;&nbsp;#[account(
-/// &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;init, payer = payer,
+/// &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;init, payer = payer, space = 8 + 8
 /// &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;seeds = [b"example_seed".as_ref()], bump = bump
 /// &nbsp;&nbsp;&nbsp;&nbsp;)]
 /// &nbsp;&nbsp;&nbsp;&nbsp;pub pda_data_account: Account<'info, MyData>,
@@ -182,7 +172,7 @@ use syn::parse_macro_input;
 /// &nbsp;&nbsp;&nbsp;&nbsp;)]
 /// &nbsp;&nbsp;&nbsp;&nbsp;pub account_for_other_program: AccountInfo<'info>,
 /// &nbsp;&nbsp;&nbsp;&nbsp;#[account(
-/// &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;init,payer = payer, space = 8 + 8,
+/// &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;init, payer = payer, space = 8 + 8,
 /// &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;owner = other_program.key(),
 /// &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;seeds = [b"other_seed".as_ref()], bump
 /// &nbsp;&nbsp;&nbsp;&nbsp;)]

+ 1 - 23
lang/syn/src/codegen/accounts/constraints.rs

@@ -480,29 +480,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
         }
         InitKind::Program { owner } => {
             // Define the space variable.
-            let space = match space {
-                // If no explicit space param was given, serialize the type to bytes
-                // and take the length (with +8 for the discriminator.)
-                None => {
-                    let account_ty = f.account_ty();
-                    match matches!(f.ty, Ty::Loader(_) | Ty::AccountLoader(_)) {
-                        false => {
-                            quote! {
-                                let space = 8 + #account_ty::default().try_to_vec().unwrap().len();
-                            }
-                        }
-                        true => {
-                            quote! {
-                                let space = 8 + anchor_lang::__private::bytemuck::bytes_of(&#account_ty::default()).len();
-                            }
-                        }
-                    }
-                }
-                // Explicit account size given. Use it.
-                Some(s) => quote! {
-                    let space = #s;
-                },
-            };
+            let space = quote! {let space = #space;};
 
             // Define the owner of the account being created. If not specified,
             // default to the currently executing program.

+ 1 - 1
lang/syn/src/lib.rs

@@ -721,7 +721,7 @@ pub enum ConstraintRentExempt {
 pub struct ConstraintInitGroup {
     pub if_needed: bool,
     pub seeds: Option<ConstraintSeedsGroup>,
-    pub payer: Option<Expr>,
+    pub payer: Expr,
     pub space: Option<Expr>,
     pub kind: InitKind,
 }

+ 23 - 12
lang/syn/src/parser/accounts/constraints.rs

@@ -505,17 +505,28 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
             }
         }
 
-        // SPL Space.
-        if self.init.is_some()
-            && self.seeds.is_some()
-            && self.token_mint.is_some()
-            && (self.mint_authority.is_some() || self.token_authority.is_some())
-            && self.space.is_some()
-        {
-            return Err(ParseError::new(
-                self.space.as_ref().unwrap().span(),
-                "space is not required for initializing an spl account",
-            ));
+        // Space.
+        if let Some(i) = &self.init {
+            let initializing_token_program_acc = self.token_mint.is_some()
+                || self.mint_authority.is_some()
+                || self.token_authority.is_some()
+                || self.associated_token_authority.is_some();
+
+            match (self.space.is_some(), initializing_token_program_acc) {
+                (true, true) => {
+                    return Err(ParseError::new(
+                        self.space.as_ref().unwrap().span(),
+                        "space is not required for initializing an spl account",
+                    ));
+                }
+                (false, false) => {
+                    return Err(ParseError::new(
+                        i.span(),
+                        "space must be provided with init",
+                    ));
+                }
+                _ => (),
+            }
         }
 
         let ConstraintGroupBuilder {
@@ -593,7 +604,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
             init: init.as_ref().map(|i| Ok(ConstraintInitGroup {
                 if_needed: i.if_needed,
                 seeds: seeds.clone(),
-                payer: into_inner!(payer.clone()).map(|a| a.target),
+                payer: into_inner!(payer.clone()).unwrap().target,
                 space: space.clone().map(|s| s.space.clone()),
                 kind: if let Some(tm) = &token_mint {
                     InitKind::Token {

+ 1 - 2
lang/syn/src/parser/accounts/mod.rs

@@ -93,8 +93,7 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> {
 
         for field in init_fields {
             // Get payer for init-ed account
-            let associated_payer_name = match field.constraints.init.clone().unwrap().payer.unwrap()
-            {
+            let associated_payer_name = match field.constraints.init.clone().unwrap().payer {
                 // composite payer, check not supported
                 Expr::Field(_) => continue,
                 field_name => field_name.to_token_stream().to_string(),

+ 6 - 3
tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/src/lib.rs

@@ -32,11 +32,14 @@ pub mod bpf_upgradeable_state {
 }
 
 #[account]
-#[derive(Default, Debug)]
 pub struct Settings {
     admin_data: u64,
 }
 
+impl Settings {
+    pub const LEN: usize = 8;
+}
+
 #[error_code]
 pub enum CustomError {
     InvalidProgramDataAddress,
@@ -49,7 +52,7 @@ pub struct SetAdminSettings<'info> {
     // In a real program, this should be a PDA,
     // so the authority cannot create multiple settings accounts.
     // Not done here for easier testing
-    #[account(init, payer = authority)]
+    #[account(init, payer = authority, space = Settings::LEN + 8)]
     pub settings: Account<'info, Settings>,
     #[account(mut)]
     pub authority: Signer<'info>,
@@ -65,7 +68,7 @@ pub struct SetAdminSettingsUseProgramState<'info> {
     // In a real program, this should be a PDA,
     // so the authority cannot create multiple settings accounts.
     // Not done here for easier testing
-    #[account(init, payer = authority)]
+    #[account(init, payer = authority, space = Settings::LEN + 8)]
     pub settings: Account<'info, Settings>,
     #[account(mut)]
     pub authority: Signer<'info>,

+ 42 - 26
tests/cfo/programs/cfo/src/lib.rs

@@ -324,6 +324,7 @@ pub struct CreateOfficer<'info> {
         seeds = [dex_program.key.as_ref()],
         bump,
         payer = authority,
+        space = Officer::LEN + 8
     )]
     officer: Box<Account<'info, Officer>>,
     #[account(
@@ -332,7 +333,7 @@ pub struct CreateOfficer<'info> {
         bump,
         payer = authority,
         token::mint = srm_mint,
-        token::authority = officer,
+        token::authority = officer
     )]
     srm_vault: Box<Account<'info, TokenAccount>>,
     #[account(
@@ -341,7 +342,7 @@ pub struct CreateOfficer<'info> {
         bump,
         payer = authority,
         token::mint = usdc_mint,
-        token::authority = officer,
+        token::authority = officer
     )]
     usdc_vault: Box<Account<'info, TokenAccount>>,
     #[account(
@@ -350,7 +351,7 @@ pub struct CreateOfficer<'info> {
         bump,
         payer = authority,
         token::mint = srm_mint,
-        token::authority = officer,
+        token::authority = officer
     )]
     stake: Box<Account<'info, TokenAccount>>,
     #[account(
@@ -359,7 +360,7 @@ pub struct CreateOfficer<'info> {
         bump,
         payer = authority,
         token::mint = srm_mint,
-        token::authority = officer,
+        token::authority = officer
     )]
     treasury: Box<Account<'info, TokenAccount>>,
     #[account(mut)]
@@ -392,6 +393,7 @@ pub struct AuthorizeMarket<'info> {
         payer = payer,
         seeds = [b"market-auth", officer.key().as_ref(), market.key.as_ref()],
         bump,
+        space = MarketAuth::LEN + 8
     )]
     market_auth: Account<'info, MarketAuth>,
     #[account(mut)]
@@ -421,7 +423,7 @@ pub struct CreateOfficerToken<'info> {
         bump,
         token::mint = mint,
         token::authority = officer,
-        payer = payer,
+        payer = payer
     )]
     token: Account<'info, TokenAccount>,
     mint: Account<'info, Mint>,
@@ -666,28 +668,31 @@ pub struct DropStakeRewardPool<'info> {
 ///
 /// PDA - [dex_program_id].
 #[account]
-#[derive(Default)]
 pub struct Officer {
     // Priviledged account.
-    pub authority: Pubkey,
+    pub authority: Pubkey, // 32
     // Vault holding the officer's SRM tokens prior to distribution.
-    pub srm_vault: Pubkey,
+    pub srm_vault: Pubkey, // 32
     // Escrow SRM vault holding tokens which are dropped onto stakers.
-    pub stake: Pubkey,
+    pub stake: Pubkey, // 32
     // SRM token account to send treasury earned tokens to.
-    pub treasury: Pubkey,
+    pub treasury: Pubkey, // 32
     // Defines the fee distribution, i.e., what percent each fee category gets.
-    pub distribution: Distribution,
+    pub distribution: Distribution, // Distribution::LEN
     // Swap frontend for the dex.
-    pub swap_program: Pubkey,
+    pub swap_program: Pubkey, // 32
     // Dex program the officer is associated with.
-    pub dex_program: Pubkey,
+    pub dex_program: Pubkey, // 32
     // SRM stake pool address
-    pub registrar: Pubkey,
+    pub registrar: Pubkey, // 32
     // MSRM stake pool address.
-    pub msrm_registrar: Pubkey,
+    pub msrm_registrar: Pubkey, // 32
     // Bump seeds for pdas.
-    pub bumps: OfficerBumps,
+    pub bumps: OfficerBumps, // OfficerBumps::LEN
+}
+
+impl Officer {
+    pub const LEN: usize = 8 * 32 + Distribution::LEN + OfficerBumps::LEN;
 }
 
 /// MarketAuth represents an authorization token created by the Officer
@@ -702,26 +707,37 @@ pub struct Officer {
 ///
 /// PDA - [b"market-auth", officer, market_address]
 #[account]
-#[derive(Default)]
 pub struct MarketAuth {
     // Bump seed for this account's PDA.
-    pub bump: u8,
+    pub bump: u8, // 1
+}
+
+impl MarketAuth {
+    pub const LEN: usize = 1;
 }
 
 #[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
 pub struct OfficerBumps {
-    pub bump: u8,
-    pub srm: u8,
-    pub usdc: u8,
-    pub stake: u8,
-    pub treasury: u8,
+    pub bump: u8,     // 1
+    pub srm: u8,      // 1
+    pub usdc: u8,     // 1
+    pub stake: u8,    // 1
+    pub treasury: u8, // 1
+}
+
+impl OfficerBumps {
+    pub const LEN: usize = 5;
 }
 
 #[derive(AnchorSerialize, AnchorDeserialize, Default, Clone)]
 pub struct Distribution {
-    burn: u8,
-    stake: u8,
-    treasury: u8,
+    burn: u8,     // 1
+    stake: u8,    // 1
+    treasury: u8, // 1
+}
+
+impl Distribution {
+    pub const LEN: usize = 3;
 }
 
 // CpiContext transformations.

+ 37 - 27
tests/ido-pool/programs/ido-pool/src/lib.rs

@@ -295,7 +295,9 @@ pub struct InitializePool<'info> {
     #[account(init,
         seeds = [ido_name.as_bytes()],
         bump,
-        payer = ido_authority)]
+        payer = ido_authority,
+        space = IdoAccount::LEN + 8
+    )]
     pub ido_account: Box<Account<'info, IdoAccount>>,
     // TODO Confirm USDC mint address on mainnet or leave open as an option for other stables
     #[account(constraint = usdc_mint.decimals == DECIMALS)]
@@ -305,7 +307,8 @@ pub struct InitializePool<'info> {
         mint::authority = ido_account,
         seeds = [ido_name.as_bytes(), b"redeemable_mint".as_ref()],
         bump,
-        payer = ido_authority)]
+        payer = ido_authority
+    )]
     pub redeemable_mint: Box<Account<'info, Mint>>,
     #[account(constraint = watermelon_mint.key() == ido_authority_watermelon.mint)]
     pub watermelon_mint: Box<Account<'info, Mint>>,
@@ -314,14 +317,16 @@ pub struct InitializePool<'info> {
         token::authority = ido_account,
         seeds = [ido_name.as_bytes(), b"pool_watermelon"],
         bump,
-        payer = ido_authority)]
+        payer = ido_authority
+    )]
     pub pool_watermelon: Box<Account<'info, TokenAccount>>,
     #[account(init,
         token::mint = usdc_mint,
         token::authority = ido_account,
         seeds = [ido_name.as_bytes(), b"pool_usdc"],
         bump,
-        payer = ido_authority)]
+        payer = ido_authority
+    )]
     pub pool_usdc: Box<Account<'info, TokenAccount>>,
     // Programs and Sysvars
     pub system_program: Program<'info, System>,
@@ -341,7 +346,8 @@ pub struct InitUserRedeemable<'info> {
             ido_account.ido_name.as_ref().trim_ascii_whitespace(),
             b"user_redeemable"],
         bump,
-        payer = user_authority)]
+        payer = user_authority
+    )]
     pub user_redeemable: Box<Account<'info, TokenAccount>>,
     // IDO Accounts
     #[account(seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace()],
@@ -401,7 +407,8 @@ pub struct InitEscrowUsdc<'info> {
             ido_account.ido_name.as_ref().trim_ascii_whitespace(),
             b"escrow_usdc"],
         bump,
-        payer = user_authority)]
+        payer = user_authority
+    )]
     pub escrow_usdc: Box<Account<'info, TokenAccount>>,
     #[account(seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace()],
         bump = ido_account.bumps.ido_account,
@@ -541,36 +548,39 @@ pub struct WithdrawFromEscrow<'info> {
 }
 
 #[account]
-#[derive(Default)]
 pub struct IdoAccount {
-    pub ido_name: [u8; 10], // Setting an arbitrary max of ten characters in the ido name.
-    pub bumps: PoolBumps,
-    pub ido_authority: Pubkey,
-
-    pub usdc_mint: Pubkey,
-    pub redeemable_mint: Pubkey,
-    pub watermelon_mint: Pubkey,
-    pub pool_usdc: Pubkey,
-    pub pool_watermelon: Pubkey,
-
-    pub num_ido_tokens: u64,
-    pub ido_times: IdoTimes,
+    pub ido_name: [u8; 10], // Setting an arbitrary max of ten characters in the ido name. // 10
+    pub bumps: PoolBumps,   // 4
+    pub ido_authority: Pubkey, // 32
+
+    pub usdc_mint: Pubkey,       // 32
+    pub redeemable_mint: Pubkey, // 32
+    pub watermelon_mint: Pubkey, // 32
+    pub pool_usdc: Pubkey,       // 32
+    pub pool_watermelon: Pubkey, // 32
+
+    pub num_ido_tokens: u64, // 8
+    pub ido_times: IdoTimes, // 32
+}
+
+impl IdoAccount {
+    pub const LEN: usize = 10 + 4 + 32 + 5 * 32 + 8 + 32;
 }
 
 #[derive(AnchorSerialize, AnchorDeserialize, Default, Clone, Copy)]
 pub struct IdoTimes {
-    pub start_ido: i64,
-    pub end_deposits: i64,
-    pub end_ido: i64,
-    pub end_escrow: i64,
+    pub start_ido: i64,    // 8
+    pub end_deposits: i64, // 8
+    pub end_ido: i64,      // 8
+    pub end_escrow: i64,   // 8
 }
 
 #[derive(AnchorSerialize, AnchorDeserialize, Default, Clone)]
 pub struct PoolBumps {
-    pub ido_account: u8,
-    pub redeemable_mint: u8,
-    pub pool_watermelon: u8,
-    pub pool_usdc: u8,
+    pub ido_account: u8,     // 1
+    pub redeemable_mint: u8, // 1
+    pub pool_watermelon: u8, // 1
+    pub pool_usdc: u8,       // 1
 }
 
 #[error_code]

+ 28 - 15
tests/misc/programs/misc/src/account.rs

@@ -1,50 +1,63 @@
 use anchor_lang::prelude::*;
 
+macro_rules! size {
+    ($name: ident, $size:expr) => {
+        impl $name {
+            pub const LEN: usize = $size;
+        }
+    };
+}
+
 pub const MAX_SIZE: usize = 10;
 
 #[account]
 pub struct Data {
-    pub udata: u128,
-    pub idata: i128,
+    pub udata: u128, // 16
+    pub idata: i128, // 16
 }
+size!(Data, 32);
 
 #[account]
-#[derive(Default)]
 pub struct DataU16 {
-    pub data: u16,
+    pub data: u16, // 2
 }
+size!(DataU16, 32);
 
 #[account]
-#[derive(Default)]
 pub struct DataI8 {
-    pub data: i8,
+    pub data: i8, // 1
 }
+size!(DataI8, 1);
 
 #[account]
 pub struct DataI16 {
-    pub data: i16,
+    pub data: i16, // 2
 }
+size!(DataI16, 2);
 
 #[account(zero_copy)]
-#[derive(Default)]
 pub struct DataZeroCopy {
-    pub data: u16,
-    pub bump: u8,
+    pub data: u16,    // 2
+    pub _padding: u8, // 1
+    pub bump: u8,     // 1
 }
+size!(DataZeroCopy, 4);
 
 #[account]
-#[derive(Default)]
 pub struct DataWithFilter {
-    pub authority: Pubkey,
-    pub filterable: Pubkey,
+    pub authority: Pubkey,  // 32
+    pub filterable: Pubkey, // 32
 }
+size!(DataWithFilter, 64);
 
 #[account]
 pub struct DataMultidimensionalArray {
-    pub data: [[u8; 10]; 10],
+    pub data: [[u8; 10]; 10], // 100
 }
+size!(DataMultidimensionalArray, 100);
 
 #[account]
 pub struct DataConstArraySize {
-    pub data: [u8; MAX_SIZE],
+    pub data: [u8; MAX_SIZE], // 10
 }
+size!(DataConstArraySize, MAX_SIZE);

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

@@ -16,15 +16,17 @@ pub struct TestTokenSeedsInit<'info> {
         payer = authority,
         mint::decimals = 6,
         mint::authority = authority,
+        
     )]
     pub mint: Account<'info, Mint>,
     #[account(
         init,
-        seeds = [b"my-token-seed".as_ref(),],
+        seeds = [b"my-token-seed".as_ref()],
         bump,
         payer = authority,
         token::mint = mint,
         token::authority = authority,
+        
     )]
     pub my_pda: Account<'info, TokenAccount>,
     #[account(mut)]
@@ -42,6 +44,7 @@ pub struct TestInitAssociatedToken<'info> {
         payer = payer,
         associated_token::mint = mint,
         associated_token::authority = payer,
+        
     )]
     pub token: Account<'info, TokenAccount>,
     pub mint: Account<'info, Mint>,
@@ -86,6 +89,7 @@ pub struct TestPdaInit<'info> {
         seeds = [b"my-seed", domain.as_bytes(), foo.key.as_ref(), &seed],
         bump,
         payer = my_payer,
+        space = DataU16::LEN + 8
     )]
     pub my_pda: Account<'info, DataU16>,
     #[account(mut)]
@@ -102,8 +106,9 @@ pub struct TestPdaInitZeroCopy<'info> {
         seeds = [b"my-seed".as_ref()],
         bump,
         payer = my_payer,
+        space = DataZeroCopy::LEN + 8
     )]
-    pub my_pda: Loader<'info, DataZeroCopy>,
+    pub my_pda: AccountLoader<'info, DataZeroCopy>,
     #[account(mut)]
     pub my_payer: Signer<'info>,
     pub system_program: Program<'info, System>,
@@ -116,7 +121,7 @@ pub struct TestPdaMutZeroCopy<'info> {
         seeds = [b"my-seed".as_ref()],
         bump = my_pda.load()?.bump,
     )]
-    pub my_pda: Loader<'info, DataZeroCopy>,
+    pub my_pda: AccountLoader<'info, DataZeroCopy>,
     /// CHECK:
     pub my_payer: AccountInfo<'info>,
 }
@@ -204,7 +209,7 @@ pub struct TestI8<'info> {
 
 #[derive(Accounts)]
 pub struct TestInit<'info> {
-    #[account(init, payer = payer)]
+    #[account(init, payer = payer, space = DataI8::LEN + 8)]
     pub data: Account<'info, DataI8>,
     #[account(mut)]
     pub payer: Signer<'info>,
@@ -213,7 +218,7 @@ pub struct TestInit<'info> {
 
 #[derive(Accounts)]
 pub struct TestInitZeroCopy<'info> {
-    #[account(init, payer = payer, space = 8 + size_of::<DataZeroCopy>())]
+    #[account(init, payer = payer, space = DataZeroCopy::LEN + 8)]
     pub data: Loader<'info, DataZeroCopy>,
     #[account(mut)]
     pub payer: Signer<'info>,
@@ -222,7 +227,7 @@ pub struct TestInitZeroCopy<'info> {
 
 #[derive(Accounts)]
 pub struct TestInitMint<'info> {
-    #[account(init, mint::decimals = 6, mint::authority = payer, mint::freeze_authority = payer, payer = payer)]
+    #[account(init, mint::decimals = 6, mint::authority = payer, mint::freeze_authority = payer, payer = payer, )]
     pub mint: Account<'info, Mint>,
     #[account(mut)]
     pub payer: Signer<'info>,
@@ -233,7 +238,7 @@ pub struct TestInitMint<'info> {
 
 #[derive(Accounts)]
 pub struct TestInitToken<'info> {
-    #[account(init, token::mint = mint, token::authority = payer, payer = payer)]
+    #[account(init, token::mint = mint, token::authority = payer, payer = payer, )]
     pub token: Account<'info, TokenAccount>,
     pub mint: Account<'info, Mint>,
     #[account(mut)]
@@ -246,14 +251,14 @@ pub struct TestInitToken<'info> {
 #[derive(Accounts)]
 pub struct TestCompositePayer<'info> {
     pub composite: TestInit<'info>,
-    #[account(init, payer = composite.payer, space = 8 + size_of::<Data>())]
+    #[account(init, payer = composite.payer, space = Data::LEN + 8)]
     pub data: Account<'info, Data>,
     pub system_program: Program<'info, System>,
 }
 
 #[derive(Accounts)]
 pub struct TestFetchAll<'info> {
-    #[account(init, payer = authority)]
+    #[account(init, payer = authority, space = DataWithFilter::LEN + 8)]
     pub data: Account<'info, DataWithFilter>,
     #[account(mut)]
     pub authority: Signer<'info>,
@@ -262,7 +267,7 @@ pub struct TestFetchAll<'info> {
 
 #[derive(Accounts)]
 pub struct TestInitWithEmptySeeds<'info> {
-    #[account(init, seeds = [], bump, payer = authority, space = 8 + size_of::<Data>())]
+    #[account(init, seeds = [], bump, payer = authority, space = Data::LEN + 8)]
     pub pda: Account<'info, Data>,
     #[account(mut)]
     pub authority: Signer<'info>,
@@ -278,7 +283,7 @@ pub struct TestEmptySeedsConstraint<'info> {
 
 #[derive(Accounts)]
 pub struct InitWithSpace<'info> {
-    #[account(init, payer = payer)]
+    #[account(init, payer = payer, space = DataU16::LEN + 8)]
     pub data: Account<'info, DataU16>,
     #[account(mut)]
     pub payer: Signer<'info>,
@@ -287,7 +292,8 @@ pub struct InitWithSpace<'info> {
 
 #[derive(Accounts)]
 pub struct TestInitIfNeeded<'info> {
-    #[account(init_if_needed, payer = payer, space = 500)]
+    // intentionally using more space (+500) to check whether space is checked when using init_if_needed
+    #[account(init_if_needed, payer = payer, space = DataU16::LEN + 8 + 500)]
     pub data: Account<'info, DataU16>,
     #[account(mut)]
     pub payer: Signer<'info>,
@@ -335,7 +341,7 @@ pub struct TestInitMintIfNeeded<'info> {
 
 #[derive(Accounts)]
 pub struct TestInitTokenIfNeeded<'info> {
-    #[account(init_if_needed, token::mint = mint, token::authority = authority, payer = payer)]
+    #[account(init_if_needed, token::mint = mint, token::authority = authority, payer = payer, )]
     pub token: Account<'info, TokenAccount>,
     pub mint: Account<'info, Mint>,
     #[account(mut)]
@@ -353,7 +359,7 @@ pub struct TestInitAssociatedTokenIfNeeded<'info> {
         init_if_needed,
         payer = payer,
         associated_token::mint = mint,
-        associated_token::authority = authority,
+        associated_token::authority = authority
     )]
     pub token: Account<'info, TokenAccount>,
     pub mint: Account<'info, Mint>,

+ 3 - 2
tests/misc/tests/misc.js

@@ -1002,7 +1002,8 @@ describe("misc", () => {
 
   it("init_if_needed throws if account exists but is not the expected space", async () => {
     const newAcc = anchor.web3.Keypair.generate();
-    await program.rpc.initWithSpace(3, {
+    const _irrelevantForTest = 3;
+    await program.rpc.initWithSpace(_irrelevantForTest, {
       accounts: {
         data: newAcc.publicKey,
         systemProgram: anchor.web3.SystemProgram.programId,
@@ -1012,7 +1013,7 @@ describe("misc", () => {
     });
 
     try {
-      await program.rpc.testInitIfNeeded(3, {
+      await program.rpc.testInitIfNeeded(_irrelevantForTest, {
         accounts: {
           data: newAcc.publicKey,
           systemProgram: anchor.web3.SystemProgram.programId,

+ 9 - 5
tests/zero-copy/programs/zero-copy/src/lib.rs

@@ -97,7 +97,8 @@ pub struct CreateBar<'info> {
         init,
         seeds = [authority.key().as_ref(), foo.key().as_ref()],
         bump,
-        payer = authority, owner = *program_id
+        payer = authority, owner = *program_id,
+        space = Bar::LEN + 8
     )]
     bar: AccountLoader<'info, Bar>,
     #[account(mut)]
@@ -132,8 +133,8 @@ pub struct UpdateLargeAccount<'info> {
 }
 
 #[account(zero_copy)]
-#[derive(Default)]
 #[repr(packed)]
+#[derive(Default)]
 pub struct Foo {
     pub authority: Pubkey,
     pub data: u64,
@@ -143,10 +144,13 @@ pub struct Foo {
 }
 
 #[account(zero_copy)]
-#[derive(Default)]
 pub struct Bar {
-    pub authority: Pubkey,
-    pub data: u64,
+    pub authority: Pubkey, // 32
+    pub data: u64,         // 8
+}
+
+impl Bar {
+    pub const LEN: usize = 32 + 8;
 }
 
 #[account(zero_copy)]