Solana CookbookWriting Programs
How to verify accounts in a Solana program
Since programs in Solana are stateless, we as a program creator have to make sure the accounts passed are validated as much as possible to avoid any malicious account entry. The basic checks one can do are
- Check if the expected signer account has actually signed
- Check if the expected state account's have been checked as writable
- Check if the expected state account's owner is the called program id
- If initializing the state for the first time, check if the account's already been initialized or not.
- Check if any cross program ids passed (whenever needed) are as expected.
A basic instruction which initializes a hero state account, but with the above mentioned checks is defined below
verify-accounts.rs
use borsh::{BorshDeserialize, BorshSerialize};use solana_program::{account_info::{next_account_info, AccountInfo},clock::Clock,entrypoint,entrypoint::ProgramResult,msg,program_error::ProgramError,pubkey::Pubkey,rent::Rent,system_program::ID as SYSTEM_PROGRAM_ID,sysvar::Sysvar,};entrypoint!(process_instruction);#[derive(BorshSerialize, BorshDeserialize, Debug)]pub struct HelloState {is_initialized: bool,}// Accounts required/// 1. [signer] Payer/// 2. [writable] Hello state account/// 3. [] System Programpub fn process_instruction(program_id: &Pubkey,accounts: &[AccountInfo],_instruction_data: &[u8],) -> ProgramResult {let accounts_iter = &mut accounts.iter();// Payer accountlet payer_account = next_account_info(accounts_iter)?;// Hello state accountlet hello_state_account = next_account_info(accounts_iter)?;// System Programlet system_program = next_account_info(accounts_iter)?;let rent = Rent::get()?;// Checking if payer account is the signerif !payer_account.is_signer {return Err(ProgramError::MissingRequiredSignature);}// Checking if hello state account is rent exemptif !rent.is_exempt(hello_state_account.lamports(), 1) {return Err(ProgramError::AccountNotRentExempt);}// Checking if hello state account is writableif !hello_state_account.is_writable {return Err(ProgramError::InvalidAccountData);}// Checking if hello state account's owner is the current programif hello_state_account.owner.ne(&program_id) {return Err(ProgramError::IllegalOwner);}// Checking if the system program is validif system_program.key.ne(&SYSTEM_PROGRAM_ID) {return Err(ProgramError::IncorrectProgramId);}let mut hello_state = HelloState::try_from_slice(&hello_state_account.data.borrow())?;// Checking if the state has already been initializedif hello_state.is_initialized {return Err(ProgramError::AccountAlreadyInitialized);}hello_state.is_initialized = true;hello_state.serialize(&mut &mut hello_state_account.data.borrow_mut()[..])?;msg!("Account initialized :)");Ok(())}