From 6d0e447017963cc402daf54e0524b43bf725ba99 Mon Sep 17 00:00:00 2001 From: Hardhat Chad Date: Fri, 23 Aug 2024 17:26:33 +0000 Subject: [PATCH] refactor loaders into utils --- api/src/loaders.rs | 200 +------------------------------------- program/src/claim.rs | 2 +- program/src/close.rs | 2 +- program/src/initialize.rs | 5 +- program/src/lib.rs | 2 - program/src/mine.rs | 2 +- program/src/open.rs | 4 +- program/src/reset.rs | 2 +- program/src/stake.rs | 4 +- program/src/update.rs | 3 +- program/src/upgrade.rs | 4 +- utils/src/lib.rs | 1 + utils/src/loaders.rs | 200 ++++++++++++++++++++++++++++++++++++++ 13 files changed, 214 insertions(+), 217 deletions(-) create mode 100644 utils/src/loaders.rs diff --git a/api/src/loaders.rs b/api/src/loaders.rs index f3114dd..ce4e13e 100644 --- a/api/src/loaders.rs +++ b/api/src/loaders.rs @@ -1,25 +1,11 @@ -use solana_program::{ - account_info::AccountInfo, program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, - system_program, sysvar, -}; -use spl_token::state::Mint; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; use crate::{ consts::*, state::{Bus, Config, Proof, Treasury}, - utils::{AccountDeserialize, Discriminator}, + utils::{loaders::*, AccountDeserialize, Discriminator}, }; -/// Errors if: -/// - Account is not a signer. -pub fn load_signer<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramError> { - if !info.is_signer { - return Err(ProgramError::MissingRequiredSignature); - } - - Ok(()) -} - /// Errors if: /// - Owner is not Ore program. /// - Address does not match the expected bus address. @@ -265,185 +251,3 @@ pub fn load_treasury_tokens<'a, 'info>( load_token_account(info, Some(&TREASURY_ADDRESS), &MINT_ADDRESS, is_writable) } - -/// Errors if: -/// - Owner is not SPL token program. -/// - Address does not match the expected mint address. -/// - Data is empty. -/// - Data cannot deserialize into a mint account. -/// - Expected to be writable, but is not. -pub fn load_mint<'a, 'info>( - info: &'a AccountInfo<'info>, - address: Pubkey, - is_writable: bool, -) -> Result<(), ProgramError> { - if info.owner.ne(&spl_token::id()) { - return Err(ProgramError::InvalidAccountOwner); - } - - if info.key.ne(&address) { - return Err(ProgramError::InvalidSeeds); - } - - if info.data_is_empty() { - return Err(ProgramError::UninitializedAccount); - } - - Mint::unpack(&info.data.borrow())?; - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} - -/// Errors if: -/// - Owner is not SPL token program. -/// - Data is empty. -/// - Data cannot deserialize into a token account. -/// - Token account owner does not match the expected owner address. -/// - Token account mint does not match the expected mint address. -/// - Expected to be writable, but is not. -pub fn load_token_account<'a, 'info>( - info: &'a AccountInfo<'info>, - owner: Option<&Pubkey>, - mint: &Pubkey, - is_writable: bool, -) -> Result<(), ProgramError> { - if info.owner.ne(&spl_token::id()) { - return Err(ProgramError::InvalidAccountOwner); - } - - if info.data_is_empty() { - return Err(ProgramError::UninitializedAccount); - } - - let account_data = info.data.borrow(); - let account = spl_token::state::Account::unpack(&account_data)?; - - if account.mint.ne(&mint) { - return Err(ProgramError::InvalidAccountData); - } - - if let Some(owner) = owner { - if account.owner.ne(owner) { - return Err(ProgramError::InvalidAccountData); - } - } - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} - -/// Errors if: -/// - Address does not match PDA derived from provided seeds. -/// - Cannot load as an uninitialized account. -pub fn load_uninitialized_pda<'a, 'info>( - info: &'a AccountInfo<'info>, - seeds: &[&[u8]], - bump: u8, - program_id: &Pubkey, -) -> Result<(), ProgramError> { - let pda = Pubkey::find_program_address(seeds, program_id); - - if info.key.ne(&pda.0) { - return Err(ProgramError::InvalidSeeds); - } - - if bump.ne(&pda.1) { - return Err(ProgramError::InvalidSeeds); - } - - load_system_account(info, true) -} - -/// Errors if: -/// - Owner is not the system program. -/// - Data is not empty. -/// - Account is not writable. -pub fn load_system_account<'a, 'info>( - info: &'a AccountInfo<'info>, - is_writable: bool, -) -> Result<(), ProgramError> { - if info.owner.ne(&system_program::id()) { - return Err(ProgramError::InvalidAccountOwner); - } - - if !info.data_is_empty() { - return Err(ProgramError::AccountAlreadyInitialized); - } - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} - -/// Errors if: -/// - Owner is not the sysvar address. -/// - Account cannot load with the expected address. -pub fn load_sysvar<'a, 'info>( - info: &'a AccountInfo<'info>, - key: Pubkey, -) -> Result<(), ProgramError> { - if info.owner.ne(&sysvar::id()) { - return Err(ProgramError::InvalidAccountOwner); - } - - load_account(info, key, false) -} - -/// Errors if: -/// - Address does not match the expected value. -/// - Expected to be writable, but is not. -pub fn load_account<'a, 'info>( - info: &'a AccountInfo<'info>, - key: Pubkey, - is_writable: bool, -) -> Result<(), ProgramError> { - if info.key.ne(&key) { - return Err(ProgramError::InvalidAccountData); - } - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} - -/// Errors if: -/// - Address does not match the expected value. -/// - Account is not executable. -pub fn load_program<'a, 'info>( - info: &'a AccountInfo<'info>, - key: Pubkey, -) -> Result<(), ProgramError> { - if info.key.ne(&key) { - return Err(ProgramError::IncorrectProgramId); - } - - if !info.executable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} - -/// Errors if: -/// - Account is not writable. -pub fn load_any<'a, 'info>( - info: &'a AccountInfo<'info>, - is_writable: bool, -) -> Result<(), ProgramError> { - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} diff --git a/program/src/claim.rs b/program/src/claim.rs index 836d3c2..970894d 100644 --- a/program/src/claim.rs +++ b/program/src/claim.rs @@ -4,7 +4,7 @@ use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, }; -use crate::utils::AccountDeserialize; +use ore_utils::{loaders::*, AccountDeserialize}; /// Claim distributes claimable ORE from the treasury to a miner. pub fn process_claim<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) -> ProgramResult { diff --git a/program/src/close.rs b/program/src/close.rs index 7f176e1..9c41fdb 100644 --- a/program/src/close.rs +++ b/program/src/close.rs @@ -4,7 +4,7 @@ use solana_program::{ system_program, }; -use crate::utils::AccountDeserialize; +use ore_utils::{loaders::*, AccountDeserialize}; /// Close closes a proof account and returns the rent to the owner. pub fn process_close<'a, 'info>(accounts: &'a [AccountInfo<'info>], _data: &[u8]) -> ProgramResult { diff --git a/program/src/initialize.rs b/program/src/initialize.rs index 67c910e..245c9a8 100644 --- a/program/src/initialize.rs +++ b/program/src/initialize.rs @@ -3,10 +3,9 @@ use std::mem::size_of; use ore_api::{ consts::*, instruction::*, - loaders::*, state::{Bus, Config, Treasury}, }; -use ore_utils::spl::create_ata; +use ore_utils::{create_pda, loaders::*, spl::create_ata, AccountDeserialize, Discriminator}; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, @@ -16,8 +15,6 @@ use solana_program::{ }; use spl_token::state::Mint; -use crate::utils::{create_pda, AccountDeserialize, Discriminator}; - /// Initialize sets up the ORE program to begin mining. pub fn process_initialize<'a, 'info>( accounts: &'a [AccountInfo<'info>], diff --git a/program/src/lib.rs b/program/src/lib.rs index bdb31b0..d732b09 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -24,8 +24,6 @@ use solana_program::{ pubkey::Pubkey, }; -pub(crate) use ore_utils as utils; - #[cfg(not(feature = "no-entrypoint"))] solana_program::entrypoint!(process_instruction); diff --git a/program/src/mine.rs b/program/src/mine.rs index 9a9c15d..4e35a62 100644 --- a/program/src/mine.rs +++ b/program/src/mine.rs @@ -24,7 +24,7 @@ use solana_program::{ sysvar::{self, Sysvar}, }; -use crate::utils::AccountDeserialize; +use ore_utils::{loaders::*, AccountDeserialize}; /// Mine validates hashes and increments a miner's collectable balance. pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) -> ProgramResult { diff --git a/program/src/open.rs b/program/src/open.rs index eac005f..21a0a9d 100644 --- a/program/src/open.rs +++ b/program/src/open.rs @@ -1,6 +1,6 @@ use std::mem::size_of; -use ore_api::{consts::*, instruction::OpenArgs, loaders::*, state::Proof}; +use ore_api::{consts::*, instruction::OpenArgs, state::Proof}; use solana_program::{ account_info::AccountInfo, clock::Clock, @@ -12,7 +12,7 @@ use solana_program::{ sysvar::{self, Sysvar}, }; -use crate::utils::{create_pda, AccountDeserialize, Discriminator}; +use ore_utils::{create_pda, loaders::*, AccountDeserialize, Discriminator}; /// Open creates a new proof account to track a miner's state. pub fn process_open<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) -> ProgramResult { diff --git a/program/src/reset.rs b/program/src/reset.rs index 24ae933..6c16a25 100644 --- a/program/src/reset.rs +++ b/program/src/reset.rs @@ -10,7 +10,7 @@ use solana_program::{ }; use spl_token::state::Mint; -use crate::utils::AccountDeserialize; +use ore_utils::{loaders::*, AccountDeserialize}; /// Reset tops up the bus balances, updates the base reward rate, and sets up the ORE program for the next epoch. pub fn process_reset<'a, 'info>(accounts: &'a [AccountInfo<'info>], _data: &[u8]) -> ProgramResult { diff --git a/program/src/stake.rs b/program/src/stake.rs index 4b9ecd1..dfc2f3c 100644 --- a/program/src/stake.rs +++ b/program/src/stake.rs @@ -1,12 +1,10 @@ use ore_api::{consts::*, instruction::StakeArgs, loaders::*, state::Proof}; -use ore_utils::spl::transfer; +use ore_utils::{loaders::*, spl::transfer, AccountDeserialize}; use solana_program::{ account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, program_error::ProgramError, sysvar::Sysvar, }; -use crate::utils::AccountDeserialize; - /// Stake deposits ORE into a proof account to earn multiplier. pub fn process_stake<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) -> ProgramResult { // Parse args. diff --git a/program/src/update.rs b/program/src/update.rs index 22ecb41..ca0d9bd 100644 --- a/program/src/update.rs +++ b/program/src/update.rs @@ -1,10 +1,9 @@ use ore_api::{loaders::*, state::Proof}; +use ore_utils::{loaders::*, AccountDeserialize}; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, }; -use crate::utils::AccountDeserialize; - /// Update changes the miner authority on a proof account. pub fn process_update<'a, 'info>( accounts: &'a [AccountInfo<'info>], diff --git a/program/src/upgrade.rs b/program/src/upgrade.rs index dbd2c3b..9ca4f0b 100644 --- a/program/src/upgrade.rs +++ b/program/src/upgrade.rs @@ -1,5 +1,5 @@ -use ore_api::{consts::*, error::OreError, instruction::StakeArgs, loaders::*}; -use ore_utils::spl::mint_to_signed; +use ore_api::{consts::*, error::OreError, instruction::StakeArgs}; +use ore_utils::{loaders::*, spl::mint_to_signed}; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, program_pack::Pack, diff --git a/utils/src/lib.rs b/utils/src/lib.rs index f8bc545..cd012fa 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -1,3 +1,4 @@ +pub mod loaders; #[cfg(feature = "spl")] pub mod spl; diff --git a/utils/src/loaders.rs b/utils/src/loaders.rs new file mode 100644 index 0000000..61bbb46 --- /dev/null +++ b/utils/src/loaders.rs @@ -0,0 +1,200 @@ +use solana_program::{ + account_info::AccountInfo, program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, + system_program, sysvar, +}; +#[cfg(feature = "spl")] +use spl_token::state::Mint; + +/// Errors if: +/// - Account is not a signer. +pub fn load_signer<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramError> { + if !info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + + Ok(()) +} + +/// Errors if: +/// - Owner is not SPL token program. +/// - Address does not match the expected mint address. +/// - Data is empty. +/// - Data cannot deserialize into a mint account. +/// - Expected to be writable, but is not. +#[cfg(feature = "spl")] +pub fn load_mint<'a, 'info>( + info: &'a AccountInfo<'info>, + address: Pubkey, + is_writable: bool, +) -> Result<(), ProgramError> { + if info.owner.ne(&spl_token::id()) { + return Err(ProgramError::InvalidAccountOwner); + } + + if info.key.ne(&address) { + return Err(ProgramError::InvalidSeeds); + } + + if info.data_is_empty() { + return Err(ProgramError::UninitializedAccount); + } + + Mint::unpack(&info.data.borrow())?; + + if is_writable && !info.is_writable { + return Err(ProgramError::InvalidAccountData); + } + + Ok(()) +} + +/// Errors if: +/// - Owner is not SPL token program. +/// - Data is empty. +/// - Data cannot deserialize into a token account. +/// - Token account owner does not match the expected owner address. +/// - Token account mint does not match the expected mint address. +/// - Expected to be writable, but is not. +#[cfg(feature = "spl")] +pub fn load_token_account<'a, 'info>( + info: &'a AccountInfo<'info>, + owner: Option<&Pubkey>, + mint: &Pubkey, + is_writable: bool, +) -> Result<(), ProgramError> { + if info.owner.ne(&spl_token::id()) { + return Err(ProgramError::InvalidAccountOwner); + } + + if info.data_is_empty() { + return Err(ProgramError::UninitializedAccount); + } + + let account_data = info.data.borrow(); + let account = spl_token::state::Account::unpack(&account_data)?; + + if account.mint.ne(&mint) { + return Err(ProgramError::InvalidAccountData); + } + + if let Some(owner) = owner { + if account.owner.ne(owner) { + return Err(ProgramError::InvalidAccountData); + } + } + + if is_writable && !info.is_writable { + return Err(ProgramError::InvalidAccountData); + } + + Ok(()) +} + +/// Errors if: +/// - Address does not match PDA derived from provided seeds. +/// - Cannot load as an uninitialized account. +pub fn load_uninitialized_pda<'a, 'info>( + info: &'a AccountInfo<'info>, + seeds: &[&[u8]], + bump: u8, + program_id: &Pubkey, +) -> Result<(), ProgramError> { + let pda = Pubkey::find_program_address(seeds, program_id); + + if info.key.ne(&pda.0) { + return Err(ProgramError::InvalidSeeds); + } + + if bump.ne(&pda.1) { + return Err(ProgramError::InvalidSeeds); + } + + load_system_account(info, true) +} + +/// Errors if: +/// - Owner is not the system program. +/// - Data is not empty. +/// - Account is not writable. +pub fn load_system_account<'a, 'info>( + info: &'a AccountInfo<'info>, + is_writable: bool, +) -> Result<(), ProgramError> { + if info.owner.ne(&system_program::id()) { + return Err(ProgramError::InvalidAccountOwner); + } + + if !info.data_is_empty() { + return Err(ProgramError::AccountAlreadyInitialized); + } + + if is_writable && !info.is_writable { + return Err(ProgramError::InvalidAccountData); + } + + Ok(()) +} + +/// Errors if: +/// - Owner is not the sysvar address. +/// - Account cannot load with the expected address. +pub fn load_sysvar<'a, 'info>( + info: &'a AccountInfo<'info>, + key: Pubkey, +) -> Result<(), ProgramError> { + if info.owner.ne(&sysvar::id()) { + return Err(ProgramError::InvalidAccountOwner); + } + + load_account(info, key, false) +} + +/// Errors if: +/// - Address does not match the expected value. +/// - Expected to be writable, but is not. +pub fn load_account<'a, 'info>( + info: &'a AccountInfo<'info>, + key: Pubkey, + is_writable: bool, +) -> Result<(), ProgramError> { + if info.key.ne(&key) { + return Err(ProgramError::InvalidAccountData); + } + + if is_writable && !info.is_writable { + return Err(ProgramError::InvalidAccountData); + } + + Ok(()) +} + +/// Errors if: +/// - Address does not match the expected value. +/// - Account is not executable. +pub fn load_program<'a, 'info>( + info: &'a AccountInfo<'info>, + key: Pubkey, +) -> Result<(), ProgramError> { + if info.key.ne(&key) { + return Err(ProgramError::IncorrectProgramId); + } + + if !info.executable { + return Err(ProgramError::InvalidAccountData); + } + + Ok(()) +} + +/// Errors if: +/// - Account is not writable. +pub fn load_any<'a, 'info>( + info: &'a AccountInfo<'info>, + is_writable: bool, +) -> Result<(), ProgramError> { + if is_writable && !info.is_writable { + return Err(ProgramError::InvalidAccountData); + } + + Ok(()) +}