diff --git a/program/src/processor/allocate.rs b/program/src/processor/allocate.rs index f99006f..a0c2bfc 100644 --- a/program/src/processor/allocate.rs +++ b/program/src/processor/allocate.rs @@ -1,7 +1,6 @@ use pinocchio::{ cpi::{Seed, Signer}, error::ProgramError, - sysvars::{rent::Rent, Sysvar}, AccountView, ProgramResult, }; use pinocchio_system::instructions::{Allocate, Assign}; @@ -40,7 +39,8 @@ pub fn allocate(accounts: &mut [AccountView], instruction_data: &[u8]) -> Progra // buffer // - if pda, must have the correct derivation + seed; otherwise must be // a signer (match the authority) - // - must be rent exempt (pre-funded account) + // - must have lamports (pre-funded account); the runtime will ensure + // that the account is rent exempt let (is_pda, bump, canonical) = if buffer.address() == authority.address() { // A keypair buffer does not require a `seed` value. @@ -139,10 +139,9 @@ pub fn allocate(accounts: &mut [AccountView], instruction_data: &[u8]) -> Progra _ => return Err(ProgramError::InvalidAccountData), } - // `buffer` length is within the permitted limit. - let minimum_balance = Rent::get()?.minimum_balance_unchecked(buffer.data_len()); - - if buffer.lamports() < minimum_balance { + // The buffer account must have non-zero lamports. The runtime will then + // ensure that the account is rent exempt. + if buffer.lamports() == 0 { return Err(ProgramError::AccountNotRentExempt); } diff --git a/program/src/processor/extend.rs b/program/src/processor/extend.rs index d0e4fc4..a3ab5e5 100644 --- a/program/src/processor/extend.rs +++ b/program/src/processor/extend.rs @@ -1,10 +1,5 @@ use core::mem::size_of; -use pinocchio::{ - account::AccountView, - error::ProgramError, - sysvars::{rent::Rent, Sysvar}, - ProgramResult, Resize, -}; +use pinocchio::{account::AccountView, error::ProgramError, ProgramResult, Resize}; use crate::state::{buffer::Buffer, AccountDiscriminator}; @@ -38,8 +33,8 @@ pub fn extend(accounts: &mut [AccountView], instruction_data: &[u8]) -> ProgramR // - authority must be a signer (validated by `validate_authority`) // - must be a buffer or metadata account // - must have a valid authority - // - must be rent exempt (pre-funded account) since we are reallocating the buffer - // account + // - must be rent exempt (pre-funded account) since we are reallocating the + // account (checked by the runtime) if account.is_data_empty() { return Err(ProgramError::InvalidAccountData); @@ -62,18 +57,11 @@ pub fn extend(accounts: &mut [AccountView], instruction_data: &[u8]) -> ProgramR } } + // Reallocates the account size. + // The length of the data is never more than `10_000_000`; adding a `u16` // will never overflow the `usize` limit. let length = account.data_len() + extend_length as usize; - - let minimum_balance = Rent::get()?.try_minimum_balance(length)?; - - if account.lamports() < minimum_balance { - return Err(ProgramError::AccountNotRentExempt); - } - - // Reallocates the account size. - // SAFETY: `account` is not borrowed at this point. unsafe { account.resize_unchecked(length) } } diff --git a/program/src/processor/initialize.rs b/program/src/processor/initialize.rs index 89a7f24..dd9846a 100644 --- a/program/src/processor/initialize.rs +++ b/program/src/processor/initialize.rs @@ -4,7 +4,6 @@ use pinocchio::{ cpi::{Seed, Signer}, error::ProgramError, instruction::seeds, - sysvars::{rent::Rent, Sysvar}, AccountView, Address, ProgramResult, }; use pinocchio_system::instructions::{Allocate, Assign}; @@ -63,6 +62,8 @@ pub fn initialize(accounts: &mut [AccountView], instruction_data: &[u8]) -> Prog // the remaining instruction data is used as the metadata account data; OR be a // pre-allocated buffer (i.e. `discriminator = 1`), in which case, no remaining // instruction data is allowed as the data must already be written to the account + // - must have lamports (pre-funded account); the runtime will ensure that the + // account is rent exempt let (derived_metadata, bump) = if canonical { derive_program_address(&[program.address().as_array(), args.seed.as_ref()], &ID) @@ -150,13 +151,6 @@ pub fn initialize(accounts: &mut [AccountView], instruction_data: &[u8]) -> Prog } .invoke_signed(signer)?; - // `space` is guranteed to be within the permitted limits. - let minimum_balance = Rent::get()?.minimum_balance_unchecked(space); - - if metadata.lamports() < minimum_balance { - return Err(ProgramError::AccountNotRentExempt); - } - // SAFETY: scoped mutable borrow of `metadata` account data. The data is // guaranteed to be allocated and assigned to the program. let metadata_account_data = unsafe { metadata.borrow_unchecked_mut() }; @@ -179,6 +173,12 @@ pub fn initialize(accounts: &mut [AccountView], instruction_data: &[u8]) -> Prog } }; + // The metadata account must have lamports. The runtime will + // then ensure that the account is rent exempt. + if metadata.lamports() == 0 { + return Err(ProgramError::AccountNotRentExempt); + } + // Initialize the metadata account. // SAFETY: there are no other active borrows to `metadata` account data and diff --git a/program/src/processor/set_data.rs b/program/src/processor/set_data.rs index 44a0d11..100fc4e 100644 --- a/program/src/processor/set_data.rs +++ b/program/src/processor/set_data.rs @@ -46,6 +46,8 @@ pub fn set_data(accounts: &mut [AccountView], instruction_data: &[u8]) -> Progra // metadata // - must be initialized // - must be mutable + // - must be rent exempt (pre-funded account) since we are reallocating the + // account (checked by the runtime) // SAFETY: Scoped immutable borrow of `metadata` account data for validation. let metadata_account_data = unsafe { metadata.borrow_unchecked() }; diff --git a/program/src/processor/write.rs b/program/src/processor/write.rs index 60efd3a..90be4c5 100644 --- a/program/src/processor/write.rs +++ b/program/src/processor/write.rs @@ -1,10 +1,6 @@ use core::cmp::max; -use pinocchio::{ - error::ProgramError, - sysvars::{rent::Rent, Sysvar}, - AccountView, ProgramResult, Resize, -}; +use pinocchio::{error::ProgramError, AccountView, ProgramResult, Resize}; use crate::state::{buffer::Buffer, header::Header, AccountDiscriminator}; @@ -35,8 +31,8 @@ pub fn write(accounts: &mut [AccountView], instruction_data: &[u8]) -> ProgramRe // target_buffer // - must be initialized - // - must be rent exempt (pre-funded account) since we are reallocating the buffer - // account + // - must be rent exempt (pre-funded account) since we are reallocating + // the account (checked by the runtime) // // source_buffer (if `args.data()` is empty) // - must be initialized @@ -95,12 +91,6 @@ pub fn write(accounts: &mut [AccountView], instruction_data: &[u8]) -> ProgramRe (max(data.len(), offset + source_data.len()), source_data) }; - let minimum_balance = Rent::get()?.try_minimum_balance(required_length)?; - - if target_buffer.lamports() < minimum_balance { - return Err(ProgramError::AccountNotRentExempt); - } - // Writes the source data to the buffer account. // SAFETY: `target_buffer` account is not borrowed at this point. diff --git a/program/tests/extend.rs b/program/tests/extend.rs index ad11d0e..9cda525 100644 --- a/program/tests/extend.rs +++ b/program/tests/extend.rs @@ -299,31 +299,6 @@ fn fail_extend_with_wrong_authority() { ); } -#[test] -fn fail_extend_without_rent_for_growth() { - let buffer_key = Pubkey::new_unique(); - let buffer_account = - create_funded_account(minimum_balance_for(Buffer::LEN), system_program::ID); - - process_instructions( - &[ - ( - &allocate(&buffer_key, &buffer_key, None, None, None), - &[Check::success()], - ), - ( - &extend(&buffer_key, &buffer_key, None, None, 1), - &[Check::err(ProgramError::AccountNotRentExempt)], - ), - ], - &[ - (buffer_key, buffer_account), - (PROGRAM_ID, Account::default()), - keyed_account_for_system_program(), - ], - ); -} - #[test] fn fail_extend_uninitialized_account() { let account_key = Pubkey::new_unique(); diff --git a/program/tests/initialize.rs b/program/tests/initialize.rs index 44088a5..6b737cd 100644 --- a/program/tests/initialize.rs +++ b/program/tests/initialize.rs @@ -378,7 +378,7 @@ fn fail_initialize_with_wrong_metadata_pda() { } #[test] -fn fail_initialize_without_rent_exemption() { +fn fail_initialize_unfunded_metadata() { let authority_key = Pubkey::new_unique(); let program_data_key = Pubkey::new_unique(); diff --git a/program/tests/write.rs b/program/tests/write.rs index b5d89a5..5fa17c6 100644 --- a/program/tests/write.rs +++ b/program/tests/write.rs @@ -265,28 +265,3 @@ fn fail_write_from_same_buffer() { ], ); } - -#[test] -fn fail_write_without_rent_for_growth() { - let buffer_key = Pubkey::new_unique(); - let buffer_account = - create_funded_account(minimum_balance_for(Buffer::LEN), system_program::ID); - - process_instructions( - &[ - ( - &allocate(&buffer_key, &buffer_key, None, None, None), - &[Check::success()], - ), - ( - &write(&buffer_key, &buffer_key, None, 0, &[1]), - &[Check::err(ProgramError::AccountNotRentExempt)], - ), - ], - &[ - (buffer_key, buffer_account), - (PROGRAM_ID, Account::default()), - keyed_account_for_system_program(), - ], - ); -}