diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 067d5855bf..42f5615f54 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -301,8 +301,9 @@ impl Pallet { /// Checks if registrations are allowed for a given subnet. /// - /// This function retrieves the subnet hyperparameters for the specified subnet and checks the `registration_allowed` flag. - /// If the subnet doesn't exist or doesn't have hyperparameters defined, it returns `false`. + /// This function retrieves the subnet hyperparameters for the specified subnet and checks the + /// `registration_allowed` flag. If the subnet doesn't exist or doesn't have hyperparameters + /// defined, it returns `false`. /// /// # Arguments /// diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 6469ca3aca..1b35279328 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -20,28 +20,30 @@ pub struct WeightsTlockPayload { impl Pallet { /// The `coinbase` function performs a four-part emission distribution process involving /// subnets, epochs, hotkeys, and nominators. + /// /// It is divided into several steps, each handling a specific part of the distribution: /// /// Step 1: Compute the block-wise emission for each subnet. - /// This involves calculating how much (TAO) should be emitted into each subnet using the - /// root epoch function. + /// This involves calculating how much (TAO) should be emitted into each subnet using the root + /// epoch function. /// /// Step 2: Accumulate the subnet block emission. - /// After calculating the block-wise emission, these values are accumulated to keep track - /// of how much each subnet should emit before the next distribution phase. This accumulation - /// is a running total that gets updated each block. + /// After calculating the block-wise emission, these values are accumulated to keep track of how + /// much each subnet should emit before the next distribution phase. This accumulation is a + /// running total that gets updated each block. /// /// Step 3: Distribute the accumulated emissions through epochs. - /// Subnets periodically distribute their accumulated emissions to hotkeys (active validators/miners) - /// in the network on a `tempo` --- the time between epochs. This step runs Yuma consensus to - /// determine how emissions are split among hotkeys based on their contributions and roles. - /// The accumulation of hotkey emissions is done through the `accumulate_hotkey_emission` function. - /// The function splits the rewards for a hotkey amongst itself and its `parents`. The parents are - /// the hotkeys that are delegating their stake to the hotkey. + /// Subnets periodically distribute their accumulated emissions to hotkeys (active + /// validators/miners) in the network on a `tempo` --- the time between epochs. This step runs + /// Yuma consensus to determine how emissions are split among hotkeys based on their + /// contributions and roles. The accumulation of hotkey emissions is done through the + /// `accumulate_hotkey_emission` function. The function splits the rewards for a hotkey amongst + /// itself and its `parents`. The parents are the hotkeys that are delegating their stake to the + /// hotkey. /// /// Step 4: Further distribute emissions from hotkeys to nominators. - /// Finally, the emissions received by hotkeys are further distributed to their nominators, - /// who are stakeholders that support the hotkeys. + /// Finally, the emissions received by hotkeys are further distributed to their nominators, who + /// are stakeholders that support the hotkeys. pub fn run_coinbase() { // --- 0. Get current block. let current_block: u64 = Self::get_current_block_as_u64(); @@ -327,9 +329,6 @@ impl Pallet { ); continue; }; - - // If we reached here, we sucessfully set weights! - return Ok(()); } Ok(()) diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 027966f927..57d0f6b6fa 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -150,7 +150,7 @@ pub fn check_vec_max_limited(vec: &[u16], max_limit: u16) -> bool { let mut vec_fixed: Vec = vec.iter().map(|e: &u16| I32F32::from_num(*e)).collect(); inplace_normalize(&mut vec_fixed); let max_value: Option<&I32F32> = vec_fixed.iter().max(); - max_value.map_or(true, |v| *v <= max_limit_fixed) + max_value.is_none_or(|v| *v <= max_limit_fixed) } #[allow(dead_code)] @@ -1229,7 +1229,7 @@ pub fn mat_ema_alpha_vec( alpha: &[I32F32], ) -> Vec> { // Check if the new matrix is empty or its first row is empty. - if new.is_empty() || new.first().map_or(true, |row| row.is_empty()) { + if new.is_empty() || new.first().is_none_or(|row| row.is_empty()) { return vec![vec![]; 1]; } diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index d919c6dbbc..bd54269c27 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -803,18 +803,6 @@ impl Pallet { I32F32::from_num(Self::get_kappa(netuid)).saturating_div(I32F32::from_num(u16::MAX)) } - pub fn get_normalized_stake(netuid: u16) -> Vec { - let n = Self::get_subnetwork_n(netuid); - let mut stake_64: Vec = (0..n) - .map(|neuron_uid| { - I64F64::from_num(Self::get_stake_for_uid_and_subnetwork(netuid, neuron_uid)) - }) - .collect(); - inplace_normalize_64(&mut stake_64); - let stake: Vec = vec_fixed64_to_fixed32(stake_64); - stake - } - pub fn get_block_at_registration(netuid: u16) -> Vec { let n = Self::get_subnetwork_n(netuid); let block_at_registration: Vec = (0..n) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 8f9703d361..9b0bf67c0d 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -54,7 +54,7 @@ mod tests; // apparently this is stabilized since rust 1.36 extern crate alloc; -pub const MAX_CRV3_COMMIT_SIZE_BYTES: u32 = 2048; +pub const MAX_CRV3_COMMIT_SIZE_BYTES: u32 = 5000; #[deny(missing_docs)] #[import_section(errors::errors)] diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index aab849994f..11f8d81bab 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -22,33 +22,42 @@ mod errors { HotKeyAccountNotExists, /// The hotkey is not registered in any subnet. HotKeyNotRegisteredInNetwork, - /// Request to stake, unstake or subscribe is made by a coldkey that is not associated with the hotkey account. + /// Request to stake, unstake or subscribe is made by a coldkey that is not associated with + /// the hotkey account. NonAssociatedColdKey, /// The hotkey is not a delegate and the signer is not the owner of the hotkey. HotKeyNotDelegateAndSignerNotOwnHotKey, /// Stake amount to withdraw is zero. StakeToWithdrawIsZero, - /// The caller is requesting removing more stake than there exists in the staking account. See: "[remove_stake()]". + /// The caller is requesting removing more stake than there exists in the staking account. + /// See: "[remove_stake()]". NotEnoughStakeToWithdraw, - /// The caller is requesting to set weights but the caller has less than minimum stake required to set weights (less than WeightsMinStake). + /// The caller is requesting to set weights but the caller has less than minimum stake + /// required to set weights (less than WeightsMinStake). NotEnoughStakeToSetWeights, - /// The caller is requesting adding more stake than there exists in the coldkey account. See: "[add_stake()]" + /// The caller is requesting adding more stake than there exists in the coldkey account. + /// See: "[add_stake()]" NotEnoughBalanceToStake, - /// The caller is trying to add stake, but for some reason the requested amount could not be withdrawn from the coldkey account. + /// The caller is trying to add stake, but for some reason the requested amount could not be + /// withdrawn from the coldkey account. BalanceWithdrawalError, - /// Unsuccessfully withdraw, balance could be zero (can not make account exist) after withdrawal. + /// Unsuccessfully withdraw, balance could be zero (can not make account exist) after + /// withdrawal. ZeroBalanceAfterWithdrawn, /// The caller is attempting to set non-self weights without being a permitted validator. NeuronNoValidatorPermit, - /// The caller is attempting to set the weight keys and values but these vectors have different size. + /// The caller is attempting to set the weight keys and values but these vectors have + /// different size. WeightVecNotEqualSize, /// The caller is attempting to set weights with duplicate UIDs in the weight matrix. DuplicateUids, - /// The caller is attempting to set weight to at least one UID that does not exist in the metagraph. + /// The caller is attempting to set weight to at least one UID that does not exist in the + /// metagraph. UidVecContainInvalidOne, /// The dispatch is attempting to set weights on chain with fewer elements than are allowed. WeightVecLengthIsLow, - /// Number of registrations in this block exceeds the allowed number (i.e., exceeds the subnet hyperparameter "max_regs_per_block"). + /// Number of registrations in this block exceeds the allowed number (i.e., exceeds the + /// subnet hyperparameter "max_regs_per_block"). TooManyRegistrationsThisBlock, /// The caller is requesting registering a neuron which already exists in the active set. HotKeyAlreadyRegisteredInSubNet, @@ -60,7 +69,8 @@ mod errors { InvalidDifficulty, /// The supplied PoW hash seal does not match the supplied work. InvalidSeal, - /// The dispatch is attempting to set weights on chain with weight value exceeding the MaxWeightLimit (max_weight_limit subnet hyperparameter). + /// The dispatch is attempting to set weights on chain with weight value exceeding the + /// MaxWeightLimit (max_weight_limit subnet hyperparameter). MaxWeightExceeded, /// The hotkey is attempting to become a delegate when the hotkey is already a delegate. HotKeyAlreadyDelegate, @@ -114,7 +124,8 @@ mod errors { DelegateTakeTooLow, /// Delegate take is too high. DelegateTakeTooHigh, - /// No commit found for the provided hotkey+netuid combination when attempting to reveal the weights. + /// No commit found for the provided hotkey+netuid combination when attempting to reveal the + /// weights. NoWeightsCommitFound, /// Committed hash does not equal the hashed reveal data. InvalidRevealCommitHashNotMatch, @@ -132,28 +143,10 @@ mod errors { AlphaLowOutOfRange, /// The coldkey has already been swapped ColdKeyAlreadyAssociated, - /// The coldkey swap transaction rate limit exceeded - ColdKeySwapTxRateLimitExceeded, - /// The new coldkey is the same as the old coldkey - NewColdKeyIsSameWithOld, - /// The coldkey does not exist - NotExistColdkey, /// The coldkey balance is not enough to pay for the swap NotEnoughBalanceToPaySwapColdKey, - /// No balance to transfer - NoBalanceToTransfer, - /// Same coldkey - SameColdkey, /// The coldkey is in arbitration ColdkeyIsInArbitration, - /// The new coldkey is already registered for the drain - DuplicateColdkey, - /// Error thrown on a coldkey swap. - ColdkeySwapError, - /// Insufficient Balance to Schedule coldkey swap - InsufficientBalanceToPerformColdkeySwap, - /// The maximum number of coldkey destinations has been reached - MaxColdkeyDestinationsReached, /// Attempting to set an invalid child for a hotkey on a network. InvalidChild, /// Duplicate child when setting children. @@ -164,16 +157,12 @@ mod errors { TooManyChildren, /// Default transaction rate limit exceeded. TxRateLimitExceeded, - /// Swap coldkey only callable by root. - SwapColdkeyOnlyCallableByRoot, /// Swap already scheduled. SwapAlreadyScheduled, /// failed to swap coldkey FailedToSchedule, /// New coldkey is hotkey NewColdKeyIsHotkey, - /// New coldkey is in arbitration - NewColdkeyIsInArbitration, /// Childkey take is invalid. InvalidChildkeyTake, /// Childkey take rate limit exceeded. diff --git a/pallets/subtensor/src/rpc_info/delegate_info.rs b/pallets/subtensor/src/rpc_info/delegate_info.rs index a41b6e17e7..a877a9730c 100644 --- a/pallets/subtensor/src/rpc_info/delegate_info.rs +++ b/pallets/subtensor/src/rpc_info/delegate_info.rs @@ -156,20 +156,6 @@ impl Pallet { total_delegated } - // Helper function to get total delegated stake for a hotkey - pub fn get_total_hotkey_delegated_stake(hotkey: &T::AccountId) -> u64 { - let mut total_delegated = 0u64; - - // Iterate through all delegators for this hotkey - for (delegator, stake) in Stake::::iter_prefix(hotkey) { - if delegator != Self::get_coldkey_for_hotkey(hotkey) { - total_delegated = total_delegated.saturating_add(stake); - } - } - - total_delegated - } - // Helper function to get the coldkey associated with a hotkey pub fn get_coldkey_for_hotkey(hotkey: &T::AccountId) -> T::AccountId { Owner::::get(hotkey) diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 9fd60ea51a..7248088954 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -3,7 +3,7 @@ use frame_support::{ storage::IterableStorageDoubleMap, traits::{ tokens::{ - fungible::{Balanced as _, Inspect as _, Mutate as _}, + fungible::{Balanced as _, Inspect as _}, Fortitude, Precision, Preservation, }, Imbalance, @@ -341,13 +341,6 @@ impl Pallet { let _ = T::Currency::deposit(coldkey, amount, Precision::BestEffort); } - pub fn set_balance_on_coldkey_account( - coldkey: &T::AccountId, - amount: <::Currency as fungible::Inspect<::AccountId>>::Balance, - ) { - T::Currency::set_balance(coldkey, amount); - } - pub fn can_remove_balance_from_coldkey_account( coldkey: &T::AccountId, amount: <::Currency as fungible::Inspect<::AccountId>>::Balance, diff --git a/pallets/subtensor/src/subnets/serving.rs b/pallets/subtensor/src/subnets/serving.rs index 7e2b9a0f0a..647b328cce 100644 --- a/pallets/subtensor/src/subnets/serving.rs +++ b/pallets/subtensor/src/subnets/serving.rs @@ -246,14 +246,6 @@ impl Pallet { rate_limit == 0 || last_serve == 0 || current_block.saturating_sub(last_serve) >= rate_limit } - pub fn has_axon_info(netuid: u16, hotkey: &T::AccountId) -> bool { - Axons::::contains_key(netuid, hotkey) - } - - pub fn has_prometheus_info(netuid: u16, hotkey: &T::AccountId) -> bool { - Prometheus::::contains_key(netuid, hotkey) - } - pub fn get_axon_info(netuid: u16, hotkey: &T::AccountId) -> AxonInfoOf { if let Some(axons) = Axons::::get(netuid, hotkey) { axons diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index 61b6be70bf..acacc5a36b 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -1,6 +1,5 @@ use super::*; use frame_support::storage::IterableStorageDoubleMap; -use frame_support::storage::IterableStorageMap; use sp_std::vec; impl Pallet { @@ -127,12 +126,6 @@ impl Pallet { } } - /// Return the total number of subnetworks available on the chain. - /// - pub fn get_number_of_subnets() -> u16 { - as IterableStorageMap>::iter().count() as u16 - } - /// Return a list of all networks a hotkey is registered on. /// pub fn get_registered_networks_for_hotkey(hotkey: &T::AccountId) -> Vec { diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index edc3af2a44..e483d83d64 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -2737,46 +2737,48 @@ fn test_blocks_since_last_step() { }); } -// // Map the retention graph for consensus guarantees with an single epoch on a graph with 512 nodes, of which the first 64 are validators, the graph is split into a major and minor set, each setting specific weight on itself and the complement on the other. -// // -// // ```import torch -// // import matplotlib.pyplot as plt -// // from matplotlib.pyplot import cm -// // %matplotlib inline -// // -// // with open('finney_consensus_0.4.txt') as f: # test output saved to finney_consensus.txt -// // retention_map = eval(f.read()) -// // -// // major_ratios = {} -// // avg_weight_devs = {} -// // for major_stake, major_weight, minor_weight, avg_weight_dev, major_ratio in retention_map: -// // major_stake = f'{major_stake:.2f}' -// // maj, min = int(round(50 * major_weight)), int(round(50 * minor_weight)) -// // avg_weight_devs.setdefault(major_stake, torch.zeros((51, 51))) -// // avg_weight_devs[major_stake][maj][min] = avg_weight_dev -// // major_ratios.setdefault(major_stake, torch.zeros((51, 51))) -// // major_ratios[major_stake][maj][min] = major_ratio -// // -// // _x = torch.linspace(0, 1, 51); _y = torch.linspace(0, 1, 51) -// // x, y = torch.meshgrid(_x, _y, indexing='ij') -// // -// // fig = plt.figure(figsize=(6, 6), dpi=70); ax = fig.gca() -// // ax.set_xticks(torch.arange(0, 1, 0.05)); ax.set_yticks(torch.arange(0, 1., 0.05)) -// // ax.set_xticklabels([f'{_:.2f}'[1:] for _ in torch.arange(0, 1., 0.05)]) -// // plt.grid(); plt.rc('grid', linestyle="dotted", color=[0.85, 0.85, 0.85]) -// // -// // isolate = ['0.60']; stakes = [0.51, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 0.99] -// // colors = cm.viridis(torch.linspace(0, 1, len(stakes) + 1)) -// // for i, stake in enumerate(stakes): -// // contours = plt.contour(x, y, major_ratios[f'{stake:.2f}'], levels=[0., stake], colors=[colors[i + 1]]) -// // if f'{stake:.2f}' in isolate: -// // contours.collections[1].set_linewidth(3) -// // plt.clabel(contours, inline=True, fontsize=10) -// // -// // plt.title(f'Major emission [$stake_{{maj}}=emission_{{maj}}$ retention lines]') -// // plt.ylabel('Minor self-weight'); plt.xlabel('Major self-weight'); plt.show() -// // ``` -// // #[test] +// Map the retention graph for consensus guarantees with an single epoch on a graph with 512 nodes, +// of which the first 64 are validators, the graph is split into a major and minor set, each setting +// specific weight on itself and the complement on the other. +// +// ```import torch +// import matplotlib.pyplot as plt +// from matplotlib.pyplot import cm +// %matplotlib inline +// +// with open('finney_consensus_0.4.txt') as f: # test output saved to finney_consensus.txt +// retention_map = eval(f.read()) +// +// major_ratios = {} +// avg_weight_devs = {} +// for major_stake, major_weight, minor_weight, avg_weight_dev, major_ratio in retention_map: +// major_stake = f'{major_stake:.2f}' +// maj, min = int(round(50 * major_weight)), int(round(50 * minor_weight)) +// avg_weight_devs.setdefault(major_stake, torch.zeros((51, 51))) +// avg_weight_devs[major_stake][maj][min] = avg_weight_dev +// major_ratios.setdefault(major_stake, torch.zeros((51, 51))) +// major_ratios[major_stake][maj][min] = major_ratio +// +// _x = torch.linspace(0, 1, 51); _y = torch.linspace(0, 1, 51) +// x, y = torch.meshgrid(_x, _y, indexing='ij') +// +// fig = plt.figure(figsize=(6, 6), dpi=70); ax = fig.gca() +// ax.set_xticks(torch.arange(0, 1, 0.05)); ax.set_yticks(torch.arange(0, 1., 0.05)) +// ax.set_xticklabels([f'{_:.2f}'[1:] for _ in torch.arange(0, 1., 0.05)]) +// plt.grid(); plt.rc('grid', linestyle="dotted", color=[0.85, 0.85, 0.85]) +// +// isolate = ['0.60']; stakes = [0.51, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 0.99] +// colors = cm.viridis(torch.linspace(0, 1, len(stakes) + 1)) +// for i, stake in enumerate(stakes): +// contours = plt.contour(x, y, major_ratios[f'{stake:.2f}'], levels=[0., stake], colors=[colors[i + 1]]) +// if f'{stake:.2f}' in isolate: +// contours.collections[1].set_linewidth(3) +// plt.clabel(contours, inline=True, fontsize=10) +// +// plt.title(f'Major emission [$stake_{{maj}}=emission_{{maj}}$ retention lines]') +// plt.ylabel('Minor self-weight'); plt.xlabel('Major self-weight'); plt.show() +// ``` +// #[test] // fn _map_consensus_guarantees() { // let netuid: u16 = 1; // let network_n: u16 = 512; @@ -2811,10 +2813,10 @@ fn test_blocks_since_last_step() { // network_n as usize, // interleave as usize, // ); - +// // new_test_ext(1).execute_with(|| { // init_run_epochs(netuid, network_n, &validators, &servers, epochs, 1, true, &stake, true, &weights, true, false, 0, true); - +// // let mut major_emission: I64F64 = I64F64::from_num(0); // let mut minor_emission: I64F64 = I64F64::from_num(0); // for set in vec![major_validators, major_servers] { diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index 63bf509a45..fd959870eb 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -5384,3 +5384,377 @@ fn test_reveal_crv3_commits_removes_past_epoch_commits() { ); }); } + +#[test] +fn test_reveal_crv3_commits_multiple_valid_commits_all_processed() { + new_test_ext(100).execute_with(|| { + use ark_serialize::CanonicalSerialize; + + let netuid: u16 = 1; + let reveal_round: u64 = 1000; + + // Initialize the network + add_network(netuid, 5, 0); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_reveal_period(netuid, 1); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + SubtensorModule::set_max_registrations_per_block(netuid, 100); + SubtensorModule::set_target_registrations_per_interval(netuid, 100); + + // Register multiple neurons (e.g., 5 neurons) + let num_neurons = 5; + let mut hotkeys = Vec::new(); + let mut neuron_uids = Vec::new(); + for i in 0..num_neurons { + let hotkey: AccountId = U256::from(i + 1); + register_ok_neuron(netuid, hotkey, U256::from(i + 100), 100_000); + SubtensorModule::set_validator_permit_for_uid(netuid, i as u16, true); + hotkeys.push(hotkey); + neuron_uids.push( + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey) + .expect("Failed to get neuron UID"), + ); + } + + let version_key = SubtensorModule::get_weights_version_key(netuid); + + // Prepare payloads and commits for each hotkey + let esk = [2; 32]; + let pk_bytes = hex::decode("83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a") + .expect("Failed to decode public key bytes"); + let pub_key = ::PublicKeyGroup::deserialize_compressed(&*pk_bytes) + .expect("Failed to deserialize public key"); + + let message = { + let mut hasher = sha2::Sha256::new(); + hasher.update(reveal_round.to_be_bytes()); + hasher.finalize().to_vec() + }; + let identity = Identity::new(b"", vec![message]); + + let mut commits = Vec::new(); + for (i, hotkey) in hotkeys.iter().enumerate() { + // Each neuron will assign weights to all neurons, including itself + let values: Vec = (0..num_neurons as u16) + .map(|v| (v + i as u16 + 1) * 10) + .collect(); + let payload = WeightsTlockPayload { + values: values.clone(), + uids: neuron_uids.clone(), + version_key, + }; + let serialized_payload = payload.encode(); + + let rng = ChaCha20Rng::seed_from_u64(i as u64); + + let ct = tle::( + pub_key, + esk, + &serialized_payload, + identity.clone(), + rng, + ) + .expect("Encryption failed"); + + let mut commit_bytes = Vec::new(); + ct.serialize_compressed(&mut commit_bytes) + .expect("Failed to serialize commit"); + + // Submit the commit + assert_ok!(SubtensorModule::do_commit_crv3_weights( + RuntimeOrigin::signed(*hotkey), + netuid, + commit_bytes + .try_into() + .expect("Failed to convert commit data"), + reveal_round + )); + + // Store the expected weights for later comparison + commits.push((hotkey, payload)); + } + + // Insert the pulse + let sig_bytes = hex::decode("b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39") + .expect("Failed to decode signature bytes"); + + pallet_drand::Pulses::::insert( + reveal_round, + Pulse { + round: reveal_round, + randomness: vec![0; 32] + .try_into() + .expect("Failed to convert randomness vector"), + signature: sig_bytes + .try_into() + .expect("Failed to convert signature bytes"), + }, + ); + + // Advance epoch to trigger reveal + step_epochs(1, netuid); + + // Verify weights for all hotkeys + let weights_sparse = SubtensorModule::get_weights_sparse(netuid); + + // Set acceptable delta for `I32F32` weights + let delta = I32F32::from_num(0.0001); + + for (hotkey, expected_payload) in commits { + let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, hotkey) + .expect("Failed to get neuron UID for hotkey") as usize; + let weights = weights_sparse + .get(neuron_uid) + .cloned() + .unwrap_or_default(); + + assert!( + !weights.is_empty(), + "Weights for neuron_uid {} should be set", + neuron_uid + ); + + // Normalize expected weights + let expected_weights: Vec<(u16, I32F32)> = expected_payload + .uids + .iter() + .zip(expected_payload.values.iter()) + .map(|(&uid, &value)| (uid, I32F32::from_num(value))) + .collect(); + + let total_expected_weight: I32F32 = + expected_weights.iter().map(|&(_, w)| w).sum(); + + let normalized_expected_weights: Vec<(u16, I32F32)> = expected_weights + .iter() + .map(|&(uid, w)| (uid, w / total_expected_weight * I32F32::from_num(30))) + .collect(); + + // Normalize actual weights + let total_weight: I32F32 = weights.iter().map(|&(_, w)| w).sum(); + + let normalized_weights: Vec<(u16, I32F32)> = weights + .iter() + .map(|&(uid, w)| (uid, w / total_weight * I32F32::from_num(30))) + .collect(); + + // Compare expected and actual weights with acceptable delta + for ((uid_expected, weight_expected), (uid_actual, weight_actual)) in + normalized_expected_weights.iter().zip(normalized_weights.iter()) + { + assert_eq!( + uid_expected, uid_actual, + "UID mismatch: expected {}, got {}", + uid_expected, uid_actual + ); + + let diff = (*weight_expected - *weight_actual).abs(); + assert!( + diff <= delta, + "Weight mismatch for uid {}: expected {}, got {}, diff {}", + uid_expected, + weight_expected, + weight_actual, + diff + ); + } + } + + // Verify that commits storage is empty + let cur_epoch = SubtensorModule::get_epoch_index( + netuid, + SubtensorModule::get_current_block_as_u64(), + ); + let commits = CRV3WeightCommits::::get(netuid, cur_epoch); + assert!( + commits.is_empty(), + "Expected no commits left in storage after reveal" + ); + }); +} + +#[test] +fn test_reveal_crv3_commits_max_neurons() { + new_test_ext(100).execute_with(|| { + use ark_serialize::CanonicalSerialize; + + let netuid: u16 = 1; + let reveal_round: u64 = 1000; + + add_network(netuid, 5, 0); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_reveal_period(netuid, 1); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + SubtensorModule::set_max_registrations_per_block(netuid, 10000); + SubtensorModule::set_target_registrations_per_interval(netuid, 10000); + SubtensorModule::set_max_allowed_uids(netuid, 10024); + + let num_neurons = 1_024; + let mut hotkeys = Vec::new(); + let mut neuron_uids = Vec::new(); + for i in 0..num_neurons { + let hotkey: AccountId = U256::from(i + 1); + register_ok_neuron(netuid, hotkey, U256::from(i + 100), 100_000); + SubtensorModule::set_validator_permit_for_uid(netuid, i as u16, true); + hotkeys.push(hotkey); + neuron_uids.push( + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey) + .expect("Failed to get neuron UID"), + ); + } + + let version_key = SubtensorModule::get_weights_version_key(netuid); + + // Prepare payloads and commits for 3 hotkeys + let esk = [2; 32]; + let pk_bytes = hex::decode("83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a") + .expect("Failed to decode public key bytes"); + let pub_key = ::PublicKeyGroup::deserialize_compressed(&*pk_bytes) + .expect("Failed to deserialize public key"); + + let message = { + let mut hasher = sha2::Sha256::new(); + hasher.update(reveal_round.to_be_bytes()); + hasher.finalize().to_vec() + }; + let identity = Identity::new(b"", vec![message]); + + let hotkeys_to_commit = &hotkeys[0..3]; // First 3 hotkeys will submit weight commits + let mut commits = Vec::new(); + for (i, hotkey) in hotkeys_to_commit.iter().enumerate() { + // Each neuron will assign weights to all neurons + let values: Vec = vec![10; num_neurons]; // Assign weight of 10 to each neuron + let payload = WeightsTlockPayload { + values: values.clone(), + uids: neuron_uids.clone(), + version_key, + }; + let serialized_payload = payload.encode(); + + let rng = ChaCha20Rng::seed_from_u64(i as u64); + + let ct = tle::( + pub_key, + esk, + &serialized_payload, + identity.clone(), + rng, + ) + .expect("Encryption failed"); + + let mut commit_bytes = Vec::new(); + ct.serialize_compressed(&mut commit_bytes) + .expect("Failed to serialize commit"); + + // Submit the commit + assert_ok!(SubtensorModule::do_commit_crv3_weights( + RuntimeOrigin::signed(*hotkey), + netuid, + commit_bytes + .try_into() + .expect("Failed to convert commit data"), + reveal_round + )); + + // Store the expected weights for later comparison + commits.push((hotkey, payload)); + } + + // Insert the pulse + let sig_bytes = hex::decode("b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39") + .expect("Failed to decode signature bytes"); + + pallet_drand::Pulses::::insert( + reveal_round, + Pulse { + round: reveal_round, + randomness: vec![0; 32] + .try_into() + .expect("Failed to convert randomness vector"), + signature: sig_bytes + .try_into() + .expect("Failed to convert signature bytes"), + }, + ); + + // Advance epoch to trigger reveal + step_epochs(1, netuid); + + // Verify weights for the hotkeys that submitted commits + let weights_sparse = SubtensorModule::get_weights_sparse(netuid); + + // Set acceptable delta for `I32F32` weights + let delta = I32F32::from_num(0.0001); // Adjust delta as needed + + for (hotkey, expected_payload) in commits { + let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, hotkey) + .expect("Failed to get neuron UID for hotkey") as usize; + let weights = weights_sparse + .get(neuron_uid) + .cloned() + .unwrap_or_default(); + + assert!( + !weights.is_empty(), + "Weights for neuron_uid {} should be set", + neuron_uid + ); + + // Normalize expected weights + let expected_weights: Vec<(u16, I32F32)> = expected_payload + .uids + .iter() + .zip(expected_payload.values.iter()) + .map(|(&uid, &value)| (uid, I32F32::from_num(value))) + .collect(); + + let total_expected_weight: I32F32 = + expected_weights.iter().map(|&(_, w)| w).sum(); + + let normalized_expected_weights: Vec<(u16, I32F32)> = expected_weights + .iter() + .map(|&(uid, w)| (uid, w / total_expected_weight * I32F32::from_num(30))) + .collect(); + + // Normalize actual weights + let total_weight: I32F32 = weights.iter().map(|&(_, w)| w).sum(); + + let normalized_weights: Vec<(u16, I32F32)> = weights + .iter() + .map(|&(uid, w)| (uid, w / total_weight * I32F32::from_num(30))) + .collect(); + + // Compare expected and actual weights with acceptable delta + for ((uid_expected, weight_expected), (uid_actual, weight_actual)) in + normalized_expected_weights.iter().zip(normalized_weights.iter()) + { + assert_eq!( + uid_expected, uid_actual, + "UID mismatch: expected {}, got {}", + uid_expected, uid_actual + ); + + let diff = (*weight_expected - *weight_actual).abs(); + assert!( + diff <= delta, + "Weight mismatch for uid {}: expected {}, got {}, diff {}", + uid_expected, + weight_expected, + weight_actual, + diff + ); + } + } + + // Verify that commits storage is empty + let cur_epoch = SubtensorModule::get_epoch_index( + netuid, + SubtensorModule::get_current_block_as_u64(), + ); + let commits = CRV3WeightCommits::::get(netuid, cur_epoch); + assert!( + commits.is_empty(), + "Expected no commits left in storage after reveal" + ); + }); +} diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 57cc387864..e69506507e 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -424,9 +424,6 @@ impl Pallet { Self::deposit_event(Event::AdjustmentAlphaSet(netuid, adjustment_alpha)); } - pub fn get_validator_prune_len(netuid: u16) -> u64 { - ValidatorPruneLen::::get(netuid) - } pub fn set_validator_prune_len(netuid: u16, validator_prune_len: u64) { ValidatorPruneLen::::insert(netuid, validator_prune_len); Self::deposit_event(Event::ValidatorPruneLenSet(netuid, validator_prune_len)); diff --git a/pallets/subtensor/src/utils/rate_limiting.rs b/pallets/subtensor/src/utils/rate_limiting.rs index b00e723186..3ab6506450 100644 --- a/pallets/subtensor/src/utils/rate_limiting.rs +++ b/pallets/subtensor/src/utils/rate_limiting.rs @@ -56,15 +56,6 @@ impl Pallet { last_block == 0 || block.saturating_sub(last_block) >= limit } - /// Check if a transaction should be rate limited globally - pub fn passes_rate_limit_globally(tx_type: &TransactionType, hotkey: &T::AccountId) -> bool { - let netuid: u16 = u16::MAX; - let block: u64 = Self::get_current_block_as_u64(); - let limit: u64 = Self::get_rate_limit(tx_type, 0); - let last_block: u64 = Self::get_last_transaction_block(hotkey, netuid, tx_type); - block.saturating_sub(last_block) >= limit - } - /// Get the block number of the last transaction for a specific hotkey, network, and transaction type pub fn get_last_transaction_block( hotkey: &T::AccountId, @@ -98,10 +89,6 @@ impl Pallet { pub fn get_last_tx_block_delegate_take(key: &T::AccountId) -> u64 { LastTxBlockDelegateTake::::get(key) } - - pub fn set_last_tx_block_childkey_take(key: &T::AccountId, block: u64) { - LastTxBlockChildKeyTake::::insert(key, block) - } pub fn get_last_tx_block_childkey_take(key: &T::AccountId) -> u64 { LastTxBlockChildKeyTake::::get(key) } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a38b5071dc..777111bdcd 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 213, + spec_version: 214, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 8ed3627d56..072f632b79 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -31,7 +31,7 @@ impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { .path .segments .last() - .map_or(false, |segment| segment.ident == "construct_runtime"); + .is_some_and(|segment| segment.ident == "construct_runtime"); if is_construct_runtime { let tokens = node.mac.tokens.clone();