diff --git a/Cargo.lock b/Cargo.lock index 9d0e51c..be9d492 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -369,7 +369,7 @@ checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -659,7 +659,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1005,7 +1005,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1016,7 +1016,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1134,7 +1134,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1157,7 +1157,7 @@ checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1251,7 +1251,7 @@ checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1264,7 +1264,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1416,7 +1416,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2184,7 +2184,7 @@ checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2266,7 +2266,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2278,7 +2278,7 @@ dependencies = [ "proc-macro-crate 2.0.1", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2357,6 +2357,7 @@ dependencies = [ "spl-associated-token-account", "spl-token", "static_assertions", + "thiserror", "tokio", ] @@ -2477,7 +2478,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2600,9 +2601,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.72" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a293318316cf6478ec1ad2a21c49390a8d5b5eae9fab736467d93fbc0edc29c5" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -2624,7 +2625,7 @@ checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2677,9 +2678,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -3065,7 +3066,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3133,7 +3134,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3178,7 +3179,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3704,7 +3705,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -4202,7 +4203,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -4543,7 +4544,7 @@ checksum = "fadbefec4f3c678215ca72bd71862697bb06b41fd77c0088902dd3203354387b" dependencies = [ "quote", "spl-discriminator-syn", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -4555,7 +4556,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.43", + "syn 2.0.48", "thiserror", ] @@ -4603,7 +4604,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -4780,9 +4781,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.43" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -4908,7 +4909,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -4919,7 +4920,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "test-case-core", ] @@ -4940,22 +4941,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.53" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.53" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -5058,7 +5059,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -5205,7 +5206,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -5442,7 +5443,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "wasm-bindgen-shared", ] @@ -5476,7 +5477,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5758,7 +5759,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -5778,7 +5779,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 114535b..16ce2d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ solana-program = "^1.16" spl-token = { version = "^4", features = ["no-entrypoint"] } spl-associated-token-account = { version = "^2.2", features = [ "no-entrypoint" ] } static_assertions = "1.1.0" +thiserror = "1.0.57" [dev-dependencies] bs64 = "0.1.2" diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..7f9d9b8 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,26 @@ +use num_enum::IntoPrimitive; +use solana_program::program_error::ProgramError; +use thiserror::Error; + +#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, IntoPrimitive)] +#[repr(u32)] +pub enum OreError { + #[error("The epoch is still active and cannot be reset")] + EpochActive = 0, + #[error("The epoch has expired and needs reset")] + EpochExpired = 1, + #[error("The provided hash was invalid")] + InvalidHash = 2, + #[error("The provided hash does not satisfy the difficulty requirement")] + InsufficientHashDifficulty = 3, + #[error("The bus has insufficient rewards to mine at this time")] + InsufficientBusRewards = 4, + #[error("The claim amount cannot be larger than the claimable rewards")] + InvalidClaimAmount = 5, +} + +impl From for ProgramError { + fn from(e: OreError) -> Self { + ProgramError::Custom(e as u32) + } +} diff --git a/src/instruction.rs b/src/instruction.rs index 51d7de2..16a04a5 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -3,7 +3,7 @@ use num_enum::TryFromPrimitive; use shank::ShankInstruction; use solana_program::pubkey::Pubkey; -use crate::state::Hash; +use crate::{impl_to_bytes, state::Hash}; #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq, ShankInstruction, TryFromPrimitive)] @@ -23,13 +23,13 @@ pub enum OreInstruction { #[account(11, name = "treasury", desc = "Ore treasury account", writable)] #[account(12, name = "treasury_tokens", desc = "Ore treasury token account", writable)] #[account(13, name = "token_program", desc = "SPL token program")] - Epoch = 0, + Reset = 0, #[account(0, name = "ore_program", desc = "Ore program")] #[account(1, name = "signer", desc = "Signer", signer)] #[account(2, name = "proof", desc = "Ore miner proof account", writable)] #[account(3, name = "system_program", desc = "Solana system program")] - Proof = 1, + CreateProof = 1, #[account(0, name = "ore_program", desc = "Ore program")] #[account(1, name = "signer", desc = "Signer", signer)] @@ -98,24 +98,12 @@ pub struct InitializeArgs { pub treasury_bump: u8, } -impl InitializeArgs { - pub fn to_bytes(&self) -> &[u8] { - bytemuck::bytes_of(self) - } -} - #[repr(C)] #[derive(Clone, Copy, Debug, Pod, Zeroable)] -pub struct ProofArgs { +pub struct CreateProofArgs { pub bump: u8, } -impl ProofArgs { - pub fn to_bytes(&self) -> &[u8] { - bytemuck::bytes_of(self) - } -} - #[repr(C)] #[derive(Clone, Copy, Debug, Pod, Zeroable)] pub struct MineArgs { @@ -123,44 +111,27 @@ pub struct MineArgs { pub nonce: [u8; 8], } -impl MineArgs { - pub fn to_bytes(&self) -> &[u8] { - 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) - } -} +impl_to_bytes!(InitializeArgs); +impl_to_bytes!(CreateProofArgs); +impl_to_bytes!(MineArgs); +impl_to_bytes!(ClaimArgs); +impl_to_bytes!(UpdateAdminArgs); +impl_to_bytes!(UpdateDifficultyArgs); diff --git a/src/lib.rs b/src/lib.rs index b5aacc5..7bf8d70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +pub mod error; pub mod instruction; mod loaders; mod processor; @@ -109,8 +110,8 @@ pub fn process_instruction( let ix = OreInstruction::try_from(*tag).or(Err(ProgramError::InvalidInstructionData))?; match ix { - OreInstruction::Epoch => process_epoch(program_id, accounts, data)?, - OreInstruction::Proof => process_proof(program_id, accounts, data)?, + OreInstruction::Reset => process_reset(program_id, accounts, data)?, + OreInstruction::CreateProof => process_create_proof(program_id, accounts, data)?, OreInstruction::Mine => process_mine(program_id, accounts, data)?, OreInstruction::Claim => process_claim(program_id, accounts, data)?, OreInstruction::Initialize => process_initialize(program_id, accounts, data)?, diff --git a/src/loaders.rs b/src/loaders.rs index eaade3d..726d4b2 100644 --- a/src/loaders.rs +++ b/src/loaders.rs @@ -1,6 +1,6 @@ use solana_program::{ - account_info::AccountInfo, program_error::ProgramError, program_memory::sol_memcmp, - program_pack::Pack, pubkey::Pubkey, system_program, + account_info::AccountInfo, program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, + system_program, }; use spl_token::state::Mint; @@ -16,8 +16,11 @@ pub fn load_signer<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), Progra Ok(()) } -pub fn load_bus<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramError> { - if !info.owner.eq(&crate::id()) { +pub fn load_bus<'a, 'info>( + info: &'a AccountInfo<'info>, + is_writable: bool, +) -> Result<(), ProgramError> { + if info.owner.ne(&crate::id()) { return Err(ProgramError::InvalidAccountOwner); } if info.data_is_empty() { @@ -31,14 +34,19 @@ pub fn load_bus<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramEr return Err(ProgramError::InvalidAccountData); } + if is_writable && !info.is_writable { + return Err(ProgramError::InvalidAccountData); + } + Ok(()) } pub fn load_proof<'a, 'info>( info: &'a AccountInfo<'info>, - signer: &Pubkey, + authority: &Pubkey, + is_writable: bool, ) -> Result<(), ProgramError> { - if !info.owner.eq(&crate::id()) { + if info.owner.ne(&crate::id()) { return Err(ProgramError::InvalidAccountOwner); } if info.data_is_empty() { @@ -48,31 +56,44 @@ pub fn load_proof<'a, 'info>( let proof_data = info.data.borrow(); let proof = bytemuck::try_from_bytes::(&proof_data).unwrap(); - // if !proof.authority.eq(&signer) { - if sol_memcmp(proof.authority.as_ref(), signer.as_ref(), 32) != 0 { + if proof.authority.ne(&authority) { + return Err(ProgramError::InvalidAccountData); + } + + if is_writable && !info.is_writable { return Err(ProgramError::InvalidAccountData); } Ok(()) } -pub fn load_treasury<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramError> { - if !info.owner.eq(&crate::id()) { +pub fn load_treasury<'a, 'info>( + info: &'a AccountInfo<'info>, + is_writable: bool, +) -> Result<(), ProgramError> { + if info.owner.ne(&crate::id()) { return Err(ProgramError::InvalidAccountOwner); } if info.data_is_empty() { return Err(ProgramError::UninitializedAccount); } - if sol_memcmp(info.key.as_ref(), TREASURY_ADDRESS.as_ref(), 32) != 0 { + if info.key.ne(&TREASURY_ADDRESS) { return Err(ProgramError::InvalidSeeds); } + if is_writable && !info.is_writable { + return Err(ProgramError::InvalidAccountData); + } + Ok(()) } -pub fn load_mint<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramError> { - if !info.owner.eq(&spl_token::id()) { +pub fn load_mint<'a, 'info>( + info: &'a AccountInfo<'info>, + is_writable: bool, +) -> Result<(), ProgramError> { + if info.owner.ne(&spl_token::id()) { return Err(ProgramError::InvalidAccountOwner); } if info.data_is_empty() { @@ -84,7 +105,11 @@ pub fn load_mint<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramE return Err(ProgramError::InvalidAccountData); } - if sol_memcmp(info.key.as_ref(), MINT_ADDRESS.as_ref(), 32) != 0 { + if info.key.ne(&MINT_ADDRESS) { + return Err(ProgramError::InvalidAccountData); + } + + if is_writable && !info.is_writable { return Err(ProgramError::InvalidAccountData); } @@ -95,8 +120,9 @@ pub fn load_token_account<'a, 'info>( info: &'a AccountInfo<'info>, owner: Option<&Pubkey>, mint: &Pubkey, + is_writable: bool, ) -> Result<(), ProgramError> { - if !info.owner.eq(&spl_token::id()) { + if info.owner.ne(&spl_token::id()) { return Err(ProgramError::InvalidAccountOwner); } if info.data_is_empty() { @@ -107,15 +133,19 @@ pub fn load_token_account<'a, 'info>( let account = spl_token::state::Account::unpack_unchecked(&account_data) .or(Err(ProgramError::InvalidAccountData))?; - if !account.mint.eq(&mint) { + if account.mint.ne(&mint) { return Err(ProgramError::InvalidAccountData); } if let Some(owner) = owner { - if sol_memcmp(account.owner.as_ref(), owner.as_ref(), 32) != 0 { + if account.owner.ne(owner) { return Err(ProgramError::InvalidAccountData); } } + if is_writable && !info.is_writable { + return Err(ProgramError::InvalidAccountData); + } + Ok(()) } @@ -124,7 +154,7 @@ pub fn load_uninitialized_pda<'a, 'info>( seeds: &[&[u8]], ) -> Result<(), ProgramError> { let key = Pubkey::create_program_address(seeds, &crate::id())?; - if !info.key.eq(&key) { + if info.key.ne(&key) { return Err(ProgramError::InvalidSeeds); } load_uninitialized_account(info) @@ -133,7 +163,7 @@ pub fn load_uninitialized_pda<'a, 'info>( pub fn load_uninitialized_account<'a, 'info>( info: &'a AccountInfo<'info>, ) -> Result<(), ProgramError> { - if !info.owner.eq(&system_program::id()) { + if info.owner.ne(&system_program::id()) { return Err(ProgramError::AccountAlreadyInitialized); } if !info.data_is_empty() { @@ -145,11 +175,35 @@ pub fn load_uninitialized_account<'a, 'info>( Ok(()) } -pub fn load_account<'a, 'info>( +pub fn load_sysvar<'a, 'info>( info: &'a AccountInfo<'info>, key: Pubkey, ) -> Result<(), ProgramError> { - if !info.key.eq(&key) { + load_account(info, key, false) +} + +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(()) +} + +pub fn load_program<'a, 'info>( + info: &'a AccountInfo<'info>, + key: Pubkey, +) -> Result<(), ProgramError> { + if info.key.ne(&key) { + return Err(ProgramError::InvalidAccountData); + } + if !info.executable { return Err(ProgramError::InvalidAccountData); } Ok(()) diff --git a/src/processor/claim.rs b/src/processor/claim.rs index ca900e3..caecef2 100644 --- a/src/processor/claim.rs +++ b/src/processor/claim.rs @@ -4,6 +4,7 @@ use solana_program::{ }; use crate::{ + error::OreError, instruction::ClaimArgs, loaders::*, state::{Proof, Treasury}, @@ -24,17 +25,22 @@ pub fn process_claim<'a, 'info>( 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())?; + load_token_account(beneficiary_info, None, mint_info.key, true)?; + load_mint(mint_info, true)?; + load_treasury(treasury_info, true)?; + load_token_account( + treasury_tokens_info, + Some(treasury_info.key), + mint_info.key, + true, + )?; + load_program(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)); + return Err(OreError::InvalidClaimAmount.into()); } // Update claimable amount diff --git a/src/processor/proof.rs b/src/processor/create_proof.rs similarity index 82% rename from src/processor/proof.rs rename to src/processor/create_proof.rs index 28b0a07..9ae5639 100644 --- a/src/processor/proof.rs +++ b/src/processor/create_proof.rs @@ -5,15 +5,15 @@ use solana_program::{ program_error::ProgramError, pubkey::Pubkey, system_program, }; -use crate::{instruction::ProofArgs, loaders::*, state::Proof, utils::create_pda, PROOF}; +use crate::{instruction::CreateProofArgs, loaders::*, state::Proof, utils::create_pda, PROOF}; -pub fn process_proof<'a, 'info>( +pub fn process_create_proof<'a, 'info>( _program_id: &Pubkey, accounts: &'a [AccountInfo<'info>], data: &[u8], ) -> ProgramResult { // Parse args - let args = bytemuck::try_from_bytes::(data) + let args = bytemuck::try_from_bytes::(data) .or(Err(ProgramError::InvalidInstructionData))?; // Validate accounts @@ -22,7 +22,7 @@ pub fn process_proof<'a, 'info>( }; load_signer(signer)?; load_uninitialized_pda(proof_info, &[PROOF, signer.key.as_ref(), &[args.bump]])?; - load_account(system_program, system_program::id())?; + load_program(system_program, system_program::id())?; // Initialize proof create_pda( diff --git a/src/processor/initialize.rs b/src/processor/initialize.rs index b13968b..5165639 100644 --- a/src/processor/initialize.rs +++ b/src/processor/initialize.rs @@ -47,10 +47,10 @@ pub fn process_initialize<'a, 'info>( return Err(ProgramError::InvalidSeeds); } 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())?; + load_program(system_program, system_program::id())?; + load_program(token_program, spl_token::id())?; + load_program(associated_token_program, spl_associated_token_account::id())?; + load_sysvar(rent_sysvar, sysvar::rent::id())?; // Initialize bus accounts let bus_infos = [ diff --git a/src/processor/mine.rs b/src/processor/mine.rs index cd612b0..bbcf283 100644 --- a/src/processor/mine.rs +++ b/src/processor/mine.rs @@ -6,12 +6,14 @@ use solana_program::{ entrypoint::ProgramResult, keccak::{hashv, Hash as KeccakHash}, program_error::ProgramError, + program_memory::sol_memcmp, pubkey::Pubkey, slot_hashes::SlotHash, sysvar::{self, Sysvar}, }; use crate::{ + error::OreError, instruction::MineArgs, loaders::*, state::{Bus, Proof, Treasury}, @@ -32,18 +34,18 @@ pub fn process_mine<'a, 'info>( return Err(ProgramError::NotEnoughAccountKeys); }; 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())?; + load_bus(bus_info, true)?; + load_proof(proof_info, signer.key, true)?; + load_treasury(treasury_info, false)?; + load_sysvar(slot_hashes_info, sysvar::slot_hashes::id())?; // Validate epoch is active let clock = Clock::get().unwrap(); let treasury_data = treasury_info.data.borrow(); let treasury = bytemuck::try_from_bytes::(&treasury_data).unwrap(); let epoch_end_at = treasury.epoch_start_at.saturating_add(EPOCH_DURATION); - if !clock.unix_timestamp.lt(&epoch_end_at) { - return Err(ProgramError::Custom(1)); + if clock.unix_timestamp.ge(&epoch_end_at) { + return Err(OreError::EpochExpired.into()); } // Validate provided hash @@ -61,7 +63,7 @@ pub fn process_mine<'a, 'info>( let mut bus_data = bus_info.data.borrow_mut(); let mut bus = bytemuck::try_from_bytes_mut::(&mut bus_data).unwrap(); if bus.available_rewards.lt(&treasury.reward_rate) { - return Err(ProgramError::Custom(1)); + return Err(OreError::InsufficientBusRewards.into()); } bus.available_rewards = bus.available_rewards.saturating_sub(treasury.reward_rate); proof.claimable_rewards = proof.claimable_rewards.saturating_add(treasury.reward_rate); @@ -86,19 +88,19 @@ pub(crate) fn validate_hash( nonce: u64, difficulty: KeccakHash, ) -> Result<(), ProgramError> { - // Validate hash correctness. + // Validate hash correctness let hash_ = hashv(&[ current_hash.as_ref(), signer.as_ref(), nonce.to_be_bytes().as_slice(), ]); - if !hash.eq(&hash_) { - return Err(ProgramError::Custom(1)); + if sol_memcmp(hash.as_ref(), hash_.as_ref(), 32) != 0 { + return Err(OreError::InvalidHash.into()); } - // Validate hash difficulty. - if !hash.le(&difficulty) { - return Err(ProgramError::Custom(1)); + // Validate hash difficulty + if hash.gt(&difficulty) { + return Err(OreError::InsufficientHashDifficulty.into()); } Ok(()) diff --git a/src/processor/mod.rs b/src/processor/mod.rs index 61d6461..ebd658b 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -1,15 +1,15 @@ mod claim; -mod epoch; +mod create_proof; mod initialize; mod mine; -mod proof; +mod reset; mod update_admin; mod update_difficulty; pub use claim::*; -pub use epoch::*; +pub use create_proof::*; pub use initialize::*; pub use mine::*; -pub use proof::*; +pub use reset::*; pub use update_admin::*; pub use update_difficulty::*; diff --git a/src/processor/epoch.rs b/src/processor/reset.rs similarity index 88% rename from src/processor/epoch.rs rename to src/processor/reset.rs index 54ee503..c876c64 100644 --- a/src/processor/epoch.rs +++ b/src/processor/reset.rs @@ -4,13 +4,14 @@ use solana_program::{ }; use crate::{ + error::OreError, loaders::*, state::{Bus, Treasury}, BUS_COUNT, BUS_EPOCH_REWARDS, EPOCH_DURATION, MAX_EPOCH_REWARDS, SMOOTHING_FACTOR, TARGET_EPOCH_REWARDS, TREASURY, }; -pub fn process_epoch<'a, 'info>( +pub fn process_reset<'a, 'info>( _program_id: &Pubkey, accounts: &'a [AccountInfo<'info>], _data: &[u8], @@ -20,18 +21,23 @@ pub fn process_epoch<'a, 'info>( return Err(ProgramError::NotEnoughAccountKeys); }; 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())?; + load_bus(bus_0_info, true)?; + load_bus(bus_1_info, true)?; + load_bus(bus_2_info, true)?; + load_bus(bus_3_info, true)?; + load_bus(bus_4_info, true)?; + load_bus(bus_5_info, true)?; + load_bus(bus_6_info, true)?; + load_bus(bus_7_info, true)?; + load_mint(mint_info, true)?; + load_treasury(treasury_info, true)?; + load_token_account( + treasury_tokens_info, + Some(treasury_info.key), + mint_info.key, + true, + )?; + load_sysvar(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, @@ -42,8 +48,8 @@ pub fn process_epoch<'a, 'info>( let mut treasury_data = treasury_info.data.borrow_mut(); let mut treasury = bytemuck::try_from_bytes_mut::(&mut treasury_data).unwrap(); let epoch_end_at = treasury.epoch_start_at.saturating_add(EPOCH_DURATION); - if !clock.unix_timestamp.ge(&epoch_end_at) { - return Err(ProgramError::Custom(1)); + if clock.unix_timestamp.lt(&epoch_end_at) { + return Err(OreError::EpochActive.into()); } // Reset busses diff --git a/src/processor/update_admin.rs b/src/processor/update_admin.rs index 74bddb1..8c7646b 100644 --- a/src/processor/update_admin.rs +++ b/src/processor/update_admin.rs @@ -19,7 +19,7 @@ pub fn process_update_admin<'a, 'info>( return Err(ProgramError::NotEnoughAccountKeys); }; load_signer(signer)?; - load_treasury(treasury_info)?; + load_treasury(treasury_info, true)?; // Validate admin signer let mut treasury_data = treasury_info.data.borrow_mut(); diff --git a/src/processor/update_difficulty.rs b/src/processor/update_difficulty.rs index e492a8c..2765b6c 100644 --- a/src/processor/update_difficulty.rs +++ b/src/processor/update_difficulty.rs @@ -19,7 +19,7 @@ pub fn process_update_difficulty<'a, 'info>( return Err(ProgramError::NotEnoughAccountKeys); }; load_signer(signer)?; - load_treasury(treasury_info)?; + load_treasury(treasury_info, true)?; // Validate admin signer let mut treasury_data = treasury_info.data.borrow_mut(); diff --git a/src/state/bus.rs b/src/state/bus.rs index bdc9a67..9f75d3d 100644 --- a/src/state/bus.rs +++ b/src/state/bus.rs @@ -1,5 +1,7 @@ use bytemuck::{Pod, Zeroable}; +use crate::impl_to_bytes; + #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct Bus { @@ -13,8 +15,4 @@ pub struct Bus { pub available_rewards: u64, } -impl Bus { - pub fn to_bytes(&self) -> &[u8] { - bytemuck::bytes_of(self) - } -} +impl_to_bytes!(Bus); diff --git a/src/state/hash.rs b/src/state/hash.rs index 7864c59..9a55030 100644 --- a/src/state/hash.rs +++ b/src/state/hash.rs @@ -3,6 +3,8 @@ use std::mem::transmute; use bytemuck::{Pod, Zeroable}; use solana_program::keccak::{Hash as KeccakHash, HASH_BYTES}; +use crate::impl_to_bytes; + #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct Hash(pub [u8; HASH_BYTES]); @@ -20,3 +22,5 @@ impl From for KeccakHash { unsafe { transmute(value) } } } + +impl_to_bytes!(Hash); diff --git a/src/state/proof.rs b/src/state/proof.rs index c87dae8..7707b9d 100644 --- a/src/state/proof.rs +++ b/src/state/proof.rs @@ -1,6 +1,8 @@ use bytemuck::{Pod, Zeroable}; use solana_program::pubkey::Pubkey; +use crate::impl_to_bytes; + use super::Hash; #[repr(C)] @@ -25,8 +27,4 @@ pub struct Proof { pub total_rewards: u64, } -impl Proof { - pub fn to_bytes(&self) -> &[u8] { - bytemuck::bytes_of(self) - } -} +impl_to_bytes!(Proof); diff --git a/src/state/treasury.rs b/src/state/treasury.rs index 4bd12b9..1e5faa5 100644 --- a/src/state/treasury.rs +++ b/src/state/treasury.rs @@ -1,6 +1,8 @@ use bytemuck::{Pod, Zeroable}; use solana_program::pubkey::Pubkey; +use crate::impl_to_bytes; + use super::Hash; #[repr(C)] @@ -25,8 +27,4 @@ pub struct Treasury { pub total_claimed_rewards: u64, } -impl Treasury { - pub fn to_bytes(&self) -> &[u8] { - bytemuck::bytes_of(self) - } -} +impl_to_bytes!(Treasury); diff --git a/src/utils.rs b/src/utils.rs index c00aa6d..3a9146e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -31,3 +31,14 @@ pub fn create_pda<'a, 'info>( )?; Ok(()) } + +#[macro_export] +macro_rules! impl_to_bytes { + ($struct_name:ident) => { + impl $struct_name { + pub fn to_bytes(&self) -> &[u8] { + bytemuck::bytes_of(self) + } + } + }; +} diff --git a/tests/test_mine.rs b/tests/test_mine.rs index 1e53af9..afacb91 100644 --- a/tests/test_mine.rs +++ b/tests/test_mine.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use ore::{ - instruction::{MineArgs, OreInstruction, ProofArgs}, + instruction::{CreateProofArgs, MineArgs, OreInstruction}, state::{Proof, Treasury}, BUS, PROOF, TREASURY, }; @@ -34,8 +34,8 @@ async fn test_mine() { AccountMeta::new_readonly(system_program::id(), false), ], data: [ - OreInstruction::Proof.to_vec(), - ProofArgs { bump: proof_pda.1 }.to_bytes().to_vec(), + OreInstruction::CreateProof.to_vec(), + CreateProofArgs { bump: proof_pda.1 }.to_bytes().to_vec(), ] .concat(), }; diff --git a/tests/test_epoch.rs b/tests/test_reset.rs similarity index 99% rename from tests/test_epoch.rs rename to tests/test_reset.rs index 3fe672b..1a71f7f 100644 --- a/tests/test_epoch.rs +++ b/tests/test_reset.rs @@ -24,7 +24,7 @@ use solana_sdk::{ use spl_token::state::{AccountState, Mint}; #[tokio::test] -async fn test_epoch() { +async fn test_reset() { // Setup let (mut banks, payer, hash) = setup_program_test_env().await; @@ -62,7 +62,7 @@ async fn test_epoch() { AccountMeta::new(treasury_tokens_address, false), AccountMeta::new_readonly(spl_token::id(), false), ], - data: [OreInstruction::Epoch.to_vec()].concat(), + data: [OreInstruction::Reset.to_vec()].concat(), }; // Submit tx