From 2a315462729fea2aebc7835db0eb1dbe77c13461 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Apr 2026 14:45:28 +0800 Subject: [PATCH 1/8] extend subtensor extension --- common/src/transaction_error.rs | 32 ++ pallets/subtensor/src/extensions/subtensor.rs | 437 ++++++++++++++---- .../subtensor/src/staking/decrease_take.rs | 18 + .../subtensor/src/staking/increase_take.rs | 27 ++ pallets/subtensor/src/subnets/registration.rs | 30 ++ pallets/subtensor/src/subnets/serving.rs | 42 ++ pallets/subtensor/src/subnets/weights.rs | 99 ++++ pallets/subtensor/src/swap/swap_hotkey.rs | 54 +++ pallets/subtensor/src/tests/mod.rs | 1 + .../tests/transaction_extension_pays_no.rs | 184 ++++++++ pallets/subtensor/src/tests/weights.rs | 6 +- 11 files changed, 832 insertions(+), 98 deletions(-) create mode 100644 pallets/subtensor/src/tests/transaction_extension_pays_no.rs diff --git a/common/src/transaction_error.rs b/common/src/transaction_error.rs index de98cdf5f4..474a894aca 100644 --- a/common/src/transaction_error.rs +++ b/common/src/transaction_error.rs @@ -30,6 +30,22 @@ pub enum CustomTransactionError { InvalidRealAccount, FailedShieldedTxParsing, InvalidShieldedTxPubKeyHash, + CommitRevealEnabled, + CommitRevealDisabled, + NonAssociatedColdKey, + InvalidIpType, + IncorrectCommitRevealVersion, + CommittingWeightsTooFast, + TooManyUnrevealedCommits, + NewHotKeyIsSameWithOld, + HotKeyAlreadyRegisteredInSubNet, + NotEnoughBalanceToPaySwapHotKey, + HotKeySwapOnSubnetIntervalNotPassed, + DelegateTakeTooLow, + DelegateTakeTooHigh, + InvalidWorkBlock, + InvalidDifficulty, + InvalidSeal, } impl From for u8 { @@ -62,6 +78,22 @@ impl From for u8 { CustomTransactionError::InvalidRealAccount => 22, CustomTransactionError::FailedShieldedTxParsing => 23, CustomTransactionError::InvalidShieldedTxPubKeyHash => 24, + CustomTransactionError::CommitRevealEnabled => 25, + CustomTransactionError::CommitRevealDisabled => 26, + CustomTransactionError::NonAssociatedColdKey => 27, + CustomTransactionError::InvalidIpType => 28, + CustomTransactionError::IncorrectCommitRevealVersion => 29, + CustomTransactionError::CommittingWeightsTooFast => 30, + CustomTransactionError::TooManyUnrevealedCommits => 31, + CustomTransactionError::NewHotKeyIsSameWithOld => 32, + CustomTransactionError::HotKeyAlreadyRegisteredInSubNet => 33, + CustomTransactionError::NotEnoughBalanceToPaySwapHotKey => 34, + CustomTransactionError::HotKeySwapOnSubnetIntervalNotPassed => 35, + CustomTransactionError::DelegateTakeTooLow => 36, + CustomTransactionError::DelegateTakeTooHigh => 37, + CustomTransactionError::InvalidWorkBlock => 38, + CustomTransactionError::InvalidDifficulty => 39, + CustomTransactionError::InvalidSeal => 40, } } } diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index 7455dbb78a..05f987fe38 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -15,7 +15,7 @@ use sp_runtime::{ use sp_std::marker::PhantomData; use sp_std::vec::Vec; use subtensor_macros::freeze_struct; -use subtensor_runtime_common::{CustomTransactionError, NetUid, NetUidStorageIndex}; +use subtensor_runtime_common::{CustomTransactionError, MechId, NetUid}; const ADD_STAKE_BURN_PRIORITY_BOOST: u64 = 100; @@ -50,37 +50,73 @@ where Pallet::::check_weights_min_stake(who, netuid) } - pub fn result_to_validity(result: Result<(), Error>, priority: u64) -> TransactionValidity { - if let Err(err) = result { - Err(match err { - Error::::AmountTooLow => CustomTransactionError::StakeAmountTooLow, - Error::::SubnetNotExists => CustomTransactionError::SubnetNotExists, - Error::::NotEnoughBalanceToStake => CustomTransactionError::BalanceTooLow, - Error::::HotKeyAccountNotExists => { - CustomTransactionError::HotkeyAccountDoesntExist - } - Error::::NotEnoughStakeToWithdraw => { - CustomTransactionError::NotEnoughStakeToWithdraw - } - Error::::InsufficientLiquidity => CustomTransactionError::InsufficientLiquidity, - Error::::SlippageTooHigh => CustomTransactionError::SlippageTooHigh, - Error::::TransferDisallowed => CustomTransactionError::TransferDisallowed, - Error::::HotKeyNotRegisteredInNetwork => { - CustomTransactionError::HotKeyNotRegisteredInNetwork - } - Error::::InvalidIpAddress => CustomTransactionError::InvalidIpAddress, - Error::::ServingRateLimitExceeded => { - CustomTransactionError::ServingRateLimitExceeded - } - Error::::InvalidPort => CustomTransactionError::InvalidPort, - _ => CustomTransactionError::BadRequest, + pub fn pallet_error_to_custom(err: Error) -> CustomTransactionError { + match err { + Error::::AmountTooLow => CustomTransactionError::StakeAmountTooLow, + Error::::SubnetNotExists => CustomTransactionError::SubnetNotExists, + Error::::NotEnoughBalanceToStake => CustomTransactionError::BalanceTooLow, + Error::::HotKeyAccountNotExists => CustomTransactionError::HotkeyAccountDoesntExist, + Error::::NotEnoughStakeToWithdraw => { + CustomTransactionError::NotEnoughStakeToWithdraw + } + Error::::InsufficientLiquidity => CustomTransactionError::InsufficientLiquidity, + Error::::SlippageTooHigh => CustomTransactionError::SlippageTooHigh, + Error::::TransferDisallowed => CustomTransactionError::TransferDisallowed, + Error::::HotKeyNotRegisteredInNetwork => { + CustomTransactionError::HotKeyNotRegisteredInNetwork + } + Error::::InvalidIpAddress => CustomTransactionError::InvalidIpAddress, + Error::::ServingRateLimitExceeded => { + CustomTransactionError::ServingRateLimitExceeded + } + Error::::InvalidPort => CustomTransactionError::InvalidPort, + Error::::CommitRevealEnabled => CustomTransactionError::CommitRevealEnabled, + Error::::CommitRevealDisabled => CustomTransactionError::CommitRevealDisabled, + Error::::NonAssociatedColdKey => CustomTransactionError::NonAssociatedColdKey, + Error::::InvalidIpType => CustomTransactionError::InvalidIpType, + Error::::IncorrectCommitRevealVersion => { + CustomTransactionError::IncorrectCommitRevealVersion + } + Error::::CommittingWeightsTooFast => { + CustomTransactionError::CommittingWeightsTooFast + } + Error::::TooManyUnrevealedCommits => { + CustomTransactionError::TooManyUnrevealedCommits + } + Error::::NewHotKeyIsSameWithOld => CustomTransactionError::NewHotKeyIsSameWithOld, + Error::::HotKeyAlreadyRegisteredInSubNet => { + CustomTransactionError::HotKeyAlreadyRegisteredInSubNet + } + Error::::NotEnoughBalanceToPaySwapHotKey => { + CustomTransactionError::NotEnoughBalanceToPaySwapHotKey + } + Error::::HotKeySwapOnSubnetIntervalNotPassed => { + CustomTransactionError::HotKeySwapOnSubnetIntervalNotPassed + } + Error::::DelegateTakeTooLow => CustomTransactionError::DelegateTakeTooLow, + Error::::DelegateTakeTooHigh => CustomTransactionError::DelegateTakeTooHigh, + Error::::InvalidWorkBlock => CustomTransactionError::InvalidWorkBlock, + Error::::InvalidDifficulty => CustomTransactionError::InvalidDifficulty, + Error::::InvalidSeal => CustomTransactionError::InvalidSeal, + Error::::InputLengthsUnequal => CustomTransactionError::InputLengthsUnequal, + Error::::MechanismDoesNotExist => CustomTransactionError::SubnetNotExists, + Error::::HotKeyNotRegisteredInSubNet => { + CustomTransactionError::HotKeyNotRegisteredInNetwork } - .into()) - } else { - Ok(ValidTransaction { + Error::::DelegateTxRateLimitExceeded | Error::::HotKeySetTxRateLimitExceeded => { + CustomTransactionError::RateLimitExceeded + } + _ => CustomTransactionError::BadRequest, + } + } + + pub fn result_to_validity(result: Result<(), Error>, priority: u64) -> TransactionValidity { + match result { + Ok(()) => Ok(ValidTransaction { priority, ..Default::default() - }) + }), + Err(err) => Err(Self::pallet_error_to_custom(err).into()), } } } @@ -116,11 +152,46 @@ where match call.is_sub_type() { Some(Call::commit_weights { netuid, .. }) => { - if Self::check_weights_min_stake(who, *netuid) { - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::StakeAmountTooLow.into()) + if !Self::check_weights_min_stake(who, *netuid) { + return Err(CustomTransactionError::StakeAmountTooLow.into()); + } + let r = Pallet::::validate_hash_commit_weights_prerequisites( + who, + *netuid, + MechId::MAIN, + ); + Self::result_to_validity(r, 0u64).map(|validity| (validity, (), origin.clone())) + } + Some(Call::commit_mechanism_weights { netuid, mecid, .. }) => { + if !Self::check_weights_min_stake(who, *netuid) { + return Err(CustomTransactionError::StakeAmountTooLow.into()); + } + let r = + Pallet::::validate_hash_commit_weights_prerequisites(who, *netuid, *mecid); + Self::result_to_validity(r, 0u64).map(|validity| (validity, (), origin.clone())) + } + Some(Call::batch_commit_weights { + netuids, + commit_hashes, + }) => { + if netuids.len() != commit_hashes.len() { + return Err(CustomTransactionError::InputLengthsUnequal.into()); } + for netuid in netuids.iter() { + let netuid: NetUid = (*netuid).into(); + if !Self::check_weights_min_stake(who, netuid) { + return Err(CustomTransactionError::StakeAmountTooLow.into()); + } + let r = Pallet::::validate_hash_commit_weights_prerequisites( + who, + netuid, + MechId::MAIN, + ); + if let Err(e) = r { + return Err(Self::pallet_error_to_custom(e).into()); + } + } + Ok((Default::default(), (), origin)) } Some(Call::reveal_weights { netuid, @@ -129,27 +200,58 @@ where salt, version_key, }) => { - if Self::check_weights_min_stake(who, *netuid) { - let provided_hash = Pallet::::get_commit_hash( - who, - NetUidStorageIndex::from(*netuid), - uids, - values, - salt, - *version_key, - ); - match Pallet::::find_commit_block_via_hash(provided_hash) { - Some(commit_block) => { - if Pallet::::is_reveal_block_range(*netuid, commit_block) { - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) - } + if !Self::check_weights_min_stake(who, *netuid) { + return Err(CustomTransactionError::StakeAmountTooLow.into()); + } + let netuid_index = Pallet::::get_mechanism_storage_index(*netuid, MechId::MAIN); + let provided_hash = Pallet::::get_commit_hash( + who, + netuid_index, + uids, + values, + salt, + *version_key, + ); + match Pallet::::find_commit_block_via_hash(provided_hash) { + Some(commit_block) => { + if Pallet::::is_reveal_block_range(*netuid, commit_block) { + Ok((Default::default(), (), origin)) + } else { + Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) } - None => Err(CustomTransactionError::CommitNotFound.into()), } - } else { - Err(CustomTransactionError::StakeAmountTooLow.into()) + None => Err(CustomTransactionError::CommitNotFound.into()), + } + } + Some(Call::reveal_mechanism_weights { + netuid, + mecid, + uids, + values, + salt, + version_key, + }) => { + if !Self::check_weights_min_stake(who, *netuid) { + return Err(CustomTransactionError::StakeAmountTooLow.into()); + } + let netuid_index = Pallet::::get_mechanism_storage_index(*netuid, *mecid); + let provided_hash = Pallet::::get_commit_hash( + who, + netuid_index, + uids, + values, + salt, + *version_key, + ); + match Pallet::::find_commit_block_via_hash(provided_hash) { + Some(commit_block) => { + if Pallet::::is_reveal_block_range(*netuid, commit_block) { + Ok((Default::default(), (), origin)) + } else { + Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) + } + } + None => Err(CustomTransactionError::CommitNotFound.into()), } } Some(Call::batch_reveal_weights { @@ -159,68 +261,175 @@ where salts_list, version_keys, }) => { - if Self::check_weights_min_stake(who, *netuid) { - let num_reveals = uids_list.len(); - if num_reveals == values_list.len() - && num_reveals == salts_list.len() - && num_reveals == version_keys.len() - { - let provided_hashes = (0..num_reveals) - .map(|i| { - Pallet::::get_commit_hash( - who, - NetUidStorageIndex::from(*netuid), - uids_list.get(i).unwrap_or(&Vec::new()), - values_list.get(i).unwrap_or(&Vec::new()), - salts_list.get(i).unwrap_or(&Vec::new()), - *version_keys.get(i).unwrap_or(&0_u64), - ) - }) - .collect::>(); + if !Self::check_weights_min_stake(who, *netuid) { + return Err(CustomTransactionError::StakeAmountTooLow.into()); + } + let netuid_index = Pallet::::get_mechanism_storage_index(*netuid, MechId::MAIN); + let num_reveals = uids_list.len(); + if num_reveals != values_list.len() + || num_reveals != salts_list.len() + || num_reveals != version_keys.len() + { + return Err(CustomTransactionError::InputLengthsUnequal.into()); + } + let provided_hashes = (0..num_reveals) + .map(|i| { + Pallet::::get_commit_hash( + who, + netuid_index, + uids_list.get(i).unwrap_or(&Vec::new()), + values_list.get(i).unwrap_or(&Vec::new()), + salts_list.get(i).unwrap_or(&Vec::new()), + *version_keys.get(i).unwrap_or(&0_u64), + ) + }) + .collect::>(); - let batch_reveal_block = provided_hashes - .iter() - .filter_map(|hash| Pallet::::find_commit_block_via_hash(*hash)) - .collect::>(); + let batch_reveal_block = provided_hashes + .iter() + .filter_map(|hash| Pallet::::find_commit_block_via_hash(*hash)) + .collect::>(); - if provided_hashes.len() == batch_reveal_block.len() { - if Pallet::::is_batch_reveal_block_range(*netuid, batch_reveal_block) - { - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) - } - } else { - Err(CustomTransactionError::CommitNotFound.into()) - } - } else { - Err(CustomTransactionError::InputLengthsUnequal.into()) - } + if provided_hashes.len() != batch_reveal_block.len() { + return Err(CustomTransactionError::CommitNotFound.into()); + } + if Pallet::::is_batch_reveal_block_range(*netuid, batch_reveal_block) { + Ok((Default::default(), (), origin)) } else { - Err(CustomTransactionError::StakeAmountTooLow.into()) + Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) } } Some(Call::set_weights { netuid, .. }) => { + if Pallet::::get_commit_reveal_weights_enabled(*netuid) { + return Err(CustomTransactionError::CommitRevealEnabled.into()); + } + if Self::check_weights_min_stake(who, *netuid) { + Ok((Default::default(), (), origin)) + } else { + Err(CustomTransactionError::StakeAmountTooLow.into()) + } + } + Some(Call::set_mechanism_weights { netuid, .. }) => { + if Pallet::::get_commit_reveal_weights_enabled(*netuid) { + return Err(CustomTransactionError::CommitRevealEnabled.into()); + } if Self::check_weights_min_stake(who, *netuid) { Ok((Default::default(), (), origin)) } else { Err(CustomTransactionError::StakeAmountTooLow.into()) } } + Some(Call::batch_set_weights { + netuids, + weights, + version_keys, + }) => { + if netuids.len() != weights.len() || netuids.len() != version_keys.len() { + return Err(CustomTransactionError::InputLengthsUnequal.into()); + } + for netuid in netuids.iter() { + let netuid: NetUid = (*netuid).into(); + if Pallet::::get_commit_reveal_weights_enabled(netuid) { + return Err(CustomTransactionError::CommitRevealEnabled.into()); + } + if !Self::check_weights_min_stake(who, netuid) { + return Err(CustomTransactionError::StakeAmountTooLow.into()); + } + } + Ok((Default::default(), (), origin)) + } Some(Call::commit_timelocked_weights { netuid, reveal_round, + commit_reveal_version, .. }) => { - if Self::check_weights_min_stake(who, *netuid) { - if *reveal_round < pallet_drand::LastStoredRound::::get() { - return Err(CustomTransactionError::InvalidRevealRound.into()); - } - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::StakeAmountTooLow.into()) + if !Self::check_weights_min_stake(who, *netuid) { + return Err(CustomTransactionError::StakeAmountTooLow.into()); + } + if *reveal_round < pallet_drand::LastStoredRound::::get() { + return Err(CustomTransactionError::InvalidRevealRound.into()); } + let r = Pallet::::validate_timelocked_commit_weights_prerequisites( + who, + *netuid, + MechId::MAIN, + *commit_reveal_version, + ); + Self::result_to_validity(r, 0u64).map(|validity| (validity, (), origin.clone())) } + Some(Call::commit_timelocked_mechanism_weights { + netuid, + mecid, + reveal_round, + commit_reveal_version, + .. + }) => { + if !Self::check_weights_min_stake(who, *netuid) { + return Err(CustomTransactionError::StakeAmountTooLow.into()); + } + if *reveal_round < pallet_drand::LastStoredRound::::get() { + return Err(CustomTransactionError::InvalidRevealRound.into()); + } + let r = Pallet::::validate_timelocked_commit_weights_prerequisites( + who, + *netuid, + *mecid, + *commit_reveal_version, + ); + Self::result_to_validity(r, 0u64).map(|validity| (validity, (), origin.clone())) + } + Some(Call::commit_crv3_mechanism_weights { + netuid, + mecid, + reveal_round, + .. + }) => { + if !Self::check_weights_min_stake(who, *netuid) { + return Err(CustomTransactionError::StakeAmountTooLow.into()); + } + if *reveal_round < pallet_drand::LastStoredRound::::get() { + return Err(CustomTransactionError::InvalidRevealRound.into()); + } + const CRV3_CALL_VERSION: u16 = 4; + let r = Pallet::::validate_timelocked_commit_weights_prerequisites( + who, + *netuid, + *mecid, + CRV3_CALL_VERSION, + ); + Self::result_to_validity(r, 0u64).map(|validity| (validity, (), origin.clone())) + } + Some(Call::decrease_take { hotkey, take }) => Self::result_to_validity( + Pallet::::validate_decrease_take(who, hotkey, *take), + 0u64, + ) + .map(|validity| (validity, (), origin.clone())), + Some(Call::increase_take { hotkey, take }) => Self::result_to_validity( + Pallet::::validate_increase_take(who, hotkey, *take), + 0u64, + ) + .map(|validity| (validity, (), origin.clone())), + Some(Call::swap_hotkey_v2 { + hotkey, + new_hotkey, + netuid, + keep_stake: _, + }) => Self::result_to_validity( + Pallet::::validate_swap_hotkey_v2_signed(who, hotkey, new_hotkey, *netuid), + 0u64, + ) + .map(|validity| (validity, (), origin.clone())), + #[cfg(feature = "pow-faucet")] + Some(Call::faucet { + block_number, + nonce, + work, + }) => Self::result_to_validity( + Pallet::::validate_faucet(who, *block_number, *nonce, work.clone()), + 0u64, + ) + .map(|validity| (validity, (), origin.clone())), Some(Call::serve_axon { netuid, version, @@ -248,6 +457,44 @@ where ) .map(|validity| (validity, (), origin.clone())) } + Some(Call::serve_axon_tls { + netuid, + version, + ip, + port, + ip_type, + protocol, + placeholder1, + placeholder2, + certificate: _, + }) => Self::result_to_validity( + Pallet::::validate_serve_axon( + who, + *netuid, + *version, + *ip, + *port, + *ip_type, + *protocol, + *placeholder1, + *placeholder2, + ), + 0u64, + ) + .map(|validity| (validity, (), origin.clone())), + Some(Call::serve_prometheus { + netuid, + version, + ip, + port, + ip_type, + }) => Self::result_to_validity( + Pallet::::validate_serve_prometheus( + who, *netuid, *version, *ip, *port, *ip_type, + ), + 0u64, + ) + .map(|validity| (validity, (), origin.clone())), Some(Call::register_network { .. }) => { if !TransactionType::RegisterNetwork.passes_rate_limit::(who) { return Err(CustomTransactionError::RateLimitExceeded.into()); diff --git a/pallets/subtensor/src/staking/decrease_take.rs b/pallets/subtensor/src/staking/decrease_take.rs index b099d0a7e7..f4ef1bb483 100644 --- a/pallets/subtensor/src/staking/decrease_take.rs +++ b/pallets/subtensor/src/staking/decrease_take.rs @@ -63,4 +63,22 @@ impl Pallet { // --- 6. Ok and return. Ok(()) } + + /// Preconditions for [`Self::do_decrease_take`] (transaction extension). + pub fn validate_decrease_take( + coldkey: &T::AccountId, + hotkey: &T::AccountId, + take: u16, + ) -> Result<(), Error> { + Self::do_take_checks(coldkey, hotkey)?; + + if let Ok(current_take) = Delegates::::try_get(hotkey) { + ensure!(take < current_take, Error::::DelegateTakeTooLow); + } + + let min_take = MinDelegateTake::::get(); + ensure!(take >= min_take, Error::::DelegateTakeTooLow); + + Ok(()) + } } diff --git a/pallets/subtensor/src/staking/increase_take.rs b/pallets/subtensor/src/staking/increase_take.rs index 65ea70a025..d0a671a95c 100644 --- a/pallets/subtensor/src/staking/increase_take.rs +++ b/pallets/subtensor/src/staking/increase_take.rs @@ -75,4 +75,31 @@ impl Pallet { // --- 8. Ok and return. Ok(()) } + + /// Preconditions for [`Self::do_increase_take`] (transaction extension). + pub fn validate_increase_take( + coldkey: &T::AccountId, + hotkey: &T::AccountId, + take: u16, + ) -> Result<(), Error> { + Self::do_take_checks(coldkey, hotkey)?; + + if let Ok(current_take) = Delegates::::try_get(hotkey) { + ensure!(take > current_take, Error::::DelegateTakeTooLow); + } + + let max_take = MaxDelegateTake::::get(); + ensure!(take <= max_take, Error::::DelegateTakeTooHigh); + + let block: u64 = Self::get_current_block_as_u64(); + ensure!( + !Self::exceeds_tx_delegate_take_rate_limit( + Self::get_last_tx_block_delegate_take(hotkey), + block + ), + Error::::DelegateTxRateLimitExceeded + ); + + Ok(()) + } } diff --git a/pallets/subtensor/src/subnets/registration.rs b/pallets/subtensor/src/subnets/registration.rs index afb09ed0eb..ff459ba5bb 100644 --- a/pallets/subtensor/src/subnets/registration.rs +++ b/pallets/subtensor/src/subnets/registration.rs @@ -159,6 +159,36 @@ impl Pallet { Self::do_register(origin, netuid, hotkey) } + /// PoW / seal checks for [`Self::do_faucet`] (transaction extension; feature `pow-faucet`). + pub fn validate_faucet( + coldkey: &T::AccountId, + block_number: u64, + nonce: u64, + work: Vec, + ) -> Result<(), Error> { + let current_block_number: u64 = Self::get_current_block_as_u64(); + ensure!( + block_number <= current_block_number, + Error::::InvalidWorkBlock + ); + ensure!( + current_block_number.saturating_sub(block_number) < 3, + Error::::InvalidWorkBlock + ); + + let difficulty: U256 = U256::from(1_000_000); + let work_hash: H256 = Self::vec_to_hash(work.clone()); + ensure!( + Self::hash_meets_difficulty(&work_hash, difficulty), + Error::::InvalidDifficulty + ); + + let seal: H256 = Self::create_seal_hash(block_number, nonce, coldkey); + ensure!(seal == work_hash, Error::::InvalidSeal); + + Ok(()) + } + pub fn do_faucet( origin: OriginFor, block_number: u64, diff --git a/pallets/subtensor/src/subnets/serving.rs b/pallets/subtensor/src/subnets/serving.rs index 29923f5ade..d87c7a8eff 100644 --- a/pallets/subtensor/src/subnets/serving.rs +++ b/pallets/subtensor/src/subnets/serving.rs @@ -369,4 +369,46 @@ impl Pallet { Ok(()) } + + /// Same checks as [`Self::do_serve_prometheus`] before storage writes (for transaction extension). + pub fn validate_serve_prometheus( + hotkey_id: &T::AccountId, + netuid: NetUid, + version: u32, + ip: u128, + port: u16, + ip_type: u8, + ) -> Result<(), Error> { + ensure!(Self::is_valid_ip_type(ip_type), Error::::InvalidIpType); + ensure!( + Self::is_valid_ip_address(ip_type, ip, false), + Error::::InvalidIpAddress + ); + + ensure!( + Self::is_hotkey_registered_on_any_network(hotkey_id), + Error::::HotKeyNotRegisteredInNetwork + ); + + let mut prev_prometheus = Self::get_prometheus_info(netuid, hotkey_id); + let current_block: u64 = Self::get_current_block_as_u64(); + ensure!( + Self::prometheus_passes_rate_limit(netuid, &prev_prometheus, current_block), + Error::::ServingRateLimitExceeded + ); + + prev_prometheus.block = Self::get_current_block_as_u64(); + prev_prometheus.version = version; + prev_prometheus.ip = ip; + prev_prometheus.port = port; + prev_prometheus.ip_type = ip_type; + + let prom_validated = Self::validate_prometheus_data(&prev_prometheus); + ensure!( + prom_validated.is_ok(), + prom_validated.err().unwrap_or(Error::::InvalidPort) + ); + + Ok(()) + } } diff --git a/pallets/subtensor/src/subnets/weights.rs b/pallets/subtensor/src/subnets/weights.rs index 43e3c7e4ba..0490371132 100644 --- a/pallets/subtensor/src/subnets/weights.rs +++ b/pallets/subtensor/src/subnets/weights.rs @@ -1322,6 +1322,105 @@ impl Pallet { .saturating_sub(netuid_plus_one) } + /// Same preconditions as [`Self::internal_commit_weights`], without persisting a new commit. + /// Used by [`crate::extensions::SubtensorTransactionExtension`] for `Pays::No` commit calls. + pub fn validate_hash_commit_weights_prerequisites( + who: &T::AccountId, + netuid: NetUid, + mecid: MechId, + ) -> Result<(), Error> { + Self::ensure_mechanism_exists(netuid, mecid) + .map_err(|_| Error::::MechanismDoesNotExist)?; + let netuid_index = Self::get_mechanism_storage_index(netuid, mecid); + + ensure!( + Self::get_commit_reveal_weights_enabled(netuid), + Error::::CommitRevealDisabled + ); + + ensure!( + Self::is_hotkey_registered_on_network(netuid, who), + Error::::HotKeyNotRegisteredInSubNet + ); + + let commit_block = Self::get_current_block_as_u64(); + let neuron_uid = Self::get_uid_for_net_and_hotkey(netuid, who) + .map_err(|_| Error::::HotKeyNotRegisteredInSubNet)?; + ensure!( + Self::check_rate_limit(netuid_index, neuron_uid, commit_block), + Error::::CommittingWeightsTooFast + ); + + let mut commits: VecDeque<(H256, u64, u64, u64)> = + WeightCommits::::get(netuid_index, who).unwrap_or_default(); + + while let Some((_, commit_block_existing, _, _)) = commits.front() { + if Self::is_commit_expired(netuid, *commit_block_existing) { + commits.pop_front(); + } else { + break; + } + } + + ensure!(commits.len() < 10, Error::::TooManyUnrevealedCommits); + + Ok(()) + } + + /// Same preconditions as [`Self::internal_commit_timelocked_weights`], without persisting. + pub fn validate_timelocked_commit_weights_prerequisites( + who: &T::AccountId, + netuid: NetUid, + mecid: MechId, + commit_reveal_version: u16, + ) -> Result<(), Error> { + Self::ensure_mechanism_exists(netuid, mecid) + .map_err(|_| Error::::MechanismDoesNotExist)?; + let netuid_index = Self::get_mechanism_storage_index(netuid, mecid); + + ensure!( + Self::get_commit_reveal_weights_enabled(netuid), + Error::::CommitRevealDisabled + ); + + ensure!( + commit_reveal_version == Self::get_commit_reveal_weights_version(), + Error::::IncorrectCommitRevealVersion + ); + + ensure!( + Self::is_hotkey_registered_on_network(netuid, who), + Error::::HotKeyNotRegisteredInSubNet + ); + + let commit_block = Self::get_current_block_as_u64(); + let neuron_uid = Self::get_uid_for_net_and_hotkey(netuid, who) + .map_err(|_| Error::::HotKeyNotRegisteredInSubNet)?; + ensure!( + Self::check_rate_limit(netuid_index, neuron_uid, commit_block), + Error::::CommittingWeightsTooFast + ); + + let cur_block = Self::get_current_block_as_u64(); + let cur_epoch = match Self::should_run_epoch(netuid, commit_block) { + true => Self::get_epoch_index(netuid, cur_block).saturating_add(1), + false => Self::get_epoch_index(netuid, cur_block), + }; + + let commits = TimelockedWeightCommits::::get(netuid_index, cur_epoch); + let unrevealed_commits_for_who = commits + .iter() + .filter(|(account, _, _, _)| account == who) + .count(); + + ensure!( + unrevealed_commits_for_who < 10, + Error::::TooManyUnrevealedCommits + ); + + Ok(()) + } + pub fn get_commit_hash( who: &T::AccountId, netuid_index: NetUidStorageIndex, diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index a7da13058a..baa8826473 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -145,6 +145,60 @@ impl Pallet { Ok(Some(weight).into()) } + /// Early checks for [`Self::do_swap_hotkey`] (matches extrinsic failure modes; used by transaction extension). + pub fn validate_swap_hotkey_v2_signed( + coldkey: &T::AccountId, + old_hotkey: &T::AccountId, + new_hotkey: &T::AccountId, + netuid: Option, + ) -> Result<(), Error> { + ensure!( + Self::coldkey_owns_hotkey(coldkey, old_hotkey), + Error::::NonAssociatedColdKey + ); + + ensure!(old_hotkey != new_hotkey, Error::::NewHotKeyIsSameWithOld); + + let block: u64 = Self::get_current_block_as_u64(); + ensure!( + !Self::exceeds_tx_rate_limit(Self::get_last_tx_block(coldkey), block), + Error::::HotKeySetTxRateLimitExceeded + ); + + match netuid { + Some(netuid) => { + ensure!( + !Self::is_hotkey_registered_on_specific_network(new_hotkey, netuid), + Error::::HotKeyAlreadyRegisteredInSubNet + ); + let hotkey_swap_interval = T::HotkeySwapOnSubnetInterval::get(); + let last_hotkey_swap_block = LastHotkeySwapOnNetuid::::get(netuid, coldkey); + ensure!( + last_hotkey_swap_block.saturating_add(hotkey_swap_interval) < block, + Error::::HotKeySwapOnSubnetIntervalNotPassed + ); + let swap_cost = T::KeySwapOnSubnetCost::get(); + ensure!( + Self::can_remove_balance_from_coldkey_account(coldkey, swap_cost), + Error::::NotEnoughBalanceToPaySwapHotKey + ); + } + None => { + ensure!( + !Self::is_hotkey_registered_on_any_network(new_hotkey), + Error::::HotKeyAlreadyRegisteredInSubNet + ); + let swap_cost = Self::get_key_swap_cost(); + ensure!( + Self::can_remove_balance_from_coldkey_account(coldkey, swap_cost.into()), + Error::::NotEnoughBalanceToPaySwapHotKey + ); + } + } + + Ok(()) + } + /// Performs the hotkey swap operation, transferring all associated data and state from the old hotkey to the new hotkey. /// /// This function executes a series of steps to ensure a complete transfer of all relevant information: diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index 7e0c477c56..c099e8f1c1 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -28,6 +28,7 @@ mod subnet_emissions; mod swap_coldkey; mod swap_hotkey; mod swap_hotkey_with_subnet; +mod transaction_extension_pays_no; mod uids; mod voting_power; mod weights; diff --git a/pallets/subtensor/src/tests/transaction_extension_pays_no.rs b/pallets/subtensor/src/tests/transaction_extension_pays_no.rs new file mode 100644 index 0000000000..74d3fb3344 --- /dev/null +++ b/pallets/subtensor/src/tests/transaction_extension_pays_no.rs @@ -0,0 +1,184 @@ +//! Transaction extension coverage for `Pays::No` subtensor calls. + +#![allow(clippy::unwrap_used)] + +use super::mock::*; +use crate::extensions::SubtensorTransactionExtension; +use crate::*; +use codec::Compact; +use frame_support::dispatch::GetDispatchInfo; +use frame_system::RawOrigin; +use sp_core::U256; +use sp_runtime::traits::{DispatchInfoOf, TransactionExtension, TxBaseImplication}; +use sp_runtime::transaction_validity::TransactionSource; +use subtensor_runtime_common::{CustomTransactionError, NetUid}; + +#[test] +fn extension_set_weights_rejects_commit_reveal_enabled() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network(netuid, 1, 0); + setup_reserves( + netuid, + 1_000_000_000_000_u64.into(), + 1_000_000_000_000_u64.into(), + ); + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); + SubtensorModule::set_stake_threshold(0); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::set_weights { + netuid, + dests: vec![1], + weights: vec![1], + version_key: 0, + }); + let info = DispatchInfoOf::<::RuntimeCall>::default(); + let extension = SubtensorTransactionExtension::::new(); + let err = extension + .validate( + RawOrigin::Signed(hotkey).into(), + &call, + &info, + 0, + (), + &TxBaseImplication(()), + TransactionSource::External, + ) + .unwrap_err(); + assert_eq!(err, CustomTransactionError::CommitRevealEnabled.into()); + }); +} + +#[test] +fn extension_batch_set_weights_rejects_mismatched_lengths() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let call = RuntimeCall::SubtensorModule(SubtensorCall::batch_set_weights { + netuids: vec![Compact(netuid)], + weights: vec![], + version_keys: vec![Compact(0_u64)], + }); + let info = DispatchInfoOf::<::RuntimeCall>::default(); + let extension = SubtensorTransactionExtension::::new(); + let err = extension + .validate( + RawOrigin::Signed(hotkey).into(), + &call, + &info, + 0, + (), + &TxBaseImplication(()), + TransactionSource::External, + ) + .unwrap_err(); + assert_eq!(err, CustomTransactionError::InputLengthsUnequal.into()); + }); +} + +#[test] +fn extension_decrease_take_rejects_non_owner_coldkey() { + new_test_ext(0).execute_with(|| { + let owner_ck = U256::from(1); + let other_ck = U256::from(2); + let hotkey = U256::from(3); + crate::Owner::::insert(hotkey, owner_ck); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::decrease_take { + hotkey, + take: MinDelegateTake::::get(), + }); + let info = DispatchInfoOf::<::RuntimeCall>::default(); + let extension = SubtensorTransactionExtension::::new(); + let err = extension + .validate( + RawOrigin::Signed(other_ck).into(), + &call, + &info, + 0, + (), + &TxBaseImplication(()), + TransactionSource::External, + ) + .unwrap_err(); + assert_eq!(err, CustomTransactionError::NonAssociatedColdKey.into()); + }); +} + +#[test] +fn extension_serve_prometheus_rejects_unregistered_hotkey() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(99); + let ip = u128::from(u32::from_be_bytes([8, 8, 8, 8])); + let call = RuntimeCall::SubtensorModule(SubtensorCall::serve_prometheus { + netuid, + version: 1, + ip, + port: 1, + ip_type: 4, + }); + let info = call.get_dispatch_info(); + assert_eq!(info.pays_fee, frame_support::dispatch::Pays::No); + + let extension = SubtensorTransactionExtension::::new(); + let err = extension + .validate( + RawOrigin::Signed(hotkey).into(), + &call, + &info, + 0, + (), + &TxBaseImplication(()), + TransactionSource::External, + ) + .unwrap_err(); + assert_eq!( + err, + CustomTransactionError::HotKeyNotRegisteredInNetwork.into() + ); + }); +} + +#[test] +fn extension_commit_weights_rejects_commit_reveal_disabled() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network_disable_commit_reveal(netuid, 1, 0); + setup_reserves( + netuid, + 1_000_000_000_000_u64.into(), + 1_000_000_000_000_u64.into(), + ); + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); + SubtensorModule::set_stake_threshold(0); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::commit_weights { + netuid, + commit_hash: sp_core::H256::zero(), + }); + let info = DispatchInfoOf::<::RuntimeCall>::default(); + let extension = SubtensorTransactionExtension::::new(); + let err = extension + .validate( + RawOrigin::Signed(hotkey).into(), + &call, + &info, + 0, + (), + &TxBaseImplication(()), + TransactionSource::External, + ) + .unwrap_err(); + assert_eq!(err, CustomTransactionError::CommitRevealDisabled.into()); + }); +} diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index 5237cca131..f685b5e185 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -244,9 +244,9 @@ fn test_set_weights_validate() { version_key: 0, }); - // Create netuid - add_network(netuid, 1, 0); - mock::setup_reserves( + // Create netuid (commit-reveal off so `set_weights` matches extension / extrinsic) + add_network_disable_commit_reveal(netuid, 1, 0); + setup_reserves( netuid, 1_000_000_000_000_u64.into(), 1_000_000_000_000_u64.into(), From d1572de772b2efa02b5f7f67ba0f1b1fc14355d3 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Apr 2026 16:49:33 +0800 Subject: [PATCH 2/8] fix all extension --- pallets/subtensor/src/extensions/subtensor.rs | 302 +++++++----------- 1 file changed, 121 insertions(+), 181 deletions(-) diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index 05f987fe38..acbdcb7588 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -15,7 +15,7 @@ use sp_runtime::{ use sp_std::marker::PhantomData; use sp_std::vec::Vec; use subtensor_macros::freeze_struct; -use subtensor_runtime_common::{CustomTransactionError, MechId, NetUid}; +use subtensor_runtime_common::{CustomTransactionError, NetUid, NetUidStorageIndex}; const ADD_STAKE_BURN_PRIORITY_BOOST: u64 = 100; @@ -151,24 +151,13 @@ where }; match call.is_sub_type() { - Some(Call::commit_weights { netuid, .. }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - let r = Pallet::::validate_hash_commit_weights_prerequisites( - who, - *netuid, - MechId::MAIN, - ); - Self::result_to_validity(r, 0u64).map(|validity| (validity, (), origin.clone())) - } - Some(Call::commit_mechanism_weights { netuid, mecid, .. }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); + Some(Call::commit_weights { netuid, .. }) + | Some(Call::commit_mechanism_weights { netuid, .. }) => { + if Self::check_weights_min_stake(who, *netuid) { + Ok((Default::default(), (), origin)) + } else { + Err(CustomTransactionError::StakeAmountTooLow.into()) } - let r = - Pallet::::validate_hash_commit_weights_prerequisites(who, *netuid, *mecid); - Self::result_to_validity(r, 0u64).map(|validity| (validity, (), origin.clone())) } Some(Call::batch_commit_weights { netuids, @@ -182,14 +171,6 @@ where if !Self::check_weights_min_stake(who, netuid) { return Err(CustomTransactionError::StakeAmountTooLow.into()); } - let r = Pallet::::validate_hash_commit_weights_prerequisites( - who, - netuid, - MechId::MAIN, - ); - if let Err(e) = r { - return Err(Self::pallet_error_to_custom(e).into()); - } } Ok((Default::default(), (), origin)) } @@ -200,58 +181,59 @@ where salt, version_key, }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - let netuid_index = Pallet::::get_mechanism_storage_index(*netuid, MechId::MAIN); - let provided_hash = Pallet::::get_commit_hash( - who, - netuid_index, - uids, - values, - salt, - *version_key, - ); - match Pallet::::find_commit_block_via_hash(provided_hash) { - Some(commit_block) => { - if Pallet::::is_reveal_block_range(*netuid, commit_block) { - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) + if Self::check_weights_min_stake(who, *netuid) { + let provided_hash = Pallet::::get_commit_hash( + who, + NetUidStorageIndex::from(*netuid), + uids, + values, + salt, + *version_key, + ); + match Pallet::::find_commit_block_via_hash(provided_hash) { + Some(commit_block) => { + if Pallet::::is_reveal_block_range(*netuid, commit_block) { + Ok((Default::default(), (), origin)) + } else { + Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) + } } + None => Err(CustomTransactionError::CommitNotFound.into()), } - None => Err(CustomTransactionError::CommitNotFound.into()), + } else { + Err(CustomTransactionError::StakeAmountTooLow.into()) } } Some(Call::reveal_mechanism_weights { netuid, - mecid, + #[warn(unused_variables)] + mecid: _, uids, values, salt, version_key, }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - let netuid_index = Pallet::::get_mechanism_storage_index(*netuid, *mecid); - let provided_hash = Pallet::::get_commit_hash( - who, - netuid_index, - uids, - values, - salt, - *version_key, - ); - match Pallet::::find_commit_block_via_hash(provided_hash) { - Some(commit_block) => { - if Pallet::::is_reveal_block_range(*netuid, commit_block) { - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) + if Self::check_weights_min_stake(who, *netuid) { + let provided_hash = Pallet::::get_commit_hash( + who, + NetUidStorageIndex::from(*netuid), + uids, + values, + salt, + *version_key, + ); + match Pallet::::find_commit_block_via_hash(provided_hash) { + Some(commit_block) => { + if Pallet::::is_reveal_block_range(*netuid, commit_block) { + Ok((Default::default(), (), origin)) + } else { + Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) + } } + None => Err(CustomTransactionError::CommitNotFound.into()), } - None => Err(CustomTransactionError::CommitNotFound.into()), + } else { + Err(CustomTransactionError::StakeAmountTooLow.into()) } } Some(Call::batch_reveal_weights { @@ -264,55 +246,45 @@ where if !Self::check_weights_min_stake(who, *netuid) { return Err(CustomTransactionError::StakeAmountTooLow.into()); } - let netuid_index = Pallet::::get_mechanism_storage_index(*netuid, MechId::MAIN); + let num_reveals = uids_list.len(); - if num_reveals != values_list.len() - || num_reveals != salts_list.len() - || num_reveals != version_keys.len() + if num_reveals == values_list.len() + && num_reveals == salts_list.len() + && num_reveals == version_keys.len() { - return Err(CustomTransactionError::InputLengthsUnequal.into()); - } - let provided_hashes = (0..num_reveals) - .map(|i| { - Pallet::::get_commit_hash( - who, - netuid_index, - uids_list.get(i).unwrap_or(&Vec::new()), - values_list.get(i).unwrap_or(&Vec::new()), - salts_list.get(i).unwrap_or(&Vec::new()), - *version_keys.get(i).unwrap_or(&0_u64), - ) - }) - .collect::>(); + let provided_hashes = (0..num_reveals) + .map(|i| { + Pallet::::get_commit_hash( + who, + NetUidStorageIndex::from(*netuid), + uids_list.get(i).unwrap_or(&Vec::new()), + values_list.get(i).unwrap_or(&Vec::new()), + salts_list.get(i).unwrap_or(&Vec::new()), + *version_keys.get(i).unwrap_or(&0_u64), + ) + }) + .collect::>(); - let batch_reveal_block = provided_hashes - .iter() - .filter_map(|hash| Pallet::::find_commit_block_via_hash(*hash)) - .collect::>(); + let batch_reveal_block = provided_hashes + .iter() + .filter_map(|hash| Pallet::::find_commit_block_via_hash(*hash)) + .collect::>(); - if provided_hashes.len() != batch_reveal_block.len() { - return Err(CustomTransactionError::CommitNotFound.into()); - } - if Pallet::::is_batch_reveal_block_range(*netuid, batch_reveal_block) { - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) - } - } - Some(Call::set_weights { netuid, .. }) => { - if Pallet::::get_commit_reveal_weights_enabled(*netuid) { - return Err(CustomTransactionError::CommitRevealEnabled.into()); - } - if Self::check_weights_min_stake(who, *netuid) { - Ok((Default::default(), (), origin)) + if provided_hashes.len() == batch_reveal_block.len() { + if Pallet::::is_batch_reveal_block_range(*netuid, batch_reveal_block) { + Ok((Default::default(), (), origin)) + } else { + Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) + } + } else { + Err(CustomTransactionError::CommitNotFound.into()) + } } else { - Err(CustomTransactionError::StakeAmountTooLow.into()) + Err(CustomTransactionError::InputLengthsUnequal.into()) } } - Some(Call::set_mechanism_weights { netuid, .. }) => { - if Pallet::::get_commit_reveal_weights_enabled(*netuid) { - return Err(CustomTransactionError::CommitRevealEnabled.into()); - } + Some(Call::set_weights { netuid, .. }) + | Some(Call::set_mechanism_weights { netuid, .. }) => { if Self::check_weights_min_stake(who, *netuid) { Ok((Default::default(), (), origin)) } else { @@ -329,9 +301,6 @@ where } for netuid in netuids.iter() { let netuid: NetUid = (*netuid).into(); - if Pallet::::get_commit_reveal_weights_enabled(netuid) { - return Err(CustomTransactionError::CommitRevealEnabled.into()); - } if !Self::check_weights_min_stake(who, netuid) { return Err(CustomTransactionError::StakeAmountTooLow.into()); } @@ -341,95 +310,66 @@ where Some(Call::commit_timelocked_weights { netuid, reveal_round, - commit_reveal_version, .. }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - if *reveal_round < pallet_drand::LastStoredRound::::get() { - return Err(CustomTransactionError::InvalidRevealRound.into()); + if Self::check_weights_min_stake(who, *netuid) { + if *reveal_round < pallet_drand::LastStoredRound::::get() { + return Err(CustomTransactionError::InvalidRevealRound.into()); + } + Ok((Default::default(), (), origin)) + } else { + Err(CustomTransactionError::StakeAmountTooLow.into()) } - let r = Pallet::::validate_timelocked_commit_weights_prerequisites( - who, - *netuid, - MechId::MAIN, - *commit_reveal_version, - ); - Self::result_to_validity(r, 0u64).map(|validity| (validity, (), origin.clone())) } Some(Call::commit_timelocked_mechanism_weights { netuid, - mecid, + mecid: _, reveal_round, - commit_reveal_version, .. - }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - if *reveal_round < pallet_drand::LastStoredRound::::get() { - return Err(CustomTransactionError::InvalidRevealRound.into()); - } - let r = Pallet::::validate_timelocked_commit_weights_prerequisites( - who, - *netuid, - *mecid, - *commit_reveal_version, - ); - Self::result_to_validity(r, 0u64).map(|validity| (validity, (), origin.clone())) - } - Some(Call::commit_crv3_mechanism_weights { + }) + | Some(Call::commit_crv3_mechanism_weights { netuid, - mecid, + mecid: _, reveal_round, .. }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); + if Self::check_weights_min_stake(who, *netuid) { + if *reveal_round < pallet_drand::LastStoredRound::::get() { + return Err(CustomTransactionError::InvalidRevealRound.into()); + } + Ok((Default::default(), (), origin)) + } else { + Err(CustomTransactionError::StakeAmountTooLow.into()) } - if *reveal_round < pallet_drand::LastStoredRound::::get() { - return Err(CustomTransactionError::InvalidRevealRound.into()); + } + Some(Call::increase_take { hotkey, take: _ }) + | Some(Call::decrease_take { hotkey, take: _ }) => { + match Pallet::::do_take_checks(who, hotkey) { + Ok(()) => Ok((Default::default(), (), origin)), + Err(err) => Err(Self::pallet_error_to_custom(err).into()), } - const CRV3_CALL_VERSION: u16 = 4; - let r = Pallet::::validate_timelocked_commit_weights_prerequisites( - who, - *netuid, - *mecid, - CRV3_CALL_VERSION, - ); - Self::result_to_validity(r, 0u64).map(|validity| (validity, (), origin.clone())) } - Some(Call::decrease_take { hotkey, take }) => Self::result_to_validity( - Pallet::::validate_decrease_take(who, hotkey, *take), - 0u64, - ) - .map(|validity| (validity, (), origin.clone())), - Some(Call::increase_take { hotkey, take }) => Self::result_to_validity( - Pallet::::validate_increase_take(who, hotkey, *take), - 0u64, - ) - .map(|validity| (validity, (), origin.clone())), + Some(Call::swap_hotkey_v2 { hotkey, - new_hotkey, - netuid, + new_hotkey: _, + netuid: _, keep_stake: _, - }) => Self::result_to_validity( - Pallet::::validate_swap_hotkey_v2_signed(who, hotkey, new_hotkey, *netuid), - 0u64, - ) - .map(|validity| (validity, (), origin.clone())), - #[cfg(feature = "pow-faucet")] - Some(Call::faucet { - block_number, - nonce, - work, - }) => Self::result_to_validity( - Pallet::::validate_faucet(who, *block_number, *nonce, work.clone()), - 0u64, - ) - .map(|validity| (validity, (), origin.clone())), + }) => { + if !Pallet::::coldkey_owns_hotkey(who, hotkey) { + return Err(CustomTransactionError::NonAssociatedColdKey.into()); + } + + let block: u64 = Pallet::::get_current_block_as_u64(); + + if !Pallet::::exceeds_tx_rate_limit(Pallet::::get_last_tx_block(&who), block) + { + return Err(CustomTransactionError::RateLimitExceeded.into()); + } + + Ok((Default::default(), (), origin)) + } + Some(Call::serve_axon { netuid, version, From 9729f191e153b1bf000501214a9b6121b68c754b Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Apr 2026 17:35:40 +0800 Subject: [PATCH 3/8] fix clippy --- pallets/subtensor/src/extensions/subtensor.rs | 3 +- .../subtensor/src/staking/decrease_take.rs | 18 ----------- .../subtensor/src/staking/increase_take.rs | 27 ----------------- pallets/subtensor/src/subnets/registration.rs | 30 ------------------- 4 files changed, 1 insertion(+), 77 deletions(-) diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index acbdcb7588..c02220d114 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -362,8 +362,7 @@ where let block: u64 = Pallet::::get_current_block_as_u64(); - if !Pallet::::exceeds_tx_rate_limit(Pallet::::get_last_tx_block(&who), block) - { + if !Pallet::::exceeds_tx_rate_limit(Pallet::::get_last_tx_block(who), block) { return Err(CustomTransactionError::RateLimitExceeded.into()); } diff --git a/pallets/subtensor/src/staking/decrease_take.rs b/pallets/subtensor/src/staking/decrease_take.rs index f4ef1bb483..b099d0a7e7 100644 --- a/pallets/subtensor/src/staking/decrease_take.rs +++ b/pallets/subtensor/src/staking/decrease_take.rs @@ -63,22 +63,4 @@ impl Pallet { // --- 6. Ok and return. Ok(()) } - - /// Preconditions for [`Self::do_decrease_take`] (transaction extension). - pub fn validate_decrease_take( - coldkey: &T::AccountId, - hotkey: &T::AccountId, - take: u16, - ) -> Result<(), Error> { - Self::do_take_checks(coldkey, hotkey)?; - - if let Ok(current_take) = Delegates::::try_get(hotkey) { - ensure!(take < current_take, Error::::DelegateTakeTooLow); - } - - let min_take = MinDelegateTake::::get(); - ensure!(take >= min_take, Error::::DelegateTakeTooLow); - - Ok(()) - } } diff --git a/pallets/subtensor/src/staking/increase_take.rs b/pallets/subtensor/src/staking/increase_take.rs index d0a671a95c..65ea70a025 100644 --- a/pallets/subtensor/src/staking/increase_take.rs +++ b/pallets/subtensor/src/staking/increase_take.rs @@ -75,31 +75,4 @@ impl Pallet { // --- 8. Ok and return. Ok(()) } - - /// Preconditions for [`Self::do_increase_take`] (transaction extension). - pub fn validate_increase_take( - coldkey: &T::AccountId, - hotkey: &T::AccountId, - take: u16, - ) -> Result<(), Error> { - Self::do_take_checks(coldkey, hotkey)?; - - if let Ok(current_take) = Delegates::::try_get(hotkey) { - ensure!(take > current_take, Error::::DelegateTakeTooLow); - } - - let max_take = MaxDelegateTake::::get(); - ensure!(take <= max_take, Error::::DelegateTakeTooHigh); - - let block: u64 = Self::get_current_block_as_u64(); - ensure!( - !Self::exceeds_tx_delegate_take_rate_limit( - Self::get_last_tx_block_delegate_take(hotkey), - block - ), - Error::::DelegateTxRateLimitExceeded - ); - - Ok(()) - } } diff --git a/pallets/subtensor/src/subnets/registration.rs b/pallets/subtensor/src/subnets/registration.rs index ff459ba5bb..afb09ed0eb 100644 --- a/pallets/subtensor/src/subnets/registration.rs +++ b/pallets/subtensor/src/subnets/registration.rs @@ -159,36 +159,6 @@ impl Pallet { Self::do_register(origin, netuid, hotkey) } - /// PoW / seal checks for [`Self::do_faucet`] (transaction extension; feature `pow-faucet`). - pub fn validate_faucet( - coldkey: &T::AccountId, - block_number: u64, - nonce: u64, - work: Vec, - ) -> Result<(), Error> { - let current_block_number: u64 = Self::get_current_block_as_u64(); - ensure!( - block_number <= current_block_number, - Error::::InvalidWorkBlock - ); - ensure!( - current_block_number.saturating_sub(block_number) < 3, - Error::::InvalidWorkBlock - ); - - let difficulty: U256 = U256::from(1_000_000); - let work_hash: H256 = Self::vec_to_hash(work.clone()); - ensure!( - Self::hash_meets_difficulty(&work_hash, difficulty), - Error::::InvalidDifficulty - ); - - let seal: H256 = Self::create_seal_hash(block_number, nonce, coldkey); - ensure!(seal == work_hash, Error::::InvalidSeal); - - Ok(()) - } - pub fn do_faucet( origin: OriginFor, block_number: u64, From 75697ee9f1b00189f0dbb94470cdb497dc52863a Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 8 Apr 2026 12:11:35 +0800 Subject: [PATCH 4/8] remove unnessary check --- pallets/subtensor/src/subnets/weights.rs | 99 ----------------------- pallets/subtensor/src/swap/swap_hotkey.rs | 54 ------------- 2 files changed, 153 deletions(-) diff --git a/pallets/subtensor/src/subnets/weights.rs b/pallets/subtensor/src/subnets/weights.rs index 0490371132..43e3c7e4ba 100644 --- a/pallets/subtensor/src/subnets/weights.rs +++ b/pallets/subtensor/src/subnets/weights.rs @@ -1322,105 +1322,6 @@ impl Pallet { .saturating_sub(netuid_plus_one) } - /// Same preconditions as [`Self::internal_commit_weights`], without persisting a new commit. - /// Used by [`crate::extensions::SubtensorTransactionExtension`] for `Pays::No` commit calls. - pub fn validate_hash_commit_weights_prerequisites( - who: &T::AccountId, - netuid: NetUid, - mecid: MechId, - ) -> Result<(), Error> { - Self::ensure_mechanism_exists(netuid, mecid) - .map_err(|_| Error::::MechanismDoesNotExist)?; - let netuid_index = Self::get_mechanism_storage_index(netuid, mecid); - - ensure!( - Self::get_commit_reveal_weights_enabled(netuid), - Error::::CommitRevealDisabled - ); - - ensure!( - Self::is_hotkey_registered_on_network(netuid, who), - Error::::HotKeyNotRegisteredInSubNet - ); - - let commit_block = Self::get_current_block_as_u64(); - let neuron_uid = Self::get_uid_for_net_and_hotkey(netuid, who) - .map_err(|_| Error::::HotKeyNotRegisteredInSubNet)?; - ensure!( - Self::check_rate_limit(netuid_index, neuron_uid, commit_block), - Error::::CommittingWeightsTooFast - ); - - let mut commits: VecDeque<(H256, u64, u64, u64)> = - WeightCommits::::get(netuid_index, who).unwrap_or_default(); - - while let Some((_, commit_block_existing, _, _)) = commits.front() { - if Self::is_commit_expired(netuid, *commit_block_existing) { - commits.pop_front(); - } else { - break; - } - } - - ensure!(commits.len() < 10, Error::::TooManyUnrevealedCommits); - - Ok(()) - } - - /// Same preconditions as [`Self::internal_commit_timelocked_weights`], without persisting. - pub fn validate_timelocked_commit_weights_prerequisites( - who: &T::AccountId, - netuid: NetUid, - mecid: MechId, - commit_reveal_version: u16, - ) -> Result<(), Error> { - Self::ensure_mechanism_exists(netuid, mecid) - .map_err(|_| Error::::MechanismDoesNotExist)?; - let netuid_index = Self::get_mechanism_storage_index(netuid, mecid); - - ensure!( - Self::get_commit_reveal_weights_enabled(netuid), - Error::::CommitRevealDisabled - ); - - ensure!( - commit_reveal_version == Self::get_commit_reveal_weights_version(), - Error::::IncorrectCommitRevealVersion - ); - - ensure!( - Self::is_hotkey_registered_on_network(netuid, who), - Error::::HotKeyNotRegisteredInSubNet - ); - - let commit_block = Self::get_current_block_as_u64(); - let neuron_uid = Self::get_uid_for_net_and_hotkey(netuid, who) - .map_err(|_| Error::::HotKeyNotRegisteredInSubNet)?; - ensure!( - Self::check_rate_limit(netuid_index, neuron_uid, commit_block), - Error::::CommittingWeightsTooFast - ); - - let cur_block = Self::get_current_block_as_u64(); - let cur_epoch = match Self::should_run_epoch(netuid, commit_block) { - true => Self::get_epoch_index(netuid, cur_block).saturating_add(1), - false => Self::get_epoch_index(netuid, cur_block), - }; - - let commits = TimelockedWeightCommits::::get(netuid_index, cur_epoch); - let unrevealed_commits_for_who = commits - .iter() - .filter(|(account, _, _, _)| account == who) - .count(); - - ensure!( - unrevealed_commits_for_who < 10, - Error::::TooManyUnrevealedCommits - ); - - Ok(()) - } - pub fn get_commit_hash( who: &T::AccountId, netuid_index: NetUidStorageIndex, diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index baa8826473..a7da13058a 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -145,60 +145,6 @@ impl Pallet { Ok(Some(weight).into()) } - /// Early checks for [`Self::do_swap_hotkey`] (matches extrinsic failure modes; used by transaction extension). - pub fn validate_swap_hotkey_v2_signed( - coldkey: &T::AccountId, - old_hotkey: &T::AccountId, - new_hotkey: &T::AccountId, - netuid: Option, - ) -> Result<(), Error> { - ensure!( - Self::coldkey_owns_hotkey(coldkey, old_hotkey), - Error::::NonAssociatedColdKey - ); - - ensure!(old_hotkey != new_hotkey, Error::::NewHotKeyIsSameWithOld); - - let block: u64 = Self::get_current_block_as_u64(); - ensure!( - !Self::exceeds_tx_rate_limit(Self::get_last_tx_block(coldkey), block), - Error::::HotKeySetTxRateLimitExceeded - ); - - match netuid { - Some(netuid) => { - ensure!( - !Self::is_hotkey_registered_on_specific_network(new_hotkey, netuid), - Error::::HotKeyAlreadyRegisteredInSubNet - ); - let hotkey_swap_interval = T::HotkeySwapOnSubnetInterval::get(); - let last_hotkey_swap_block = LastHotkeySwapOnNetuid::::get(netuid, coldkey); - ensure!( - last_hotkey_swap_block.saturating_add(hotkey_swap_interval) < block, - Error::::HotKeySwapOnSubnetIntervalNotPassed - ); - let swap_cost = T::KeySwapOnSubnetCost::get(); - ensure!( - Self::can_remove_balance_from_coldkey_account(coldkey, swap_cost), - Error::::NotEnoughBalanceToPaySwapHotKey - ); - } - None => { - ensure!( - !Self::is_hotkey_registered_on_any_network(new_hotkey), - Error::::HotKeyAlreadyRegisteredInSubNet - ); - let swap_cost = Self::get_key_swap_cost(); - ensure!( - Self::can_remove_balance_from_coldkey_account(coldkey, swap_cost.into()), - Error::::NotEnoughBalanceToPaySwapHotKey - ); - } - } - - Ok(()) - } - /// Performs the hotkey swap operation, transferring all associated data and state from the old hotkey to the new hotkey. /// /// This function executes a series of steps to ensure a complete transfer of all relevant information: From 51e4a93d04f6eab8eb36b10870e739dd64fc192d Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 8 Apr 2026 12:20:52 +0800 Subject: [PATCH 5/8] more unit tests --- pallets/subtensor/src/extensions/subtensor.rs | 5 +- .../tests/transaction_extension_pays_no.rs | 658 ++++++++++++++++-- 2 files changed, 586 insertions(+), 77 deletions(-) diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index c02220d114..8dbfd9a2bb 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -206,8 +206,7 @@ where } Some(Call::reveal_mechanism_weights { netuid, - #[warn(unused_variables)] - mecid: _, + mecid: _, uids, values, salt, @@ -362,7 +361,7 @@ where let block: u64 = Pallet::::get_current_block_as_u64(); - if !Pallet::::exceeds_tx_rate_limit(Pallet::::get_last_tx_block(who), block) { + if Pallet::::exceeds_tx_rate_limit(Pallet::::get_last_tx_block(who), block) { return Err(CustomTransactionError::RateLimitExceeded.into()); } diff --git a/pallets/subtensor/src/tests/transaction_extension_pays_no.rs b/pallets/subtensor/src/tests/transaction_extension_pays_no.rs index 74d3fb3344..acb49b9bcb 100644 --- a/pallets/subtensor/src/tests/transaction_extension_pays_no.rs +++ b/pallets/subtensor/src/tests/transaction_extension_pays_no.rs @@ -1,4 +1,4 @@ -//! Transaction extension coverage for `Pays::No` subtensor calls. +//! Transaction extension coverage for extrinsics handled by [`crate::extensions::SubtensorTransactionExtension`]. #![allow(clippy::unwrap_used)] @@ -7,19 +7,46 @@ use crate::extensions::SubtensorTransactionExtension; use crate::*; use codec::Compact; use frame_support::dispatch::GetDispatchInfo; +use frame_support::{BoundedVec, assert_ok, traits::ConstU32}; use frame_system::RawOrigin; +use pallet_drand::LastStoredRound; +use sp_core::H256; use sp_core::U256; use sp_runtime::traits::{DispatchInfoOf, TransactionExtension, TxBaseImplication}; -use sp_runtime::transaction_validity::TransactionSource; -use subtensor_runtime_common::{CustomTransactionError, NetUid}; +use sp_runtime::transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidityError, +}; +use subtensor_runtime_common::{CustomTransactionError, MechId, NetUid, TaoBalance}; + +fn dispatch_info() -> sp_runtime::traits::DispatchInfoOf<::RuntimeCall> +{ + DispatchInfoOf::<::RuntimeCall>::default() +} + +fn validate_signed( + signer: U256, + call: &RuntimeCall, +) -> Result { + SubtensorTransactionExtension::::new() + .validate( + RawOrigin::Signed(signer).into(), + call, + &dispatch_info(), + 0, + (), + &TxBaseImplication(()), + TransactionSource::External, + ) + .map(|(v, _, _)| v) +} #[test] -fn extension_set_weights_rejects_commit_reveal_enabled() { +fn extension_set_weights_rejects_stake_too_low() { new_test_ext(0).execute_with(|| { let netuid = NetUid::from(1); let hotkey = U256::from(1); let coldkey = U256::from(2); - add_network(netuid, 1, 0); + add_network_disable_commit_reveal(netuid, 1, 0); setup_reserves( netuid, 1_000_000_000_000_u64.into(), @@ -27,9 +54,7 @@ fn extension_set_weights_rejects_commit_reveal_enabled() { ); SubtensorModule::append_neuron(netuid, &hotkey, 0); crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); - SubtensorModule::set_stake_threshold(0); - SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_stake_threshold(1_000_000_000_000u64); let call = RuntimeCall::SubtensorModule(SubtensorCall::set_weights { netuid, @@ -37,20 +62,36 @@ fn extension_set_weights_rejects_commit_reveal_enabled() { weights: vec![1], version_key: 0, }); - let info = DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - let err = extension - .validate( - RawOrigin::Signed(hotkey).into(), - &call, - &info, - 0, - (), - &TxBaseImplication(()), - TransactionSource::External, - ) - .unwrap_err(); - assert_eq!(err, CustomTransactionError::CommitRevealEnabled.into()); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); + }); +} + +#[test] +fn extension_set_mechanism_weights_rejects_stake_too_low() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network_disable_commit_reveal(netuid, 1, 0); + setup_reserves( + netuid, + 1_000_000_000_000_u64.into(), + 1_000_000_000_000_u64.into(), + ); + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + SubtensorModule::set_stake_threshold(1_000_000_000_000u64); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::set_mechanism_weights { + netuid, + mecid: MechId::MAIN, + dests: vec![1], + weights: vec![1], + version_key: 0, + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); }); } @@ -64,23 +105,289 @@ fn extension_batch_set_weights_rejects_mismatched_lengths() { weights: vec![], version_keys: vec![Compact(0_u64)], }); - let info = DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - let err = extension - .validate( - RawOrigin::Signed(hotkey).into(), - &call, - &info, - 0, - (), - &TxBaseImplication(()), - TransactionSource::External, - ) - .unwrap_err(); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::InputLengthsUnequal.into()); + }); +} + +#[test] +fn extension_batch_set_weights_rejects_stake_too_low() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network_disable_commit_reveal(netuid, 1, 0); + setup_reserves( + netuid, + 1_000_000_000_000_u64.into(), + 1_000_000_000_000_u64.into(), + ); + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + SubtensorModule::set_stake_threshold(1_000_000_000_000u64); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::batch_set_weights { + netuids: vec![Compact(netuid)], + weights: vec![vec![(Compact(0u16), Compact(1u16))]], + version_keys: vec![Compact(0u64)], + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); + }); +} + +#[test] +fn extension_commit_weights_rejects_stake_too_low() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network(netuid, 1, 0); + setup_reserves( + netuid, + 1_000_000_000_000_u64.into(), + 1_000_000_000_000_u64.into(), + ); + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_stake_threshold(1_000_000_000_000u64); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::commit_weights { + netuid, + commit_hash: H256::zero(), + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); + }); +} + +#[test] +fn extension_commit_mechanism_weights_rejects_stake_too_low() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network(netuid, 1, 0); + setup_reserves( + netuid, + 1_000_000_000_000_u64.into(), + 1_000_000_000_000_u64.into(), + ); + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_stake_threshold(1_000_000_000_000u64); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::commit_mechanism_weights { + netuid, + mecid: MechId::MAIN, + commit_hash: H256::zero(), + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); + }); +} + +#[test] +fn extension_batch_commit_weights_rejects_mismatched_lengths() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let call = RuntimeCall::SubtensorModule(SubtensorCall::batch_commit_weights { + netuids: vec![Compact(netuid)], + commit_hashes: vec![], + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::InputLengthsUnequal.into()); + }); +} + +#[test] +fn extension_reveal_weights_rejects_stake_too_low() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + add_network(netuid, 1, 0); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_stake_threshold(1_000_000_000_000u64); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::reveal_weights { + netuid, + uids: vec![0], + values: vec![1], + salt: vec![1], + version_key: 0, + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); + }); +} + +#[test] +fn extension_reveal_weights_rejects_commit_not_found() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network(netuid, 1, 0); + setup_reserves( + netuid, + 1_000_000_000_000_u64.into(), + 1_000_000_000_000_u64.into(), + ); + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_stake_threshold(0); + SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); + assert_ok!(SubtensorModule::do_add_stake( + RuntimeOrigin::signed(hotkey), + hotkey, + netuid, + TaoBalance::from(500_000_000_000_u64) + )); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::reveal_weights { + netuid, + uids: vec![0], + values: vec![1], + salt: vec![1], + version_key: 0, + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::CommitNotFound.into()); + }); +} + +#[test] +fn extension_reveal_mechanism_weights_rejects_commit_not_found() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network(netuid, 1, 0); + setup_reserves( + netuid, + 1_000_000_000_000_u64.into(), + 1_000_000_000_000_u64.into(), + ); + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_stake_threshold(0); + SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); + assert_ok!(SubtensorModule::do_add_stake( + RuntimeOrigin::signed(hotkey), + hotkey, + netuid, + TaoBalance::from(500_000_000_000_u64) + )); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::reveal_mechanism_weights { + netuid, + mecid: MechId::MAIN, + uids: vec![0], + values: vec![1], + salt: vec![1], + version_key: 0, + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::CommitNotFound.into()); + }); +} + +#[test] +fn extension_batch_reveal_weights_rejects_mismatched_vector_lengths() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network(netuid, 1, 0); + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + SubtensorModule::set_stake_threshold(0); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::batch_reveal_weights { + netuid, + uids_list: vec![vec![0]], + values_list: vec![], + salts_list: vec![vec![1]], + version_keys: vec![0], + }); + let err = validate_signed(hotkey, &call).unwrap_err(); assert_eq!(err, CustomTransactionError::InputLengthsUnequal.into()); }); } +#[test] +fn extension_commit_timelocked_weights_rejects_invalid_reveal_round() { + new_test_ext(0).execute_with(|| { + LastStoredRound::::put(1_000_u64); + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + add_network(netuid, 1, 0); + SubtensorModule::set_stake_threshold(0); + + let commit = + BoundedVec::>::try_from(vec![0u8]).unwrap(); + let call = RuntimeCall::SubtensorModule(SubtensorCall::commit_timelocked_weights { + netuid, + commit, + reveal_round: 500, + commit_reveal_version: 0, + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::InvalidRevealRound.into()); + }); +} + +#[test] +fn extension_commit_timelocked_mechanism_weights_rejects_invalid_reveal_round() { + new_test_ext(0).execute_with(|| { + LastStoredRound::::put(2_000_u64); + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + add_network(netuid, 1, 0); + SubtensorModule::set_stake_threshold(0); + + let commit = + BoundedVec::>::try_from(vec![1u8]).unwrap(); + let call = + RuntimeCall::SubtensorModule(SubtensorCall::commit_timelocked_mechanism_weights { + netuid, + mecid: MechId::MAIN, + commit, + reveal_round: 100, + commit_reveal_version: 0, + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::InvalidRevealRound.into()); + }); +} + +#[test] +fn extension_commit_crv3_mechanism_weights_rejects_invalid_reveal_round() { + new_test_ext(0).execute_with(|| { + LastStoredRound::::put(500u64); + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + add_network(netuid, 1, 0); + SubtensorModule::set_stake_threshold(0); + + let commit = + BoundedVec::>::try_from(vec![2u8]).unwrap(); + let call = RuntimeCall::SubtensorModule(SubtensorCall::commit_crv3_mechanism_weights { + netuid, + mecid: MechId::MAIN, + commit, + reveal_round: 100, + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::InvalidRevealRound.into()); + }); +} + #[test] fn extension_decrease_take_rejects_non_owner_coldkey() { new_test_ext(0).execute_with(|| { @@ -93,20 +400,137 @@ fn extension_decrease_take_rejects_non_owner_coldkey() { hotkey, take: MinDelegateTake::::get(), }); - let info = DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - let err = extension + let err = validate_signed(other_ck, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::NonAssociatedColdKey.into()); + }); +} + +#[test] +fn extension_decrease_take_rejects_missing_hotkey_owner() { + new_test_ext(0).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(99); + let call = RuntimeCall::SubtensorModule(SubtensorCall::decrease_take { + hotkey, + take: MinDelegateTake::::get(), + }); + let err = validate_signed(coldkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::HotkeyAccountDoesntExist.into()); + }); +} + +#[test] +fn extension_increase_take_rejects_non_owner_coldkey() { + new_test_ext(0).execute_with(|| { + let owner_ck = U256::from(10); + let other_ck = U256::from(11); + let hotkey = U256::from(12); + crate::Owner::::insert(hotkey, owner_ck); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::increase_take { hotkey, take: 100 }); + let err = validate_signed(other_ck, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::NonAssociatedColdKey.into()); + }); +} + +#[test] +fn extension_swap_hotkey_v2_rejects_non_owner_coldkey() { + new_test_ext(0).execute_with(|| { + let owner_ck = U256::from(20); + let other_ck = U256::from(21); + let old_hk = U256::from(22); + let new_hk = U256::from(23); + crate::Owner::::insert(old_hk, owner_ck); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::swap_hotkey_v2 { + hotkey: old_hk, + new_hotkey: new_hk, + netuid: None, + keep_stake: false, + }); + let err = validate_signed(other_ck, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::NonAssociatedColdKey.into()); + }); +} + +#[test] +fn extension_swap_hotkey_v2_rejects_rate_limited() { + new_test_ext(0).execute_with(|| { + let coldkey = U256::from(30); + let old_hk = U256::from(31); + let new_hk = U256::from(32); + crate::Owner::::insert(old_hk, coldkey); + + SubtensorModule::set_tx_rate_limit(100); + SubtensorModule::set_last_tx_block(&coldkey, 1); + System::set_block_number(1u64.into()); + + let err = SubtensorTransactionExtension::::new() .validate( - RawOrigin::Signed(other_ck).into(), - &call, - &info, + RawOrigin::Signed(coldkey).into(), + &RuntimeCall::SubtensorModule(SubtensorCall::swap_hotkey_v2 { + hotkey: old_hk, + new_hotkey: new_hk, + netuid: None, + keep_stake: false, + }), + &dispatch_info(), 0, (), &TxBaseImplication(()), TransactionSource::External, ) .unwrap_err(); - assert_eq!(err, CustomTransactionError::NonAssociatedColdKey.into()); + assert_eq!(err, CustomTransactionError::RateLimitExceeded.into()); + }); +} + +#[test] +fn extension_serve_axon_rejects_unregistered_hotkey() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(40); + let ip = u128::from(u32::from_be_bytes([8, 8, 8, 8])); + let call = RuntimeCall::SubtensorModule(SubtensorCall::serve_axon { + netuid, + version: 1, + ip, + port: 1, + ip_type: 4, + protocol: 0, + placeholder1: 0, + placeholder2: 0, + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!( + err, + CustomTransactionError::HotKeyNotRegisteredInNetwork.into() + ); + }); +} + +#[test] +fn extension_serve_axon_tls_rejects_unregistered_hotkey() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(41); + let ip = u128::from(u32::from_be_bytes([8, 8, 8, 8])); + let call = RuntimeCall::SubtensorModule(SubtensorCall::serve_axon_tls { + netuid, + version: 1, + ip, + port: 1, + ip_type: 4, + protocol: 0, + placeholder1: 0, + placeholder2: 0, + certificate: vec![], + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!( + err, + CustomTransactionError::HotKeyNotRegisteredInNetwork.into() + ); }); } @@ -126,18 +550,7 @@ fn extension_serve_prometheus_rejects_unregistered_hotkey() { let info = call.get_dispatch_info(); assert_eq!(info.pays_fee, frame_support::dispatch::Pays::No); - let extension = SubtensorTransactionExtension::::new(); - let err = extension - .validate( - RawOrigin::Signed(hotkey).into(), - &call, - &info, - 0, - (), - &TxBaseImplication(()), - TransactionSource::External, - ) - .unwrap_err(); + let err = validate_signed(hotkey, &call).unwrap_err(); assert_eq!( err, CustomTransactionError::HotKeyNotRegisteredInNetwork.into() @@ -146,39 +559,136 @@ fn extension_serve_prometheus_rejects_unregistered_hotkey() { } #[test] -fn extension_commit_weights_rejects_commit_reveal_disabled() { +fn extension_associate_evm_key_rejects_uid_not_found() { new_test_ext(0).execute_with(|| { let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let coldkey = U256::from(2); - add_network_disable_commit_reveal(netuid, 1, 0); - setup_reserves( + add_network(netuid, 1, 0); + let hotkey = U256::from(50); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::associate_evm_key { netuid, - 1_000_000_000_000_u64.into(), - 1_000_000_000_000_u64.into(), - ); - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); - SubtensorModule::set_stake_threshold(0); + evm_key: sp_core::H160::zero(), + block_number: 0, + signature: sp_core::ecdsa::Signature::from_raw([0u8; 65]), + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::UidNotFound.into()); + }); +} - let call = RuntimeCall::SubtensorModule(SubtensorCall::commit_weights { +#[test] +fn extension_add_stake_burn_rejects_not_subnet_owner() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let owner = U256::from(60); + let not_owner = U256::from(61); + let hotkey = U256::from(62); + add_network(netuid, 1, 0); + SubnetOwner::::insert(netuid, owner); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake_burn { + hotkey, netuid, - commit_hash: sp_core::H256::zero(), + amount: TaoBalance::from(1u64), + limit: None, }); - let info = DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - let err = extension + let err = SubtensorTransactionExtension::::new() .validate( - RawOrigin::Signed(hotkey).into(), + RawOrigin::Signed(not_owner).into(), &call, - &info, + &dispatch_info(), 0, (), &TxBaseImplication(()), TransactionSource::External, ) .unwrap_err(); - assert_eq!(err, CustomTransactionError::CommitRevealDisabled.into()); + assert_eq!( + err, + TransactionValidityError::Invalid(InvalidTransaction::BadSigner) + ); + }); +} + +#[test] +fn extension_register_network_rejects_global_rate_limit() { + new_test_ext(0).execute_with(|| { + let limit = 50u64; + NetworkRateLimit::::put(limit); + System::set_block_number(200u64.into()); + SubtensorModule::set_network_last_lock_block(170); + + let coldkey = U256::from(70); + let hotkey = U256::from(71); + let call = RuntimeCall::SubtensorModule(SubtensorCall::register_network { hotkey }); + let err = validate_signed(coldkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::RateLimitExceeded.into()); + }); +} + +#[test] +fn extension_register_network_accepts_after_global_cooldown() { + new_test_ext(0).execute_with(|| { + let limit = 50u64; + NetworkRateLimit::::put(limit); + System::set_block_number(200u64.into()); + SubtensorModule::set_network_last_lock_block(150); + + let coldkey = U256::from(72); + let hotkey = U256::from(73); + let call = RuntimeCall::SubtensorModule(SubtensorCall::register_network { hotkey }); + assert!(validate_signed(coldkey, &call).is_ok()); + }); +} + +#[test] +fn extension_associate_evm_key_rejects_associate_rate_limit() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let tempo: u16 = 2; + let modality: u16 = 2; + add_network(netuid, tempo, modality); + + let coldkey = U256::from(80); + let hotkey = U256::from(81); + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + register_ok_neuron(netuid, hotkey, coldkey, 0); + + let uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); + System::set_block_number(300u64.into()); + let now = SubtensorModule::get_current_block_as_u64(); + AssociatedEvmAddress::::insert(netuid, uid, (sp_core::H160::zero(), now)); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::associate_evm_key { + netuid, + evm_key: sp_core::H160::zero(), + block_number: 0, + signature: sp_core::ecdsa::Signature::from_raw([0u8; 65]), + }); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!( + err, + CustomTransactionError::EvmKeyAssociateRateLimitExceeded.into() + ); + }); +} + +#[test] +fn extension_add_stake_burn_boosts_priority_for_subnet_owner() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let owner = U256::from(90); + let hotkey = U256::from(91); + add_network(netuid, 1, 0); + SubnetOwner::::insert(netuid, owner); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake_burn { + hotkey, + netuid, + amount: TaoBalance::from(1u64), + limit: None, + }); + let v = validate_signed(owner, &call).unwrap(); + assert_eq!(v.priority, 100); }); } From c3c261cd56640603cebde73741bc6b164ecabd49 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 8 Apr 2026 14:43:19 +0800 Subject: [PATCH 6/8] remove unnecessary error mapping --- common/src/transaction_error.rs | 32 +---------------- pallets/subtensor/src/extensions/subtensor.rs | 35 ------------------- 2 files changed, 1 insertion(+), 66 deletions(-) diff --git a/common/src/transaction_error.rs b/common/src/transaction_error.rs index 474a894aca..cc3054a30d 100644 --- a/common/src/transaction_error.rs +++ b/common/src/transaction_error.rs @@ -30,22 +30,7 @@ pub enum CustomTransactionError { InvalidRealAccount, FailedShieldedTxParsing, InvalidShieldedTxPubKeyHash, - CommitRevealEnabled, - CommitRevealDisabled, NonAssociatedColdKey, - InvalidIpType, - IncorrectCommitRevealVersion, - CommittingWeightsTooFast, - TooManyUnrevealedCommits, - NewHotKeyIsSameWithOld, - HotKeyAlreadyRegisteredInSubNet, - NotEnoughBalanceToPaySwapHotKey, - HotKeySwapOnSubnetIntervalNotPassed, - DelegateTakeTooLow, - DelegateTakeTooHigh, - InvalidWorkBlock, - InvalidDifficulty, - InvalidSeal, } impl From for u8 { @@ -78,22 +63,7 @@ impl From for u8 { CustomTransactionError::InvalidRealAccount => 22, CustomTransactionError::FailedShieldedTxParsing => 23, CustomTransactionError::InvalidShieldedTxPubKeyHash => 24, - CustomTransactionError::CommitRevealEnabled => 25, - CustomTransactionError::CommitRevealDisabled => 26, - CustomTransactionError::NonAssociatedColdKey => 27, - CustomTransactionError::InvalidIpType => 28, - CustomTransactionError::IncorrectCommitRevealVersion => 29, - CustomTransactionError::CommittingWeightsTooFast => 30, - CustomTransactionError::TooManyUnrevealedCommits => 31, - CustomTransactionError::NewHotKeyIsSameWithOld => 32, - CustomTransactionError::HotKeyAlreadyRegisteredInSubNet => 33, - CustomTransactionError::NotEnoughBalanceToPaySwapHotKey => 34, - CustomTransactionError::HotKeySwapOnSubnetIntervalNotPassed => 35, - CustomTransactionError::DelegateTakeTooLow => 36, - CustomTransactionError::DelegateTakeTooHigh => 37, - CustomTransactionError::InvalidWorkBlock => 38, - CustomTransactionError::InvalidDifficulty => 39, - CustomTransactionError::InvalidSeal => 40, + CustomTransactionError::NonAssociatedColdKey => 25, } } } diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index 8dbfd9a2bb..2643a9009c 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -70,42 +70,7 @@ where CustomTransactionError::ServingRateLimitExceeded } Error::::InvalidPort => CustomTransactionError::InvalidPort, - Error::::CommitRevealEnabled => CustomTransactionError::CommitRevealEnabled, - Error::::CommitRevealDisabled => CustomTransactionError::CommitRevealDisabled, Error::::NonAssociatedColdKey => CustomTransactionError::NonAssociatedColdKey, - Error::::InvalidIpType => CustomTransactionError::InvalidIpType, - Error::::IncorrectCommitRevealVersion => { - CustomTransactionError::IncorrectCommitRevealVersion - } - Error::::CommittingWeightsTooFast => { - CustomTransactionError::CommittingWeightsTooFast - } - Error::::TooManyUnrevealedCommits => { - CustomTransactionError::TooManyUnrevealedCommits - } - Error::::NewHotKeyIsSameWithOld => CustomTransactionError::NewHotKeyIsSameWithOld, - Error::::HotKeyAlreadyRegisteredInSubNet => { - CustomTransactionError::HotKeyAlreadyRegisteredInSubNet - } - Error::::NotEnoughBalanceToPaySwapHotKey => { - CustomTransactionError::NotEnoughBalanceToPaySwapHotKey - } - Error::::HotKeySwapOnSubnetIntervalNotPassed => { - CustomTransactionError::HotKeySwapOnSubnetIntervalNotPassed - } - Error::::DelegateTakeTooLow => CustomTransactionError::DelegateTakeTooLow, - Error::::DelegateTakeTooHigh => CustomTransactionError::DelegateTakeTooHigh, - Error::::InvalidWorkBlock => CustomTransactionError::InvalidWorkBlock, - Error::::InvalidDifficulty => CustomTransactionError::InvalidDifficulty, - Error::::InvalidSeal => CustomTransactionError::InvalidSeal, - Error::::InputLengthsUnequal => CustomTransactionError::InputLengthsUnequal, - Error::::MechanismDoesNotExist => CustomTransactionError::SubnetNotExists, - Error::::HotKeyNotRegisteredInSubNet => { - CustomTransactionError::HotKeyNotRegisteredInNetwork - } - Error::::DelegateTxRateLimitExceeded | Error::::HotKeySetTxRateLimitExceeded => { - CustomTransactionError::RateLimitExceeded - } _ => CustomTransactionError::BadRequest, } } From d7048897666428038e35aed04ba386182d33e46f Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 8 Apr 2026 14:58:03 +0800 Subject: [PATCH 7/8] clean up code --- pallets/subtensor/src/extensions/subtensor.rs | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index 2643a9009c..9eebf0f136 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -50,38 +50,37 @@ where Pallet::::check_weights_min_stake(who, netuid) } - pub fn pallet_error_to_custom(err: Error) -> CustomTransactionError { - match err { - Error::::AmountTooLow => CustomTransactionError::StakeAmountTooLow, - Error::::SubnetNotExists => CustomTransactionError::SubnetNotExists, - Error::::NotEnoughBalanceToStake => CustomTransactionError::BalanceTooLow, - Error::::HotKeyAccountNotExists => CustomTransactionError::HotkeyAccountDoesntExist, - Error::::NotEnoughStakeToWithdraw => { - CustomTransactionError::NotEnoughStakeToWithdraw - } - Error::::InsufficientLiquidity => CustomTransactionError::InsufficientLiquidity, - Error::::SlippageTooHigh => CustomTransactionError::SlippageTooHigh, - Error::::TransferDisallowed => CustomTransactionError::TransferDisallowed, - Error::::HotKeyNotRegisteredInNetwork => { - CustomTransactionError::HotKeyNotRegisteredInNetwork - } - Error::::InvalidIpAddress => CustomTransactionError::InvalidIpAddress, - Error::::ServingRateLimitExceeded => { - CustomTransactionError::ServingRateLimitExceeded - } - Error::::InvalidPort => CustomTransactionError::InvalidPort, - Error::::NonAssociatedColdKey => CustomTransactionError::NonAssociatedColdKey, - _ => CustomTransactionError::BadRequest, - } - } - pub fn result_to_validity(result: Result<(), Error>, priority: u64) -> TransactionValidity { match result { Ok(()) => Ok(ValidTransaction { priority, ..Default::default() }), - Err(err) => Err(Self::pallet_error_to_custom(err).into()), + Err(err) => Err(match err { + Error::::AmountTooLow => CustomTransactionError::StakeAmountTooLow, + Error::::SubnetNotExists => CustomTransactionError::SubnetNotExists, + Error::::NotEnoughBalanceToStake => CustomTransactionError::BalanceTooLow, + Error::::HotKeyAccountNotExists => { + CustomTransactionError::HotkeyAccountDoesntExist + } + Error::::NotEnoughStakeToWithdraw => { + CustomTransactionError::NotEnoughStakeToWithdraw + } + Error::::InsufficientLiquidity => CustomTransactionError::InsufficientLiquidity, + Error::::SlippageTooHigh => CustomTransactionError::SlippageTooHigh, + Error::::TransferDisallowed => CustomTransactionError::TransferDisallowed, + Error::::HotKeyNotRegisteredInNetwork => { + CustomTransactionError::HotKeyNotRegisteredInNetwork + } + Error::::InvalidIpAddress => CustomTransactionError::InvalidIpAddress, + Error::::ServingRateLimitExceeded => { + CustomTransactionError::ServingRateLimitExceeded + } + Error::::InvalidPort => CustomTransactionError::InvalidPort, + Error::::NonAssociatedColdKey => CustomTransactionError::NonAssociatedColdKey, + _ => CustomTransactionError::BadRequest, + } + .into()), } } } @@ -308,10 +307,8 @@ where } Some(Call::increase_take { hotkey, take: _ }) | Some(Call::decrease_take { hotkey, take: _ }) => { - match Pallet::::do_take_checks(who, hotkey) { - Ok(()) => Ok((Default::default(), (), origin)), - Err(err) => Err(Self::pallet_error_to_custom(err).into()), - } + Self::result_to_validity(Pallet::::do_take_checks(who, hotkey), 0u64) + .map(|validity| (validity, (), origin.clone())) } Some(Call::swap_hotkey_v2 { From 94338a926d65173b5372b80469338b4074ca1022 Mon Sep 17 00:00:00 2001 From: open-junius Date: Sat, 18 Apr 2026 09:31:18 +0800 Subject: [PATCH 8/8] remove redundant code --- pallets/subtensor/src/extensions/subtensor.rs | 3 +- pallets/subtensor/src/subnets/serving.rs | 42 +++---------------- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index 9eebf0f136..9fdc3af268 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -391,7 +391,8 @@ where }) => Self::result_to_validity( Pallet::::validate_serve_prometheus( who, *netuid, *version, *ip, *port, *ip_type, - ), + ) + .map(|_| ()), 0u64, ) .map(|validity| (validity, (), origin.clone())), diff --git a/pallets/subtensor/src/subnets/serving.rs b/pallets/subtensor/src/subnets/serving.rs index d87c7a8eff..5416e3df5d 100644 --- a/pallets/subtensor/src/subnets/serving.rs +++ b/pallets/subtensor/src/subnets/serving.rs @@ -170,43 +170,11 @@ impl Pallet { // We check the callers (hotkey) signature. let hotkey_id = ensure_signed(origin)?; - // Check the ip signature validity. - ensure!(Self::is_valid_ip_type(ip_type), Error::::InvalidIpType); - ensure!( - Self::is_valid_ip_address(ip_type, ip, false), - Error::::InvalidIpAddress - ); - - // Ensure the hotkey is registered somewhere. - ensure!( - Self::is_hotkey_registered_on_any_network(&hotkey_id), - Error::::HotKeyNotRegisteredInNetwork - ); - - // We get the previous axon info assoicated with this ( netuid, uid ) - let mut prev_prometheus = Self::get_prometheus_info(netuid, &hotkey_id); - let current_block: u64 = Self::get_current_block_as_u64(); - ensure!( - Self::prometheus_passes_rate_limit(netuid, &prev_prometheus, current_block), - Error::::ServingRateLimitExceeded - ); - - // We insert the prometheus meta. - prev_prometheus.block = Self::get_current_block_as_u64(); - prev_prometheus.version = version; - prev_prometheus.ip = ip; - prev_prometheus.port = port; - prev_prometheus.ip_type = ip_type; - - // Validate prometheus data with delegate func - let prom_validated = Self::validate_prometheus_data(&prev_prometheus); - ensure!( - prom_validated.is_ok(), - prom_validated.err().unwrap_or(Error::::InvalidPort) - ); + let updated_prometheus = + Self::validate_serve_prometheus(&hotkey_id, netuid, version, ip, port, ip_type)?; // Insert new prometheus data - Prometheus::::insert(netuid, hotkey_id.clone(), prev_prometheus); + Prometheus::::insert(netuid, hotkey_id.clone(), updated_prometheus); // We deposit prometheus served event. log::debug!("PrometheusServed( hotkey:{:?} ) ", hotkey_id.clone()); @@ -378,7 +346,7 @@ impl Pallet { ip: u128, port: u16, ip_type: u8, - ) -> Result<(), Error> { + ) -> Result> { ensure!(Self::is_valid_ip_type(ip_type), Error::::InvalidIpType); ensure!( Self::is_valid_ip_address(ip_type, ip, false), @@ -409,6 +377,6 @@ impl Pallet { prom_validated.err().unwrap_or(Error::::InvalidPort) ); - Ok(()) + Ok(prev_prometheus) } }