Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2456,6 +2456,7 @@ pub enum CustomTransactionError {
InputLengthsUnequal,
UidNotFound,
EvmKeyAssociateRateLimitExceeded,
InvalidSubnetOwner,
}

impl From<CustomTransactionError> for u8 {
Expand Down Expand Up @@ -2483,6 +2484,7 @@ impl From<CustomTransactionError> for u8 {
CustomTransactionError::InputLengthsUnequal => 18,
CustomTransactionError::UidNotFound => 19,
CustomTransactionError::EvmKeyAssociateRateLimitExceeded => 20,
CustomTransactionError::InvalidSubnetOwner => 21,
}
}
}
Expand Down
35 changes: 34 additions & 1 deletion pallets/subtensor/src/transaction_extension.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -49,6 +49,13 @@ where
Pallet::<T>::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::<T>::get(netuid) == *who
}

pub fn result_to_validity(result: Result<(), Error<T>>, priority: u64) -> TransactionValidity {
if let Err(err) = result {
Err(match err {
Expand Down Expand Up @@ -117,6 +124,8 @@ where
_source: TransactionSource,
) -> ValidateResult<Self::Val, <T as frame_system::Config>::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));
};
Expand Down Expand Up @@ -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)),
}
}
Expand Down