From 49d5e95dbb72712f7b8367874b1e3820b6179dbe Mon Sep 17 00:00:00 2001 From: Kriptikz Date: Wed, 24 Jul 2024 22:26:07 -0500 Subject: [PATCH] update mine transaction introspection to use declared proof --- api/src/error.rs | 4 ++++ program/src/mine.rs | 30 ++++++++++++++++-------------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/api/src/error.rs b/api/src/error.rs index e3d8ee0..f334a77 100644 --- a/api/src/error.rs +++ b/api/src/error.rs @@ -27,6 +27,10 @@ pub enum OreError { 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, } impl From for ProgramError { diff --git a/program/src/mine.rs b/program/src/mine.rs index ea7673a..b8a12ce 100644 --- a/program/src/mine.rs +++ b/program/src/mine.rs @@ -25,7 +25,7 @@ use solana_program::{ sysvar::{self, instructions::load_current_index, Sysvar}, }; -use crate::utils::AccountDeserialize; +use crate::{find_and_parse_declared_proof, utils::AccountDeserialize}; /// Mine is the primary workhorse instruction of the Ore program. Its responsibilities include: /// 1. Calculate the hash from the provided nonce. @@ -61,10 +61,21 @@ pub fn process_mine<'a, 'info>( load_sysvar(instructions_sysvar, sysvar::instructions::id())?; load_sysvar(slot_hashes_sysvar, sysvar::slot_hashes::id())?; - // Validate this is the only mine ix in the transaction. - if !introspect_transaction(&instructions_sysvar.data.borrow()).unwrap_or(false) { - return Err(OreError::TransactionInvalid.into()); - } + /// 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 + /// transaction, there is a financial incentive to sybil multiple keypairs and pack as many hashes + /// as possible into each transaction to minimize fee / hash. + /// + /// If each transaction is limited to one hash only, then a user will minimize their fee / hash + /// by allocating all their hashpower to finding the single most difficult hash they can. + if let Ok(pubkey) = find_and_parse_declared_proof(&instructions_sysvar.data.borrow()) { + if !pubkey.eq(proof_info.key) { + return Err(OreError::DeclaredProofMissmatch.into()); + } + } else { + return Err(OreError::FindAndParseDeclaredProofFailed.into()); + }; // Validate epoch is active. let config_data = config_info.data.borrow(); @@ -182,15 +193,6 @@ pub fn process_mine<'a, 'info>( Ok(()) } -/// Require that there is only one `mine` instruction per transaction and it is called from the -/// top level of the transaction. -/// -/// The intent here is to disincentivize sybil. As long as a user can fit multiple hashes in a single -/// transaction, there is a financial incentive to sybil multiple keypairs and pack as many hashes -/// as possible into each transaction to minimize fee / hash. -/// -/// If each transaction is limited to one hash only, then a user will minimize their fee / hash -/// by allocating all their hashpower to finding the single most difficult hash they can. fn introspect_transaction(msg: &[u8]) -> Result { #[allow(deprecated)] let idx = load_current_index(msg);