Skip to content
Draft
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: 1 addition & 1 deletion pallets/admin-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1685,7 +1685,7 @@ pub mod pallet {
#[pallet::weight((
Weight::from_parts(3_918_000, 0) // TODO: add benchmarks
.saturating_add(T::DbWeight::get().writes(1_u64)),
DispatchClass::Operational,
DispatchClass::Normal,
Pays::Yes
))]
pub fn sudo_set_subnet_owner_hotkey(
Expand Down
2 changes: 1 addition & 1 deletion pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2413,7 +2413,7 @@ mod dispatches {
Weight::from_parts(5_711_000, 0)
.saturating_add(T::DbWeight::get().reads(0_u64))
.saturating_add(T::DbWeight::get().writes(1_u64)),
DispatchClass::Operational,
DispatchClass::Normal,
Pays::Yes
))]
pub fn sudo_set_root_claim_threshold(
Expand Down
71 changes: 58 additions & 13 deletions runtime/src/sudo_wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use codec::{Decode, DecodeWithMemTracking, Encode};
use frame_support::dispatch::{DispatchInfo, PostDispatchInfo};
use frame_support::dispatch::{DispatchClass, DispatchInfo, PostDispatchInfo};
use frame_support::traits::IsSubType;
use frame_system::Config;
use pallet_shield::AuthorityOriginExt;
use pallet_shield::Call as MevShieldCall;
use pallet_sudo::Call as SudoCall;
use scale_info::TypeInfo;
use sp_runtime::impl_tx_ext_default;
Expand Down Expand Up @@ -34,12 +36,12 @@ impl<T: Config + Send + Sync + TypeInfo> SudoTransactionExtension<T> {
}
}

impl<T: Config + Send + Sync + TypeInfo + pallet_sudo::Config>
TransactionExtension<<T as Config>::RuntimeCall> for SudoTransactionExtension<T>
impl<T> TransactionExtension<<T as Config>::RuntimeCall> for SudoTransactionExtension<T>
where
T: Config + Send + Sync + TypeInfo + pallet_sudo::Config + pallet_shield::Config,
<T as Config>::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
<T as Config>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + Clone,
<T as Config>::RuntimeCall: IsSubType<SudoCall<T>>,
<T as Config>::RuntimeCall: IsSubType<SudoCall<T>> + IsSubType<MevShieldCall<T>>,
{
const IDENTIFIER: &'static str = "SudoTransactionExtension";

Expand All @@ -53,30 +55,73 @@ where
&self,
origin: <T as Config>::RuntimeOrigin,
call: &<T as Config>::RuntimeCall,
_info: &DispatchInfoOf<<T as Config>::RuntimeCall>,
info: &DispatchInfoOf<<T as Config>::RuntimeCall>,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl Implication,
_source: TransactionSource,
) -> ValidateResult<Self::Val, <T as Config>::RuntimeCall> {
// Ensure the transaction is signed, else we just skip the extension.
let Some(who) = origin.as_system_origin_signer() else {
return Ok((Default::default(), (), origin));
};
// --------------------------------------------------------------------
// 1) pallet_sudo exception:
// If this is a sudo call, require it be signed by the configured sudo key.
// --------------------------------------------------------------------
if let Some(_sudo_call) = IsSubType::<SudoCall<T>>::is_sub_type(call) {
// Sudo extrinsics must be signed.
let Some(who) = origin.as_system_origin_signer() else {
return Err(InvalidTransaction::BadSigner.into());
};

// Check validity of the signer for sudo call
if let Some(_sudo_call) = IsSubType::<pallet_sudo::Call<T>>::is_sub_type(call) {
let sudo_key = pallet_sudo::pallet::Key::<T>::get();

// No sudo key configured → reject
// No sudo key configured → reject.
let Some(expected_who) = sudo_key else {
return Err(InvalidTransaction::BadSigner.into());
};

// Signer does not match the sudo key → reject
// Signer does not match the sudo key → reject.
if *who != expected_who {
return Err(InvalidTransaction::BadSigner.into());
}

// Valid sudo transaction → allow into pool.
return Ok((Default::default(), (), origin));
}

// --------------------------------------------------------------------
// 2) Generic BadOrigin spam prevention for *all pallets*:
//
// Default rule:
// - If DispatchClass is Operational, only allow Root-origin transactions.
// - If a *signed* tx is Operational but NOT Root → reject from the pool.
//
// Remaining exception (Operational but NOT Root-only):
// - MevShield::announce_next_key : must pass T::AuthorityOrigin::ensure_validator(origin)
// --------------------------------------------------------------------
if info.class == DispatchClass::Operational {
// Always allow true Root origins.
if frame_system::ensure_root(origin.clone()).is_ok() {
return Ok((Default::default(), (), origin));
}

// Exception: MevShield::announce_next_key (Operational, but signed-validator origin)
if let Some(mev_call) = IsSubType::<MevShieldCall<T>>::is_sub_type(call) {
match mev_call {
MevShieldCall::announce_next_key { .. } => {
// Only a current Aura validator may call this.
if T::AuthorityOrigin::ensure_validator(origin.clone()).is_err() {
return Err(InvalidTransaction::BadSigner.into());
}
return Ok((Default::default(), (), origin));
}
_ => {}
}
}

// Default Operational rule: signed Operational txs that aren't Root and aren't
// one of the allowed exceptions are rejected from the pool.
if origin.as_system_origin_signer().is_some() {
return Err(InvalidTransaction::Call.into());
}
}

Ok((Default::default(), (), origin))
Expand Down
Loading