From 3bfe8506f44b8748f265944adf3717e8822403aa Mon Sep 17 00:00:00 2001 From: Hardhat Chad Date: Fri, 26 Jul 2024 21:26:47 +0000 Subject: [PATCH] auth --- api/src/error.rs | 10 +---- api/src/instruction.rs | 36 +++++++--------- program/src/declare_proof.rs | 82 ++++++++++++++++++------------------ program/src/lib.rs | 6 +-- program/src/mine.rs | 20 ++++----- 5 files changed, 70 insertions(+), 84 deletions(-) diff --git a/api/src/error.rs b/api/src/error.rs index f334a77..d4873b6 100644 --- a/api/src/error.rs +++ b/api/src/error.rs @@ -23,14 +23,8 @@ 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, - #[error("Only the declared proof can be processed in this transaction")] - DeclaredProofMissmatch = 11, - #[error("Failed to find and parse the declared proof from the transaction")] - FindAndParseDeclaredProofFailed = 12, + #[error("The proof does not match the expected account")] + AuthFailed = 9, } impl From for ProgramError { diff --git a/api/src/instruction.rs b/api/src/instruction.rs index f461785..5ccb7db 100644 --- a/api/src/instruction.rs +++ b/api/src/instruction.rs @@ -17,6 +17,10 @@ use crate::{ #[derive(Clone, Copy, Debug, Eq, PartialEq, ShankInstruction, TryFromPrimitive)] #[rustfmt::skip] pub enum OreInstruction { + #[account(0, name = "ore_program", desc = "Ore program")] + #[account(1, name = "proof", desc = "Ore proof account")] + Auth = 0, + #[account(0, name = "ore_program", desc = "Ore program")] #[account(1, name = "signer", desc = "Signer", signer)] #[account(2, name = "beneficiary", desc = "Beneficiary token account", writable)] @@ -24,13 +28,13 @@ pub enum OreInstruction { #[account(4, name = "treasury", desc = "Ore treasury account", writable)] #[account(5, name = "treasury_tokens", desc = "Ore treasury token account", writable)] #[account(6, name = "token_program", desc = "SPL token program")] - Claim = 0, + Claim = 1, #[account(0, name = "ore_program", desc = "Ore program")] #[account(1, name = "signer", desc = "Signer", signer)] #[account(2, name = "proof", desc = "Ore proof account", writable)] #[account(3, name = "system_program", desc = "Solana system program")] - Close = 1, + Close = 2, #[account(0, name = "ore_program", desc = "Ore program")] #[account(1, name = "signer", desc = "Signer", signer)] @@ -39,7 +43,7 @@ pub enum OreInstruction { #[account(4, name = "noise", desc = "Ore noise account")] #[account(5, name = "proof", desc = "Ore proof account", writable)] #[account(6, name = "slot_hashes", desc = "Solana slot hashes sysvar")] - Mine = 2, + Mine = 3, #[account(0, name = "ore_program", desc = "Ore program")] #[account(1, name = "signer", desc = "Signer", signer)] @@ -48,7 +52,7 @@ pub enum OreInstruction { #[account(4, name = "proof", desc = "Ore proof account", writable)] #[account(5, name = "system_program", desc = "Solana system program")] #[account(6, name = "slot_hashes", desc = "Solana slot hashes sysvar")] - Open = 3, + Open = 4, #[account(0, name = "ore_program", desc = "Ore program")] #[account(1, name = "signer", desc = "Signer", signer)] @@ -65,7 +69,7 @@ pub enum OreInstruction { #[account(12, name = "treasury", desc = "Ore treasury account", writable)] #[account(13, name = "treasury_tokens", desc = "Ore treasury token account", writable)] #[account(14, name = "token_program", desc = "SPL token program")] - Reset = 4, + Reset = 5, #[account(0, name = "ore_program", desc = "Ore program")] #[account(1, name = "signer", desc = "Signer", signer)] @@ -73,12 +77,12 @@ pub enum OreInstruction { #[account(3, name = "sender", desc = "Signer token account", writable)] #[account(4, name = "treasury_tokens", desc = "Ore treasury token account", writable)] #[account(5, name = "token_program", desc = "SPL token program")] - Stake = 5, + Stake = 6, #[account(0, name = "ore_program", desc = "Ore program")] #[account(1, name = "signer", desc = "Signer", signer)] #[account(2, name = "proof", desc = "Ore proof account", writable)] - Update = 6, + Update = 7, #[account(0, name = "ore_program", desc = "Ore program")] #[account(1, name = "signer", desc = "Signer", signer)] @@ -88,11 +92,7 @@ pub enum OreInstruction { #[account(5, name = "mint", desc = "Ore token mint account", writable)] #[account(6, name = "mint_v1", desc = "Ore v1 token mint account", writable)] #[account(7, name = "token_program", desc = "SPL token program")] - Upgrade = 7, - - #[account(0, name = "ore_program", desc = "Ore program")] - #[account(1, name = "proof", desc = "Ore proof account")] - DeclareProof = 8, + Upgrade = 8, #[account(0, name = "ore_program", desc = "Ore program")] #[account(1, name = "signer", desc = "Admin signer", signer)] @@ -228,16 +228,12 @@ pub fn close(signer: Pubkey) -> Instruction { } } -/// Builds a declare proof instruction. -pub fn declare_proof( - proof: Pubkey, -) -> Instruction { +/// Builds an auth instruction. +pub fn auth(proof: Pubkey) -> Instruction { Instruction { program_id: crate::id(), - accounts: vec![ - AccountMeta::new_readonly(proof, false), - ], - data: OreInstruction::DeclareProof.to_vec(), + accounts: vec![AccountMeta::new_readonly(proof, false)], + data: OreInstruction::Auth.to_vec(), } } diff --git a/program/src/declare_proof.rs b/program/src/declare_proof.rs index 9f4e458..c46d45e 100644 --- a/program/src/declare_proof.rs +++ b/program/src/declare_proof.rs @@ -1,17 +1,18 @@ +use ore_api::instruction::OreInstruction; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, + pubkey, pubkey::Pubkey, sanitize::SanitizeError, - serialize_utils::{read_pubkey, read_u16}, + serialize_utils::{read_pubkey, read_u16, read_u8}, }; - /// DeclareProof is used by other instructions in the same transaction. /// - Other instructions will use transaction introspection to ensure they /// only process the declared proof. -/// - Other instructions will use find_and_parse_declared_proof with the +/// - Other instructions will use find_and_parse_declared_proof with the /// introspection data /// /// Safety requirements: @@ -19,20 +20,20 @@ use solana_program::{ /// low as possible. Other instructions that use the declared proof handle /// validation via the loader. /// - Only one account should be provided. -pub fn process_declare_proof<'a, 'info>( +pub fn process_auth<'a, 'info>( _program_id: &Pubkey, accounts: &'a [AccountInfo<'info>], _data: &[u8], ) -> ProgramResult { - let [_proof_info] = - accounts - else { + let [_proof_info] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; Ok(()) } +const COMPUTE_BUDGET_PROGRAM_ID: Pubkey = pubkey!("ComputeBudget111111111111111111111111111111"); + /// Require that only the declared proof can be processed in this transaction. /// /// The intent here is to disincentivize sybil. As long as a user can fit multiple hashes in a single @@ -45,41 +46,38 @@ pub fn process_declare_proof<'a, 'info>( /// Errors if: /// - Fails to find and parse the declared proof pubkey in the second instruction /// of the transaction -pub fn find_and_parse_declared_proof(data: &[u8]) -> Result { - // start the current byte index at 0 - let mut current = 0; - let num_instructions = read_u16(&mut current, data)?; - - if num_instructions < 2 { - // Not enough instructions in this transaction. - // The declare_proof instruction must be included with - // the other instruction in the same transaction. - return Err(SanitizeError::IndexOutOfBounds) +pub fn authenticate(data: &[u8]) -> Result, SanitizeError> { + // Start the current byte index at 0 + let mut curr = 0; + let num_instructions = read_u16(&mut curr, data)?; + let pc = curr; + for i in 0..num_instructions as usize { + curr = pc + i * 2; + curr = read_u16(&mut curr, data)? as usize; + let num_accounts = read_u16(&mut curr, data)? as usize; + let mut ac = curr; + curr += num_accounts * 33; + let program_id = read_pubkey(&mut curr, data)?; + if program_id.eq(&ore_api::ID) { + curr += 2; + if let Ok(ix) = OreInstruction::try_from(read_u8(&mut curr, data)?) { + // Validate ix is an auth + if ix.ne(&OreInstruction::Auth) { + return Ok(None); + } + // Valid num accounts is correct + if num_accounts.ne(&1) { + return Ok(None); + } + // Return provided pubkey + let address = read_pubkey(&mut ac, data)?; + return Ok(Some(address)); + } else { + // Otherwise return + return Ok(None); + } + } } - // The first ix should be a set compute budget ix - // The second ix should be a declare_proof ix - // The starting index is the second ix - let index = 1; - - current += index * 2; - let start = read_u16(&mut current, data)?; - - current = start as usize; - - current += 2; // skip the accounts length variable - current += 1; // skip the meta_byte - - // The only account provided should be the proof pubkey - let proof_pubkey = read_pubkey(&mut current, data)?; - - // There shouldn't be any more accounts, - // this should be the Ore program ID - let program_pubkey = read_pubkey(&mut current, data)?; - if program_pubkey.eq(&ore_api::ID) { - return Ok(proof_pubkey); - } else { - return Err(SanitizeError::InvalidValue); - } + Ok(None) } - diff --git a/program/src/lib.rs b/program/src/lib.rs index 1846e63..977b051 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -1,7 +1,7 @@ mod claim; mod close; -mod initialize; mod declare_proof; +mod initialize; mod mine; mod open; mod reset; @@ -11,8 +11,8 @@ mod upgrade; use claim::*; use close::*; -use initialize::*; use declare_proof::*; +use initialize::*; use mine::*; use open::*; use reset::*; @@ -45,9 +45,9 @@ pub fn process_instruction( .ok_or(ProgramError::InvalidInstructionData)?; match OreInstruction::try_from(*tag).or(Err(ProgramError::InvalidInstructionData))? { + OreInstruction::Auth => process_auth(program_id, accounts, data)?, OreInstruction::Claim => process_claim(program_id, accounts, data)?, OreInstruction::Close => process_close(program_id, accounts, data)?, - OreInstruction::DeclareProof => process_declare_proof(program_id, accounts, data)?, OreInstruction::Mine => process_mine(program_id, accounts, data)?, OreInstruction::Open => process_open(program_id, accounts, data)?, OreInstruction::Reset => process_reset(program_id, accounts, data)?, diff --git a/program/src/mine.rs b/program/src/mine.rs index 8749399..569f24c 100644 --- a/program/src/mine.rs +++ b/program/src/mine.rs @@ -5,7 +5,7 @@ use ore_api::{ consts::*, error::OreError, event::MineEvent, - instruction::{MineArgs, OreInstruction}, + instruction::MineArgs, loaders::*, state::{Bus, Config, Proof}, }; @@ -19,13 +19,11 @@ use solana_program::{ log::sol_log, program_error::ProgramError, pubkey::Pubkey, - sanitize::SanitizeError, - serialize_utils::{read_pubkey, read_u16, read_u8}, slot_hashes::SlotHash, - sysvar::{self, instructions::load_current_index, Sysvar}, + sysvar::{self, Sysvar}, }; -use crate::{find_and_parse_declared_proof, utils::AccountDeserialize}; +use crate::{authenticate, utils::AccountDeserialize}; /// Mine is the primary workhorse instruction of the Ore program. Its responsibilities include: /// 1. Calculate the hash from the provided nonce. @@ -62,13 +60,14 @@ pub fn process_mine<'a, 'info>( load_sysvar(instructions_sysvar, sysvar::instructions::id())?; load_sysvar(slot_hashes_sysvar, sysvar::slot_hashes::id())?; - if let Ok(pubkey) = find_and_parse_declared_proof(&instructions_sysvar.data.borrow()) { - if !pubkey.eq(proof_info.key) { - return Err(OreError::DeclaredProofMissmatch.into()); + // Authenticate the proof account + if let Ok(Some(auth_address)) = authenticate(&instructions_sysvar.data.borrow()) { + if auth_address.ne(proof_info.key) { + return Err(OreError::AuthFailed.into()); } } else { - return Err(OreError::FindAndParseDeclaredProofFailed.into()); - }; + return Err(OreError::AuthFailed.into()); + } // Validate epoch is active. let config_data = config_info.data.borrow(); @@ -185,4 +184,3 @@ pub fn process_mine<'a, 'info>( Ok(()) } -