diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 7d42880a21..a876ddeb89 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2456,6 +2456,7 @@ pub enum CustomTransactionError { InputLengthsUnequal, UidNotFound, EvmKeyAssociateRateLimitExceeded, + InvalidSubnetOwner, } impl From for u8 { @@ -2483,6 +2484,7 @@ impl From for u8 { CustomTransactionError::InputLengthsUnequal => 18, CustomTransactionError::UidNotFound => 19, CustomTransactionError::EvmKeyAssociateRateLimitExceeded => 20, + CustomTransactionError::InvalidSubnetOwner => 21, } } } diff --git a/pallets/subtensor/src/transaction_extension.rs b/pallets/subtensor/src/transaction_extension.rs index cf1d410ea9..af545f90b4 100644 --- a/pallets/subtensor/src/transaction_extension.rs +++ b/pallets/subtensor/src/transaction_extension.rs @@ -1,6 +1,6 @@ use crate::{ BalancesCall, Call, ColdkeySwapScheduled, Config, CustomTransactionError, Error, Pallet, - TransactionType, + SubnetOwner, TransactionType, }; use codec::{Decode, DecodeWithMemTracking, Encode}; use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; @@ -49,6 +49,13 @@ where Pallet::::check_weights_min_stake(who, netuid) } + /// Check if the caller is the subnet owner or root origin. + /// Returns true if the caller is the subnet owner, false otherwise. + /// Note: Root origin is allowed but we can't detect it here, so it will be checked in dispatch. + pub fn is_subnet_owner(who: &T::AccountId, netuid: NetUid) -> bool { + SubnetOwner::::get(netuid) == *who + } + pub fn result_to_validity(result: Result<(), Error>, priority: u64) -> TransactionValidity { if let Err(err) = result { Err(match err { @@ -117,6 +124,8 @@ where _source: TransactionSource, ) -> ValidateResult::RuntimeCall> { // Ensure the transaction is signed, else we just skip the extension. + // Note: Root origin is allowed for subnet owner calls but we can't detect it here, + // so it will be validated in the dispatch function. let Some(who) = origin.as_system_origin_signer() else { return Ok((Default::default(), None, origin)); }; @@ -298,6 +307,30 @@ where Err(_) => Err(CustomTransactionError::UidNotFound.into()), } } + // Filter incorrect subnet owner calls + // These calls require subnet owner validation and should be filtered + // in the transaction pool to prevent invalid transactions from being added. + Some(Call::start_call { netuid, .. }) => { + if Self::is_subnet_owner(who, *netuid) { + Ok((Default::default(), Some(who.clone()), origin)) + } else { + Err(CustomTransactionError::InvalidSubnetOwner.into()) + } + } + Some(Call::update_symbol { netuid, .. }) => { + if Self::is_subnet_owner(who, *netuid) { + Ok((Default::default(), Some(who.clone()), origin)) + } else { + Err(CustomTransactionError::InvalidSubnetOwner.into()) + } + } + Some(Call::sudo_set_root_claim_threshold { netuid, .. }) => { + if Self::is_subnet_owner(who, *netuid) { + Ok((Default::default(), Some(who.clone()), origin)) + } else { + Err(CustomTransactionError::InvalidSubnetOwner.into()) + } + } _ => Ok((Default::default(), Some(who.clone()), origin)), } }