From bcba77eb7c5c6ff0dca89d2b43b94093f2020a53 Mon Sep 17 00:00:00 2001 From: Hardhat Chad Date: Fri, 5 Jul 2024 15:25:49 +0000 Subject: [PATCH 1/2] Disallow closing the top stake account --- api/src/error.rs | 4 ++++ program/src/close.rs | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/api/src/error.rs b/api/src/error.rs index a0fdbf9..e3d8ee0 100644 --- a/api/src/error.rs +++ b/api/src/error.rs @@ -23,6 +23,10 @@ pub enum OreError { ToleranceOverflow = 7, #[error("The maximum supply has been reached")] MaxSupply = 8, + #[error("This account cannot be closed because it's the top staker")] + CannotClose = 9, + #[error("This account cannot be crowned because its last stake was too recent")] + CannotCrown = 10, } impl From for ProgramError { diff --git a/program/src/close.rs b/program/src/close.rs index d92bb20..052f87d 100644 --- a/program/src/close.rs +++ b/program/src/close.rs @@ -1,4 +1,8 @@ -use ore_api::{loaders::*, state::Proof}; +use ore_api::{ + error::OreError, + loaders::*, + state::{Config, Proof}, +}; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey, system_program, @@ -20,13 +24,21 @@ pub fn process_close<'a, 'info>( _data: &[u8], ) -> ProgramResult { // Load accounts - let [signer, proof_info, system_program] = accounts else { + let [signer, config_info, proof_info, system_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; load_signer(signer)?; + load_config(config_info, false)?; load_proof(proof_info, signer.key, true)?; load_program(system_program, system_program::id())?; + // Validate the account is not the crowned top staker. + let config_data = config_info.data.borrow(); + let config = Config::try_from_bytes(&config_data)?; + if config.top_staker.eq(proof_info.key) { + return Err(OreError::CannotClose.into()); + } + // Validate balance is zero let proof_data = proof_info.data.borrow(); let proof = Proof::try_from_bytes(&proof_data)?; From ec441fef54fc9e4df15ab65ab0634b049daf8505 Mon Sep 17 00:00:00 2001 From: Hardhat Chad Date: Fri, 5 Jul 2024 15:26:06 +0000 Subject: [PATCH 2/2] Disallow crowning if last stake was less than one minute ago --- program/src/crown.rs | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/program/src/crown.rs b/program/src/crown.rs index 39e3d93..8849f9c 100644 --- a/program/src/crown.rs +++ b/program/src/crown.rs @@ -1,10 +1,12 @@ use ore_api::{ + consts::ONE_MINUTE, + error::OreError, loaders::*, state::{Config, Proof}, }; use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, - pubkey::Pubkey, + account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, + program_error::ProgramError, pubkey::Pubkey, sysvar::Sysvar, }; use crate::utils::AccountDeserialize; @@ -23,15 +25,23 @@ pub fn process_crown<'a, 'info>( load_config(config_info, true)?; load_any_proof(proof_new_info, false)?; - // Load config - let mut config_data = config_info.data.borrow_mut(); - let config = Config::try_from_bytes_mut(&mut config_data)?; - - // Load proposed new top staker + // Load the proof accounts. + let clock = Clock::get().unwrap(); let proof_new_data = proof_new_info.data.borrow(); let proof_new = Proof::try_from_bytes(&proof_new_data)?; + if proof_new + .last_stake_at + .saturating_add(ONE_MINUTE) + .gt(&clock.unix_timestamp) + { + return Err(OreError::CannotCrown.into()); + } - // If top staker is the defualt null balance, skip this. + // If top staker is the default null address, skip this. + let mut config_data = config_info.data.borrow_mut(); + let config = Config::try_from_bytes_mut(&mut config_data)?; + let proof_data = proof_info.data.borrow(); + let proof = Proof::try_from_bytes(&proof_data)?; if config.top_staker.ne(&Pubkey::new_from_array([0; 32])) { // Load current top staker load_any_proof(proof_info, false)?; @@ -40,14 +50,12 @@ pub fn process_crown<'a, 'info>( } // Compare balances - let proof_data = proof_info.data.borrow(); - let proof = Proof::try_from_bytes(&proof_data)?; if proof_new.balance.lt(&proof.balance) { return Ok(()); } } - // Crown the new top staker + // Crown the new top staker. config.max_stake = proof_new.balance; config.top_staker = *proof_new_info.key;