diff --git a/src/instruction.rs b/src/instruction.rs index c2ecc14..51d7de2 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -1,6 +1,7 @@ use bytemuck::{Pod, Zeroable}; use num_enum::TryFromPrimitive; use shank::ShankInstruction; +use solana_program::pubkey::Pubkey; use crate::state::Hash; @@ -67,13 +68,13 @@ pub enum OreInstruction { #[account(16, name = "rent", desc = "Solana rent sysvar")] Initialize = 100, - // TODO - // #[account(0, name = "ore_program", desc = "Ore program")] - // UpdateAdmin = 102, + #[account(0, name = "ore_program", desc = "Ore program")] + #[account(1, name = "treasury", desc = "Ore treasury account")] + UpdateAdmin = 102, - // TODO - // #[account(0, name = "ore_program", desc = "Ore program")] - // UpdateDifficulty = 103, + #[account(0, name = "ore_program", desc = "Ore program")] + #[account(1, name = "treasury", desc = "Ore treasury account")] + UpdateDifficulty = 103, } impl OreInstruction { @@ -127,3 +128,39 @@ impl MineArgs { bytemuck::bytes_of(self) } } + +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +pub struct ClaimArgs { + pub amount: u64, +} + +impl ClaimArgs { + pub fn to_bytes(&self) -> &[u8] { + bytemuck::bytes_of(self) + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +pub struct UpdateAdminArgs { + pub new_admin: Pubkey, +} + +impl UpdateAdminArgs { + pub fn to_bytes(&self) -> &[u8] { + bytemuck::bytes_of(self) + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +pub struct UpdateDifficultyArgs { + pub new_difficulty: Hash, +} + +impl UpdateDifficultyArgs { + pub fn to_bytes(&self) -> &[u8] { + bytemuck::bytes_of(self) + } +} diff --git a/src/lib.rs b/src/lib.rs index 3e5c9eb..b5aacc5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,6 @@ pub const INITIAL_DIFFICULTY: Hash = Hash::new_from_array([ ]); /// The mint address of the ORE token. -// pub const MINT_ADDRESS: Pubkey = pubkey!("37TDfMS8NHpyhyCXBrY9m7rRrtj1f7TrFzD1iXqmTeUX"); pub const MINT_ADDRESS: Pubkey = pubkey!("DY4JVebraRXg9BGt4MRU4mvqHGDzmi2Ay1HGjDU5YeNf"); /// The decimal precision of the ORE token. @@ -115,6 +114,8 @@ pub fn process_instruction( OreInstruction::Mine => process_mine(program_id, accounts, data)?, OreInstruction::Claim => process_claim(program_id, accounts, data)?, OreInstruction::Initialize => process_initialize(program_id, accounts, data)?, + OreInstruction::UpdateAdmin => process_update_admin(program_id, accounts, data)?, + OreInstruction::UpdateDifficulty => process_update_difficulty(program_id, accounts, data)?, } Ok(()) diff --git a/src/loaders.rs b/src/loaders.rs index 59f20bb..eaade3d 100644 --- a/src/loaders.rs +++ b/src/loaders.rs @@ -9,30 +9,15 @@ use crate::{ BUS_COUNT, MINT_ADDRESS, TREASURY_ADDRESS, }; -pub fn load_signer<'a, 'info>( - info: &'a AccountInfo<'info>, -) -> Result<&'a AccountInfo<'info>, ProgramError> { +pub fn load_signer<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramError> { if !info.is_signer { return Err(ProgramError::MissingRequiredSignature); } - Ok(info) + Ok(()) } -pub fn load_uninitialized_pda<'a, 'info>( - info: &'a AccountInfo<'info>, - seeds: &[&[u8]], -) -> Result<&'a AccountInfo<'info>, ProgramError> { - let key = Pubkey::create_program_address(seeds, &crate::id())?; - if sol_memcmp(info.key.as_ref(), key.as_ref(), 32) != 0 { - return Err(ProgramError::InvalidSeeds); - } - load_uninitialized_account(info) -} - -pub fn load_bus<'a, 'info>( - info: &'a AccountInfo<'info>, -) -> Result<&'a AccountInfo<'info>, ProgramError> { - if sol_memcmp(info.owner.as_ref(), crate::id().as_ref(), 32) != 0 { +pub fn load_bus<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramError> { + if !info.owner.eq(&crate::id()) { return Err(ProgramError::InvalidAccountOwner); } if info.data_is_empty() { @@ -46,14 +31,14 @@ pub fn load_bus<'a, 'info>( return Err(ProgramError::InvalidAccountData); } - Ok(info) + Ok(()) } pub fn load_proof<'a, 'info>( info: &'a AccountInfo<'info>, signer: &Pubkey, -) -> Result<&'a AccountInfo<'info>, ProgramError> { - if sol_memcmp(info.owner.as_ref(), crate::id().as_ref(), 32) != 0 { +) -> Result<(), ProgramError> { + if !info.owner.eq(&crate::id()) { return Err(ProgramError::InvalidAccountOwner); } if info.data_is_empty() { @@ -68,13 +53,11 @@ pub fn load_proof<'a, 'info>( return Err(ProgramError::InvalidAccountData); } - Ok(info) + Ok(()) } -pub fn load_treasury<'a, 'info>( - info: &'a AccountInfo<'info>, -) -> Result<&'a AccountInfo<'info>, ProgramError> { - if sol_memcmp(info.owner.as_ref(), crate::id().as_ref(), 32) != 0 { +pub fn load_treasury<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramError> { + if !info.owner.eq(&crate::id()) { return Err(ProgramError::InvalidAccountOwner); } if info.data_is_empty() { @@ -85,13 +68,11 @@ pub fn load_treasury<'a, 'info>( return Err(ProgramError::InvalidSeeds); } - Ok(info) + Ok(()) } -pub fn load_mint<'a, 'info>( - info: &'a AccountInfo<'info>, -) -> Result<&'a AccountInfo<'info>, ProgramError> { - if sol_memcmp(info.owner.as_ref(), spl_token::id().as_ref(), 32) != 0 { +pub fn load_mint<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramError> { + if !info.owner.eq(&spl_token::id()) { return Err(ProgramError::InvalidAccountOwner); } if info.data_is_empty() { @@ -107,15 +88,15 @@ pub fn load_mint<'a, 'info>( return Err(ProgramError::InvalidAccountData); } - Ok(info) + Ok(()) } pub fn load_token_account<'a, 'info>( info: &'a AccountInfo<'info>, - owner: &Pubkey, + owner: Option<&Pubkey>, mint: &Pubkey, -) -> Result<&'a AccountInfo<'info>, ProgramError> { - if sol_memcmp(info.owner.as_ref(), spl_token::id().as_ref(), 32) != 0 { +) -> Result<(), ProgramError> { + if !info.owner.eq(&spl_token::id()) { return Err(ProgramError::InvalidAccountOwner); } if info.data_is_empty() { @@ -126,20 +107,33 @@ pub fn load_token_account<'a, 'info>( let account = spl_token::state::Account::unpack_unchecked(&account_data) .or(Err(ProgramError::InvalidAccountData))?; - if sol_memcmp(account.mint.as_ref(), mint.as_ref(), 32) != 0 { + if !account.mint.eq(&mint) { return Err(ProgramError::InvalidAccountData); } - if sol_memcmp(account.owner.as_ref(), owner.as_ref(), 32) != 0 { - return Err(ProgramError::InvalidAccountData); + if let Some(owner) = owner { + if sol_memcmp(account.owner.as_ref(), owner.as_ref(), 32) != 0 { + return Err(ProgramError::InvalidAccountData); + } } - Ok(info) + Ok(()) +} + +pub fn load_uninitialized_pda<'a, 'info>( + info: &'a AccountInfo<'info>, + seeds: &[&[u8]], +) -> Result<(), ProgramError> { + let key = Pubkey::create_program_address(seeds, &crate::id())?; + if !info.key.eq(&key) { + return Err(ProgramError::InvalidSeeds); + } + load_uninitialized_account(info) } pub fn load_uninitialized_account<'a, 'info>( info: &'a AccountInfo<'info>, -) -> Result<&'a AccountInfo<'info>, ProgramError> { - if sol_memcmp(info.owner.as_ref(), system_program::id().as_ref(), 32) != 0 { +) -> Result<(), ProgramError> { + if !info.owner.eq(&system_program::id()) { return Err(ProgramError::AccountAlreadyInitialized); } if !info.data_is_empty() { @@ -148,15 +142,15 @@ pub fn load_uninitialized_account<'a, 'info>( if !info.is_writable { return Err(ProgramError::InvalidAccountData); } - Ok(info) + Ok(()) } pub fn load_account<'a, 'info>( info: &'a AccountInfo<'info>, key: Pubkey, -) -> Result<&'a AccountInfo<'info>, ProgramError> { - if sol_memcmp(info.key.as_ref(), key.as_ref(), 32) != 0 { +) -> Result<(), ProgramError> { + if !info.key.eq(&key) { return Err(ProgramError::InvalidAccountData); } - Ok(info) + Ok(()) } diff --git a/src/processor/claim.rs b/src/processor/claim.rs index cca32ae..ca900e3 100644 --- a/src/processor/claim.rs +++ b/src/processor/claim.rs @@ -1,10 +1,68 @@ -use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey}; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, + pubkey::Pubkey, +}; + +use crate::{ + instruction::ClaimArgs, + loaders::*, + state::{Proof, Treasury}, + TREASURY, +}; pub fn process_claim<'a, 'info>( _program_id: &Pubkey, accounts: &'a [AccountInfo<'info>], data: &[u8], ) -> ProgramResult { - // TODO + // Parse args + let args = bytemuck::try_from_bytes::(data) + .or(Err(ProgramError::InvalidInstructionData))?; + + // Validate accounts + let [signer, beneficiary_info, mint_info, proof_info, treasury_info, treasury_tokens_info, token_program] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + load_signer(signer)?; + load_token_account(beneficiary_info, None, mint_info.key)?; + load_mint(mint_info)?; + load_treasury(treasury_info)?; + load_token_account(treasury_tokens_info, Some(treasury_info.key), mint_info.key)?; + load_account(token_program, spl_token::id())?; + + // Validate claim amout + let mut proof_data = proof_info.data.borrow_mut(); + let mut proof = bytemuck::try_from_bytes_mut::(&mut proof_data).unwrap(); + if proof.claimable_rewards.lt(&args.amount) { + return Err(ProgramError::Custom(1)); + } + + // Update claimable amount + proof.claimable_rewards = proof.claimable_rewards.saturating_sub(args.amount); + + // Update lifetime status + let mut treasury_data = treasury_info.data.borrow_mut(); + let mut treasury = bytemuck::try_from_bytes_mut::(&mut treasury_data).unwrap(); + treasury.total_claimed_rewards = treasury.total_claimed_rewards.saturating_add(args.amount); + + // Distribute tokens from treasury to beneficiary + solana_program::program::invoke_signed( + &spl_token::instruction::transfer( + &spl_token::id(), + treasury_tokens_info.key, + beneficiary_info.key, + treasury_info.key, + &[treasury_info.key], + args.amount, + )?, + &[ + token_program.clone(), + treasury_tokens_info.clone(), + beneficiary_info.clone(), + treasury_info.clone(), + ], + &[&[TREASURY, &[treasury.bump as u8]]], + )?; + Ok(()) } diff --git a/src/processor/epoch.rs b/src/processor/epoch.rs index a99caa0..54ee503 100644 --- a/src/processor/epoch.rs +++ b/src/processor/epoch.rs @@ -15,67 +15,28 @@ pub fn process_epoch<'a, 'info>( accounts: &'a [AccountInfo<'info>], _data: &[u8], ) -> ProgramResult { + // Validate accounts let [signer, bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, mint_info, treasury_info, treasury_tokens_info, token_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - let _ = load_signer(signer)?; - let _ = load_bus(bus_0_info)?; - let _ = load_bus(bus_1_info)?; - let _ = load_bus(bus_2_info)?; - let _ = load_bus(bus_3_info)?; - let _ = load_bus(bus_4_info)?; - let _ = load_bus(bus_5_info)?; - let _ = load_bus(bus_6_info)?; - let _ = load_bus(bus_7_info)?; - let _ = load_mint(mint_info)?; - let _ = load_treasury(treasury_info)?; - let _ = load_token_account(treasury_tokens_info, treasury_info.key, mint_info.key)?; - let _ = load_account(token_program, spl_token::id())?; - + load_signer(signer)?; + load_bus(bus_0_info)?; + load_bus(bus_1_info)?; + load_bus(bus_2_info)?; + load_bus(bus_3_info)?; + load_bus(bus_4_info)?; + load_bus(bus_5_info)?; + load_bus(bus_6_info)?; + load_bus(bus_7_info)?; + load_mint(mint_info)?; + load_treasury(treasury_info)?; + load_token_account(treasury_tokens_info, Some(treasury_info.key), mint_info.key)?; + load_account(token_program, spl_token::id())?; let busses: [&AccountInfo; 8] = [ bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, ]; - // let _ = load_signer(signer)?; - // let _ = load_bus(bus_info)?; - // let _ = load_proof(proof_info, signer.key)?; - // let _ = load_treasury(treasury_info)?; - // let _ = load_account(slot_hashes_info, sysvar::slot_hashes::id())?; - - // let accounts_iter = &mut accounts.iter(); - - // Account 1: Signer - // let _signer = load_signer(next_account_info(accounts_iter)?)?; - - // Accounts 2-9: Busses - // let busses = vec![ - // load_bus(next_account_info(accounts_iter)?)?, - // load_bus(next_account_info(accounts_iter)?)?, - // load_bus(next_account_info(accounts_iter)?)?, - // load_bus(next_account_info(accounts_iter)?)?, - // load_bus(next_account_info(accounts_iter)?)?, - // load_bus(next_account_info(accounts_iter)?)?, - // load_bus(next_account_info(accounts_iter)?)?, - // load_bus(next_account_info(accounts_iter)?)?, - // ]; - - // Account 10: Mint - // let mint = load_mint(next_account_info(accounts_iter)?)?; - - // Account 11: Treasury - // let treasury_info = load_treasury(next_account_info(accounts_iter)?)?; - - // Account 12: Treasury tokens - // let treasury_tokens = load_token_account( - // next_account_info(accounts_iter)?, - // treasury_info.key, - // mint.key, - // )?; - - // Account 13: Token program - // let token_program = load_account(next_account_info(accounts_iter)?, spl_token::id())?; - // Validate epoch has ended let clock = Clock::get().unwrap(); let mut treasury_data = treasury_info.data.borrow_mut(); diff --git a/src/processor/initialize.rs b/src/processor/initialize.rs index 9ae3160..b13968b 100644 --- a/src/processor/initialize.rs +++ b/src/processor/initialize.rs @@ -3,11 +3,8 @@ use std::mem::size_of; use solana_program::program_pack::Pack; use solana_program::{self, sysvar}; use solana_program::{ - account_info::{next_account_info, AccountInfo}, - entrypoint::ProgramResult, - program_error::ProgramError, - pubkey::Pubkey, - system_program, + account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, + pubkey::Pubkey, system_program, }; use spl_token::state::Mint; @@ -24,87 +21,43 @@ pub fn process_initialize<'a, 'info>( accounts: &'a [AccountInfo<'info>], data: &[u8], ) -> ProgramResult { - let accounts_iter = &mut accounts.iter(); + // Parse args let args = bytemuck::try_from_bytes::(data) .or(Err(ProgramError::InvalidInstructionData))?; - // Account 1: Signer - let signer = load_signer(next_account_info(accounts_iter)?)?; - - // Accounts 2-9: Busses - let busses = vec![ - load_uninitialized_pda( - next_account_info(accounts_iter)?, - &[BUS, &[0], &[args.bus_0_bump]], - )?, - load_uninitialized_pda( - next_account_info(accounts_iter)?, - &[BUS, &[1], &[args.bus_1_bump]], - )?, - load_uninitialized_pda( - next_account_info(accounts_iter)?, - &[BUS, &[2], &[args.bus_2_bump]], - )?, - load_uninitialized_pda( - next_account_info(accounts_iter)?, - &[BUS, &[3], &[args.bus_3_bump]], - )?, - load_uninitialized_pda( - next_account_info(accounts_iter)?, - &[BUS, &[4], &[args.bus_4_bump]], - )?, - load_uninitialized_pda( - next_account_info(accounts_iter)?, - &[BUS, &[5], &[args.bus_5_bump]], - )?, - load_uninitialized_pda( - next_account_info(accounts_iter)?, - &[BUS, &[6], &[args.bus_6_bump]], - )?, - load_uninitialized_pda( - next_account_info(accounts_iter)?, - &[BUS, &[7], &[args.bus_7_bump]], - )?, - ]; - - // Account 10: Mint - let mint = load_uninitialized_pda( - next_account_info(accounts_iter)?, - &[MINT, &[args.mint_bump]], - )?; - if !mint.key.eq(&MINT_ADDRESS) { + // Validate accounts + let [signer, bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, mint_info, treasury_info, treasury_tokens_info, system_program, token_program, associated_token_program, rent_sysvar] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + load_signer(signer)?; + load_uninitialized_pda(bus_0_info, &[BUS, &[0], &[args.bus_0_bump]])?; + load_uninitialized_pda(bus_0_info, &[BUS, &[0], &[args.bus_0_bump]])?; + load_uninitialized_pda(bus_0_info, &[BUS, &[0], &[args.bus_0_bump]])?; + load_uninitialized_pda(bus_0_info, &[BUS, &[0], &[args.bus_0_bump]])?; + load_uninitialized_pda(bus_0_info, &[BUS, &[0], &[args.bus_0_bump]])?; + load_uninitialized_pda(bus_0_info, &[BUS, &[0], &[args.bus_0_bump]])?; + load_uninitialized_pda(bus_0_info, &[BUS, &[0], &[args.bus_0_bump]])?; + load_uninitialized_pda(bus_0_info, &[BUS, &[0], &[args.bus_0_bump]])?; + load_uninitialized_pda(mint_info, &[MINT, &[args.mint_bump]])?; + if !mint_info.key.eq(&MINT_ADDRESS) { return Err(ProgramError::InvalidAccountData); } - - // Account 11: Treasury - let treasury_info = load_uninitialized_pda( - next_account_info(accounts_iter)?, - &[TREASURY, &[args.treasury_bump]], - )?; + load_uninitialized_pda(treasury_info, &[TREASURY, &[args.treasury_bump]])?; if !treasury_info.key.eq(&TREASURY_ADDRESS) { return Err(ProgramError::InvalidSeeds); } - - // Account 12: Treasury tokens - let treasury_tokens = load_uninitialized_account(next_account_info(accounts_iter)?)?; - - // Account 13: System program - let system_program = load_account(next_account_info(accounts_iter)?, system_program::id())?; - - // Account 14: Token program - let token_program = load_account(next_account_info(accounts_iter)?, spl_token::id())?; - - // Account 15: Associated token program - let associated_token_program = load_account( - next_account_info(accounts_iter)?, - spl_associated_token_account::id(), - )?; - - // Account 16: Rent sysvar - let rent_sysvar = load_account(next_account_info(accounts_iter)?, sysvar::rent::id())?; + load_uninitialized_account(treasury_tokens_info)?; + load_account(system_program, system_program::id())?; + load_account(token_program, spl_token::id())?; + load_account(associated_token_program, spl_associated_token_account::id())?; + load_account(rent_sysvar, sysvar::rent::id())?; // Initialize bus accounts - let bus_bumps = vec![ + let bus_infos = [ + bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, + bus_7_info, + ]; + let bus_bumps = [ args.bus_0_bump, args.bus_1_bump, args.bus_2_bump, @@ -116,14 +69,14 @@ pub fn process_initialize<'a, 'info>( ]; for i in 0..BUS_COUNT { create_pda( - busses[i], + bus_infos[i], &crate::id(), size_of::(), &[BUS, &[i as u8], &[bus_bumps[i]]], system_program, signer, )?; - busses[i].try_borrow_mut_data()?.copy_from_slice( + bus_infos[i].try_borrow_mut_data()?.copy_from_slice( Bus { bump: bus_bumps[i] as u32, id: i as u32, @@ -154,7 +107,7 @@ pub fn process_initialize<'a, 'info>( // Initialize mint create_pda( - mint, + mint_info, &spl_token::id(), Mint::LEN, &[MINT, &[args.mint_bump]], @@ -164,14 +117,14 @@ pub fn process_initialize<'a, 'info>( solana_program::program::invoke_signed( &spl_token::instruction::initialize_mint( &spl_token::id(), - mint.key, + mint_info.key, treasury_info.key, None, TOKEN_DECIMALS, )?, &[ token_program.clone(), - mint.clone(), + mint_info.clone(), treasury_info.clone(), rent_sysvar.clone(), ], @@ -183,15 +136,15 @@ pub fn process_initialize<'a, 'info>( &spl_associated_token_account::instruction::create_associated_token_account( signer.key, treasury_info.key, - mint.key, + mint_info.key, &spl_token::id(), ), &[ associated_token_program.clone(), signer.clone(), - treasury_tokens.clone(), + treasury_tokens_info.clone(), treasury_info.clone(), - mint.clone(), + mint_info.clone(), system_program.clone(), token_program.clone(), ], diff --git a/src/processor/mine.rs b/src/processor/mine.rs index d95712f..cd612b0 100644 --- a/src/processor/mine.rs +++ b/src/processor/mine.rs @@ -1,7 +1,7 @@ use std::mem::size_of; use solana_program::{ - account_info::{next_account_info, AccountInfo}, + account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, keccak::{hashv, Hash as KeccakHash}, @@ -27,15 +27,15 @@ pub fn process_mine<'a, 'info>( let args = bytemuck::try_from_bytes::(data).or(Err(ProgramError::InvalidInstructionData))?; - // Parse accounts + // Validate accounts let [signer, bus_info, proof_info, treasury_info, slot_hashes_info] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - let _ = load_signer(signer)?; - let _ = load_bus(bus_info)?; - let _ = load_proof(proof_info, signer.key)?; - let _ = load_treasury(treasury_info)?; - let _ = load_account(slot_hashes_info, sysvar::slot_hashes::id())?; + load_signer(signer)?; + load_bus(bus_info)?; + load_proof(proof_info, signer.key)?; + load_treasury(treasury_info)?; + load_account(slot_hashes_info, sysvar::slot_hashes::id())?; // Validate epoch is active let clock = Clock::get().unwrap(); diff --git a/src/processor/mod.rs b/src/processor/mod.rs index da9c3c5..61d6461 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -3,9 +3,13 @@ mod epoch; mod initialize; mod mine; mod proof; +mod update_admin; +mod update_difficulty; pub use claim::*; pub use epoch::*; pub use initialize::*; pub use mine::*; pub use proof::*; +pub use update_admin::*; +pub use update_difficulty::*; diff --git a/src/processor/proof.rs b/src/processor/proof.rs index 35ca8b2..28b0a07 100644 --- a/src/processor/proof.rs +++ b/src/processor/proof.rs @@ -1,12 +1,8 @@ use std::mem::size_of; use solana_program::{ - account_info::{next_account_info, AccountInfo}, - entrypoint::ProgramResult, - keccak::hashv, - program_error::ProgramError, - pubkey::Pubkey, - system_program, + account_info::AccountInfo, entrypoint::ProgramResult, keccak::hashv, + program_error::ProgramError, pubkey::Pubkey, system_program, }; use crate::{instruction::ProofArgs, loaders::*, state::Proof, utils::create_pda, PROOF}; @@ -16,21 +12,17 @@ pub fn process_proof<'a, 'info>( accounts: &'a [AccountInfo<'info>], data: &[u8], ) -> ProgramResult { - let accounts_iter = &mut accounts.iter(); + // Parse args let args = bytemuck::try_from_bytes::(data) .or(Err(ProgramError::InvalidInstructionData))?; - // Account 1: Signer - let signer = load_signer(next_account_info(accounts_iter)?)?; - - // Account 2: Proof - let proof_info = load_uninitialized_pda( - next_account_info(accounts_iter)?, - &[PROOF, signer.key.as_ref(), &[args.bump]], - )?; - - // Account 3: System program - let system_program = load_account(next_account_info(accounts_iter)?, system_program::id())?; + // Validate accounts + let [signer, proof_info, system_program] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + load_signer(signer)?; + load_uninitialized_pda(proof_info, &[PROOF, signer.key.as_ref(), &[args.bump]])?; + load_account(system_program, system_program::id())?; // Initialize proof create_pda( diff --git a/src/processor/update_admin.rs b/src/processor/update_admin.rs new file mode 100644 index 0000000..74bddb1 --- /dev/null +++ b/src/processor/update_admin.rs @@ -0,0 +1,35 @@ +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, + pubkey::Pubkey, +}; + +use crate::{instruction::UpdateAdminArgs, loaders::*, state::Treasury}; + +pub fn process_update_admin<'a, 'info>( + _program_id: &Pubkey, + accounts: &'a [AccountInfo<'info>], + data: &[u8], +) -> ProgramResult { + // Parse args + let args = bytemuck::try_from_bytes::(data) + .or(Err(ProgramError::InvalidInstructionData))?; + + // Validate accounts + let [signer, treasury_info] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + load_signer(signer)?; + load_treasury(treasury_info)?; + + // Validate admin signer + let mut treasury_data = treasury_info.data.borrow_mut(); + let mut treasury = bytemuck::try_from_bytes_mut::(&mut treasury_data).unwrap(); + if !treasury.admin.eq(&signer.key) { + return Err(ProgramError::MissingRequiredSignature); + } + + // Update admin + treasury.admin = args.new_admin; + + Ok(()) +} diff --git a/src/processor/update_difficulty.rs b/src/processor/update_difficulty.rs new file mode 100644 index 0000000..e492a8c --- /dev/null +++ b/src/processor/update_difficulty.rs @@ -0,0 +1,35 @@ +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, + pubkey::Pubkey, +}; + +use crate::{instruction::UpdateDifficultyArgs, loaders::*, state::Treasury}; + +pub fn process_update_difficulty<'a, 'info>( + _program_id: &Pubkey, + accounts: &'a [AccountInfo<'info>], + data: &[u8], +) -> ProgramResult { + // Parse args + let args = bytemuck::try_from_bytes::(data) + .or(Err(ProgramError::InvalidInstructionData))?; + + // Validate accounts + let [signer, treasury_info] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + load_signer(signer)?; + load_treasury(treasury_info)?; + + // Validate admin signer + let mut treasury_data = treasury_info.data.borrow_mut(); + let mut treasury = bytemuck::try_from_bytes_mut::(&mut treasury_data).unwrap(); + if !treasury.admin.eq(&signer.key) { + return Err(ProgramError::MissingRequiredSignature); + } + + // Update admin + treasury.difficulty = args.new_difficulty; + + Ok(()) +}