diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index b10c617d0c..d872e6be71 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -986,6 +986,8 @@ pub mod pallet { StakeTooLowForRoot, // --- Thrown when a hotkey attempts to join the root subnet with too little stake AllNetworksInImmunity, // --- Thrown when all subnets are in the immunity period NotEnoughBalance, + NotRootSubnet, + IsRoot, NoNeuronIdAvailable, // -- Thrown when no neuron id is available /// Thrown a stake would be below the minimum threshold for nominator validations NomStakeBelowMinimumThreshold, @@ -1305,6 +1307,81 @@ pub mod pallet { Self::do_set_weights(origin, netuid, dests, weights, version_key) } + // # Args: + // * `origin`: (Origin): + // - The caller, a hotkey who wishes to set their weights. + // + // * `netuid` (u16): + // - The network uid we are setting these weights on. + // + // * `hotkey` (T::AccountId): + // - The hotkey associated with the operation and the calling coldkey. + // + // * `dests` (Vec): + // - The edge endpoint for the weight, i.e. j for w_ij. + // + // * 'weights' (Vec): + // - The u16 integer encoded weights. Interpreted as rational + // values in the range [0,1]. They must sum to in32::MAX. + // + // * 'version_key' ( u64 ): + // - The network version key to check if the validator is up to date. + // + // # Event: + // + // * WeightsSet; + // - On successfully setting the weights on chain. + // + // # Raises: + // + // * NonAssociatedColdKey; + // - Attempting to set weights on a non-associated cold key. + // + // * 'NetworkDoesNotExist': + // - Attempting to set weights on a non-existent network. + // + // * 'NotRootSubnet': + // - Attempting to set weights on a subnet that is not the root network. + // + // * 'WeightVecNotEqualSize': + // - Attempting to set weights with uids not of same length. + // + // * 'InvalidUid': + // - Attempting to set weights with invalid uids. + // + // * 'NotRegistered': + // - Attempting to set weights from a non registered account. + // + // * 'NotSettingEnoughWeights': + // - Attempting to set weights with fewer weights than min. + // + // * 'IncorrectNetworkVersionKey': + // - Attempting to set weights with the incorrect network version key. + // + // * 'SettingWeightsTooFast': + // - Attempting to set weights too fast. + // + // * 'NotSettingEnoughWeights': + // - Attempting to set weights with fewer weights than min. + // + // * 'MaxWeightExceeded': + // - Attempting to set weights with max value exceeding limit. + // + #[pallet::call_index(8)] + #[pallet::weight((Weight::from_parts(10_151_000_000, 0) + .saturating_add(T::DbWeight::get().reads(4104)) + .saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::No))] + pub fn set_root_weights( + origin: OriginFor, + netuid: u16, + hotkey: T::AccountId, + dests: Vec, + weights: Vec, + version_key: u64, + ) -> DispatchResult { + Self::do_set_root_weights(origin, netuid, hotkey, dests, weights, version_key) + } + // --- Sets the key as a delegate. // // # Args: @@ -1866,6 +1943,18 @@ where Err(InvalidTransaction::Call.into()) } } + Some(Call::set_root_weights { netuid, .. }) => { + if Self::check_weights_min_stake(who) { + let priority: u64 = Self::get_priority_set_weights(who, *netuid); + Ok(ValidTransaction { + priority: priority, + longevity: 1, + ..Default::default() + }) + } else { + return Err(InvalidTransaction::Call.into()); + } + } Some(Call::add_stake { .. }) => Ok(ValidTransaction { priority: Self::get_priority_vanilla(), ..Default::default() diff --git a/pallets/subtensor/src/root.rs b/pallets/subtensor/src/root.rs index 635f93266c..0a19d294bf 100644 --- a/pallets/subtensor/src/root.rs +++ b/pallets/subtensor/src/root.rs @@ -576,6 +576,123 @@ impl Pallet { Ok(()) } + pub fn do_set_root_weights( + origin: T::RuntimeOrigin, + netuid: u16, + hotkey: T::AccountId, + uids: Vec, + values: Vec, + version_key: u64, + ) -> dispatch::DispatchResult { + // --- 1. Check the caller's signature. This is the coldkey of a registered account. + let coldkey = ensure_signed(origin)?; + log::info!( + "do_set_root_weights( origin:{:?} netuid:{:?}, uids:{:?}, values:{:?})", + coldkey, + netuid, + uids, + values + ); + + // --- 2. Check that the signer coldkey owns the hotkey + ensure!( + Self::coldkey_owns_hotkey(&coldkey, &hotkey) + && Self::get_owning_coldkey_for_hotkey(&hotkey) == coldkey, + Error::::NonAssociatedColdKey + ); + + // --- 3. Check to see if this is a valid network. + ensure!( + Self::if_subnet_exist(netuid), + Error::::NetworkDoesNotExist + ); + + // --- 4. Check that this is the root network. + ensure!(netuid == Self::get_root_netuid(), Error::::NotRootSubnet); + + // --- 5. Check that the length of uid list and value list are equal for this network. + ensure!( + Self::uids_match_values(&uids, &values), + Error::::WeightVecNotEqualSize + ); + + // --- 6. Check to see if the number of uids is within the max allowed uids for this network. + // For the root network this number is the number of subnets. + ensure!( + !Self::contains_invalid_root_uids(&uids), + Error::::InvalidUid + ); + + // --- 7. Check to see if the hotkey is registered to the passed network. + ensure!( + Self::is_hotkey_registered_on_network(netuid, &hotkey), + Error::::NotRegistered + ); + + // --- 8. Check to see if the hotkey has enough stake to set weights. + ensure!( + Self::get_total_stake_for_hotkey(&hotkey) >= Self::get_weights_min_stake(), + Error::::NotEnoughStakeToSetWeights + ); + + // --- 9. Ensure version_key is up-to-date. + ensure!( + Self::check_version_key(netuid, version_key), + Error::::IncorrectNetworkVersionKey + ); + + // --- 10. Get the neuron uid of associated hotkey on network netuid. + let neuron_uid = Self::get_uid_for_net_and_hotkey(netuid, &hotkey)?; + + // --- 11. Ensure the uid is not setting weights faster than the weights_set_rate_limit. + let current_block: u64 = Self::get_current_block_as_u64(); + ensure!( + Self::check_rate_limit(netuid, neuron_uid, current_block), + Error::::SettingWeightsTooFast + ); + + // --- 12. Ensure the passed uids contain no duplicates. + ensure!(!Self::has_duplicate_uids(&uids), Error::::DuplicateUids); + + // --- 13. Ensure that the weights have the required length. + ensure!( + Self::check_length(netuid, neuron_uid, &uids, &values), + Error::::NotSettingEnoughWeights + ); + + // --- 14. Max-upscale the weights. + let max_upscaled_weights: Vec = vec_u16_max_upscale_to_u16(&values); + + // --- 15. Ensure the weights are max weight limited + ensure!( + Self::max_weight_limited(netuid, neuron_uid, &uids, &max_upscaled_weights), + Error::::MaxWeightExceeded + ); + + // --- 16. Zip weights for sinking to storage map. + let mut zipped_weights: Vec<(u16, u16)> = vec![]; + for (uid, val) in uids.iter().zip(max_upscaled_weights.iter()) { + zipped_weights.push((*uid, *val)) + } + + // --- 17. Set weights under netuid, uid double map entry. + Weights::::insert(netuid, neuron_uid, zipped_weights); + + // --- 18. Set the activity for the weights on this network. + Self::set_last_update_for_uid(netuid, neuron_uid, current_block); + + // --- 19. Emit the tracking event. + log::info!( + "RootWeightsSet( netuid:{:?}, neuron_uid:{:?} )", + netuid, + neuron_uid + ); + Self::deposit_event(Event::WeightsSet(netuid, neuron_uid)); + + // --- 20. Return ok. + Ok(()) + } + pub fn do_vote_root( origin: T::RuntimeOrigin, hotkey: &T::AccountId, diff --git a/pallets/subtensor/src/weights.rs b/pallets/subtensor/src/weights.rs index c912af61d9..17d5b01cd0 100644 --- a/pallets/subtensor/src/weights.rs +++ b/pallets/subtensor/src/weights.rs @@ -76,6 +76,9 @@ impl Pallet { values ); + // --- Check that the netuid is not the root network. + ensure!(netuid != Self::get_root_netuid(), Error::::IsRoot); + // --- 2. Check that the length of uid list and value list are equal for this network. ensure!( Self::uids_match_values(&uids, &values), @@ -89,19 +92,10 @@ impl Pallet { ); // --- 4. Check to see if the number of uids is within the max allowed uids for this network. - // For the root network this number is the number of subnets. - if netuid == Self::get_root_netuid() { - // --- 4.a. Ensure that the passed uids are valid for the network. - ensure!( - !Self::contains_invalid_root_uids(&uids), - Error::::InvalidUid - ); - } else { - ensure!( - Self::check_len_uids_within_allowed(netuid, &uids), - Error::::TooManyUids - ); - } + ensure!( + Self::check_len_uids_within_allowed(netuid, &uids), + Error::::TooManyUids + ); // --- 5. Check to see if the hotkey is registered to the passed network. ensure!( @@ -121,11 +115,8 @@ impl Pallet { Error::::IncorrectNetworkVersionKey ); - // --- 8. Get the neuron uid of associated hotkey on network netuid. - - let neuron_uid = Self::get_uid_for_net_and_hotkey(netuid, &hotkey)?; - // --- 9. Ensure the uid is not setting weights faster than the weights_set_rate_limit. + let neuron_uid = Self::get_uid_for_net_and_hotkey(netuid, &hotkey)?; let current_block: u64 = Self::get_current_block_as_u64(); ensure!( Self::check_rate_limit(netuid, neuron_uid, current_block), @@ -133,23 +124,19 @@ impl Pallet { ); // --- 10. Check that the neuron uid is an allowed validator permitted to set non-self weights. - if netuid != Self::get_root_netuid() { - ensure!( - Self::check_validator_permit(netuid, neuron_uid, &uids, &values), - Error::::NoValidatorPermit - ); - } + ensure!( + Self::check_validator_permit(netuid, neuron_uid, &uids, &values), + Error::::NoValidatorPermit + ); // --- 11. Ensure the passed uids contain no duplicates. ensure!(!Self::has_duplicate_uids(&uids), Error::::DuplicateUids); // --- 12. Ensure that the passed uids are valid for the network. - if netuid != Self::get_root_netuid() { - ensure!( - !Self::contains_invalid_uids(netuid, &uids), - Error::::InvalidUid - ); - } + ensure!( + !Self::contains_invalid_uids(netuid, &uids), + Error::::InvalidUid + ); // --- 13. Ensure that the weights have the required length. ensure!( diff --git a/pallets/subtensor/tests/root.rs b/pallets/subtensor/tests/root.rs index 27a652851c..02b0458929 100644 --- a/pallets/subtensor/tests/root.rs +++ b/pallets/subtensor/tests/root.rs @@ -1,5 +1,5 @@ use crate::mock::*; -use frame_support::assert_ok; +use frame_support::{assert_err, assert_ok}; use frame_system::Config; use frame_system::{EventRecord, Phase}; use pallet_subtensor::migration; @@ -30,6 +30,34 @@ fn test_root_register_network_exist() { }); } +#[test] +fn test_set_weights_not_root_error() { + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + + let dests = vec![0]; + let weights = vec![1]; + let version_key: u64 = 0; + let hotkey = U256::from(1); + let coldkey = U256::from(2); + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey, coldkey, 2143124); + + assert_err!( + SubtensorModule::set_root_weights( + RuntimeOrigin::signed(coldkey), + netuid, + hotkey, + dests.clone(), + weights.clone(), + version_key, + ), + Error::::NotRootSubnet + ); + }); +} + #[test] fn test_root_register_normal_on_root_fails() { new_test_ext(1).execute_with(|| { @@ -175,7 +203,7 @@ fn test_root_set_weights() { SubtensorModule::set_max_allowed_uids(root_netuid, n as u16); for i in 0..n { let hotkey_account_id: U256 = U256::from(i); - let coldkey_account_id: U256 = U256::from(i); + let coldkey_account_id: U256 = U256::from(i + 456); SubtensorModule::add_balance_to_coldkey_account( &coldkey_account_id, 1_000_000_000_000_000, @@ -201,17 +229,57 @@ fn test_root_set_weights() { for netuid in 1..n { log::debug!("Adding network with netuid: {}", netuid); assert_ok!(SubtensorModule::register_network( - <::RuntimeOrigin>::signed(U256::from(netuid)) + <::RuntimeOrigin>::signed(U256::from(netuid + 456)) )); } + // Test that signing with hotkey will fail. + for i in 0..n { + let hotkey = U256::from(i); + let uids: Vec = vec![i as u16]; + let values: Vec = vec![1]; + assert_err!( + SubtensorModule::set_root_weights( + <::RuntimeOrigin>::signed(hotkey), + root_netuid, + hotkey, + uids, + values, + 0, + ), + Error::::NonAssociatedColdKey + ); + } + + // Test that signing an unassociated coldkey will fail. + let unassociated_coldkey = U256::from(612); + for i in 0..n { + let hotkey = U256::from(i); + let uids: Vec = vec![i as u16]; + let values: Vec = vec![1]; + assert_err!( + SubtensorModule::set_root_weights( + <::RuntimeOrigin>::signed(unassociated_coldkey), + root_netuid, + hotkey, + uids, + values, + 0, + ), + Error::::NonAssociatedColdKey + ); + } + // Set weights into diagonal matrix. for i in 0..n { + let hotkey = U256::from(i); + let coldkey = U256::from(i + 456); let uids: Vec = vec![i as u16]; let values: Vec = vec![1]; - assert_ok!(SubtensorModule::set_weights( - <::RuntimeOrigin>::signed(U256::from(i)), + assert_ok!(SubtensorModule::set_root_weights( + <::RuntimeOrigin>::signed(coldkey), root_netuid, + hotkey, uids, values, 0, @@ -322,11 +390,14 @@ fn test_root_set_weights_out_of_order_netuids() { // Set weights into diagonal matrix. for (i, netuid) in subnets.iter().enumerate() { let uids: Vec = vec![*netuid]; - let values: Vec = vec![1]; - assert_ok!(SubtensorModule::set_weights( - <::RuntimeOrigin>::signed(U256::from(i)), + + let coldkey = U256::from(i); + let hotkey = U256::from(i); + assert_ok!(SubtensorModule::set_root_weights( + <::RuntimeOrigin>::signed(coldkey), root_netuid, + hotkey, uids, values, 0, @@ -503,9 +574,10 @@ fn test_network_pruning() { &hot )); assert!(SubtensorModule::get_uid_for_net_and_hotkey(root_netuid, &hot).is_ok()); - assert_ok!(SubtensorModule::set_weights( - <::RuntimeOrigin>::signed(hot), + assert_ok!(SubtensorModule::set_root_weights( + <::RuntimeOrigin>::signed(cold), root_netuid, + hot, uids, values, 0 @@ -636,9 +708,10 @@ fn test_weights_after_network_pruning() { log::info!("uids set: {:?}", uids); log::info!("values set: {:?}", values); log::info!("In netuid: {:?}", root_netuid); - assert_ok!(SubtensorModule::set_weights( - <::RuntimeOrigin>::signed(hot), + assert_ok!(SubtensorModule::set_root_weights( + <::RuntimeOrigin>::signed(cold), root_netuid, + hot, uids, values, 0 diff --git a/pallets/subtensor/tests/weights.rs b/pallets/subtensor/tests/weights.rs index c1467abda4..3d7673982e 100644 --- a/pallets/subtensor/tests/weights.rs +++ b/pallets/subtensor/tests/weights.rs @@ -1,6 +1,6 @@ mod mock; use frame_support::{ - assert_ok, + assert_err, assert_ok, dispatch::{DispatchClass, GetDispatchInfo, Pays}, }; use mock::*; @@ -35,6 +35,29 @@ fn test_set_weights_dispatch_info_ok() { }); } +#[test] +fn test_set_weights_is_root_error() { + new_test_ext(0).execute_with(|| { + let root_netuid: u16 = 0; + + let dests = vec![0]; + let weights = vec![1]; + let version_key: u64 = 0; + let hotkey = U256::from(1); + + assert_err!( + SubtensorModule::set_weights( + RuntimeOrigin::signed(hotkey), + root_netuid, + dests.clone(), + weights.clone(), + version_key, + ), + Error::::IsRoot + ); + }); +} + // Test ensures that uid has validator permit to set non-self weights. #[test] fn test_weights_err_no_validator_permit() {