From 12e4f10819e6572aac92bcb09f8cb752cb936eba Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 20 Mar 2025 12:16:07 -0400 Subject: [PATCH 01/45] Add move_stake and transfer_stake to staking precompile v2 --- precompiles/src/solidity/stakingV2.abi | 66 ++++++++++++++++++++++++++ precompiles/src/solidity/stakingV2.sol | 56 ++++++++++++++++++++++ precompiles/src/staking.rs | 52 ++++++++++++++++++++ 3 files changed, 174 insertions(+) diff --git a/precompiles/src/solidity/stakingV2.abi b/precompiles/src/solidity/stakingV2.abi index 21dd2761e4..b43c8cbe62 100644 --- a/precompiles/src/solidity/stakingV2.abi +++ b/precompiles/src/solidity/stakingV2.abi @@ -137,5 +137,71 @@ "outputs": [], "stateMutability": "nonpayable", "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "origin_hotkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "destination_hotkey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "origin_netuid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "destination_netuid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "moveStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "destination_coldkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "origin_netuid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "destination_netuid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" } ] diff --git a/precompiles/src/solidity/stakingV2.sol b/precompiles/src/solidity/stakingV2.sol index 67ac0cb129..87be899799 100644 --- a/precompiles/src/solidity/stakingV2.sol +++ b/precompiles/src/solidity/stakingV2.sol @@ -46,6 +46,62 @@ interface IStaking { uint256 netuid ) external; + /** + * @dev Moves a subtensor stake `amount` associated with the `hotkey` to a different hotkey + * `destination_hotkey`. + * + * This function allows external accounts and contracts to move staked TAO from one hotkey to another, + * which effectively calls `move_stake` on the subtensor pallet with specified origin and destination + * hotkeys as parameters being the hashed address mappings of H160 sender address to Substrate ss58 + * address as implemented in Frontier HashedAddressMapping: + * https://github.com/polkadot-evm/frontier/blob/2e219e17a526125da003e64ef22ec037917083fa/frame/evm/src/lib.rs#L739 + * + * @param origin_hotkey The origin hotkey public key (32 bytes). + * @param destination_hotkey The destination hotkey public key (32 bytes). + * @param origin_netuid The subnet to move stake from (uint256). + * @param destination_netuid The subnet to move stake to (uint256). + * @param amount The amount to move in rao. + * + * Requirements: + * - `origin_hotkey` and `destination_hotkey` must be valid hotkeys registered on the network, ensuring + * that the stake is correctly attributed. + */ + function moveStake( + bytes32 origin_hotkey, + bytes32 destination_hotkey, + uint256 origin_netuid, + uint256 destination_netuid, + uint256 amount + ) external; + + /** + * @dev Transfer a subtensor stake `amount` associated with the transaction signer to a different coldkey + * `destination_coldkey`. + * + * This function allows external accounts and contracts to transfer staked TAO to another coldkey, + * which effectively calls `transfer_stake` on the subtensor pallet with specified destination + * coldkey as a parameter being the hashed address mapping of H160 sender address to Substrate ss58 + * address as implemented in Frontier HashedAddressMapping: + * https://github.com/polkadot-evm/frontier/blob/2e219e17a526125da003e64ef22ec037917083fa/frame/evm/src/lib.rs#L739 + * + * @param destination_coldkey The destination coldkey public key (32 bytes). + * @param hotkey The hotkey public key (32 bytes). + * @param origin_netuid The subnet to move stake from (uint256). + * @param destination_netuid The subnet to move stake to (uint256). + * @param amount The amount to move in rao. + * + * Requirements: + * - `origin_hotkey` and `destination_hotkey` must be valid hotkeys registered on the network, ensuring + * that the stake is correctly attributed. + */ + function transferStake( + bytes32 destination_coldkey, + bytes32 hotkey, + uint256 origin_netuid, + uint256 destination_netuid, + uint256 amount + ) external; + /** * @dev Returns the amount of RAO staked by the coldkey. * diff --git a/precompiles/src/staking.rs b/precompiles/src/staking.rs index 192e55f57c..c6e34d5309 100644 --- a/precompiles/src/staking.rs +++ b/precompiles/src/staking.rs @@ -119,6 +119,58 @@ where handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) } + #[precompile::public("moveStake(bytes32,bytes32,uint256,uint256,uint256)")] + fn move_stake( + handle: &mut impl PrecompileHandle, + origin_hotkey: H256, + destination_hotkey: H256, + origin_netuid: U256, + destination_netuid: U256, + amount_alpha: U256, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let origin_hotkey = R::AccountId::from(origin_hotkey.0); + let destination_hotkey = R::AccountId::from(destination_hotkey.0); + let origin_netuid = try_u16_from_u256(origin_netuid)?; + let destination_netuid = try_u16_from_u256(destination_netuid)?; + let alpha_amount = amount_alpha.unique_saturated_into(); + let call = pallet_subtensor::Call::::move_stake { + origin_hotkey, + destination_hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("transferStake(bytes32,bytes32,uint256,uint256,uint256)")] + fn transfer_stake( + handle: &mut impl PrecompileHandle, + destination_coldkey: H256, + hotkey: H256, + origin_netuid: U256, + destination_netuid: U256, + amount_alpha: U256, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let destination_coldkey = R::AccountId::from(destination_coldkey.0); + let hotkey = R::AccountId::from(hotkey.0); + let origin_netuid = try_u16_from_u256(origin_netuid)?; + let destination_netuid = try_u16_from_u256(destination_netuid)?; + let alpha_amount = amount_alpha.unique_saturated_into(); + let call = pallet_subtensor::Call::::transfer_stake { + destination_coldkey, + hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + #[precompile::public("getTotalColdkeyStake(bytes32)")] #[precompile::view] fn get_total_coldkey_stake( From 606df3d8e866fbb9ee02f96e6dcb3f8196df197c Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 25 Mar 2025 20:12:44 +0800 Subject: [PATCH 02/45] init solution --- pallets/subtensor/src/rpc_info/metagraph.rs | 817 ++++++++++++++++++++ 1 file changed, 817 insertions(+) diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index 3d5ee7537b..712ed5b28a 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -107,6 +107,261 @@ pub struct Metagraph { alpha_dividends_per_hotkey: Vec<(AccountId, Compact)>, // List of dividend payout in alpha via subnet. } +#[freeze_struct("d745be982b4d29ea")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct SelectiveMetagraph { + // Subnet index + netuid: Compact, + + // Name and symbol + name: Option>>, // name + symbol: Option>>, // token symbol + identity: Option>, // identity information. + network_registered_at: Option>, // block at registration + + // Keys for owner. + owner_hotkey: Option, // hotkey + owner_coldkey: Option, // coldkey. + + // Tempo terms. + block: Option>, // block at call. + tempo: Option>, // epoch tempo + last_step: Option>, // last epoch + blocks_since_last_step: Option>, // blocks since last epoch. + + // Subnet emission terms + subnet_emission: Option>, // subnet emission via stao + alpha_in: Option>, // amount of alpha in reserve + alpha_out: Option>, // amount of alpha outstanding + tao_in: Option>, // amount of tao injected per block + alpha_out_emission: Option>, // amount injected in alpha reserves per block + alpha_in_emission: Option>, // amount injected outstanding per block + tao_in_emission: Option>, // amount of tao injected per block + pending_alpha_emission: Option>, // pending alpha to be distributed + pending_root_emission: Option>, // panding tao for root divs to be distributed + subnet_volume: Option>, // volume of the subnet in TAO + moving_price: Option, // subnet moving price. + + // Hparams for epoch + rho: Option>, // subnet rho param + kappa: Option>, // subnet kappa param + + // Validator params + min_allowed_weights: Option>, // min allowed weights per val + max_weights_limit: Option>, // max allowed weights per val + weights_version: Option>, // allowed weights version + weights_rate_limit: Option>, // rate limit on weights. + activity_cutoff: Option>, // validator weights cut off period in blocks + max_validators: Option>, // max allowed validators. + + // Registration + num_uids: Option>, + max_uids: Option>, + burn: Option>, // current burn cost.. + difficulty: Option>, // current difficulty. + registration_allowed: Option, // allows registrations. + pow_registration_allowed: Option, // pow registration enabled. + immunity_period: Option>, // subnet miner immunity period + min_difficulty: Option>, // min pow difficulty + max_difficulty: Option>, // max pow difficulty + min_burn: Option>, // min tao burn + max_burn: Option>, // max tao burn + adjustment_alpha: Option>, // adjustment speed for registration params. + adjustment_interval: Option>, // pow and burn adjustment interval + target_regs_per_interval: Option>, // target registrations per interval + max_regs_per_block: Option>, // max registrations per block. + serving_rate_limit: Option>, // axon serving rate limit + + // CR + commit_reveal_weights_enabled: Option, // Is CR enabled. + commit_reveal_period: Option>, // Commit reveal interval + + // Bonds + liquid_alpha_enabled: Option, // Bonds liquid enabled. + alpha_high: Option>, // Alpha param high + alpha_low: Option>, // Alpha param low + bonds_moving_avg: Option>, // Bonds moving avg + + // Metagraph info. + hotkeys: Option>, // hotkey per UID + coldkeys: Option>, // coldkey per UID + identities: Option>>, // coldkeys identities + axons: Option>, // UID axons. + active: Option>, // Avtive per UID + validator_permit: Option>, // Val permit per UID + pruning_score: Option>>, // Pruning per UID + last_update: Option>>, // Last update per UID + emission: Option>>, // Emission per UID + dividends: Option>>, // Dividends per UID + incentives: Option>>, // Mining incentives per UID + consensus: Option>>, // Consensus per UID + trust: Option>>, // Trust per UID + rank: Option>>, // Rank per UID + block_at_registration: Option>>, // Reg block per UID + alpha_stake: Option>>, // Alpha staked per UID + tao_stake: Option>>, // TAO staked per UID + total_stake: Option>>, // Total stake per UID + + // Dividend break down. + tao_dividends_per_hotkey: Option)>>, // List of dividend payouts in tao via root. + alpha_dividends_per_hotkey: Option)>>, // List of dividend payout in alpha via subnet. +} + +impl Default for SelectiveMetagraph +where + AccountId: TypeInfo + Encode + Decode, +{ + fn default() -> Self { + Self { + netuid: 0.into(), + name: None, + symbol: None, + identity: None, + network_registered_at: None, + owner_hotkey: None, + owner_coldkey: None, + block: None, + tempo: None, + last_step: None, + blocks_since_last_step: None, + subnet_emission: None, + alpha_in: None, + alpha_out: None, + tao_in: None, + alpha_out_emission: None, + alpha_in_emission: None, + tao_in_emission: None, + pending_alpha_emission: None, + pending_root_emission: None, + subnet_volume: None, + moving_price: None, + rho: None, + kappa: None, + min_allowed_weights: None, + max_weights_limit: None, + weights_version: None, + weights_rate_limit: None, + activity_cutoff: None, + max_validators: None, + num_uids: None, + max_uids: None, + burn: None, + difficulty: None, + registration_allowed: None, + pow_registration_allowed: None, + immunity_period: None, + min_difficulty: None, + max_difficulty: None, + min_burn: None, + max_burn: None, + adjustment_alpha: None, + adjustment_interval: None, + target_regs_per_interval: None, + max_regs_per_block: None, + serving_rate_limit: None, + commit_reveal_weights_enabled: None, + commit_reveal_period: None, + liquid_alpha_enabled: None, + alpha_high: None, + alpha_low: None, + bonds_moving_avg: None, + hotkeys: None, + coldkeys: None, + identities: None, + axons: None, + active: None, + validator_permit: None, + pruning_score: None, + last_update: None, + emission: None, + dividends: None, + incentives: None, + consensus: None, + trust: None, + rank: None, + block_at_registration: None, + alpha_stake: None, + tao_stake: None, + total_stake: None, + tao_dividends_per_hotkey: None, + alpha_dividends_per_hotkey: None, + } + } +} + +pub enum SelectiveMetagraphIndex { + Name, + Symbol, + Identity, + NetworkRegisteredAt, + OwnerHotkey, + OwnerColdkey, + Block, + Tempo, + LastStep, + BlocksSinceLastStep, + SubnetEmission, + AlphaIn, + AlphaOut, + TaoIn, + AlphaOutEmission, + AlphaInEmission, + TaoInEmission, + PendingAlphaEmission, + PendingRootEmission, + SubnetVolume, + MovingPrice, + Rho, + Kappa, + MinAllowedWeights, + MaxWeightsLimit, + WeightsVersion, + WeightsRateLimit, + ActivityCutoff, + MaxValidators, + NumUids, + MaxUids, + Burn, + Difficulty, + RegistrationAllowed, + PowRegistrationAllowed, + ImmunityPeriod, + MinDifficulty, + MaxDifficulty, + MinBurn, + MaxBurn, + AdjustmentAlpha, + AdjustmentInterval, + TargetRegsPerInterval, + MaxRegsPerBlock, + ServingRateLimit, + CommitRevealWeightsEnabled, + CommitRevealPeriod, + LiquidAlphaEnabled, + AlphaHigh, + AlphaLow, + BondsMovingAvg, + Hotkeys, + Coldkeys, + Identities, + Axons, + Active, + ValidatorPermit, + PruningScore, + LastUpdate, + Emission, + Dividends, + Incentives, + Consensus, + Trust, + Rank, + BlockAtRegistration, + AlphaStake, + TaoStake, + TotalStake, + TaoDividendsPerHotkey, + AlphaDividendsPerHotkey, +} impl Pallet { pub fn get_metagraph(netuid: u16) -> Option> { if !Self::if_subnet_exist(netuid) { @@ -291,4 +546,566 @@ impl Pallet { } metagraphs } + + pub fn get_selective_metagraph( + netuid: u16, + metagraph_name: &str, + ) -> Option> { + if !Self::if_subnet_exist(netuid) { + return None; + } + + match metagraph_name { + // Name and symbol + "name" => Some(SelectiveMetagraph { + netuid: netuid.into(), + name: Some( + Self::get_name_for_subnet(netuid) + .into_iter() + .map(Compact) + .collect(), + ), + ..Default::default() + }), + "symbol" => Some(SelectiveMetagraph { + netuid: netuid.into(), + symbol: Some( + Self::get_symbol_for_subnet(netuid) + .into_iter() + .map(Compact) + .collect(), + ), + ..Default::default() + }), + "identity" => Some(SelectiveMetagraph { + netuid: netuid.into(), + identity: Some(SubnetIdentitiesV2::::get(netuid)), + ..Default::default() + }), + "network_registered_at" => Some(SelectiveMetagraph { + netuid: netuid.into(), + network_registered_at: Some(NetworkRegisteredAt::::get(netuid).into()), + ..Default::default() + }), + + // Keys for owner. + "owner_hotkey" => Some(SelectiveMetagraph { + netuid: netuid.into(), + owner_hotkey: Some(SubnetOwnerHotkey::::get(netuid)), + ..Default::default() + }), + "owner_coldkey" => Some(SelectiveMetagraph { + netuid: netuid.into(), + owner_coldkey: Some(SubnetOwner::::get(netuid)), + ..Default::default() + }), + + // Tempo terms. + "block" => Some(SelectiveMetagraph { + netuid: netuid.into(), + block: Some(Pallet::::get_current_block_as_u64().into()), + ..Default::default() + }), + "tempo" => Some(SelectiveMetagraph { + netuid: netuid.into(), + tempo: Some(Self::get_tempo(netuid).into()), + ..Default::default() + }), + "last_step" => Some(SelectiveMetagraph { + netuid: netuid.into(), + last_step: Some(LastMechansimStepBlock::::get(netuid).into()), + ..Default::default() + }), + "blocks_since_last_step" => { + let current_block: u64 = Pallet::::get_current_block_as_u64(); + let last_step = LastMechansimStepBlock::::get(netuid); + let blocks_since_last_step: u64 = current_block.saturating_sub(last_step); + Some(SelectiveMetagraph { + netuid: netuid.into(), + blocks_since_last_step: Some(blocks_since_last_step.into()), + ..Default::default() + }) + } + + // Subnet emission terms + "subnet_emission" => Some(SelectiveMetagraph { + netuid: netuid.into(), + subnet_emission: Some(0.into()), + ..Default::default() + }), + "alpha_in" => Some(SelectiveMetagraph { + netuid: netuid.into(), + alpha_in: Some(SubnetAlphaIn::::get(netuid).into()), + ..Default::default() + }), + "alpha_out" => Some(SelectiveMetagraph { + netuid: netuid.into(), + alpha_out: Some(SubnetAlphaOut::::get(netuid).into()), + ..Default::default() + }), + "tao_in" => Some(SelectiveMetagraph { + netuid: netuid.into(), + tao_in: Some(SubnetTAO::::get(netuid).into()), + ..Default::default() + }), + "alpha_out_emission" => Some(SelectiveMetagraph { + netuid: netuid.into(), + alpha_out_emission: Some(SubnetAlphaOutEmission::::get(netuid).into()), + ..Default::default() + }), + "alpha_in_emission" => Some(SelectiveMetagraph { + netuid: netuid.into(), + alpha_in_emission: Some(SubnetAlphaInEmission::::get(netuid).into()), + ..Default::default() + }), + "tao_in_emission" => Some(SelectiveMetagraph { + netuid: netuid.into(), + tao_in_emission: Some(SubnetTaoInEmission::::get(netuid).into()), + ..Default::default() + }), + "pending_alpha_emission" => Some(SelectiveMetagraph { + netuid: netuid.into(), + pending_alpha_emission: Some(PendingEmission::::get(netuid).into()), + ..Default::default() + }), + "pending_root_emission" => Some(SelectiveMetagraph { + netuid: netuid.into(), + pending_root_emission: Some(PendingRootDivs::::get(netuid).into()), + ..Default::default() + }), + "subnet_volume" => Some(SelectiveMetagraph { + netuid: netuid.into(), + subnet_volume: Some(SubnetVolume::::get(netuid).into()), + ..Default::default() + }), + "moving_price" => Some(SelectiveMetagraph { + netuid: netuid.into(), + moving_price: Some(SubnetMovingPrice::::get(netuid)), + ..Default::default() + }), + + // Hparams for epoch + "rho" => Some(SelectiveMetagraph { + netuid: netuid.into(), + rho: Some(Self::get_rho(netuid).into()), + ..Default::default() + }), + "kappa" => Some(SelectiveMetagraph { + netuid: netuid.into(), + kappa: Some(Self::get_kappa(netuid).into()), + ..Default::default() + }), + + // Validator params + "min_allowed_weights" => Some(SelectiveMetagraph { + netuid: netuid.into(), + min_allowed_weights: Some(Self::get_min_allowed_weights(netuid).into()), + ..Default::default() + }), + "max_weights_limit" => Some(SelectiveMetagraph { + netuid: netuid.into(), + max_weights_limit: Some(Self::get_max_weight_limit(netuid).into()), + ..Default::default() + }), + "weights_version" => Some(SelectiveMetagraph { + netuid: netuid.into(), + weights_version: Some(Self::get_weights_version_key(netuid).into()), + ..Default::default() + }), + "weights_rate_limit" => Some(SelectiveMetagraph { + netuid: netuid.into(), + weights_rate_limit: Some(Self::get_weights_set_rate_limit(netuid).into()), + ..Default::default() + }), + "activity_cutoff" => Some(SelectiveMetagraph { + netuid: netuid.into(), + activity_cutoff: Some(Self::get_activity_cutoff(netuid).into()), + ..Default::default() + }), + "max_validators" => Some(SelectiveMetagraph { + netuid: netuid.into(), + max_validators: Some(Self::get_max_allowed_validators(netuid).into()), + ..Default::default() + }), + + // Registration + "num_uids" => Some(SelectiveMetagraph { + netuid: netuid.into(), + num_uids: Some(Self::get_subnetwork_n(netuid).into()), + ..Default::default() + }), + "max_uids" => Some(SelectiveMetagraph { + netuid: netuid.into(), + max_uids: Some(Self::get_max_allowed_uids(netuid).into()), + ..Default::default() + }), + "registration_allowed" => Some(SelectiveMetagraph { + netuid: netuid.into(), + registration_allowed: Some(Self::get_network_registration_allowed(netuid).into()), + ..Default::default() + }), + "pow_registration_allowed" => Some(SelectiveMetagraph { + netuid: netuid.into(), + pow_registration_allowed: Some( + Self::get_network_pow_registration_allowed(netuid).into(), + ), + ..Default::default() + }), + "difficulty" => Some(SelectiveMetagraph { + netuid: netuid.into(), + difficulty: Some(Self::get_difficulty_as_u64(netuid).into()), + ..Default::default() + }), + + "burn" => Some(SelectiveMetagraph { + netuid: netuid.into(), + burn: Some(Self::get_burn_as_u64(netuid).into()), + ..Default::default() + }), + + "immunity_period" => Some(SelectiveMetagraph { + netuid: netuid.into(), + immunity_period: Some(Self::get_immunity_period(netuid).into()), + ..Default::default() + }), + "min_difficulty" => Some(SelectiveMetagraph { + netuid: netuid.into(), + min_difficulty: Some(Self::get_min_difficulty(netuid).into()), + ..Default::default() + }), + "max_difficulty" => Some(SelectiveMetagraph { + netuid: netuid.into(), + max_difficulty: Some(Self::get_max_difficulty(netuid).into()), + ..Default::default() + }), + "min_burn" => Some(SelectiveMetagraph { + netuid: netuid.into(), + min_burn: Some(Self::get_min_burn_as_u64(netuid).into()), + ..Default::default() + }), + "max_burn" => Some(SelectiveMetagraph { + netuid: netuid.into(), + max_burn: Some(Self::get_max_burn_as_u64(netuid).into()), + ..Default::default() + }), + "adjustment_alpha" => Some(SelectiveMetagraph { + netuid: netuid.into(), + adjustment_alpha: Some(Self::get_adjustment_alpha(netuid).into()), + ..Default::default() + }), + "adjustment_interval" => Some(SelectiveMetagraph { + netuid: netuid.into(), + adjustment_interval: Some(Self::get_adjustment_interval(netuid).into()), + ..Default::default() + }), + "target_regs_per_interval" => Some(SelectiveMetagraph { + netuid: netuid.into(), + target_regs_per_interval: Some( + Self::get_target_registrations_per_interval(netuid).into(), + ), + ..Default::default() + }), + "max_regs_per_block" => Some(SelectiveMetagraph { + netuid: netuid.into(), + max_regs_per_block: Some(Self::get_max_registrations_per_block(netuid).into()), + ..Default::default() + }), + "serving_rate_limit" => Some(SelectiveMetagraph { + netuid: netuid.into(), + serving_rate_limit: Some(Self::get_serving_rate_limit(netuid).into()), + ..Default::default() + }), + + // CR + "commit_reveal_weights_enabled" => Some(SelectiveMetagraph { + netuid: netuid.into(), + commit_reveal_weights_enabled: Some(Self::get_commit_reveal_weights_enabled( + netuid, + )), + ..Default::default() + }), + "commit_reveal_period" => Some(SelectiveMetagraph { + netuid: netuid.into(), + commit_reveal_period: Some(Self::get_reveal_period(netuid).into()), + ..Default::default() + }), + + // Bonds + "liquid_alpha_enabled" => Some(SelectiveMetagraph { + netuid: netuid.into(), + liquid_alpha_enabled: Some(Self::get_liquid_alpha_enabled(netuid)), + ..Default::default() + }), + "alpha_high" => Some(SelectiveMetagraph { + netuid: netuid.into(), + alpha_high: Some(Self::get_alpha_values(netuid).1.into()), + ..Default::default() + }), + "alpha_low" => Some(SelectiveMetagraph { + netuid: netuid.into(), + alpha_low: Some(Self::get_alpha_values(netuid).0.into()), + ..Default::default() + }), + "bonds_moving_avg" => Some(SelectiveMetagraph { + netuid: netuid.into(), + bonds_moving_avg: Some(Self::get_bonds_moving_average(netuid).into()), + ..Default::default() + }), + + // Metagraph info. + "hotkeys" => { + let n: u16 = Self::get_subnetwork_n(netuid); + let mut hotkeys: Vec = vec![]; + for uid in 0..n { + let hotkey = Keys::::get(netuid, uid); + hotkeys.push(hotkey.clone()); + } + + Some(SelectiveMetagraph { + netuid: netuid.into(), + hotkeys: Some(hotkeys), + ..Default::default() + }) + } + "coldkeys" => { + let n: u16 = Self::get_subnetwork_n(netuid); + let mut coldkeys: Vec = vec![]; + for uid in 0..n { + let hotkey = Keys::::get(netuid, uid); + let coldkey = Owner::::get(hotkey.clone()); + coldkeys.push(coldkey.clone()); + } + Some(SelectiveMetagraph { + netuid: netuid.into(), + coldkeys: Some(coldkeys), + ..Default::default() + }) + } + "identities" => { + let n: u16 = Self::get_subnetwork_n(netuid); + let mut identities: Vec> = vec![]; + for uid in 0..n { + let hotkey = Keys::::get(netuid, uid); + let coldkey = Owner::::get(hotkey.clone()); + identities.push(IdentitiesV2::::get(coldkey.clone())); + } + Some(SelectiveMetagraph { + netuid: netuid.into(), + identities: Some(identities), + ..Default::default() + }) + } + "axons" => { + let n: u16 = Self::get_subnetwork_n(netuid); + let mut axons: Vec = vec![]; + for uid in 0..n { + let hotkey = Keys::::get(netuid, uid); + axons.push(Self::get_axon_info(netuid, &hotkey)); + } + Some(SelectiveMetagraph { + netuid: netuid.into(), + axons: Some(axons), + ..Default::default() + }) + } + "active" => Some(SelectiveMetagraph { + netuid: netuid.into(), + active: Some(Active::::get(netuid)), + ..Default::default() + }), + "validator_permit" => Some(SelectiveMetagraph { + netuid: netuid.into(), + active: Some(ValidatorPermit::::get(netuid)), + ..Default::default() + }), + + "pruning_score" => Some(SelectiveMetagraph { + netuid: netuid.into(), + pruning_score: Some( + PruningScores::::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), + ), + ..Default::default() + }), + + "last_update" => Some(SelectiveMetagraph { + netuid: netuid.into(), + last_update: Some( + LastUpdate::::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), + ), + ..Default::default() + }), + + "emission" => Some(SelectiveMetagraph { + netuid: netuid.into(), + emission: Some( + Emission::::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), + ), + ..Default::default() + }), + + "dividends" => Some(SelectiveMetagraph { + netuid: netuid.into(), + dividends: Some( + Dividends::::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), + ), + ..Default::default() + }), + + "incentives" => Some(SelectiveMetagraph { + netuid: netuid.into(), + incentives: Some( + Incentive::::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), + ), + ..Default::default() + }), + + "consensus" => Some(SelectiveMetagraph { + netuid: netuid.into(), + consensus: Some( + Consensus::::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), + ), + ..Default::default() + }), + + "trust" => Some(SelectiveMetagraph { + netuid: netuid.into(), + trust: Some( + Trust::::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), + ), + ..Default::default() + }), + "rank" => Some(SelectiveMetagraph { + netuid: netuid.into(), + rank: Some( + Rank::::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), + ), + ..Default::default() + }), + "block_at_registration" => { + let n: u16 = Self::get_subnetwork_n(netuid); + let mut block_at_registration: Vec> = vec![]; + for uid in 0..n { + block_at_registration.push(BlockAtRegistration::::get(netuid, uid).into()); + } + Some(SelectiveMetagraph { + netuid: netuid.into(), + block_at_registration: Some(block_at_registration), + ..Default::default() + }) + } + "alpha_stake" => { + let (_, alpha_stake_fl, _): (Vec, Vec, Vec) = + Self::get_stake_weights_for_network(netuid); + Some(SelectiveMetagraph { + netuid: netuid.into(), + alpha_stake: Some( + alpha_stake_fl + .iter() + .map(|xi| Compact::from(fixed64_to_u64(*xi))) + .collect::>>(), + ), + ..Default::default() + }) + } + "tao_stake" => { + let (_, _, tao_stake_fl): (Vec, Vec, Vec) = + Self::get_stake_weights_for_network(netuid); + Some(SelectiveMetagraph { + netuid: netuid.into(), + tao_stake: Some( + tao_stake_fl + .iter() + .map(|xi| Compact::from(fixed64_to_u64(*xi))) + .collect::>>(), + ), + ..Default::default() + }) + } + "total_stake" => { + let (total_stake_fl, _, _): (Vec, Vec, Vec) = + Self::get_stake_weights_for_network(netuid); + Some(SelectiveMetagraph { + netuid: netuid.into(), + total_stake: Some( + total_stake_fl + .iter() + .map(|xi| Compact::from(fixed64_to_u64(*xi))) + .collect::>>(), + ), + ..Default::default() + }) + } + + // Dividend break down. + "tao_dividends_per_hotkey" => { + let n: u16 = Self::get_subnetwork_n(netuid); + let mut hotkeys: Vec = vec![]; + for uid in 0..n { + let hotkey = Keys::::get(netuid, uid); + hotkeys.push(hotkey.clone()); + } + let mut tao_dividends_per_hotkey: Vec<(T::AccountId, Compact)> = vec![]; + for hotkey in hotkeys.clone() { + let tao_divs = TaoDividendsPerSubnet::::get(netuid, hotkey.clone()); + tao_dividends_per_hotkey.push((hotkey.clone(), tao_divs.into())); + } + Some(SelectiveMetagraph { + netuid: netuid.into(), + tao_dividends_per_hotkey: Some(tao_dividends_per_hotkey), + ..Default::default() + }) + } + "alpha_dividends_per_hotkey" => { + let mut alpha_dividends_per_hotkey: Vec<(T::AccountId, Compact)> = vec![]; + let n: u16 = Self::get_subnetwork_n(netuid); + let mut hotkeys: Vec = vec![]; + + for uid in 0..n { + let hotkey = Keys::::get(netuid, uid); + hotkeys.push(hotkey.clone()); + } + + for hotkey in hotkeys.clone() { + let alpha_divs = AlphaDividendsPerSubnet::::get(netuid, hotkey.clone()); + alpha_dividends_per_hotkey.push((hotkey.clone(), alpha_divs.into())); + } + Some(SelectiveMetagraph { + netuid: netuid.into(), + alpha_dividends_per_hotkey: Some(alpha_dividends_per_hotkey), + ..Default::default() + }) + } + _ => { + Some(SelectiveMetagraph { + // Subnet index + netuid: netuid.into(), + ..Default::default() + }) + } + } + } } From b53b7490d538e1252c0a10ce31db0a54df976ec8 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 25 Mar 2025 20:31:50 +0800 Subject: [PATCH 03/45] commit Cargo.lock --- pallets/subtensor/src/rpc_info/metagraph.rs | 227 +++++++++++++------- 1 file changed, 153 insertions(+), 74 deletions(-) diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index 712ed5b28a..d1c47d8e3f 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -362,6 +362,85 @@ pub enum SelectiveMetagraphIndex { TaoDividendsPerHotkey, AlphaDividendsPerHotkey, } + +impl SelectiveMetagraphIndex { + fn from_index(index: usize) -> Option { + match index { + 0 => Some(SelectiveMetagraphIndex::Name), + 1 => Some(SelectiveMetagraphIndex::Symbol), + 2 => Some(SelectiveMetagraphIndex::Identity), + 3 => Some(SelectiveMetagraphIndex::NetworkRegisteredAt), + 4 => Some(SelectiveMetagraphIndex::OwnerHotkey), + 5 => Some(SelectiveMetagraphIndex::OwnerColdkey), + 6 => Some(SelectiveMetagraphIndex::Block), + 7 => Some(SelectiveMetagraphIndex::Tempo), + 8 => Some(SelectiveMetagraphIndex::LastStep), + 9 => Some(SelectiveMetagraphIndex::BlocksSinceLastStep), + 10 => Some(SelectiveMetagraphIndex::SubnetEmission), + 11 => Some(SelectiveMetagraphIndex::AlphaIn), + 12 => Some(SelectiveMetagraphIndex::AlphaOut), + 13 => Some(SelectiveMetagraphIndex::TaoIn), + 14 => Some(SelectiveMetagraphIndex::AlphaOutEmission), + 15 => Some(SelectiveMetagraphIndex::AlphaInEmission), + 16 => Some(SelectiveMetagraphIndex::TaoInEmission), + 17 => Some(SelectiveMetagraphIndex::PendingAlphaEmission), + 18 => Some(SelectiveMetagraphIndex::PendingRootEmission), + 19 => Some(SelectiveMetagraphIndex::SubnetVolume), + 20 => Some(SelectiveMetagraphIndex::MovingPrice), + 21 => Some(SelectiveMetagraphIndex::Rho), + 22 => Some(SelectiveMetagraphIndex::Kappa), + 23 => Some(SelectiveMetagraphIndex::MinAllowedWeights), + 24 => Some(SelectiveMetagraphIndex::MaxWeightsLimit), + 25 => Some(SelectiveMetagraphIndex::WeightsVersion), + 26 => Some(SelectiveMetagraphIndex::WeightsRateLimit), + 27 => Some(SelectiveMetagraphIndex::ActivityCutoff), + 28 => Some(SelectiveMetagraphIndex::MaxValidators), + 29 => Some(SelectiveMetagraphIndex::NumUids), + 30 => Some(SelectiveMetagraphIndex::MaxUids), + 31 => Some(SelectiveMetagraphIndex::Burn), + 32 => Some(SelectiveMetagraphIndex::Difficulty), + 33 => Some(SelectiveMetagraphIndex::RegistrationAllowed), + 34 => Some(SelectiveMetagraphIndex::PowRegistrationAllowed), + 35 => Some(SelectiveMetagraphIndex::ImmunityPeriod), + 36 => Some(SelectiveMetagraphIndex::MinDifficulty), + 37 => Some(SelectiveMetagraphIndex::MaxDifficulty), + 38 => Some(SelectiveMetagraphIndex::MinBurn), + 39 => Some(SelectiveMetagraphIndex::MaxBurn), + 40 => Some(SelectiveMetagraphIndex::AdjustmentAlpha), + 41 => Some(SelectiveMetagraphIndex::AdjustmentInterval), + 42 => Some(SelectiveMetagraphIndex::TargetRegsPerInterval), + 43 => Some(SelectiveMetagraphIndex::MaxRegsPerBlock), + 44 => Some(SelectiveMetagraphIndex::ServingRateLimit), + 45 => Some(SelectiveMetagraphIndex::CommitRevealWeightsEnabled), + 46 => Some(SelectiveMetagraphIndex::CommitRevealPeriod), + 47 => Some(SelectiveMetagraphIndex::LiquidAlphaEnabled), + 48 => Some(SelectiveMetagraphIndex::AlphaHigh), + 49 => Some(SelectiveMetagraphIndex::AlphaLow), + 50 => Some(SelectiveMetagraphIndex::BondsMovingAvg), + 51 => Some(SelectiveMetagraphIndex::Hotkeys), + 52 => Some(SelectiveMetagraphIndex::Coldkeys), + 53 => Some(SelectiveMetagraphIndex::Identities), + 54 => Some(SelectiveMetagraphIndex::Axons), + 55 => Some(SelectiveMetagraphIndex::Active), + 56 => Some(SelectiveMetagraphIndex::ValidatorPermit), + 57 => Some(SelectiveMetagraphIndex::PruningScore), + 58 => Some(SelectiveMetagraphIndex::LastUpdate), + 59 => Some(SelectiveMetagraphIndex::Emission), + 60 => Some(SelectiveMetagraphIndex::Dividends), + 61 => Some(SelectiveMetagraphIndex::Incentives), + 62 => Some(SelectiveMetagraphIndex::Consensus), + 63 => Some(SelectiveMetagraphIndex::Trust), + 64 => Some(SelectiveMetagraphIndex::Rank), + 65 => Some(SelectiveMetagraphIndex::BlockAtRegistration), + 66 => Some(SelectiveMetagraphIndex::AlphaStake), + 67 => Some(SelectiveMetagraphIndex::TaoStake), + 68 => Some(SelectiveMetagraphIndex::TotalStake), + 69 => Some(SelectiveMetagraphIndex::TaoDividendsPerHotkey), + 70 => Some(SelectiveMetagraphIndex::AlphaDividendsPerHotkey), + _ => None, + } + } +} impl Pallet { pub fn get_metagraph(netuid: u16) -> Option> { if !Self::if_subnet_exist(netuid) { @@ -549,15 +628,15 @@ impl Pallet { pub fn get_selective_metagraph( netuid: u16, - metagraph_name: &str, + metagraph_index: u16, ) -> Option> { if !Self::if_subnet_exist(netuid) { return None; } - match metagraph_name { + match SelectiveMetagraphIndex::from_index(metagraph_index as usize) { // Name and symbol - "name" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Name) => Some(SelectiveMetagraph { netuid: netuid.into(), name: Some( Self::get_name_for_subnet(netuid) @@ -567,7 +646,7 @@ impl Pallet { ), ..Default::default() }), - "symbol" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Symbol) => Some(SelectiveMetagraph { netuid: netuid.into(), symbol: Some( Self::get_symbol_for_subnet(netuid) @@ -577,46 +656,46 @@ impl Pallet { ), ..Default::default() }), - "identity" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Identity) => Some(SelectiveMetagraph { netuid: netuid.into(), identity: Some(SubnetIdentitiesV2::::get(netuid)), ..Default::default() }), - "network_registered_at" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::NetworkRegisteredAt) => Some(SelectiveMetagraph { netuid: netuid.into(), network_registered_at: Some(NetworkRegisteredAt::::get(netuid).into()), ..Default::default() }), // Keys for owner. - "owner_hotkey" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::OwnerHotkey) => Some(SelectiveMetagraph { netuid: netuid.into(), owner_hotkey: Some(SubnetOwnerHotkey::::get(netuid)), ..Default::default() }), - "owner_coldkey" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::OwnerColdkey) => Some(SelectiveMetagraph { netuid: netuid.into(), owner_coldkey: Some(SubnetOwner::::get(netuid)), ..Default::default() }), // Tempo terms. - "block" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Block) => Some(SelectiveMetagraph { netuid: netuid.into(), block: Some(Pallet::::get_current_block_as_u64().into()), ..Default::default() }), - "tempo" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Tempo) => Some(SelectiveMetagraph { netuid: netuid.into(), tempo: Some(Self::get_tempo(netuid).into()), ..Default::default() }), - "last_step" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::LastStep) => Some(SelectiveMetagraph { netuid: netuid.into(), last_step: Some(LastMechansimStepBlock::::get(netuid).into()), ..Default::default() }), - "blocks_since_last_step" => { + Some(SelectiveMetagraphIndex::BlocksSinceLastStep) => { let current_block: u64 = Pallet::::get_current_block_as_u64(); let last_step = LastMechansimStepBlock::::get(netuid); let blocks_since_last_step: u64 = current_block.saturating_sub(last_step); @@ -628,232 +707,232 @@ impl Pallet { } // Subnet emission terms - "subnet_emission" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::SubnetEmission) => Some(SelectiveMetagraph { netuid: netuid.into(), subnet_emission: Some(0.into()), ..Default::default() }), - "alpha_in" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::AlphaIn) => Some(SelectiveMetagraph { netuid: netuid.into(), alpha_in: Some(SubnetAlphaIn::::get(netuid).into()), ..Default::default() }), - "alpha_out" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::AlphaOut) => Some(SelectiveMetagraph { netuid: netuid.into(), alpha_out: Some(SubnetAlphaOut::::get(netuid).into()), ..Default::default() }), - "tao_in" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::TaoIn) => Some(SelectiveMetagraph { netuid: netuid.into(), tao_in: Some(SubnetTAO::::get(netuid).into()), ..Default::default() }), - "alpha_out_emission" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::AlphaOutEmission) => Some(SelectiveMetagraph { netuid: netuid.into(), alpha_out_emission: Some(SubnetAlphaOutEmission::::get(netuid).into()), ..Default::default() }), - "alpha_in_emission" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::AlphaInEmission) => Some(SelectiveMetagraph { netuid: netuid.into(), alpha_in_emission: Some(SubnetAlphaInEmission::::get(netuid).into()), ..Default::default() }), - "tao_in_emission" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::TaoInEmission) => Some(SelectiveMetagraph { netuid: netuid.into(), tao_in_emission: Some(SubnetTaoInEmission::::get(netuid).into()), ..Default::default() }), - "pending_alpha_emission" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::PendingAlphaEmission) => Some(SelectiveMetagraph { netuid: netuid.into(), pending_alpha_emission: Some(PendingEmission::::get(netuid).into()), ..Default::default() }), - "pending_root_emission" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::PendingRootEmission) => Some(SelectiveMetagraph { netuid: netuid.into(), pending_root_emission: Some(PendingRootDivs::::get(netuid).into()), ..Default::default() }), - "subnet_volume" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::SubnetVolume) => Some(SelectiveMetagraph { netuid: netuid.into(), subnet_volume: Some(SubnetVolume::::get(netuid).into()), ..Default::default() }), - "moving_price" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::MovingPrice) => Some(SelectiveMetagraph { netuid: netuid.into(), moving_price: Some(SubnetMovingPrice::::get(netuid)), ..Default::default() }), // Hparams for epoch - "rho" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Rho) => Some(SelectiveMetagraph { netuid: netuid.into(), rho: Some(Self::get_rho(netuid).into()), ..Default::default() }), - "kappa" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Kappa) => Some(SelectiveMetagraph { netuid: netuid.into(), kappa: Some(Self::get_kappa(netuid).into()), ..Default::default() }), // Validator params - "min_allowed_weights" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::MinAllowedWeights) => Some(SelectiveMetagraph { netuid: netuid.into(), min_allowed_weights: Some(Self::get_min_allowed_weights(netuid).into()), ..Default::default() }), - "max_weights_limit" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::MaxWeightsLimit) => Some(SelectiveMetagraph { netuid: netuid.into(), max_weights_limit: Some(Self::get_max_weight_limit(netuid).into()), ..Default::default() }), - "weights_version" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::WeightsVersion) => Some(SelectiveMetagraph { netuid: netuid.into(), weights_version: Some(Self::get_weights_version_key(netuid).into()), ..Default::default() }), - "weights_rate_limit" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::WeightsRateLimit) => Some(SelectiveMetagraph { netuid: netuid.into(), weights_rate_limit: Some(Self::get_weights_set_rate_limit(netuid).into()), ..Default::default() }), - "activity_cutoff" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::ActivityCutoff) => Some(SelectiveMetagraph { netuid: netuid.into(), activity_cutoff: Some(Self::get_activity_cutoff(netuid).into()), ..Default::default() }), - "max_validators" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::MaxValidators) => Some(SelectiveMetagraph { netuid: netuid.into(), max_validators: Some(Self::get_max_allowed_validators(netuid).into()), ..Default::default() }), // Registration - "num_uids" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::NumUids) => Some(SelectiveMetagraph { netuid: netuid.into(), num_uids: Some(Self::get_subnetwork_n(netuid).into()), ..Default::default() }), - "max_uids" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::MaxUids) => Some(SelectiveMetagraph { netuid: netuid.into(), max_uids: Some(Self::get_max_allowed_uids(netuid).into()), ..Default::default() }), - "registration_allowed" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::RegistrationAllowed) => Some(SelectiveMetagraph { netuid: netuid.into(), registration_allowed: Some(Self::get_network_registration_allowed(netuid).into()), ..Default::default() }), - "pow_registration_allowed" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::PowRegistrationAllowed) => Some(SelectiveMetagraph { netuid: netuid.into(), pow_registration_allowed: Some( Self::get_network_pow_registration_allowed(netuid).into(), ), ..Default::default() }), - "difficulty" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Difficulty) => Some(SelectiveMetagraph { netuid: netuid.into(), difficulty: Some(Self::get_difficulty_as_u64(netuid).into()), ..Default::default() }), - "burn" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Burn) => Some(SelectiveMetagraph { netuid: netuid.into(), burn: Some(Self::get_burn_as_u64(netuid).into()), ..Default::default() }), - "immunity_period" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::ImmunityPeriod) => Some(SelectiveMetagraph { netuid: netuid.into(), immunity_period: Some(Self::get_immunity_period(netuid).into()), ..Default::default() }), - "min_difficulty" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::MinDifficulty) => Some(SelectiveMetagraph { netuid: netuid.into(), min_difficulty: Some(Self::get_min_difficulty(netuid).into()), ..Default::default() }), - "max_difficulty" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::MaxDifficulty) => Some(SelectiveMetagraph { netuid: netuid.into(), max_difficulty: Some(Self::get_max_difficulty(netuid).into()), ..Default::default() }), - "min_burn" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::MinBurn) => Some(SelectiveMetagraph { netuid: netuid.into(), min_burn: Some(Self::get_min_burn_as_u64(netuid).into()), ..Default::default() }), - "max_burn" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::MaxBurn) => Some(SelectiveMetagraph { netuid: netuid.into(), max_burn: Some(Self::get_max_burn_as_u64(netuid).into()), ..Default::default() }), - "adjustment_alpha" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::AdjustmentAlpha) => Some(SelectiveMetagraph { netuid: netuid.into(), adjustment_alpha: Some(Self::get_adjustment_alpha(netuid).into()), ..Default::default() }), - "adjustment_interval" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::AdjustmentInterval) => Some(SelectiveMetagraph { netuid: netuid.into(), adjustment_interval: Some(Self::get_adjustment_interval(netuid).into()), ..Default::default() }), - "target_regs_per_interval" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::TargetRegsPerInterval) => Some(SelectiveMetagraph { netuid: netuid.into(), target_regs_per_interval: Some( Self::get_target_registrations_per_interval(netuid).into(), ), ..Default::default() }), - "max_regs_per_block" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::MaxRegsPerBlock) => Some(SelectiveMetagraph { netuid: netuid.into(), max_regs_per_block: Some(Self::get_max_registrations_per_block(netuid).into()), ..Default::default() }), - "serving_rate_limit" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::ServingRateLimit) => Some(SelectiveMetagraph { netuid: netuid.into(), serving_rate_limit: Some(Self::get_serving_rate_limit(netuid).into()), ..Default::default() }), // CR - "commit_reveal_weights_enabled" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::CommitRevealWeightsEnabled) => Some(SelectiveMetagraph { netuid: netuid.into(), commit_reveal_weights_enabled: Some(Self::get_commit_reveal_weights_enabled( netuid, )), ..Default::default() }), - "commit_reveal_period" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::CommitRevealPeriod) => Some(SelectiveMetagraph { netuid: netuid.into(), commit_reveal_period: Some(Self::get_reveal_period(netuid).into()), ..Default::default() }), // Bonds - "liquid_alpha_enabled" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::LiquidAlphaEnabled) => Some(SelectiveMetagraph { netuid: netuid.into(), liquid_alpha_enabled: Some(Self::get_liquid_alpha_enabled(netuid)), ..Default::default() }), - "alpha_high" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::AlphaHigh) => Some(SelectiveMetagraph { netuid: netuid.into(), alpha_high: Some(Self::get_alpha_values(netuid).1.into()), ..Default::default() }), - "alpha_low" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::AlphaLow) => Some(SelectiveMetagraph { netuid: netuid.into(), alpha_low: Some(Self::get_alpha_values(netuid).0.into()), ..Default::default() }), - "bonds_moving_avg" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::BondsMovingAvg) => Some(SelectiveMetagraph { netuid: netuid.into(), bonds_moving_avg: Some(Self::get_bonds_moving_average(netuid).into()), ..Default::default() }), // Metagraph info. - "hotkeys" => { + Some(SelectiveMetagraphIndex::Hotkeys) => { let n: u16 = Self::get_subnetwork_n(netuid); let mut hotkeys: Vec = vec![]; for uid in 0..n { @@ -867,7 +946,7 @@ impl Pallet { ..Default::default() }) } - "coldkeys" => { + Some(SelectiveMetagraphIndex::Coldkeys) => { let n: u16 = Self::get_subnetwork_n(netuid); let mut coldkeys: Vec = vec![]; for uid in 0..n { @@ -881,7 +960,7 @@ impl Pallet { ..Default::default() }) } - "identities" => { + Some(SelectiveMetagraphIndex::Identities) => { let n: u16 = Self::get_subnetwork_n(netuid); let mut identities: Vec> = vec![]; for uid in 0..n { @@ -895,7 +974,7 @@ impl Pallet { ..Default::default() }) } - "axons" => { + Some(SelectiveMetagraphIndex::Axons) => { let n: u16 = Self::get_subnetwork_n(netuid); let mut axons: Vec = vec![]; for uid in 0..n { @@ -908,18 +987,18 @@ impl Pallet { ..Default::default() }) } - "active" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Active) => Some(SelectiveMetagraph { netuid: netuid.into(), active: Some(Active::::get(netuid)), ..Default::default() }), - "validator_permit" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::ValidatorPermit) => Some(SelectiveMetagraph { netuid: netuid.into(), active: Some(ValidatorPermit::::get(netuid)), ..Default::default() }), - "pruning_score" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::PruningScore) => Some(SelectiveMetagraph { netuid: netuid.into(), pruning_score: Some( PruningScores::::get(netuid) @@ -930,7 +1009,7 @@ impl Pallet { ..Default::default() }), - "last_update" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::LastUpdate) => Some(SelectiveMetagraph { netuid: netuid.into(), last_update: Some( LastUpdate::::get(netuid) @@ -941,7 +1020,7 @@ impl Pallet { ..Default::default() }), - "emission" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Emission) => Some(SelectiveMetagraph { netuid: netuid.into(), emission: Some( Emission::::get(netuid) @@ -952,7 +1031,7 @@ impl Pallet { ..Default::default() }), - "dividends" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Dividends) => Some(SelectiveMetagraph { netuid: netuid.into(), dividends: Some( Dividends::::get(netuid) @@ -963,7 +1042,7 @@ impl Pallet { ..Default::default() }), - "incentives" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Incentives) => Some(SelectiveMetagraph { netuid: netuid.into(), incentives: Some( Incentive::::get(netuid) @@ -974,7 +1053,7 @@ impl Pallet { ..Default::default() }), - "consensus" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Consensus) => Some(SelectiveMetagraph { netuid: netuid.into(), consensus: Some( Consensus::::get(netuid) @@ -985,7 +1064,7 @@ impl Pallet { ..Default::default() }), - "trust" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Trust) => Some(SelectiveMetagraph { netuid: netuid.into(), trust: Some( Trust::::get(netuid) @@ -995,7 +1074,7 @@ impl Pallet { ), ..Default::default() }), - "rank" => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Rank) => Some(SelectiveMetagraph { netuid: netuid.into(), rank: Some( Rank::::get(netuid) @@ -1005,7 +1084,7 @@ impl Pallet { ), ..Default::default() }), - "block_at_registration" => { + Some(SelectiveMetagraphIndex::BlockAtRegistration) => { let n: u16 = Self::get_subnetwork_n(netuid); let mut block_at_registration: Vec> = vec![]; for uid in 0..n { @@ -1017,7 +1096,7 @@ impl Pallet { ..Default::default() }) } - "alpha_stake" => { + Some(SelectiveMetagraphIndex::AlphaStake) => { let (_, alpha_stake_fl, _): (Vec, Vec, Vec) = Self::get_stake_weights_for_network(netuid); Some(SelectiveMetagraph { @@ -1031,7 +1110,7 @@ impl Pallet { ..Default::default() }) } - "tao_stake" => { + Some(SelectiveMetagraphIndex::TaoStake) => { let (_, _, tao_stake_fl): (Vec, Vec, Vec) = Self::get_stake_weights_for_network(netuid); Some(SelectiveMetagraph { @@ -1045,7 +1124,7 @@ impl Pallet { ..Default::default() }) } - "total_stake" => { + Some(SelectiveMetagraphIndex::TotalStake) => { let (total_stake_fl, _, _): (Vec, Vec, Vec) = Self::get_stake_weights_for_network(netuid); Some(SelectiveMetagraph { @@ -1061,7 +1140,7 @@ impl Pallet { } // Dividend break down. - "tao_dividends_per_hotkey" => { + Some(SelectiveMetagraphIndex::TaoDividendsPerHotkey) => { let n: u16 = Self::get_subnetwork_n(netuid); let mut hotkeys: Vec = vec![]; for uid in 0..n { @@ -1079,7 +1158,7 @@ impl Pallet { ..Default::default() }) } - "alpha_dividends_per_hotkey" => { + Some(SelectiveMetagraphIndex::AlphaDividendsPerHotkey) => { let mut alpha_dividends_per_hotkey: Vec<(T::AccountId, Compact)> = vec![]; let n: u16 = Self::get_subnetwork_n(netuid); let mut hotkeys: Vec = vec![]; @@ -1099,7 +1178,7 @@ impl Pallet { ..Default::default() }) } - _ => { + None => { Some(SelectiveMetagraph { // Subnet index netuid: netuid.into(), From 67d05672254742d98b0cd48df63a422d1075d48b Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 25 Mar 2025 22:58:36 +0800 Subject: [PATCH 04/45] more code --- pallets/subtensor/src/rpc_info/metagraph.rs | 171 +++++++++++++++++++- 1 file changed, 168 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index d1c47d8e3f..61a4746741 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -107,9 +107,9 @@ pub struct Metagraph { alpha_dividends_per_hotkey: Vec<(AccountId, Compact)>, // List of dividend payout in alpha via subnet. } -#[freeze_struct("d745be982b4d29ea")] +#[freeze_struct("182c7375fee9db7b")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] -pub struct SelectiveMetagraph { +pub struct SelectiveMetagraph { // Subnet index netuid: Compact, @@ -207,9 +207,174 @@ pub struct SelectiveMetagraph { alpha_dividends_per_hotkey: Option)>>, // List of dividend payout in alpha via subnet. } +impl SelectiveMetagraph +where + AccountId: TypeInfo + Encode + Decode + Clone, +{ + pub fn merge_value( + &mut self, + other: &Self, + metagraph_index: SelectiveMetagraphIndex, + ) -> &mut Self { + match SelectiveMetagraphIndex::from_index(metagraph_index as usize) { + // Name and symbol + Some(SelectiveMetagraphIndex::Name) => self.name = other.name.clone(), + Some(SelectiveMetagraphIndex::Symbol) => self.symbol = other.symbol.clone(), + Some(SelectiveMetagraphIndex::Identity) => self.identity = other.identity.clone(), + Some(SelectiveMetagraphIndex::NetworkRegisteredAt) => { + self.network_registered_at = other.network_registered_at + } + Some(SelectiveMetagraphIndex::OwnerHotkey) => { + self.owner_hotkey = other.owner_hotkey.clone() + } + Some(SelectiveMetagraphIndex::OwnerColdkey) => { + self.owner_coldkey = other.owner_coldkey.clone() + } + Some(SelectiveMetagraphIndex::Block) => self.block = other.block, + Some(SelectiveMetagraphIndex::Tempo) => self.tempo = other.tempo, + Some(SelectiveMetagraphIndex::LastStep) => self.last_step = other.last_step, + Some(SelectiveMetagraphIndex::BlocksSinceLastStep) => { + self.blocks_since_last_step = other.blocks_since_last_step + } + Some(SelectiveMetagraphIndex::SubnetEmission) => { + self.subnet_emission = other.subnet_emission + } + Some(SelectiveMetagraphIndex::AlphaIn) => self.alpha_in = other.alpha_in, + Some(SelectiveMetagraphIndex::AlphaOut) => self.alpha_out = other.alpha_out, + Some(SelectiveMetagraphIndex::TaoIn) => self.tao_in = other.tao_in, + Some(SelectiveMetagraphIndex::AlphaOutEmission) => { + self.alpha_out_emission = other.alpha_out_emission + } + Some(SelectiveMetagraphIndex::AlphaInEmission) => { + self.alpha_in_emission = other.alpha_in_emission + } + Some(SelectiveMetagraphIndex::TaoInEmission) => { + self.tao_in_emission = other.tao_in_emission + } + Some(SelectiveMetagraphIndex::PendingAlphaEmission) => { + self.pending_alpha_emission = other.pending_alpha_emission + } + Some(SelectiveMetagraphIndex::PendingRootEmission) => { + self.pending_root_emission = other.pending_root_emission + } + Some(SelectiveMetagraphIndex::SubnetVolume) => self.subnet_volume = other.subnet_volume, + Some(SelectiveMetagraphIndex::MovingPrice) => self.moving_price = other.moving_price, + Some(SelectiveMetagraphIndex::Rho) => self.rho = other.rho, + Some(SelectiveMetagraphIndex::Kappa) => self.kappa = other.kappa, + Some(SelectiveMetagraphIndex::MinAllowedWeights) => { + self.min_allowed_weights = other.min_allowed_weights + } + Some(SelectiveMetagraphIndex::MaxWeightsLimit) => { + self.max_weights_limit = other.max_weights_limit + } + Some(SelectiveMetagraphIndex::WeightsVersion) => { + self.weights_version = other.weights_version + } + Some(SelectiveMetagraphIndex::WeightsRateLimit) => { + self.weights_rate_limit = other.weights_rate_limit + } + Some(SelectiveMetagraphIndex::ActivityCutoff) => { + self.activity_cutoff = other.activity_cutoff + } + Some(SelectiveMetagraphIndex::MaxValidators) => { + self.max_validators = other.max_validators + } + Some(SelectiveMetagraphIndex::NumUids) => self.num_uids = other.num_uids, + Some(SelectiveMetagraphIndex::MaxUids) => self.max_uids = other.max_uids, + Some(SelectiveMetagraphIndex::Burn) => self.burn = other.burn, + Some(SelectiveMetagraphIndex::Difficulty) => self.difficulty = other.difficulty, + Some(SelectiveMetagraphIndex::RegistrationAllowed) => { + self.registration_allowed = other.registration_allowed + } + Some(SelectiveMetagraphIndex::PowRegistrationAllowed) => { + self.pow_registration_allowed = other.pow_registration_allowed + } + Some(SelectiveMetagraphIndex::ImmunityPeriod) => { + self.immunity_period = other.immunity_period + } + Some(SelectiveMetagraphIndex::MinDifficulty) => { + self.min_difficulty = other.min_difficulty + } + Some(SelectiveMetagraphIndex::MaxDifficulty) => { + self.max_difficulty = other.max_difficulty + } + Some(SelectiveMetagraphIndex::MinBurn) => self.min_burn = other.min_burn, + Some(SelectiveMetagraphIndex::MaxBurn) => self.max_burn = other.max_burn, + Some(SelectiveMetagraphIndex::AdjustmentAlpha) => { + self.adjustment_alpha = other.adjustment_alpha + } + Some(SelectiveMetagraphIndex::AdjustmentInterval) => { + self.adjustment_interval = other.adjustment_interval + } + Some(SelectiveMetagraphIndex::TargetRegsPerInterval) => { + self.target_regs_per_interval = other.target_regs_per_interval + } + Some(SelectiveMetagraphIndex::MaxRegsPerBlock) => { + self.max_regs_per_block = other.max_regs_per_block + } + Some(SelectiveMetagraphIndex::ServingRateLimit) => { + self.serving_rate_limit = other.serving_rate_limit + } + Some(SelectiveMetagraphIndex::CommitRevealWeightsEnabled) => { + self.commit_reveal_weights_enabled = other.commit_reveal_weights_enabled + } + Some(SelectiveMetagraphIndex::CommitRevealPeriod) => { + self.commit_reveal_period = other.commit_reveal_period + } + Some(SelectiveMetagraphIndex::LiquidAlphaEnabled) => { + self.liquid_alpha_enabled = other.liquid_alpha_enabled + } + Some(SelectiveMetagraphIndex::AlphaHigh) => self.alpha_high = other.alpha_high, + Some(SelectiveMetagraphIndex::AlphaLow) => self.alpha_low = other.alpha_low, + Some(SelectiveMetagraphIndex::BondsMovingAvg) => { + self.bonds_moving_avg = other.bonds_moving_avg + } + Some(SelectiveMetagraphIndex::Hotkeys) => self.hotkeys = other.hotkeys.clone(), + Some(SelectiveMetagraphIndex::Coldkeys) => self.coldkeys = other.coldkeys.clone(), + Some(SelectiveMetagraphIndex::Identities) => self.identities = other.identities.clone(), + Some(SelectiveMetagraphIndex::Axons) => self.axons = other.axons.clone(), + Some(SelectiveMetagraphIndex::Active) => self.active = other.active.clone(), + Some(SelectiveMetagraphIndex::ValidatorPermit) => { + self.validator_permit = other.validator_permit.clone() + } + Some(SelectiveMetagraphIndex::PruningScore) => { + self.pruning_score = other.pruning_score.clone() + } + Some(SelectiveMetagraphIndex::LastUpdate) => { + self.last_update = other.last_update.clone() + } + Some(SelectiveMetagraphIndex::Emission) => self.emission = other.emission.clone(), + Some(SelectiveMetagraphIndex::Dividends) => self.dividends = other.dividends.clone(), + Some(SelectiveMetagraphIndex::Incentives) => self.incentives = other.incentives.clone(), + Some(SelectiveMetagraphIndex::Consensus) => self.consensus = other.consensus.clone(), + Some(SelectiveMetagraphIndex::Trust) => self.trust = other.trust.clone(), + Some(SelectiveMetagraphIndex::Rank) => self.rank = other.rank.clone(), + Some(SelectiveMetagraphIndex::BlockAtRegistration) => { + self.block_at_registration = other.block_at_registration.clone() + } + Some(SelectiveMetagraphIndex::AlphaStake) => { + self.alpha_stake = other.alpha_stake.clone() + } + Some(SelectiveMetagraphIndex::TaoStake) => self.tao_stake = other.tao_stake.clone(), + Some(SelectiveMetagraphIndex::TotalStake) => { + self.total_stake = other.total_stake.clone() + } + Some(SelectiveMetagraphIndex::TaoDividendsPerHotkey) => { + self.tao_dividends_per_hotkey = other.tao_dividends_per_hotkey.clone() + } + Some(SelectiveMetagraphIndex::AlphaDividendsPerHotkey) => { + self.alpha_dividends_per_hotkey = other.alpha_dividends_per_hotkey.clone() + } + + None => {} + }; + self + } +} + impl Default for SelectiveMetagraph where - AccountId: TypeInfo + Encode + Decode, + AccountId: TypeInfo + Encode + Decode + Clone, { fn default() -> Self { Self { From e88256a96490632008ffe3a94e9c6b3a479b96f8 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 26 Mar 2025 08:48:00 +0800 Subject: [PATCH 05/45] compile ok --- pallets/subtensor/rpc/src/lib.rs | 26 ++ pallets/subtensor/runtime-api/src/lib.rs | 3 +- pallets/subtensor/src/rpc_info/metagraph.rs | 321 ++++++++++---------- runtime/src/lib.rs | 7 +- 4 files changed, 197 insertions(+), 160 deletions(-) diff --git a/pallets/subtensor/rpc/src/lib.rs b/pallets/subtensor/rpc/src/lib.rs index 776ab15b45..b3b60206dd 100644 --- a/pallets/subtensor/rpc/src/lib.rs +++ b/pallets/subtensor/rpc/src/lib.rs @@ -64,6 +64,13 @@ pub trait SubtensorCustomApi { fn get_subnet_state(&self, netuid: u16, at: Option) -> RpcResult>; #[method(name = "subnetInfo_getLockCost")] fn get_network_lock_cost(&self, at: Option) -> RpcResult; + #[method(name = "subnetInfo_getSelectiveMetagraph")] + fn get_selective_metagraph( + &self, + netuid: u16, + metagraph_index: Vec, + at: Option, + ) -> RpcResult>; } pub struct SubtensorCustom { @@ -390,4 +397,23 @@ where Error::RuntimeError(format!("Unable to get subnet lock cost: {:?}", e)).into() }) } + + fn get_selective_metagraph( + &self, + netuid: u16, + metagraph_index: Vec, + at: Option<::Hash>, + ) -> RpcResult> { + let api = self.client.runtime_api(); + let at = at.unwrap_or_else(|| self.client.info().best_hash); + + match api.get_selective_metagraph(at, netuid, metagraph_index) { + Ok(result) => Ok(result.encode()), + Err(e) => Err(Error::RuntimeError(format!( + "Unable to get selective metagraph: {:?}", + e + )) + .into()), + } + } } diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index 8f90197bb5..1a2f34aa9e 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -5,7 +5,7 @@ use codec::Compact; use pallet_subtensor::rpc_info::{ delegate_info::DelegateInfo, dynamic_info::DynamicInfo, - metagraph::Metagraph, + metagraph::{Metagraph, SelectiveMetagraph}, neuron_info::{NeuronInfo, NeuronInfoLite}, show_subnet::SubnetState, stake_info::StakeInfo, @@ -40,6 +40,7 @@ sp_api::decl_runtime_apis! { fn get_metagraph(netuid: u16) -> Option>; fn get_dynamic_info(netuid: u16) -> Option>; fn get_subnet_state(netuid: u16) -> Option>; + fn get_selective_metagraph(netuid: u16, metagraph_indexes: Vec) -> Option>; } pub trait StakeInfoRuntimeApi { diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index 61a4746741..ba36a117a1 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -211,12 +211,8 @@ impl SelectiveMetagraph where AccountId: TypeInfo + Encode + Decode + Clone, { - pub fn merge_value( - &mut self, - other: &Self, - metagraph_index: SelectiveMetagraphIndex, - ) -> &mut Self { - match SelectiveMetagraphIndex::from_index(metagraph_index as usize) { + pub fn merge_value(&mut self, other: &Self, metagraph_index: usize) { + match SelectiveMetagraphIndex::from_index(metagraph_index) { // Name and symbol Some(SelectiveMetagraphIndex::Name) => self.name = other.name.clone(), Some(SelectiveMetagraphIndex::Symbol) => self.symbol = other.symbol.clone(), @@ -368,7 +364,6 @@ where None => {} }; - self } } @@ -793,15 +788,27 @@ impl Pallet { pub fn get_selective_metagraph( netuid: u16, - metagraph_index: u16, + metagraph_indexes: Vec, ) -> Option> { if !Self::if_subnet_exist(netuid) { - return None; + None + } else { + let mut result = SelectiveMetagraph::default(); + for index in metagraph_indexes.iter() { + let value = Self::get_single_selective_metagraph(netuid, *index); + result.merge_value(&value, *index as usize); + } + Some(result) } + } + fn get_single_selective_metagraph( + netuid: u16, + metagraph_index: u16, + ) -> SelectiveMetagraph { match SelectiveMetagraphIndex::from_index(metagraph_index as usize) { // Name and symbol - Some(SelectiveMetagraphIndex::Name) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Name) => SelectiveMetagraph { netuid: netuid.into(), name: Some( Self::get_name_for_subnet(netuid) @@ -810,8 +817,8 @@ impl Pallet { .collect(), ), ..Default::default() - }), - Some(SelectiveMetagraphIndex::Symbol) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::Symbol) => SelectiveMetagraph { netuid: netuid.into(), symbol: Some( Self::get_symbol_for_subnet(netuid) @@ -820,281 +827,281 @@ impl Pallet { .collect(), ), ..Default::default() - }), - Some(SelectiveMetagraphIndex::Identity) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::Identity) => SelectiveMetagraph { netuid: netuid.into(), identity: Some(SubnetIdentitiesV2::::get(netuid)), ..Default::default() - }), - Some(SelectiveMetagraphIndex::NetworkRegisteredAt) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::NetworkRegisteredAt) => SelectiveMetagraph { netuid: netuid.into(), network_registered_at: Some(NetworkRegisteredAt::::get(netuid).into()), ..Default::default() - }), + }, // Keys for owner. - Some(SelectiveMetagraphIndex::OwnerHotkey) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::OwnerHotkey) => SelectiveMetagraph { netuid: netuid.into(), owner_hotkey: Some(SubnetOwnerHotkey::::get(netuid)), ..Default::default() - }), - Some(SelectiveMetagraphIndex::OwnerColdkey) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::OwnerColdkey) => SelectiveMetagraph { netuid: netuid.into(), owner_coldkey: Some(SubnetOwner::::get(netuid)), ..Default::default() - }), + }, // Tempo terms. - Some(SelectiveMetagraphIndex::Block) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Block) => SelectiveMetagraph { netuid: netuid.into(), block: Some(Pallet::::get_current_block_as_u64().into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::Tempo) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::Tempo) => SelectiveMetagraph { netuid: netuid.into(), tempo: Some(Self::get_tempo(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::LastStep) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::LastStep) => SelectiveMetagraph { netuid: netuid.into(), last_step: Some(LastMechansimStepBlock::::get(netuid).into()), ..Default::default() - }), + }, Some(SelectiveMetagraphIndex::BlocksSinceLastStep) => { let current_block: u64 = Pallet::::get_current_block_as_u64(); let last_step = LastMechansimStepBlock::::get(netuid); let blocks_since_last_step: u64 = current_block.saturating_sub(last_step); - Some(SelectiveMetagraph { + SelectiveMetagraph { netuid: netuid.into(), blocks_since_last_step: Some(blocks_since_last_step.into()), ..Default::default() - }) + } } // Subnet emission terms - Some(SelectiveMetagraphIndex::SubnetEmission) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::SubnetEmission) => SelectiveMetagraph { netuid: netuid.into(), subnet_emission: Some(0.into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::AlphaIn) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::AlphaIn) => SelectiveMetagraph { netuid: netuid.into(), alpha_in: Some(SubnetAlphaIn::::get(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::AlphaOut) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::AlphaOut) => SelectiveMetagraph { netuid: netuid.into(), alpha_out: Some(SubnetAlphaOut::::get(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::TaoIn) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::TaoIn) => SelectiveMetagraph { netuid: netuid.into(), tao_in: Some(SubnetTAO::::get(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::AlphaOutEmission) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::AlphaOutEmission) => SelectiveMetagraph { netuid: netuid.into(), alpha_out_emission: Some(SubnetAlphaOutEmission::::get(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::AlphaInEmission) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::AlphaInEmission) => SelectiveMetagraph { netuid: netuid.into(), alpha_in_emission: Some(SubnetAlphaInEmission::::get(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::TaoInEmission) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::TaoInEmission) => SelectiveMetagraph { netuid: netuid.into(), tao_in_emission: Some(SubnetTaoInEmission::::get(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::PendingAlphaEmission) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::PendingAlphaEmission) => SelectiveMetagraph { netuid: netuid.into(), pending_alpha_emission: Some(PendingEmission::::get(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::PendingRootEmission) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::PendingRootEmission) => SelectiveMetagraph { netuid: netuid.into(), pending_root_emission: Some(PendingRootDivs::::get(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::SubnetVolume) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::SubnetVolume) => SelectiveMetagraph { netuid: netuid.into(), subnet_volume: Some(SubnetVolume::::get(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::MovingPrice) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::MovingPrice) => SelectiveMetagraph { netuid: netuid.into(), moving_price: Some(SubnetMovingPrice::::get(netuid)), ..Default::default() - }), + }, // Hparams for epoch - Some(SelectiveMetagraphIndex::Rho) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Rho) => SelectiveMetagraph { netuid: netuid.into(), rho: Some(Self::get_rho(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::Kappa) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::Kappa) => SelectiveMetagraph { netuid: netuid.into(), kappa: Some(Self::get_kappa(netuid).into()), ..Default::default() - }), + }, // Validator params - Some(SelectiveMetagraphIndex::MinAllowedWeights) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::MinAllowedWeights) => SelectiveMetagraph { netuid: netuid.into(), min_allowed_weights: Some(Self::get_min_allowed_weights(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::MaxWeightsLimit) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::MaxWeightsLimit) => SelectiveMetagraph { netuid: netuid.into(), max_weights_limit: Some(Self::get_max_weight_limit(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::WeightsVersion) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::WeightsVersion) => SelectiveMetagraph { netuid: netuid.into(), weights_version: Some(Self::get_weights_version_key(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::WeightsRateLimit) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::WeightsRateLimit) => SelectiveMetagraph { netuid: netuid.into(), weights_rate_limit: Some(Self::get_weights_set_rate_limit(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::ActivityCutoff) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::ActivityCutoff) => SelectiveMetagraph { netuid: netuid.into(), activity_cutoff: Some(Self::get_activity_cutoff(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::MaxValidators) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::MaxValidators) => SelectiveMetagraph { netuid: netuid.into(), max_validators: Some(Self::get_max_allowed_validators(netuid).into()), ..Default::default() - }), + }, // Registration - Some(SelectiveMetagraphIndex::NumUids) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::NumUids) => SelectiveMetagraph { netuid: netuid.into(), num_uids: Some(Self::get_subnetwork_n(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::MaxUids) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::MaxUids) => SelectiveMetagraph { netuid: netuid.into(), max_uids: Some(Self::get_max_allowed_uids(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::RegistrationAllowed) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::RegistrationAllowed) => SelectiveMetagraph { netuid: netuid.into(), registration_allowed: Some(Self::get_network_registration_allowed(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::PowRegistrationAllowed) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::PowRegistrationAllowed) => SelectiveMetagraph { netuid: netuid.into(), pow_registration_allowed: Some( Self::get_network_pow_registration_allowed(netuid).into(), ), ..Default::default() - }), - Some(SelectiveMetagraphIndex::Difficulty) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::Difficulty) => SelectiveMetagraph { netuid: netuid.into(), difficulty: Some(Self::get_difficulty_as_u64(netuid).into()), ..Default::default() - }), + }, - Some(SelectiveMetagraphIndex::Burn) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Burn) => SelectiveMetagraph { netuid: netuid.into(), burn: Some(Self::get_burn_as_u64(netuid).into()), ..Default::default() - }), + }, - Some(SelectiveMetagraphIndex::ImmunityPeriod) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::ImmunityPeriod) => SelectiveMetagraph { netuid: netuid.into(), immunity_period: Some(Self::get_immunity_period(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::MinDifficulty) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::MinDifficulty) => SelectiveMetagraph { netuid: netuid.into(), min_difficulty: Some(Self::get_min_difficulty(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::MaxDifficulty) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::MaxDifficulty) => SelectiveMetagraph { netuid: netuid.into(), max_difficulty: Some(Self::get_max_difficulty(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::MinBurn) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::MinBurn) => SelectiveMetagraph { netuid: netuid.into(), min_burn: Some(Self::get_min_burn_as_u64(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::MaxBurn) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::MaxBurn) => SelectiveMetagraph { netuid: netuid.into(), max_burn: Some(Self::get_max_burn_as_u64(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::AdjustmentAlpha) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::AdjustmentAlpha) => SelectiveMetagraph { netuid: netuid.into(), adjustment_alpha: Some(Self::get_adjustment_alpha(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::AdjustmentInterval) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::AdjustmentInterval) => SelectiveMetagraph { netuid: netuid.into(), adjustment_interval: Some(Self::get_adjustment_interval(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::TargetRegsPerInterval) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::TargetRegsPerInterval) => SelectiveMetagraph { netuid: netuid.into(), target_regs_per_interval: Some( Self::get_target_registrations_per_interval(netuid).into(), ), ..Default::default() - }), - Some(SelectiveMetagraphIndex::MaxRegsPerBlock) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::MaxRegsPerBlock) => SelectiveMetagraph { netuid: netuid.into(), max_regs_per_block: Some(Self::get_max_registrations_per_block(netuid).into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::ServingRateLimit) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::ServingRateLimit) => SelectiveMetagraph { netuid: netuid.into(), serving_rate_limit: Some(Self::get_serving_rate_limit(netuid).into()), ..Default::default() - }), + }, // CR - Some(SelectiveMetagraphIndex::CommitRevealWeightsEnabled) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::CommitRevealWeightsEnabled) => SelectiveMetagraph { netuid: netuid.into(), commit_reveal_weights_enabled: Some(Self::get_commit_reveal_weights_enabled( netuid, )), ..Default::default() - }), - Some(SelectiveMetagraphIndex::CommitRevealPeriod) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::CommitRevealPeriod) => SelectiveMetagraph { netuid: netuid.into(), commit_reveal_period: Some(Self::get_reveal_period(netuid).into()), ..Default::default() - }), + }, // Bonds - Some(SelectiveMetagraphIndex::LiquidAlphaEnabled) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::LiquidAlphaEnabled) => SelectiveMetagraph { netuid: netuid.into(), liquid_alpha_enabled: Some(Self::get_liquid_alpha_enabled(netuid)), ..Default::default() - }), - Some(SelectiveMetagraphIndex::AlphaHigh) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::AlphaHigh) => SelectiveMetagraph { netuid: netuid.into(), alpha_high: Some(Self::get_alpha_values(netuid).1.into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::AlphaLow) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::AlphaLow) => SelectiveMetagraph { netuid: netuid.into(), alpha_low: Some(Self::get_alpha_values(netuid).0.into()), ..Default::default() - }), - Some(SelectiveMetagraphIndex::BondsMovingAvg) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::BondsMovingAvg) => SelectiveMetagraph { netuid: netuid.into(), bonds_moving_avg: Some(Self::get_bonds_moving_average(netuid).into()), ..Default::default() - }), + }, // Metagraph info. Some(SelectiveMetagraphIndex::Hotkeys) => { @@ -1105,11 +1112,11 @@ impl Pallet { hotkeys.push(hotkey.clone()); } - Some(SelectiveMetagraph { + SelectiveMetagraph { netuid: netuid.into(), hotkeys: Some(hotkeys), ..Default::default() - }) + } } Some(SelectiveMetagraphIndex::Coldkeys) => { let n: u16 = Self::get_subnetwork_n(netuid); @@ -1119,11 +1126,11 @@ impl Pallet { let coldkey = Owner::::get(hotkey.clone()); coldkeys.push(coldkey.clone()); } - Some(SelectiveMetagraph { + SelectiveMetagraph { netuid: netuid.into(), coldkeys: Some(coldkeys), ..Default::default() - }) + } } Some(SelectiveMetagraphIndex::Identities) => { let n: u16 = Self::get_subnetwork_n(netuid); @@ -1133,11 +1140,11 @@ impl Pallet { let coldkey = Owner::::get(hotkey.clone()); identities.push(IdentitiesV2::::get(coldkey.clone())); } - Some(SelectiveMetagraph { + SelectiveMetagraph { netuid: netuid.into(), identities: Some(identities), ..Default::default() - }) + } } Some(SelectiveMetagraphIndex::Axons) => { let n: u16 = Self::get_subnetwork_n(netuid); @@ -1146,24 +1153,24 @@ impl Pallet { let hotkey = Keys::::get(netuid, uid); axons.push(Self::get_axon_info(netuid, &hotkey)); } - Some(SelectiveMetagraph { + SelectiveMetagraph { netuid: netuid.into(), axons: Some(axons), ..Default::default() - }) + } } - Some(SelectiveMetagraphIndex::Active) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Active) => SelectiveMetagraph { netuid: netuid.into(), active: Some(Active::::get(netuid)), ..Default::default() - }), - Some(SelectiveMetagraphIndex::ValidatorPermit) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::ValidatorPermit) => SelectiveMetagraph { netuid: netuid.into(), active: Some(ValidatorPermit::::get(netuid)), ..Default::default() - }), + }, - Some(SelectiveMetagraphIndex::PruningScore) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::PruningScore) => SelectiveMetagraph { netuid: netuid.into(), pruning_score: Some( PruningScores::::get(netuid) @@ -1172,9 +1179,9 @@ impl Pallet { .collect(), ), ..Default::default() - }), + }, - Some(SelectiveMetagraphIndex::LastUpdate) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::LastUpdate) => SelectiveMetagraph { netuid: netuid.into(), last_update: Some( LastUpdate::::get(netuid) @@ -1183,9 +1190,9 @@ impl Pallet { .collect(), ), ..Default::default() - }), + }, - Some(SelectiveMetagraphIndex::Emission) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Emission) => SelectiveMetagraph { netuid: netuid.into(), emission: Some( Emission::::get(netuid) @@ -1194,9 +1201,9 @@ impl Pallet { .collect(), ), ..Default::default() - }), + }, - Some(SelectiveMetagraphIndex::Dividends) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Dividends) => SelectiveMetagraph { netuid: netuid.into(), dividends: Some( Dividends::::get(netuid) @@ -1205,9 +1212,9 @@ impl Pallet { .collect(), ), ..Default::default() - }), + }, - Some(SelectiveMetagraphIndex::Incentives) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Incentives) => SelectiveMetagraph { netuid: netuid.into(), incentives: Some( Incentive::::get(netuid) @@ -1216,9 +1223,9 @@ impl Pallet { .collect(), ), ..Default::default() - }), + }, - Some(SelectiveMetagraphIndex::Consensus) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Consensus) => SelectiveMetagraph { netuid: netuid.into(), consensus: Some( Consensus::::get(netuid) @@ -1227,9 +1234,9 @@ impl Pallet { .collect(), ), ..Default::default() - }), + }, - Some(SelectiveMetagraphIndex::Trust) => Some(SelectiveMetagraph { + Some(SelectiveMetagraphIndex::Trust) => SelectiveMetagraph { netuid: netuid.into(), trust: Some( Trust::::get(netuid) @@ -1238,8 +1245,8 @@ impl Pallet { .collect(), ), ..Default::default() - }), - Some(SelectiveMetagraphIndex::Rank) => Some(SelectiveMetagraph { + }, + Some(SelectiveMetagraphIndex::Rank) => SelectiveMetagraph { netuid: netuid.into(), rank: Some( Rank::::get(netuid) @@ -1248,23 +1255,23 @@ impl Pallet { .collect(), ), ..Default::default() - }), + }, Some(SelectiveMetagraphIndex::BlockAtRegistration) => { let n: u16 = Self::get_subnetwork_n(netuid); let mut block_at_registration: Vec> = vec![]; for uid in 0..n { block_at_registration.push(BlockAtRegistration::::get(netuid, uid).into()); } - Some(SelectiveMetagraph { + SelectiveMetagraph { netuid: netuid.into(), block_at_registration: Some(block_at_registration), ..Default::default() - }) + } } Some(SelectiveMetagraphIndex::AlphaStake) => { let (_, alpha_stake_fl, _): (Vec, Vec, Vec) = Self::get_stake_weights_for_network(netuid); - Some(SelectiveMetagraph { + SelectiveMetagraph { netuid: netuid.into(), alpha_stake: Some( alpha_stake_fl @@ -1273,12 +1280,12 @@ impl Pallet { .collect::>>(), ), ..Default::default() - }) + } } Some(SelectiveMetagraphIndex::TaoStake) => { let (_, _, tao_stake_fl): (Vec, Vec, Vec) = Self::get_stake_weights_for_network(netuid); - Some(SelectiveMetagraph { + SelectiveMetagraph { netuid: netuid.into(), tao_stake: Some( tao_stake_fl @@ -1287,12 +1294,12 @@ impl Pallet { .collect::>>(), ), ..Default::default() - }) + } } Some(SelectiveMetagraphIndex::TotalStake) => { let (total_stake_fl, _, _): (Vec, Vec, Vec) = Self::get_stake_weights_for_network(netuid); - Some(SelectiveMetagraph { + SelectiveMetagraph { netuid: netuid.into(), total_stake: Some( total_stake_fl @@ -1301,7 +1308,7 @@ impl Pallet { .collect::>>(), ), ..Default::default() - }) + } } // Dividend break down. @@ -1317,11 +1324,11 @@ impl Pallet { let tao_divs = TaoDividendsPerSubnet::::get(netuid, hotkey.clone()); tao_dividends_per_hotkey.push((hotkey.clone(), tao_divs.into())); } - Some(SelectiveMetagraph { + SelectiveMetagraph { netuid: netuid.into(), tao_dividends_per_hotkey: Some(tao_dividends_per_hotkey), ..Default::default() - }) + } } Some(SelectiveMetagraphIndex::AlphaDividendsPerHotkey) => { let mut alpha_dividends_per_hotkey: Vec<(T::AccountId, Compact)> = vec![]; @@ -1337,19 +1344,17 @@ impl Pallet { let alpha_divs = AlphaDividendsPerSubnet::::get(netuid, hotkey.clone()); alpha_dividends_per_hotkey.push((hotkey.clone(), alpha_divs.into())); } - Some(SelectiveMetagraph { + SelectiveMetagraph { netuid: netuid.into(), alpha_dividends_per_hotkey: Some(alpha_dividends_per_hotkey), ..Default::default() - }) - } - None => { - Some(SelectiveMetagraph { - // Subnet index - netuid: netuid.into(), - ..Default::default() - }) + } } + None => SelectiveMetagraph { + // Subnet index + netuid: netuid.into(), + ..Default::default() + }, } } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 22659c130a..1fdf121a07 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -33,7 +33,7 @@ use pallet_registry::CanRegisterIdentity; use pallet_subtensor::rpc_info::{ delegate_info::DelegateInfo, dynamic_info::DynamicInfo, - metagraph::Metagraph, + metagraph::{Metagraph, SelectiveMetagraph}, neuron_info::{NeuronInfo, NeuronInfoLite}, show_subnet::SubnetState, stake_info::StakeInfo, @@ -2080,6 +2080,11 @@ impl_runtime_apis! { fn get_all_dynamic_info() -> Vec>> { SubtensorModule::get_all_dynamic_info() } + + fn get_selective_metagraph(netuid: u16, metagraph_indexes: Vec) -> Option> { + SubtensorModule::get_selective_metagraph(netuid, metagraph_indexes) + } + } impl subtensor_custom_rpc_runtime_api::StakeInfoRuntimeApi for Runtime { From b3cb0f9222747f466bb829aa3b6ed99079ade217 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 26 Mar 2025 22:10:08 +0800 Subject: [PATCH 06/45] add test case --- pallets/subtensor/src/rpc_info/metagraph.rs | 107 ++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index ba36a117a1..255e5a30fb 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -1358,3 +1358,110 @@ impl Pallet { } } } + +#[test] +fn test_selective_metagraph() { + let mut metagraph = SelectiveMetagraph::::default(); + let expected = SelectiveMetagraph:: { + netuid: 0_u16.into(), + name: None, + symbol: None, + identity: None, + network_registered_at: None, + owner_hotkey: None, + owner_coldkey: None, + block: None, + tempo: None, + last_step: None, + blocks_since_last_step: None, + subnet_emission: None, + alpha_in: None, + alpha_out: None, + tao_in: None, + alpha_out_emission: None, + alpha_in_emission: None, + tao_in_emission: None, + pending_alpha_emission: None, + pending_root_emission: None, + subnet_volume: None, + moving_price: None, + rho: None, + kappa: None, + min_allowed_weights: None, + max_weights_limit: None, + weights_version: None, + weights_rate_limit: None, + activity_cutoff: None, + max_validators: None, + num_uids: None, + max_uids: None, + burn: None, + difficulty: None, + registration_allowed: None, + pow_registration_allowed: None, + immunity_period: None, + min_difficulty: None, + max_difficulty: None, + min_burn: None, + max_burn: None, + adjustment_alpha: None, + adjustment_interval: None, + target_regs_per_interval: None, + max_regs_per_block: None, + serving_rate_limit: None, + commit_reveal_weights_enabled: None, + commit_reveal_period: None, + liquid_alpha_enabled: None, + alpha_high: None, + alpha_low: None, + bonds_moving_avg: None, + hotkeys: None, + coldkeys: None, + identities: None, + axons: None, + active: None, + validator_permit: None, + pruning_score: None, + last_update: None, + emission: None, + dividends: None, + incentives: None, + consensus: None, + trust: None, + rank: None, + block_at_registration: None, + alpha_stake: None, + tao_stake: None, + total_stake: None, + tao_dividends_per_hotkey: None, + alpha_dividends_per_hotkey: None, + }; + + // test init value + assert_eq!(metagraph, expected); + + let wrong_index: usize = 100; + let metagraph_name = SelectiveMetagraph:: { + netuid: 0_u16.into(), + name: Some(vec![1_u8].into_iter().map(Compact).collect()), + ..Default::default() + }; + + // test merge function + metagraph.merge_value(&metagraph_name, wrong_index); + assert!(metagraph.name.is_none()); + + let name_index: usize = 0; + metagraph.merge_value(&metagraph_name, name_index); + assert!(metagraph.name.is_some()); + + let alph_low_index: usize = 49; + let metagraph_alpha_low = SelectiveMetagraph:: { + netuid: 0_u16.into(), + alpha_low: Some(0_16.into()), + ..Default::default() + }; + assert!(metagraph.alpha_low.is_none()); + metagraph.merge_value(&metagraph_alpha_low, alph_low_index); + assert!(metagraph.alpha_low.is_some()); +} From a90ae123de2325104235feac13d39907cdcb9d8a Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 26 Mar 2025 22:34:29 +0800 Subject: [PATCH 07/45] fix clippy --- pallets/subtensor/src/rpc_info/metagraph.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index 255e5a30fb..fe6ab0ed90 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -1458,7 +1458,7 @@ fn test_selective_metagraph() { let alph_low_index: usize = 49; let metagraph_alpha_low = SelectiveMetagraph:: { netuid: 0_u16.into(), - alpha_low: Some(0_16.into()), + alpha_low: Some(0_u16.into()), ..Default::default() }; assert!(metagraph.alpha_low.is_none()); From 62f8189455baa938dda58cf00c2d87ee9426bb41 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 26 Mar 2025 23:47:27 +0800 Subject: [PATCH 08/45] update version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index b18a2a9c1f..40cd4fe660 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -207,7 +207,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: 255, + spec_version: 256, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 41d3bf9a1cd2b2fb463722072f6faa70e537e3c6 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Wed, 26 Mar 2025 17:33:45 -0700 Subject: [PATCH 09/45] just store decrypted bytes --- pallets/commitments/src/lib.rs | 46 +++++----------------------------- 1 file changed, 6 insertions(+), 40 deletions(-) diff --git a/pallets/commitments/src/lib.rs b/pallets/commitments/src/lib.rs index a62084de5c..3707eeeda3 100644 --- a/pallets/commitments/src/lib.rs +++ b/pallets/commitments/src/lib.rs @@ -172,7 +172,7 @@ pub mod pallet { u16, Twox64Concat, T::AccountId, - RevealedData, T::MaxFields, BlockNumberFor>, + (Vec, u64), // Data, RevealBlock OptionQuery, >; @@ -478,7 +478,6 @@ where impl Pallet { pub fn reveal_timelocked_commitments() -> DispatchResult { - let current_block = >::block_number(); let index = TimelockedIndex::::get(); for (netuid, who) in index.clone() { let Some(mut registration) = >::get(netuid, &who) else { @@ -570,50 +569,17 @@ impl Pallet { continue; } - let mut reader = &decrypted_bytes[..]; - let revealed_info: CommitmentInfo = - match Decode::decode(&mut reader) { - Ok(info) => info, - Err(e) => { - log::warn!( - "Failed to decode decrypted data for {:?}: {:?}", - who, - e - ); - remain_fields.push(Data::TimelockEncrypted { - encrypted, - reveal_round, - }); - continue; - } - }; - - revealed_fields.push(revealed_info); + revealed_fields.push(decrypted_bytes); + } other => remain_fields.push(other), } } - if !revealed_fields.is_empty() { - let mut all_revealed_data = Vec::new(); - for info in revealed_fields { - all_revealed_data.extend(info.fields.into_inner()); - } - - let bounded_revealed = BoundedVec::try_from(all_revealed_data) - .map_err(|_| "Could not build BoundedVec for revealed fields")?; - - let combined_revealed_info = CommitmentInfo { - fields: bounded_revealed, - }; - - let revealed_data = RevealedData { - info: combined_revealed_info, - revealed_block: current_block, - deposit: registration.deposit, - }; - >::insert(netuid, &who, revealed_data); + let current_block = >::block_number(); + for field in revealed_fields { + >::insert(netuid, &who, (field, current_block.saturated_into::())); Self::deposit_event(Event::CommitmentRevealed { netuid, who: who.clone(), From 6bcbafcc6fffc5563e15bb1040330c743528f66a Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Wed, 26 Mar 2025 17:37:58 -0700 Subject: [PATCH 10/45] fix test compile errors --- pallets/commitments/src/tests.rs | 74 ++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index f03e99080e..e06b906709 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -16,6 +16,7 @@ use frame_support::{ traits::{Currency, Get, ReservableCurrency}, }; use frame_system::Pallet as System; +use sp_core::Decode; #[allow(clippy::indexing_slicing)] #[test] @@ -319,18 +320,17 @@ fn happy_path_timelock_commitments() { let revealed = RevealedCommitments::::get(netuid, who).expect("Should have revealed data"); - let revealed_inner = &revealed.info; - assert_eq!(revealed_inner.fields.len(), 1); - match &revealed_inner.fields[0] { - Data::Raw(bounded_bytes) => { - assert_eq!( - bounded_bytes.as_slice(), - message_text, - "Decrypted text from on-chain storage must match the original message" - ); - } - other => panic!("Expected Data::Raw(...) in revealed, got {:?}", other), - } + let (revealed_bytes, _reveal_block) = revealed; + + let revealed_str = sp_std::str::from_utf8(&revealed_bytes) + .expect("Expected valid UTF-8 in the revealed bytes for this test"); + + let original_str = sp_std::str::from_utf8(message_text) + .expect("`message_text` is valid UTF-8"); + assert!( + revealed_str.contains(original_str), + "Revealed data must contain the original message text." + ); }); } @@ -529,14 +529,23 @@ fn reveal_timelocked_commitment_single_field_entry_is_removed_after_reveal() { System::::set_block_number(9999); assert_ok!(Pallet::::reveal_timelocked_commitments()); - let revealed = + let (revealed_bytes, _reveal_block) = RevealedCommitments::::get(netuid, who).expect("Expected to find revealed data"); + + // The decrypted bytes have some extra SCALE metadata in front: + // we slice off the first two bytes before checking the string. + let offset = 2; + let truncated = &revealed_bytes[offset..]; + let revealed_str = sp_std::str::from_utf8(truncated) + .expect("Truncated bytes should be valid UTF-8 in this test"); + + let original_str = sp_std::str::from_utf8(message_text) + .expect("`message_text` should be valid UTF-8"); assert_eq!( - revealed.info.fields.len(), - 1, - "Should have exactly 1 revealed field" + revealed_str, + original_str, + "Expected the revealed data (minus prefix) to match the original message" ); - assert!( crate::CommitmentOf::::get(netuid, who).is_none(), "Expected CommitmentOf entry to be removed after reveal" @@ -641,14 +650,21 @@ fn reveal_timelocked_multiple_fields_only_correct_ones_removed() { let revealed_data = RevealedCommitments::::get(netuid, who) .expect("Expected revealed data for TLE #1 and #2"); + let (revealed_bytes, _reveal_block) = revealed_data; + + let revealed_info: CommitmentInfo<::MaxFields> = + Decode::decode(&mut &revealed_bytes[..]) + .expect("Decoding must not fail for multiple timelock test"); + assert_eq!( - revealed_data.info.fields.len(), + revealed_info.fields.len(), 2, "We revealed both TLE #1 and TLE #2 in the same pass" ); + let mut found_msg1 = false; let mut found_msg2 = false; - for item in &revealed_data.info.fields { + for item in &revealed_info.fields { if let Data::Raw(bytes) = item { if bytes.as_slice() == msg_1 { found_msg1 = true; @@ -661,7 +677,6 @@ fn reveal_timelocked_multiple_fields_only_correct_ones_removed() { found_msg1 && found_msg2, "Should see both TLE #1 and TLE #2 in the revealed data" ); - // 9) A second reveal call now does nothing, because no timelocks remain System::::set_block_number(51); assert_ok!(Pallet::::reveal_timelocked_commitments()); @@ -1214,13 +1229,18 @@ fn on_initialize_reveals_matured_timelocks() { "No longer in TimelockedIndex after reveal." ); - let revealed_data = revealed_opt.expect("expected to not panic"); - assert_eq!(revealed_data.info.fields.len(), 1); - if let Data::Raw(bound_bytes) = &revealed_data.info.fields[0] { - assert_eq!(bound_bytes.as_slice(), message_text); - } else { - panic!("Expected a Data::Raw variant in revealed data."); - } + let (revealed_bytes, reveal_block) = revealed_opt.expect("expected to not panic"); + assert_eq!(reveal_block, 2, "Should have revealed at block #2"); + + let revealed_str = sp_std::str::from_utf8(&revealed_bytes) + .expect("Expected valid UTF-8 in the revealed bytes for this test"); + + let original_str = sp_std::str::from_utf8(message_text) + .expect("`message_text` is valid UTF-8"); + assert!( + revealed_str.contains(original_str), + "Revealed data must contain the original message text." + ); }); } From 67c0044a5b9d04b441d50616f76ddafd8a0b4f43 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Wed, 26 Mar 2025 17:50:17 -0700 Subject: [PATCH 11/45] rm deprecated test --- pallets/commitments/src/tests.rs | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index e06b906709..9dfe806d52 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -450,37 +450,6 @@ fn reveal_timelocked_commitment_empty_decrypted_data_is_skipped() { }); } -#[test] -fn reveal_timelocked_commitment_decode_failure_is_skipped() { - new_test_ext().execute_with(|| { - let who = 999; - let netuid = 8; - let commit_block = 42u64; - System::::set_block_number(commit_block); - let plaintext = vec![0xAA, 0xBB, 0xCC, 0xDD, 0xEE]; - let reveal_round = 1000; - let real_ct = produce_ciphertext(&plaintext, reveal_round); - let data = Data::TimelockEncrypted { - encrypted: real_ct, - reveal_round, - }; - let fields = BoundedVec::try_from(vec![data]).expect("Expected not to panic"); - let info = CommitmentInfo { fields }; - let origin = RuntimeOrigin::signed(who); - assert_ok!(Pallet::::set_commitment( - origin, - netuid, - Box::new(info) - )); - let sig_bytes = - hex::decode(DRAND_QUICKNET_SIG_HEX.as_bytes()).expect("Expected not to panic"); - insert_drand_pulse(reveal_round, &sig_bytes); - System::::set_block_number(9999); - assert_ok!(Pallet::::reveal_timelocked_commitments()); - assert!(RevealedCommitments::::get(netuid, who).is_none()); - }); -} - #[test] fn reveal_timelocked_commitment_single_field_entry_is_removed_after_reveal() { new_test_ext().execute_with(|| { From af123953fd886b2cd61d200b1ffef01aa6147298 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Wed, 26 Mar 2025 18:21:34 -0700 Subject: [PATCH 12/45] keep all revealed instead of replacing --- pallets/commitments/src/lib.rs | 28 +++++++++++++++------- pallets/commitments/src/tests.rs | 41 ++++++++++---------------------- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/pallets/commitments/src/lib.rs b/pallets/commitments/src/lib.rs index 3707eeeda3..777d8c91d7 100644 --- a/pallets/commitments/src/lib.rs +++ b/pallets/commitments/src/lib.rs @@ -172,7 +172,7 @@ pub mod pallet { u16, Twox64Concat, T::AccountId, - (Vec, u64), // Data, RevealBlock + Vec<(Vec, u64)>, // Reveals<(Data, RevealBlock)> OptionQuery, >; @@ -577,15 +577,27 @@ impl Pallet { } } - let current_block = >::block_number(); - for field in revealed_fields { - >::insert(netuid, &who, (field, current_block.saturated_into::())); - Self::deposit_event(Event::CommitmentRevealed { - netuid, - who: who.clone(), - }); + if !revealed_fields.is_empty() { + let mut existing_reveals = + RevealedCommitments::::get(netuid, &who).unwrap_or_default(); + + let current_block = >::block_number(); + let block_u64 = current_block.saturated_into::(); + + // Push newly revealed items onto the tail of existing_reveals and emit the event + for revealed_bytes in revealed_fields { + existing_reveals.push((revealed_bytes, block_u64)); + + Self::deposit_event(Event::CommitmentRevealed { + netuid, + who: who.clone(), + }); + } + + RevealedCommitments::::insert(netuid, &who, existing_reveals); } + registration.info.fields = BoundedVec::try_from(remain_fields) .map_err(|_| "Failed to build BoundedVec for remain_fields")?; diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index 9dfe806d52..da66857a46 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -16,7 +16,6 @@ use frame_support::{ traits::{Currency, Get, ReservableCurrency}, }; use frame_system::Pallet as System; -use sp_core::Decode; #[allow(clippy::indexing_slicing)] #[test] @@ -320,7 +319,7 @@ fn happy_path_timelock_commitments() { let revealed = RevealedCommitments::::get(netuid, who).expect("Should have revealed data"); - let (revealed_bytes, _reveal_block) = revealed; + let (revealed_bytes, _reveal_block) = revealed[0].clone(); let revealed_str = sp_std::str::from_utf8(&revealed_bytes) .expect("Expected valid UTF-8 in the revealed bytes for this test"); @@ -498,8 +497,8 @@ fn reveal_timelocked_commitment_single_field_entry_is_removed_after_reveal() { System::::set_block_number(9999); assert_ok!(Pallet::::reveal_timelocked_commitments()); - let (revealed_bytes, _reveal_block) = - RevealedCommitments::::get(netuid, who).expect("Expected to find revealed data"); + let revealed = RevealedCommitments::::get(netuid, who).expect("Expected to find revealed data"); + let (revealed_bytes, _reveal_block) = revealed[0].clone(); // The decrypted bytes have some extra SCALE metadata in front: // we slice off the first two bytes before checking the string. @@ -619,33 +618,17 @@ fn reveal_timelocked_multiple_fields_only_correct_ones_removed() { let revealed_data = RevealedCommitments::::get(netuid, who) .expect("Expected revealed data for TLE #1 and #2"); - let (revealed_bytes, _reveal_block) = revealed_data; + let (revealed_bytes1, reveal_block1) = revealed_data[0].clone(); + let (revealed_bytes2, reveal_block2) = revealed_data[1].clone(); - let revealed_info: CommitmentInfo<::MaxFields> = - Decode::decode(&mut &revealed_bytes[..]) - .expect("Decoding must not fail for multiple timelock test"); + let truncated1 = &revealed_bytes1[2..]; + let truncated2 = &revealed_bytes2[2..]; - assert_eq!( - revealed_info.fields.len(), - 2, - "We revealed both TLE #1 and TLE #2 in the same pass" - ); + assert_eq!(truncated1, msg_1); + assert_eq!(reveal_block1, 50); + assert_eq!(truncated2, msg_2); + assert_eq!(reveal_block2, 50); - let mut found_msg1 = false; - let mut found_msg2 = false; - for item in &revealed_info.fields { - if let Data::Raw(bytes) = item { - if bytes.as_slice() == msg_1 { - found_msg1 = true; - } else if bytes.as_slice() == msg_2 { - found_msg2 = true; - } - } - } - assert!( - found_msg1 && found_msg2, - "Should see both TLE #1 and TLE #2 in the revealed data" - ); // 9) A second reveal call now does nothing, because no timelocks remain System::::set_block_number(51); assert_ok!(Pallet::::reveal_timelocked_commitments()); @@ -1198,7 +1181,7 @@ fn on_initialize_reveals_matured_timelocks() { "No longer in TimelockedIndex after reveal." ); - let (revealed_bytes, reveal_block) = revealed_opt.expect("expected to not panic"); + let (revealed_bytes, reveal_block) = revealed_opt.expect("expected to not panic")[0].clone(); assert_eq!(reveal_block, 2, "Should have revealed at block #2"); let revealed_str = sp_std::str::from_utf8(&revealed_bytes) From ad0ae60881c7b4e971bedbb157bd096f91515ec2 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 28 Mar 2025 10:22:09 +0800 Subject: [PATCH 13/45] commit Cargo.lock --- pallets/subtensor/src/staking/helpers.rs | 9 +++++ precompiles/src/lib.rs | 6 ++-- precompiles/src/staking.rs | 45 ++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 5aff56ea28..55e5cb854d 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -277,4 +277,13 @@ impl Pallet { Ok(credit) } + + // Returns the total amount of stake under a hotkey (delegative or otherwise) on a subnet + // + pub fn get_total_stake_for_hotkey_on_netuid(hotkey: &T::AccountId, netuid: u16) -> u64 { + let alpha: I96F32 = + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + let tao_price: I96F32 = Self::get_alpha_price(netuid); + alpha.saturating_mul(tao_price).saturating_to_num::() + } } diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs index 42f250d563..ed0c2222a2 100644 --- a/precompiles/src/lib.rs +++ b/precompiles/src/lib.rs @@ -45,7 +45,7 @@ where + pallet_admin_utils::Config + pallet_subtensor::Config + pallet_proxy::Config, - R::AccountId: From<[u8; 32]> + ByteArray, + R::AccountId: From<[u8; 32]> + ByteArray + Into<[u8; 32]>, ::RuntimeCall: From> + From> + From> @@ -69,7 +69,7 @@ where + pallet_admin_utils::Config + pallet_subtensor::Config + pallet_proxy::Config, - R::AccountId: From<[u8; 32]> + ByteArray, + R::AccountId: From<[u8; 32]> + ByteArray + Into<[u8; 32]>, ::RuntimeCall: From> + From> + From> @@ -111,7 +111,7 @@ where + pallet_admin_utils::Config + pallet_subtensor::Config + pallet_proxy::Config, - R::AccountId: From<[u8; 32]> + ByteArray, + R::AccountId: From<[u8; 32]> + ByteArray + Into<[u8; 32]>, ::RuntimeCall: From> + From> + From> diff --git a/precompiles/src/staking.rs b/precompiles/src/staking.rs index 192e55f57c..8e6454d5cc 100644 --- a/precompiles/src/staking.rs +++ b/precompiles/src/staking.rs @@ -25,8 +25,8 @@ // - Precompile checks the result of do_remove_stake and, in case of a failure, reverts the transaction. // +use alloc::vec::Vec; use core::marker::PhantomData; - use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo}; use frame_system::RawOrigin; use pallet_evm::{ @@ -35,6 +35,7 @@ use pallet_evm::{ use precompile_utils::EvmResult; use sp_core::{H256, U256}; use sp_runtime::traits::{Dispatchable, StaticLookup, UniqueSaturatedInto}; +use sp_std::vec; use subtensor_runtime_common::ProxyType; use crate::{PrecompileExt, PrecompileHandleExt}; @@ -52,7 +53,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_proxy::Config, - R::AccountId: From<[u8; 32]>, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::RuntimeCall: From> + From> + GetDispatchInfo @@ -70,7 +71,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_proxy::Config, - R::AccountId: From<[u8; 32]>, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::RuntimeCall: From> + From> + GetDispatchInfo @@ -161,6 +162,44 @@ where Ok(stake.into()) } + #[precompile::public("getAlphaStakedValidators(bytes32,uint256)")] + #[precompile::view] + fn get_alpha_staked_validators( + _handle: &mut impl PrecompileHandle, + hotkey: H256, + netuid: U256, + ) -> EvmResult> { + let hotkey = R::AccountId::from(hotkey.0); + let mut coldkeys: Vec = vec![]; + let netuid = try_u16_from_u256(netuid)?; + for ((coldkey, netuid_in_alpha), _) in pallet_subtensor::Alpha::::iter_prefix((hotkey,)) + { + if netuid == netuid_in_alpha { + let key: [u8; 32] = coldkey.try_into().map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("the value is outside of u16 bounds".into()), + })?; + + coldkeys.push(key.into()); + } + } + + Ok(coldkeys) + } + + #[precompile::public("getTotalAlphaStaked(bytes32,uint256)")] + #[precompile::view] + fn get_total_alpha_staked( + _handle: &mut impl PrecompileHandle, + hotkey: H256, + netuid: U256, + ) -> EvmResult { + let hotkey = R::AccountId::from(hotkey.0); + let netuid = try_u16_from_u256(netuid)?; + let stake = pallet_subtensor::Pallet::::get_stake_for_hotkey_on_subnet(&hotkey, netuid); + + Ok(stake.into()) + } + #[precompile::public("addProxy(bytes32)")] fn add_proxy(handle: &mut impl PrecompileHandle, delegate: H256) -> EvmResult<()> { let account_id = handle.caller_account_id::(); From c38dde8efa7dc03dd7464015c82424fbabdbe585 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 28 Mar 2025 10:23:30 +0800 Subject: [PATCH 14/45] cargo fix --- precompiles/src/staking.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/precompiles/src/staking.rs b/precompiles/src/staking.rs index 8e6454d5cc..52ce687351 100644 --- a/precompiles/src/staking.rs +++ b/precompiles/src/staking.rs @@ -175,10 +175,7 @@ where for ((coldkey, netuid_in_alpha), _) in pallet_subtensor::Alpha::::iter_prefix((hotkey,)) { if netuid == netuid_in_alpha { - let key: [u8; 32] = coldkey.try_into().map_err(|_| PrecompileFailure::Error { - exit_status: ExitError::Other("the value is outside of u16 bounds".into()), - })?; - + let key: [u8; 32] = coldkey.into(); coldkeys.push(key.into()); } } From 7f224355ff4968ad20ee11fc4c3d0b872a436ea0 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 28 Mar 2025 14:10:12 +0800 Subject: [PATCH 15/45] remove unused func --- pallets/subtensor/src/staking/helpers.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 55e5cb854d..5aff56ea28 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -277,13 +277,4 @@ impl Pallet { Ok(credit) } - - // Returns the total amount of stake under a hotkey (delegative or otherwise) on a subnet - // - pub fn get_total_stake_for_hotkey_on_netuid(hotkey: &T::AccountId, netuid: u16) -> u64 { - let alpha: I96F32 = - I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); - let tao_price: I96F32 = Self::get_alpha_price(netuid); - alpha.saturating_mul(tao_price).saturating_to_num::() - } } From 0824f1df584d84b88c59d9454ba58d2bb4f7e5b0 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 28 Mar 2025 14:22:15 +0800 Subject: [PATCH 16/45] update solidity --- precompiles/src/solidity/stakingV2.abi | 48 ++++++++++++++++++++++++++ precompiles/src/solidity/stakingV2.sol | 44 +++++++++++++++++++++-- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/precompiles/src/solidity/stakingV2.abi b/precompiles/src/solidity/stakingV2.abi index 21dd2761e4..b0cc272ab8 100644 --- a/precompiles/src/solidity/stakingV2.abi +++ b/precompiles/src/solidity/stakingV2.abi @@ -35,6 +35,30 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "netuid", + "type": "uint256" + } + ], + "name": "getAlphaStakedValidators", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -64,6 +88,30 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "netuid", + "type": "uint256" + } + ], + "name": "getTotalAlphaStaked", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/precompiles/src/solidity/stakingV2.sol b/precompiles/src/solidity/stakingV2.sol index 67ac0cb129..ce15448f1e 100644 --- a/precompiles/src/solidity/stakingV2.sol +++ b/precompiles/src/solidity/stakingV2.sol @@ -20,7 +20,11 @@ interface IStaking { * - `hotkey` must be a valid hotkey registered on the network, ensuring that the stake is * correctly attributed. */ - function addStake(bytes32 hotkey, uint256 amount, uint256 netuid) external payable; + function addStake( + bytes32 hotkey, + uint256 amount, + uint256 netuid + ) external payable; /** * @dev Removes a subtensor stake `amount` from the specified `hotkey`. @@ -56,7 +60,9 @@ interface IStaking { * @param coldkey The coldkey public key (32 bytes). * @return The amount of RAO staked by the coldkey. */ - function getTotalColdkeyStake(bytes32 coldkey) external view returns (uint256); + function getTotalColdkeyStake( + bytes32 coldkey + ) external view returns (uint256); /** * @dev Returns the total amount of stake under a hotkey (delegative or otherwise) @@ -68,7 +74,9 @@ interface IStaking { * @param hotkey The hotkey public key (32 bytes). * @return The total amount of RAO staked under the hotkey. */ - function getTotalHotkeyStake(bytes32 hotkey) external view returns (uint256); + function getTotalHotkeyStake( + bytes32 hotkey + ) external view returns (uint256); /** * @dev Returns the stake amount associated with the specified `hotkey` and `coldkey`. @@ -100,4 +108,34 @@ interface IStaking { * @param delegate The public key (32 bytes) of the delegate. */ function removeProxy(bytes32 delegate) external; + + /** + * @dev Returns the validators that have staked alpha under a hotkey. + * + * This function retrieves the validators that have staked alpha under a specific hotkey. + * It is a view function, meaning it does not modify the state of the contract and is free to call. + * + * @param hotkey The hotkey public key (32 bytes). + * @param netuid The subnet the stake is on (uint256). + * @return An array of validators that have staked alpha under the hotkey. + */ + function getAlphaStakedValidators( + bytes32 hotkey, + uint256 netuid + ) external view returns (uint256[] memory); + + /** + * @dev Returns the total amount of alpha staked under a hotkey. + * + * This function retrieves the total amount of alpha staked under a specific hotkey. + * It is a view function, meaning it does not modify the state of the contract and is free to call. + * + * @param hotkey The hotkey public key (32 bytes). + * @param netuid The subnet the stake is on (uint256). + * @return The total amount of alpha staked under the hotkey. + */ + function getTotalAlphaStaked( + bytes32 hotkey, + uint256 netuid + ) external view returns (uint256); } From 6ee0b21d2ae7c762699447f32daf19af5ec77605 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 28 Mar 2025 14:30:00 +0800 Subject: [PATCH 17/45] bump runtime version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index e9ead1812f..dc00e1d2fa 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -207,7 +207,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: 257, + spec_version: 258, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From c513a4ee4d11ca140b74c3e3e20eef30ca17b44f Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 28 Mar 2025 16:57:43 +0800 Subject: [PATCH 18/45] add new evm test case --- .../test/staking.precompile.stake-get.test.ts | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 evm-tests/test/staking.precompile.stake-get.test.ts diff --git a/evm-tests/test/staking.precompile.stake-get.test.ts b/evm-tests/test/staking.precompile.stake-get.test.ts new file mode 100644 index 0000000000..493b593930 --- /dev/null +++ b/evm-tests/test/staking.precompile.stake-get.test.ts @@ -0,0 +1,80 @@ +import * as assert from "assert"; +import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate" +import { devnet } from "@polkadot-api/descriptors" +import { TypedApi } from "polkadot-api"; +import { convertPublicKeyToSs58 } from "../src/address-utils" +import { tao } from "../src/balance-math" +import { + forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister, + setTxRateLimit, setTempo, setWeightsSetRateLimit, setSubnetOwnerCut, setMaxAllowedUids, + setMinDelegateTake, becomeDelegate, setActivityCutoff, addStake, setWeight, rootRegister +} from "../src/subtensor" +import { PublicClient } from "viem"; +import { generateRandomEthersWallet, getPublicClient } from "../src/utils" +import { ISTAKING_ADDRESS, ISTAKING_V2_ADDRESS, IStakingABI, IStakingV2ABI } from "../src/contracts/staking" +import { ETH_LOCAL_URL } from "../src/config"; + +describe("Test neuron precompile reveal weights", () => { + const hotkey = getRandomSubstrateKeypair(); + const coldkey = getRandomSubstrateKeypair(); + let publicClient: PublicClient; + + // const validator = getRandomSubstrateKeypair(); + // const miner = getRandomSubstrateKeypair(); + // const nominator = getRandomSubstrateKeypair(); + + let api: TypedApi + + before(async () => { + const root_netuid = 0; + const root_tempo = 1; // neet root epoch to happen before subnet tempo + const subnet_tempo = 1; + publicClient = await getPublicClient(ETH_LOCAL_URL) + api = await getDevnetApi() + + // await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(alice.publicKey)) + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) + // await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(validator.publicKey)) + // await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(miner.publicKey)) + // await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(nominator.publicKey)) + // await forceSetBalanceToEthAddress(api, wallet1.address) + // await forceSetBalanceToEthAddress(api, wallet2.address) + let netuid = await addNewSubnetwork(api, hotkey, coldkey) + + console.log("test the case on subnet ", netuid) + + await setTxRateLimit(api, BigInt(0)) + await setTempo(api, root_netuid, root_tempo) + await setTempo(api, netuid, subnet_tempo) + await setWeightsSetRateLimit(api, netuid, BigInt(0)) + + // await burnedRegister(api, netuid, convertPublicKeyToSs58(validator.publicKey), coldkey) + // await burnedRegister(api, netuid, convertPublicKeyToSs58(miner.publicKey), coldkey) + // await burnedRegister(api, netuid, convertPublicKeyToSs58(nominator.publicKey), coldkey) + await setSubnetOwnerCut(api, 0) + await setActivityCutoff(api, netuid, 65535) + await setMaxAllowedUids(api, netuid, 65535) + await setMinDelegateTake(api, 0) + // await becomeDelegate(api, convertPublicKeyToSs58(validator.publicKey), coldkey) + // await becomeDelegate(api, convertPublicKeyToSs58(miner.publicKey), coldkey) + }) + + it("Staker receives rewards", async () => { + let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 + + await addStake(api, netuid, convertPublicKeyToSs58(hotkey.publicKey), tao(1), coldkey) + + const value = await publicClient.readContract({ + address: ISTAKING_ADDRESS, + abi: IStakingABI, + functionName: "getStake", + args: [hotkey.publicKey, // Convert to bytes32 format + hotkey.publicKey, + netuid] + }) + + console.log(value) + + }) +}) From f5ddec8a6020063f0c30a20502e04f4ed74a086e Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 28 Mar 2025 21:09:33 +0800 Subject: [PATCH 19/45] add test file --- evm-tests/src/config.ts | 2 +- evm-tests/src/contracts/staking.ts | 48 +++++++++++ .../test/staking.precompile.stake-get.test.ts | 79 +++++++------------ 3 files changed, 77 insertions(+), 52 deletions(-) diff --git a/evm-tests/src/config.ts b/evm-tests/src/config.ts index 601c89c8c1..00b942f802 100644 --- a/evm-tests/src/config.ts +++ b/evm-tests/src/config.ts @@ -2,7 +2,7 @@ export const ETH_LOCAL_URL = 'http://localhost:9944' export const SUB_LOCAL_URL = 'ws://localhost:9944' export const SS58_PREFIX = 42; // set the tx timeout as 2 second when eable the fast-blocks feature. -export const TX_TIMEOUT = 2000; +export const TX_TIMEOUT = 3000; export const IED25519VERIFY_ADDRESS = "0x0000000000000000000000000000000000000402"; export const IEd25519VerifyABI = [ diff --git a/evm-tests/src/contracts/staking.ts b/evm-tests/src/contracts/staking.ts index 9a30d307ba..af4422ca96 100644 --- a/evm-tests/src/contracts/staking.ts +++ b/evm-tests/src/contracts/staking.ts @@ -137,6 +137,30 @@ export const IStakingV2ABI = [ "stateMutability": "payable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "netuid", + "type": "uint256" + } + ], + "name": "getAlphaStakedValidators", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -166,6 +190,30 @@ export const IStakingV2ABI = [ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "netuid", + "type": "uint256" + } + ], + "name": "getTotalAlphaStaked", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/evm-tests/test/staking.precompile.stake-get.test.ts b/evm-tests/test/staking.precompile.stake-get.test.ts index 493b593930..37a23d8db2 100644 --- a/evm-tests/test/staking.precompile.stake-get.test.ts +++ b/evm-tests/test/staking.precompile.stake-get.test.ts @@ -5,59 +5,26 @@ import { TypedApi } from "polkadot-api"; import { convertPublicKeyToSs58 } from "../src/address-utils" import { tao } from "../src/balance-math" import { - forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister, - setTxRateLimit, setTempo, setWeightsSetRateLimit, setSubnetOwnerCut, setMaxAllowedUids, - setMinDelegateTake, becomeDelegate, setActivityCutoff, addStake, setWeight, rootRegister + forceSetBalanceToSs58Address, addNewSubnetwork, addStake, } from "../src/subtensor" -import { PublicClient } from "viem"; -import { generateRandomEthersWallet, getPublicClient } from "../src/utils" -import { ISTAKING_ADDRESS, ISTAKING_V2_ADDRESS, IStakingABI, IStakingV2ABI } from "../src/contracts/staking" -import { ETH_LOCAL_URL } from "../src/config"; +import { ethers } from "ethers"; +import { generateRandomEthersWallet } from "../src/utils" +import { ISTAKING_V2_ADDRESS, IStakingV2ABI } from "../src/contracts/staking" +import { log } from "console"; -describe("Test neuron precompile reveal weights", () => { +describe("Test staking precompile get methods", () => { const hotkey = getRandomSubstrateKeypair(); const coldkey = getRandomSubstrateKeypair(); - let publicClient: PublicClient; - - // const validator = getRandomSubstrateKeypair(); - // const miner = getRandomSubstrateKeypair(); - // const nominator = getRandomSubstrateKeypair(); + const wallet1 = generateRandomEthersWallet(); let api: TypedApi before(async () => { - const root_netuid = 0; - const root_tempo = 1; // neet root epoch to happen before subnet tempo - const subnet_tempo = 1; - publicClient = await getPublicClient(ETH_LOCAL_URL) api = await getDevnetApi() - - // await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(alice.publicKey)) await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - // await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(validator.publicKey)) - // await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(miner.publicKey)) - // await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(nominator.publicKey)) - // await forceSetBalanceToEthAddress(api, wallet1.address) - // await forceSetBalanceToEthAddress(api, wallet2.address) let netuid = await addNewSubnetwork(api, hotkey, coldkey) - - console.log("test the case on subnet ", netuid) - - await setTxRateLimit(api, BigInt(0)) - await setTempo(api, root_netuid, root_tempo) - await setTempo(api, netuid, subnet_tempo) - await setWeightsSetRateLimit(api, netuid, BigInt(0)) - - // await burnedRegister(api, netuid, convertPublicKeyToSs58(validator.publicKey), coldkey) - // await burnedRegister(api, netuid, convertPublicKeyToSs58(miner.publicKey), coldkey) - // await burnedRegister(api, netuid, convertPublicKeyToSs58(nominator.publicKey), coldkey) - await setSubnetOwnerCut(api, 0) - await setActivityCutoff(api, netuid, 65535) - await setMaxAllowedUids(api, netuid, 65535) - await setMinDelegateTake(api, 0) - // await becomeDelegate(api, convertPublicKeyToSs58(validator.publicKey), coldkey) - // await becomeDelegate(api, convertPublicKeyToSs58(miner.publicKey), coldkey) + console.log("will test in subnet: ", netuid) }) it("Staker receives rewards", async () => { @@ -65,16 +32,26 @@ describe("Test neuron precompile reveal weights", () => { await addStake(api, netuid, convertPublicKeyToSs58(hotkey.publicKey), tao(1), coldkey) - const value = await publicClient.readContract({ - address: ISTAKING_ADDRESS, - abi: IStakingABI, - functionName: "getStake", - args: [hotkey.publicKey, // Convert to bytes32 format - hotkey.publicKey, - netuid] - }) - - console.log(value) + const contract = new ethers.Contract( + ISTAKING_V2_ADDRESS, + IStakingV2ABI, + wallet1 + ); + + const stake = BigInt( + await contract.getStake(hotkey.publicKey, coldkey.publicKey, netuid) + ); + + // validator returned as bigint now. + const validators = + await contract.getAlphaStakedValidators(hotkey.publicKey, netuid) + + const alpha = BigInt( + await contract.getTotalAlphaStaked(hotkey.publicKey, netuid) + ); + assert.ok(stake > 0) + assert.equal(validators.length, 1) + assert.ok(alpha > 0) }) }) From aa2f376d7936e4b619244943e0659923129147fd Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 10:05:18 -0700 Subject: [PATCH 20/45] fmt --- pallets/commitments/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pallets/commitments/src/lib.rs b/pallets/commitments/src/lib.rs index 777d8c91d7..037e10d00d 100644 --- a/pallets/commitments/src/lib.rs +++ b/pallets/commitments/src/lib.rs @@ -570,7 +570,6 @@ impl Pallet { } revealed_fields.push(decrypted_bytes); - } other => remain_fields.push(other), @@ -581,7 +580,7 @@ impl Pallet { let mut existing_reveals = RevealedCommitments::::get(netuid, &who).unwrap_or_default(); - let current_block = >::block_number(); + let current_block = >::block_number(); let block_u64 = current_block.saturated_into::(); // Push newly revealed items onto the tail of existing_reveals and emit the event @@ -597,7 +596,6 @@ impl Pallet { RevealedCommitments::::insert(netuid, &who, existing_reveals); } - registration.info.fields = BoundedVec::try_from(remain_fields) .map_err(|_| "Failed to build BoundedVec for remain_fields")?; From 05a31c9922ff76891be77c0880ee77e2735902dc Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 10:05:39 -0700 Subject: [PATCH 21/45] add test timelocked_index_complex_scenario_works --- pallets/commitments/src/mock.rs | 1 + pallets/commitments/src/tests.rs | 220 ++++++++++++++++++++++++++++--- 2 files changed, 205 insertions(+), 16 deletions(-) diff --git a/pallets/commitments/src/mock.rs b/pallets/commitments/src/mock.rs index cc2482ff88..c8f6b1e1b2 100644 --- a/pallets/commitments/src/mock.rs +++ b/pallets/commitments/src/mock.rs @@ -207,6 +207,7 @@ use tle::{ibe::fullident::Identity, stream_ciphers::AESGCMStreamCipherProvider, pub const DRAND_QUICKNET_PUBKEY_HEX: &str = "83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6\ a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809b\ d274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a"; +pub const DRAND_QUICKNET_SIG_2000_HEX: &str = "b6cb8f482a0b15d45936a4c4ea08e98a087e71787caee3f4d07a8a9843b1bc5423c6b3c22f446488b3137eaca799c77e"; // round 20000 pub const DRAND_QUICKNET_SIG_HEX: &str = "b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39"; /// Inserts a Drand pulse for `round` with the given `signature_bytes`. diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index da66857a46..9f9e8c9181 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -6,8 +6,8 @@ use crate::{ CommitmentInfo, CommitmentOf, Config, Data, Error, Event, MaxSpace, Pallet, RateLimit, Registration, RevealedCommitments, TimelockedIndex, mock::{ - Balances, DRAND_QUICKNET_SIG_HEX, RuntimeEvent, RuntimeOrigin, Test, insert_drand_pulse, - new_test_ext, produce_ciphertext, + Balances, DRAND_QUICKNET_SIG_2000_HEX, DRAND_QUICKNET_SIG_HEX, RuntimeEvent, RuntimeOrigin, + Test, TestMaxFields, insert_drand_pulse, new_test_ext, produce_ciphertext, }, }; use frame_support::pallet_prelude::Hooks; @@ -323,9 +323,9 @@ fn happy_path_timelock_commitments() { let revealed_str = sp_std::str::from_utf8(&revealed_bytes) .expect("Expected valid UTF-8 in the revealed bytes for this test"); - - let original_str = sp_std::str::from_utf8(message_text) - .expect("`message_text` is valid UTF-8"); + + let original_str = + sp_std::str::from_utf8(message_text).expect("`message_text` is valid UTF-8"); assert!( revealed_str.contains(original_str), "Revealed data must contain the original message text." @@ -497,21 +497,21 @@ fn reveal_timelocked_commitment_single_field_entry_is_removed_after_reveal() { System::::set_block_number(9999); assert_ok!(Pallet::::reveal_timelocked_commitments()); - let revealed = RevealedCommitments::::get(netuid, who).expect("Expected to find revealed data"); + let revealed = + RevealedCommitments::::get(netuid, who).expect("Expected to find revealed data"); let (revealed_bytes, _reveal_block) = revealed[0].clone(); // The decrypted bytes have some extra SCALE metadata in front: // we slice off the first two bytes before checking the string. - let offset = 2; + let offset = 2; let truncated = &revealed_bytes[offset..]; let revealed_str = sp_std::str::from_utf8(truncated) .expect("Truncated bytes should be valid UTF-8 in this test"); - let original_str = sp_std::str::from_utf8(message_text) - .expect("`message_text` should be valid UTF-8"); + let original_str = + sp_std::str::from_utf8(message_text).expect("`message_text` should be valid UTF-8"); assert_eq!( - revealed_str, - original_str, + revealed_str, original_str, "Expected the revealed data (minus prefix) to match the original message" ); assert!( @@ -625,7 +625,7 @@ fn reveal_timelocked_multiple_fields_only_correct_ones_removed() { let truncated2 = &revealed_bytes2[2..]; assert_eq!(truncated1, msg_1); - assert_eq!(reveal_block1, 50); + assert_eq!(reveal_block1, 50); assert_eq!(truncated2, msg_2); assert_eq!(reveal_block2, 50); @@ -1181,14 +1181,15 @@ fn on_initialize_reveals_matured_timelocks() { "No longer in TimelockedIndex after reveal." ); - let (revealed_bytes, reveal_block) = revealed_opt.expect("expected to not panic")[0].clone(); + let (revealed_bytes, reveal_block) = + revealed_opt.expect("expected to not panic")[0].clone(); assert_eq!(reveal_block, 2, "Should have revealed at block #2"); let revealed_str = sp_std::str::from_utf8(&revealed_bytes) .expect("Expected valid UTF-8 in the revealed bytes for this test"); - - let original_str = sp_std::str::from_utf8(message_text) - .expect("`message_text` is valid UTF-8"); + + let original_str = + sp_std::str::from_utf8(message_text).expect("`message_text` is valid UTF-8"); assert!( revealed_str.contains(original_str), "Revealed data must contain the original message text." @@ -1234,3 +1235,190 @@ fn set_commitment_unreserve_leftover_fails() { ); }); } + +#[test] +fn timelocked_index_complex_scenario_works() { + new_test_ext().execute_with(|| { + System::::set_block_number(1); + + let netuid = 42; + let user_a = 1000; + let user_b = 2000; + let user_c = 3000; + + let make_timelock_data = |plaintext: &[u8], round: u64| { + let inner = CommitmentInfo:: { + fields: BoundedVec::try_from(vec![Data::Raw( + plaintext.to_vec().try_into().expect("<=128 bytes"), + )]) + .expect("1 field is fine"), + }; + let ct = produce_ciphertext(&inner.encode(), round); + Data::TimelockEncrypted { + encrypted: ct, + reveal_round: round, + } + }; + + let make_raw_data = |payload: &[u8]| Data::Raw(payload.to_vec().try_into().unwrap()); + + // ---------------------------------------------------- + // (1) USER A => no timelocks => NOT in index + // ---------------------------------------------------- + let info_a1 = CommitmentInfo:: { + fields: BoundedVec::try_from(vec![make_raw_data(b"A-regular")]) + .expect("1 field is fine"), + }; + assert_ok!(Pallet::::set_commitment( + RuntimeOrigin::signed(user_a), + netuid, + Box::new(info_a1), + )); + assert!( + !TimelockedIndex::::get().contains(&(netuid, user_a)), + "A has no timelocks => not in TimelockedIndex" + ); + + // ---------------------------------------------------- + // (2) USER B => Single TLE => BUT USE round=2000! + // => B is in index + // ---------------------------------------------------- + let b_timelock_1 = make_timelock_data(b"B first TLE", 2000); + let info_b1 = CommitmentInfo:: { + fields: BoundedVec::try_from(vec![b_timelock_1]).expect("Single TLE is fine"), + }; + assert_ok!(Pallet::::set_commitment( + RuntimeOrigin::signed(user_b), + netuid, + Box::new(info_b1), + )); + let idx = TimelockedIndex::::get(); + assert!(!idx.contains(&(netuid, user_a)), "A not in index"); + assert!(idx.contains(&(netuid, user_b)), "B in index (has TLE)"); + + // ---------------------------------------------------- + // (3) USER A => 2 timelocks: round=1000 & round=2000 + // => A is in index + // ---------------------------------------------------- + let a_timelock_1 = make_timelock_data(b"A TLE #1", 1000); + let a_timelock_2 = make_timelock_data(b"A TLE #2", 2000); + let info_a2 = CommitmentInfo:: { + fields: BoundedVec::try_from(vec![a_timelock_1, a_timelock_2]) + .expect("2 TLE fields OK"), + }; + assert_ok!(Pallet::::set_commitment( + RuntimeOrigin::signed(user_a), + netuid, + Box::new(info_a2), + )); + + let idx = TimelockedIndex::::get(); + assert!(idx.contains(&(netuid, user_a)), "A in index"); + assert!(idx.contains(&(netuid, user_b)), "B still in index"); + + // ---------------------------------------------------- + // (4) USER B => remove all timelocks => B out of index + // ---------------------------------------------------- + let info_b2 = CommitmentInfo:: { + fields: BoundedVec::try_from(vec![make_raw_data(b"B back to raw")]) + .expect("no TLE => B out"), + }; + assert_ok!(Pallet::::set_commitment( + RuntimeOrigin::signed(user_b), + netuid, + Box::new(info_b2), + )); + let idx = TimelockedIndex::::get(); + assert!(idx.contains(&(netuid, user_a)), "A remains"); + assert!( + !idx.contains(&(netuid, user_b)), + "B removed after losing TLEs" + ); + + // ---------------------------------------------------- + // (5) USER B => re-add TLE => round=2000 => back in index + // ---------------------------------------------------- + let b_timelock_2 = make_timelock_data(b"B TLE #2", 2000); + let info_b3 = CommitmentInfo:: { + fields: BoundedVec::try_from(vec![b_timelock_2]).unwrap(), + }; + assert_ok!(Pallet::::set_commitment( + RuntimeOrigin::signed(user_b), + netuid, + Box::new(info_b3), + )); + let idx = TimelockedIndex::::get(); + assert!(idx.contains(&(netuid, user_a)), "A in index"); + assert!(idx.contains(&(netuid, user_b)), "B back in index"); + + // ---------------------------------------------------- + // (6) USER C => sets 1 TLE => round=2000 => in index + // ---------------------------------------------------- + let c_timelock_1 = make_timelock_data(b"C TLE #1", 2000); + let info_c1 = CommitmentInfo:: { + fields: BoundedVec::try_from(vec![c_timelock_1]).unwrap(), + }; + assert_ok!(Pallet::::set_commitment( + RuntimeOrigin::signed(user_c), + netuid, + Box::new(info_c1), + )); + let idx = TimelockedIndex::::get(); + assert!(idx.contains(&(netuid, user_a)), "A"); + assert!(idx.contains(&(netuid, user_b)), "B"); + assert!(idx.contains(&(netuid, user_c)), "C"); + + // ---------------------------------------------------- + // (7) Partial reveal for round=1000 => affects only A + // because B & C have round=2000 + // ---------------------------------------------------- + let drand_sig_1000 = + hex::decode(DRAND_QUICKNET_SIG_HEX).expect("decode signature for round=1000"); + insert_drand_pulse(1000, &drand_sig_1000); + + System::::set_block_number(10); + assert_ok!(Pallet::::reveal_timelocked_commitments()); + + // After revealing round=1000: + // - A: Loses TLE #1 (1000), still has TLE #2 (2000) => remains in index + // - B: referencing 2000 => unaffected => remains + // - C: referencing 2000 => remains + let idx = TimelockedIndex::::get(); + assert!( + idx.contains(&(netuid, user_a)), + "A has leftover round=2000 => remains in index" + ); + assert!(idx.contains(&(netuid, user_b)), "B unaffected"); + assert!(idx.contains(&(netuid, user_c)), "C unaffected"); + + // ---------------------------------------------------- + // (8) Reveal round=2000 => fully remove A, B, and C + // ---------------------------------------------------- + let drand_sig_2000 = + hex::decode(DRAND_QUICKNET_SIG_2000_HEX).expect("decode signature for round=2000"); + insert_drand_pulse(2000, &drand_sig_2000); + + System::::set_block_number(11); + assert_ok!(Pallet::::reveal_timelocked_commitments()); + + // Now: + // - A's final TLE (#2 at 2000) is removed => A out + // - B had 2000 => out + // - C had 2000 => out + let idx = TimelockedIndex::::get(); + assert!( + !idx.contains(&(netuid, user_a)), + "A removed after 2000 reveal" + ); + assert!( + !idx.contains(&(netuid, user_b)), + "B removed after 2000 reveal" + ); + assert!( + !idx.contains(&(netuid, user_c)), + "C removed after 2000 reveal" + ); + + assert_eq!(idx.len(), 0, "All users revealed => index is empty"); + }); +} From 4b7ef1d849f4c5e078f943c45b46ee111101d078 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 10:41:11 -0700 Subject: [PATCH 22/45] add migration for new storage --- pallets/subtensor/src/macros/hooks.rs | 4 +- .../migrate_upgrade_revealed_commitments.rs | 58 ++++++++++++++ pallets/subtensor/src/migrations/mod.rs | 1 + pallets/subtensor/src/tests/migration.rs | 75 +++++++++++++++++++ 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 pallets/subtensor/src/migrations/migrate_upgrade_revealed_commitments.rs diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 834e6c86bb..49fc4ccfe5 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -85,7 +85,9 @@ mod hooks { // Set last emission block number for all existed subnets before start call feature applied .saturating_add(migrations::migrate_set_first_emission_block_number::migrate_set_first_emission_block_number::()) // Remove all zero value entries in TotalHotkeyAlpha - .saturating_add(migrations::migrate_remove_zero_total_hotkey_alpha::migrate_remove_zero_total_hotkey_alpha::()); + .saturating_add(migrations::migrate_remove_zero_total_hotkey_alpha::migrate_remove_zero_total_hotkey_alpha::()) + // Wipe existing items to prevent bad decoding for new type + .saturating_add(migrations::migrate_upgrade_revealed_commitments::migrate_upgrade_revealed_commitments::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_upgrade_revealed_commitments.rs b/pallets/subtensor/src/migrations/migrate_upgrade_revealed_commitments.rs new file mode 100644 index 0000000000..ce3bff62ec --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_upgrade_revealed_commitments.rs @@ -0,0 +1,58 @@ +use super::*; +use crate::HasMigrationRun; +use frame_support::{traits::Get, weights::Weight}; +use scale_info::prelude::string::String; +use sp_io::{KillStorageResult, hashing::twox_128, storage::clear_prefix}; + +pub fn migrate_upgrade_revealed_commitments() -> Weight { + let migration_name = b"migrate_revealed_commitments_v2".to_vec(); + let mut weight = T::DbWeight::get().reads(1); + + if HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + migration_name + ); + return weight; + } + + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + // ------------------------------------------------------------- + // 1) Clear the old `RevealedCommitments` storage from the `Commitments` pallet + // ------------------------------------------------------------- + let mut revealed_commitments_prefix = Vec::new(); + revealed_commitments_prefix.extend_from_slice(&twox_128("Commitments".as_bytes())); + revealed_commitments_prefix.extend_from_slice(&twox_128("RevealedCommitments".as_bytes())); + + let removal_result = clear_prefix(&revealed_commitments_prefix, Some(u32::MAX)); + let removed_entries_count = match removal_result { + KillStorageResult::AllRemoved(removed) => removed as u64, + KillStorageResult::SomeRemaining(removed) => { + log::warn!("Failed to remove some items during `migrate_revealed_commitments`."); + removed as u64 + } + }; + weight = weight.saturating_add(T::DbWeight::get().writes(removed_entries_count)); + + log::info!( + "Removed {} entries from `RevealedCommitments`.", + removed_entries_count + ); + + // ------------------------------------------------------------- + // 2) Mark this migration as completed + // ------------------------------------------------------------- + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index b342d54979..23fb3cde1f 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -21,3 +21,4 @@ pub mod migrate_to_v1_separate_emission; pub mod migrate_to_v2_fixed_total_stake; pub mod migrate_total_issuance; pub mod migrate_transfer_ownership_to_foundation; +pub mod migrate_upgrade_revealed_commitments; diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 0628127413..5efc4f152a 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -480,3 +480,78 @@ fn test_migrate_remove_zero_total_hotkey_alpha() { ); }); } + +#[test] +fn test_migrate_revealed_commitments() { + new_test_ext(1).execute_with(|| { + // -------------------------------- + // Step 1: Simulate Old Storage Entries + // -------------------------------- + const MIGRATION_NAME: &str = "migrate_revealed_commitments_v2"; + + // Pallet prefix == twox_128("Commitments") + let pallet_prefix = twox_128("Commitments".as_bytes()); + // Storage item prefix == twox_128("RevealedCommitments") + let storage_prefix = twox_128("RevealedCommitments".as_bytes()); + + // Example keys for the DoubleMap: + // Key1 (netuid) uses Identity (no hash) + // Key2 (account) uses Twox64Concat + let netuid: u16 = 123; + let account_id: u64 = 999; // Or however your test `AccountId` is represented + + // Construct the full storage key for `RevealedCommitments(netuid, account_id)` + let mut storage_key = Vec::new(); + storage_key.extend_from_slice(&pallet_prefix); + storage_key.extend_from_slice(&storage_prefix); + + // Identity for netuid => no hashing, just raw encode + storage_key.extend_from_slice(&netuid.encode()); + + // Twox64Concat for account + let account_hashed = Twox64Concat::hash(&account_id.encode()); + storage_key.extend_from_slice(&account_hashed); + + // Simulate an old value we might have stored: + // For example, the old type was `RevealedData` + // We'll just store a random encoded value for demonstration + let old_value = (vec![1, 2, 3, 4], 42u64); + put_raw(&storage_key, &old_value.encode()); + + // Confirm the storage value is set + let stored_value = get_raw(&storage_key).expect("Expected to get a value"); + let decoded_value = <(Vec, u64)>::decode(&mut &stored_value[..]) + .expect("Failed to decode the old revealed commitments"); + assert_eq!(decoded_value, old_value); + + // Also confirm that the migration has NOT run yet + assert!( + !HasMigrationRun::::get(MIGRATION_NAME.as_bytes().to_vec()), + "Migration should not have run yet" + ); + + // -------------------------------- + // Step 2: Run the Migration + // -------------------------------- + let weight = crate::migrations::migrate_upgrade_revealed_commitments::migrate_upgrade_revealed_commitments::(); + + // Migration should be marked as run + assert!( + HasMigrationRun::::get(MIGRATION_NAME.as_bytes().to_vec()), + "Migration should now be marked as run" + ); + + // -------------------------------- + // Step 3: Verify Migration Effects + // -------------------------------- + // The old key/value should be removed + let stored_value_after = get_raw(&storage_key); + assert!( + stored_value_after.is_none(), + "Old storage entry should be cleared" + ); + + // Weight returned should be > 0 (some cost was incurred clearing storage) + assert!(!weight.is_zero(), "Migration weight should be non-zero"); + }); +} From bdda4d93793d6cdd064bd055e69e62989b229465 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 10:45:11 -0700 Subject: [PATCH 23/45] clippy --- pallets/commitments/src/tests.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index 9f9e8c9181..ae6bc0404b 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -449,6 +449,7 @@ fn reveal_timelocked_commitment_empty_decrypted_data_is_skipped() { }); } +#[allow(clippy::indexing_slicing)] #[test] fn reveal_timelocked_commitment_single_field_entry_is_removed_after_reveal() { new_test_ext().execute_with(|| { @@ -1260,7 +1261,8 @@ fn timelocked_index_complex_scenario_works() { } }; - let make_raw_data = |payload: &[u8]| Data::Raw(payload.to_vec().try_into().unwrap()); + let make_raw_data = + |payload: &[u8]| Data::Raw(payload.to_vec().try_into().expect("expected to not panic")); // ---------------------------------------------------- // (1) USER A => no timelocks => NOT in index @@ -1340,7 +1342,7 @@ fn timelocked_index_complex_scenario_works() { // ---------------------------------------------------- let b_timelock_2 = make_timelock_data(b"B TLE #2", 2000); let info_b3 = CommitmentInfo:: { - fields: BoundedVec::try_from(vec![b_timelock_2]).unwrap(), + fields: BoundedVec::try_from(vec![b_timelock_2]).expect("expected to not panic"), }; assert_ok!(Pallet::::set_commitment( RuntimeOrigin::signed(user_b), @@ -1356,7 +1358,7 @@ fn timelocked_index_complex_scenario_works() { // ---------------------------------------------------- let c_timelock_1 = make_timelock_data(b"C TLE #1", 2000); let info_c1 = CommitmentInfo:: { - fields: BoundedVec::try_from(vec![c_timelock_1]).unwrap(), + fields: BoundedVec::try_from(vec![c_timelock_1]).expect("expected to not panic"), }; assert_ok!(Pallet::::set_commitment( RuntimeOrigin::signed(user_c), From acbbe521cbf782f7194c28aab5f69c19f8cfb8ac Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 11:48:35 -0700 Subject: [PATCH 24/45] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index dc00e1d2fa..96e4306002 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -207,7 +207,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: 258, + spec_version: 259, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 0c2cd89ad7faec0987baed146088680f75571152 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 14:49:25 -0700 Subject: [PATCH 25/45] don't save bad commitments --- pallets/commitments/src/lib.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/pallets/commitments/src/lib.rs b/pallets/commitments/src/lib.rs index 037e10d00d..75cc31b2ea 100644 --- a/pallets/commitments/src/lib.rs +++ b/pallets/commitments/src/lib.rs @@ -527,10 +527,7 @@ impl Pallet { .ok(); let Some(sig) = sig else { - remain_fields.push(Data::TimelockEncrypted { - encrypted, - reveal_round, - }); + log::warn!("No sig after deserialization"); continue; }; @@ -546,10 +543,7 @@ impl Pallet { .ok(); let Some(commit) = commit else { - remain_fields.push(Data::TimelockEncrypted { - encrypted, - reveal_round, - }); + log::warn!("No commit after deserialization"); continue; }; @@ -562,10 +556,7 @@ impl Pallet { .unwrap_or_default(); if decrypted_bytes.is_empty() { - remain_fields.push(Data::TimelockEncrypted { - encrypted, - reveal_round, - }); + log::warn!("Bytes were decrypted for {:?} but they are empty", who); continue; } From b987c05f3e640e5721f4b1da497f4cc96b1c7120 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 15:05:14 -0700 Subject: [PATCH 26/45] add test reveal_timelocked_bad_timelocks_are_removed --- pallets/commitments/src/tests.rs | 149 ++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index ae6bc0404b..03eaeae96a 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -15,7 +15,7 @@ use frame_support::{ BoundedVec, assert_noop, assert_ok, traits::{Currency, Get, ReservableCurrency}, }; -use frame_system::Pallet as System; +use frame_system::{Pallet as System, RawOrigin}; #[allow(clippy::indexing_slicing)] #[test] @@ -1424,3 +1424,150 @@ fn timelocked_index_complex_scenario_works() { assert_eq!(idx.len(), 0, "All users revealed => index is empty"); }); } + +#[allow(clippy::indexing_slicing)] +#[test] +fn reveal_timelocked_bad_timelocks_are_removed() { + new_test_ext().execute_with(|| { + // + // 1) Prepare multiple Data::TimelockEncrypted fields with different “badness” scenarios + one good field + // + // Round used for valid Drand signature + let valid_round = 1000; + // Round used for intentionally invalid Drand signature + let invalid_sig_round = 999; + // Round that has *no* Drand pulse => timelock remains stored, not revealed yet + let no_pulse_round = 2001; + + // (a) TLE #1: Round=999 => Drand pulse *exists* but signature is invalid => skip/deleted + let plaintext_1 = b"BadSignature"; + let ciphertext_1 = produce_ciphertext(plaintext_1, invalid_sig_round); + let tle_bad_sig = Data::TimelockEncrypted { + encrypted: ciphertext_1, + reveal_round: invalid_sig_round, + }; + + // (b) TLE #2: Round=1000 => Drand signature is valid, but ciphertext is corrupted => skip/deleted + let plaintext_2 = b"CorruptedCiphertext"; + let good_ct_2 = produce_ciphertext(plaintext_2, valid_round); + let mut corrupted_ct_2 = good_ct_2.into_inner(); + if !corrupted_ct_2.is_empty() { + corrupted_ct_2[0] ^= 0xFF; // flip a byte + } + let tle_corrupted = Data::TimelockEncrypted { + encrypted: corrupted_ct_2.try_into().expect("Expected not to panic"), + reveal_round: valid_round, + }; + + // (c) TLE #3: Round=1000 => Drand signature valid, ciphertext good, *but* plaintext is empty => skip/deleted + let empty_good_ct = produce_ciphertext(&[], valid_round); + let tle_empty_plaintext = Data::TimelockEncrypted { + encrypted: empty_good_ct, + reveal_round: valid_round, + }; + + // (d) TLE #4: Round=1000 => Drand signature valid, ciphertext valid, nonempty plaintext => should be revealed + let plaintext_4 = b"Hello, I decrypt fine!"; + let good_ct_4 = produce_ciphertext(plaintext_4, valid_round); + let tle_good = Data::TimelockEncrypted { + encrypted: good_ct_4, + reveal_round: valid_round, + }; + + // (e) TLE #5: Round=2001 => no Drand pulse => remains in storage + let plaintext_5 = b"Still waiting for next round!"; + let good_ct_5 = produce_ciphertext(plaintext_5, no_pulse_round); + let tle_no_pulse = Data::TimelockEncrypted { + encrypted: good_ct_5, + reveal_round: no_pulse_round, + }; + + // + // 2) Assemble them all in one CommitmentInfo + // + let fields = vec![ + tle_bad_sig, // #1 + tle_corrupted, // #2 + tle_empty_plaintext, // #3 + tle_good, // #4 + tle_no_pulse, // #5 + ]; + let fields_bounded = BoundedVec::try_from(fields).expect("Should not exceed MaxFields"); + let info = CommitmentInfo { + fields: fields_bounded, + }; + + // + // 3) Insert the commitment + // + let who = 123; + let netuid = 777; + System::::set_block_number(1); + assert_ok!(Pallet::::set_commitment( + RawOrigin::Signed(who).into(), + netuid, + Box::new(info) + )); + + // + // 4) Insert pulses: + // - Round=999 => invalid signature => attempts to parse => fails => remove TLE #1 + // - Round=1000 => valid signature => TLE #2 is corrupted => remove; #3 empty => remove; #4 reveals successfully + // - Round=2001 => no signature => TLE #5 remains + // + let bad_sig = [0x33u8; 10]; // obviously invalid for TinyBLS + insert_drand_pulse(invalid_sig_round, &bad_sig); + + let drand_sig_1000 = hex::decode(DRAND_QUICKNET_SIG_HEX).expect("Expected not to panic"); + insert_drand_pulse(valid_round, &drand_sig_1000); + + // + // 5) Call reveal => “bad” items are removed, “good” is revealed, “not ready” remains + // + System::::set_block_number(2); + assert_ok!(Pallet::::reveal_timelocked_commitments()); + + // + // 6) Check final storage + // + // (a) TLE #5 => still in fields => same user remains in CommitmentOf => TimelockedIndex includes them + let registration_after = + CommitmentOf::::get(netuid, who).expect("Should still exist"); + assert_eq!( + registration_after.info.fields.len(), + 1, + "Only the unrevealed TLE #5 should remain" + ); + let leftover = ®istration_after.info.fields[0]; + match leftover { + Data::TimelockEncrypted { reveal_round, .. } => { + assert_eq!(*reveal_round, no_pulse_round, "Should be TLE #5 leftover"); + } + _ => panic!("Expected the leftover field to be TLE #5"), + }; + assert!( + TimelockedIndex::::get().contains(&(netuid, who)), + "Still in index because there's one remaining timelock (#5)." + ); + + // (b) TLE #4 => revealed => check that the plaintext matches + let revealed = RevealedCommitments::::get(netuid, who) + .expect("Should have at least one revealed item for TLE #4"); + let (revealed_bytes, reveal_block) = &revealed[0]; + assert_eq!(*reveal_block, 2, "Revealed at block #2"); + + let revealed_str = sp_std::str::from_utf8(revealed_bytes) + .expect("Truncated bytes should be valid UTF-8 in this test"); + + let original_str = + sp_std::str::from_utf8(plaintext_4).expect("plaintext_4 should be valid UTF-8"); + + assert_eq!( + revealed_str, original_str, + "Expected revealed data to match the original plaintext" + ); + + // (c) TLE #1 / #2 / #3 => removed => do NOT appear in leftover fields, nor in revealed (they were invalid) + assert_eq!(revealed.len(), 1, "Only TLE #4 ended up in revealed list"); + }); +} From 0481bb998aa49bcbbad97dc0c99b5253c1278d7e Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:33:12 -0700 Subject: [PATCH 27/45] only keep the 10 most recent revealed commitments --- pallets/commitments/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pallets/commitments/src/lib.rs b/pallets/commitments/src/lib.rs index 75cc31b2ea..495bfdf858 100644 --- a/pallets/commitments/src/lib.rs +++ b/pallets/commitments/src/lib.rs @@ -584,6 +584,12 @@ impl Pallet { }); } + const MAX_REVEALS: usize = 10; + if existing_reveals.len() > MAX_REVEALS { + let remove_count = existing_reveals.len() - MAX_REVEALS; + existing_reveals.drain(0..remove_count); + } + RevealedCommitments::::insert(netuid, &who, existing_reveals); } From 59dfa9b9246e85134481639f6d9f87f1803a1bdf Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:38:34 -0700 Subject: [PATCH 28/45] add tests for revealed limit --- pallets/commitments/src/tests.rs | 129 +++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index 03eaeae96a..659456cc34 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -1571,3 +1571,132 @@ fn reveal_timelocked_bad_timelocks_are_removed() { assert_eq!(revealed.len(), 1, "Only TLE #4 ended up in revealed list"); }); } + +#[test] +fn revealed_commitments_keeps_only_10_items() { + new_test_ext().execute_with(|| { + let netuid = 1; + let who = 2; + let reveal_round = 1000; + + let drand_sig_bytes = hex::decode(DRAND_QUICKNET_SIG_HEX).expect("Should decode DRAND sig"); + insert_drand_pulse(reveal_round, &drand_sig_bytes); + + // --- 1) Build 12 TimelockEncrypted fields --- + // Each one has a unique plaintext "TLE #i" + const TOTAL_TLES: usize = 12; + let mut fields = Vec::with_capacity(TOTAL_TLES); + + for i in 0..TOTAL_TLES { + let plaintext = format!("TLE #{}", i).into_bytes(); + let ciphertext = produce_ciphertext(&plaintext, reveal_round); + let timelock = Data::TimelockEncrypted { + encrypted: ciphertext, + reveal_round, + }; + fields.push(timelock); + } + let fields_bounded = BoundedVec::try_from(fields).expect("Should not exceed MaxFields"); + let info = CommitmentInfo { fields: fields_bounded }; + + // --- 2) Set the commitment => 12 timelocks in storage --- + System::::set_block_number(1); + assert_ok!(Pallet::::set_commitment( + RuntimeOrigin::signed(who), + netuid, + Box::new(info) + )); + + // --- 3) Reveal => all 12 are decrypted in one shot --- + System::::set_block_number(2); + assert_ok!(Pallet::::reveal_timelocked_commitments()); + + // --- 4) Check we only keep 10 in `RevealedCommitments` --- + let revealed = RevealedCommitments::::get(netuid, who) + .expect("Should have at least some revealed data"); + assert_eq!( + revealed.len(), + 10, + "We must only keep the newest 10, out of 12 total" + ); + + // The oldest 2 ("TLE #0" and "TLE #1") must be dropped. + // The items in `revealed` now correspond to "TLE #2" .. "TLE #11". + for (idx, (revealed_bytes, reveal_block)) in revealed.iter().enumerate() { + // Convert to UTF-8 + let revealed_str = sp_std::str::from_utf8(revealed_bytes) + .expect("Decrypted data should be valid UTF-8 for this test case"); + + // We expect them to be TLE #2..TLE #11 + let expected_index = idx + 2; // since we dropped #0 and #1 + let expected_str = format!("TLE #{}", expected_index); + assert_eq!(revealed_str, expected_str, "Check which TLE is kept"); + + // Also check it was revealed at block 2 + assert_eq!(*reveal_block, 2, "All reveal in the same block #2"); + } + }); +} + + +#[test] +fn revealed_commitments_keeps_only_10_items_across_multiple_calls() { + new_test_ext().execute_with(|| { + let netuid = 1; + let who = 2; + let reveal_round = 1000; + + let drand_sig_bytes = hex::decode(DRAND_QUICKNET_SIG_HEX).expect("Should decode DRAND sig"); + insert_drand_pulse(reveal_round, &drand_sig_bytes); + + // + // We'll create 12 separate commitments, each with a single TLE (#0..#11). + // + const TOTAL_TLES: usize = 12; + for i in 0..TOTAL_TLES { + let plaintext = format!("TLE #{}", i).into_bytes(); + let ciphertext = produce_ciphertext(&plaintext, reveal_round); + + let timelock = Data::TimelockEncrypted { + encrypted: ciphertext, + reveal_round, + }; + let fields = BoundedVec::try_from(vec![timelock]) + .expect("Single field is well within MaxFields"); + + let info = CommitmentInfo { fields }; + assert_ok!(Pallet::::set_commitment( + RuntimeOrigin::signed(who), + netuid, + Box::new(info) + )); + } + + System::::set_block_number(2); + assert_ok!(Pallet::::reveal_timelocked_commitments()); + + let revealed = RevealedCommitments::::get(netuid, who) + .expect("Should have revealed items after decryption"); + + assert_eq!( + revealed.len(), + 10, + "We must only keep the 10 newest out of 12 total" + ); + + // + // The first 2 TLEs we inserted ("TLE #0" and "TLE #1") should be dropped, + // leaving us with "TLE #2" through "TLE #11". + // + for (idx, (revealed_bytes, reveal_block)) in revealed.iter().enumerate() { + let revealed_str = + sp_std::str::from_utf8(revealed_bytes).expect("Should be valid UTF-8 in this test"); + + let expected_num = idx + 2; // i.e. TLE #2..#11 + let expected_str = format!("TLE #{}", expected_num); + + assert_eq!(revealed_str, expected_str, "Check which TLE is stored"); + assert_eq!(*reveal_block, 2, "All items revealed at block #2"); + } + }); +} From 23a0a962fcb3349e8101785f99f37e3d2636de11 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:48:18 -0700 Subject: [PATCH 29/45] fix test --- pallets/commitments/src/tests.rs | 74 +++++++++++++++++++------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index 659456cc34..39f4973fcb 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -1597,7 +1597,9 @@ fn revealed_commitments_keeps_only_10_items() { fields.push(timelock); } let fields_bounded = BoundedVec::try_from(fields).expect("Should not exceed MaxFields"); - let info = CommitmentInfo { fields: fields_bounded }; + let info = CommitmentInfo { + fields: fields_bounded, + }; // --- 2) Set the commitment => 12 timelocks in storage --- System::::set_block_number(1); @@ -1638,65 +1640,79 @@ fn revealed_commitments_keeps_only_10_items() { }); } - #[test] -fn revealed_commitments_keeps_only_10_items_across_multiple_calls() { +fn revealed_commitments_keeps_only_10_newest_with_individual_single_field_commits() { new_test_ext().execute_with(|| { let netuid = 1; let who = 2; let reveal_round = 1000; - let drand_sig_bytes = hex::decode(DRAND_QUICKNET_SIG_HEX).expect("Should decode DRAND sig"); + let drand_sig_bytes = hex::decode(DRAND_QUICKNET_SIG_HEX).expect("decode DRAND sig"); insert_drand_pulse(reveal_round, &drand_sig_bytes); - // - // We'll create 12 separate commitments, each with a single TLE (#0..#11). - // - const TOTAL_TLES: usize = 12; - for i in 0..TOTAL_TLES { + // We will add 12 separate timelocks, one per iteration, each in its own set_commitment call. + // After each insertion, we call reveal + increment the block by 1. + + for i in 0..12 { + System::::set_block_number(i as u64 + 1); + let plaintext = format!("TLE #{}", i).into_bytes(); let ciphertext = produce_ciphertext(&plaintext, reveal_round); - let timelock = Data::TimelockEncrypted { + let new_timelock = Data::TimelockEncrypted { encrypted: ciphertext, reveal_round, }; - let fields = BoundedVec::try_from(vec![timelock]) - .expect("Single field is well within MaxFields"); + let fields = BoundedVec::try_from(vec![new_timelock]) + .expect("Single field is well within MaxFields"); let info = CommitmentInfo { fields }; + assert_ok!(Pallet::::set_commitment( RuntimeOrigin::signed(who), netuid, Box::new(info) )); - } - System::::set_block_number(2); - assert_ok!(Pallet::::reveal_timelocked_commitments()); + assert_ok!(Pallet::::reveal_timelocked_commitments()); - let revealed = RevealedCommitments::::get(netuid, who) - .expect("Should have revealed items after decryption"); + let revealed = RevealedCommitments::::get(netuid, who).unwrap_or_default(); + let expected_count = (i + 1).min(10); + assert_eq!( + revealed.len(), + expected_count, + "At iteration {}, we keep at most 10 reveals", + i + ); + } + let revealed = RevealedCommitments::::get(netuid, who).unwrap(); assert_eq!( revealed.len(), 10, - "We must only keep the 10 newest out of 12 total" + "After 12 total commits, only 10 remain revealed" ); - // - // The first 2 TLEs we inserted ("TLE #0" and "TLE #1") should be dropped, - // leaving us with "TLE #2" through "TLE #11". - // + // Check that TLE #0 and TLE #1 are dropped; TLE #2..#11 remain in ascending order. for (idx, (revealed_bytes, reveal_block)) in revealed.iter().enumerate() { let revealed_str = - sp_std::str::from_utf8(revealed_bytes).expect("Should be valid UTF-8 in this test"); - - let expected_num = idx + 2; // i.e. TLE #2..#11 - let expected_str = format!("TLE #{}", expected_num); - - assert_eq!(revealed_str, expected_str, "Check which TLE is stored"); - assert_eq!(*reveal_block, 2, "All items revealed at block #2"); + sp_std::str::from_utf8(revealed_bytes).expect("Should be valid UTF-8"); + let expected_i = idx + 2; // i=0 => "TLE #2", i=1 => "TLE #3", etc. + let expected_str = format!("TLE #{}", expected_i); + + assert_eq!( + revealed_str, expected_str, + "Revealed data #{} should match the truncated TLE #{}", + idx, + expected_i + ); + + let expected_reveal_block = expected_i as u64 + 1; + assert_eq!( + *reveal_block, expected_reveal_block, + "Check which block TLE #{} was revealed in", + expected_i + ); } }); } From 8cc4049d4eb6e50712ada11a4418507e52cccf8b Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:48:28 -0700 Subject: [PATCH 30/45] fmt --- pallets/commitments/src/tests.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index 39f4973fcb..21548942ac 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -1703,8 +1703,7 @@ fn revealed_commitments_keeps_only_10_newest_with_individual_single_field_commit assert_eq!( revealed_str, expected_str, "Revealed data #{} should match the truncated TLE #{}", - idx, - expected_i + idx, expected_i ); let expected_reveal_block = expected_i as u64 + 1; From da30941ee12c9ad672325e249b32815e5ae7a98b Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:50:39 -0700 Subject: [PATCH 31/45] clippy --- pallets/commitments/src/lib.rs | 2 +- pallets/commitments/src/tests.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/commitments/src/lib.rs b/pallets/commitments/src/lib.rs index 495bfdf858..c59b32437b 100644 --- a/pallets/commitments/src/lib.rs +++ b/pallets/commitments/src/lib.rs @@ -586,7 +586,7 @@ impl Pallet { const MAX_REVEALS: usize = 10; if existing_reveals.len() > MAX_REVEALS { - let remove_count = existing_reveals.len() - MAX_REVEALS; + let remove_count = existing_reveals.len().saturating_sub(MAX_REVEALS); existing_reveals.drain(0..remove_count); } diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index 21548942ac..4a0f80be5d 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -1686,7 +1686,8 @@ fn revealed_commitments_keeps_only_10_newest_with_individual_single_field_commit ); } - let revealed = RevealedCommitments::::get(netuid, who).unwrap(); + let revealed = + RevealedCommitments::::get(netuid, who).expect("expected to not panic"); assert_eq!( revealed.len(), 10, From d4f3413590818984211f13310009161cd1d70470 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Sat, 29 Mar 2025 12:38:16 -0400 Subject: [PATCH 32/45] Do not use saturating_sub for signed types --- .../subtensor/src/coinbase/block_emission.rs | 21 ++- pallets/subtensor/src/coinbase/block_step.rs | 50 ++--- .../subtensor/src/coinbase/run_coinbase.rs | 176 +++++++++--------- pallets/subtensor/src/rpc_info/stake_info.rs | 4 +- pallets/subtensor/src/staking/helpers.rs | 18 +- pallets/subtensor/src/staking/move_stake.rs | 4 +- pallets/subtensor/src/staking/remove_stake.rs | 10 +- pallets/subtensor/src/staking/stake_utils.rs | 155 +++++++-------- pallets/subtensor/src/tests/children.rs | 10 +- pallets/subtensor/src/tests/coinbase.rs | 108 +++++------ pallets/subtensor/src/tests/delegate_info.rs | 5 +- pallets/subtensor/src/tests/emission.rs | 2 +- pallets/subtensor/src/tests/move_stake.rs | 32 ++-- pallets/subtensor/src/tests/staking.rs | 15 +- pallets/subtensor/src/tests/staking2.rs | 38 ++-- pallets/subtensor/src/tests/swap_coldkey.rs | 4 +- pallets/subtensor/src/tests/weights.rs | 6 +- pallets/subtensor/src/utils/misc.rs | 8 +- pallets/utility/src/tests.rs | 3 +- 19 files changed, 338 insertions(+), 331 deletions(-) diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index 1a63c16a07..cd9d778b8a 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -1,7 +1,10 @@ use super::*; use frame_support::traits::Get; use safe_math::*; -use substrate_fixed::{transcendental::log2, types::I96F32}; +use substrate_fixed::{ + transcendental::log2, + types::{I96F32, U96F32}, +}; impl Pallet { /// Calculates the dynamic TAO emission for a given subnet. @@ -31,15 +34,15 @@ impl Pallet { alpha_block_emission: u64, ) -> (u64, u64, u64) { // Init terms. - let mut tao_in_emission: I96F32 = I96F32::saturating_from_num(tao_emission); - let float_alpha_block_emission: I96F32 = I96F32::saturating_from_num(alpha_block_emission); + let mut tao_in_emission: U96F32 = U96F32::saturating_from_num(tao_emission); + let float_alpha_block_emission: U96F32 = U96F32::saturating_from_num(alpha_block_emission); // Get alpha price for subnet. - let alpha_price: I96F32 = Self::get_alpha_price(netuid); + let alpha_price: U96F32 = Self::get_alpha_price(netuid); log::debug!("{:?} - alpha_price: {:?}", netuid, alpha_price); // Get initial alpha_in - let mut alpha_in_emission: I96F32 = I96F32::saturating_from_num(tao_emission) + let mut alpha_in_emission: U96F32 = U96F32::saturating_from_num(tao_emission) .checked_div(alpha_price) .unwrap_or(float_alpha_block_emission); @@ -60,11 +63,11 @@ impl Pallet { } // Avoid rounding errors. - if tao_in_emission < I96F32::saturating_from_num(1) - || alpha_in_emission < I96F32::saturating_from_num(1) + if tao_in_emission < U96F32::saturating_from_num(1) + || alpha_in_emission < U96F32::saturating_from_num(1) { - alpha_in_emission = I96F32::saturating_from_num(0); - tao_in_emission = I96F32::saturating_from_num(0); + alpha_in_emission = U96F32::saturating_from_num(0); + tao_in_emission = U96F32::saturating_from_num(0); } // Set Alpha in emission. diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index 669f8e09da..a7e658e89a 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -1,7 +1,7 @@ use super::*; use frame_support::storage::IterableStorageMap; use safe_math::*; -use substrate_fixed::types::{I96F32, I110F18}; +use substrate_fixed::types::{U96F32, U110F18}; impl Pallet { /// Executes the necessary operations for each block. @@ -11,8 +11,8 @@ impl Pallet { // --- 1. Adjust difficulties. Self::adjust_registration_terms_for_networks(); // --- 2. Get the current coinbase emission. - let block_emission: I96F32 = - I96F32::saturating_from_num(Self::get_block_emission().unwrap_or(0)); + let block_emission: U96F32 = + U96F32::saturating_from_num(Self::get_block_emission().unwrap_or(0)); log::debug!("Block emission: {:?}", block_emission); // --- 3. Run emission through network. Self::run_coinbase(block_emission); @@ -191,7 +191,7 @@ impl Pallet { } /// Calculates the upgraded difficulty by multiplying the current difficulty by the ratio ( reg_actual + reg_target / reg_target + reg_target ) - /// We use I110F18 to avoid any overflows on u64. Also min_difficulty and max_difficulty bound the range. + /// We use U110F18 to avoid any overflows on u64. Also min_difficulty and max_difficulty bound the range. /// pub fn upgraded_difficulty( netuid: u16, @@ -199,25 +199,25 @@ impl Pallet { registrations_this_interval: u16, target_registrations_per_interval: u16, ) -> u64 { - let updated_difficulty: I110F18 = I110F18::saturating_from_num(current_difficulty) - .saturating_mul(I110F18::saturating_from_num( + let updated_difficulty: U110F18 = U110F18::saturating_from_num(current_difficulty) + .saturating_mul(U110F18::saturating_from_num( registrations_this_interval.saturating_add(target_registrations_per_interval), )) - .safe_div(I110F18::saturating_from_num( + .safe_div(U110F18::saturating_from_num( target_registrations_per_interval.saturating_add(target_registrations_per_interval), )); - let alpha: I110F18 = I110F18::saturating_from_num(Self::get_adjustment_alpha(netuid)) - .safe_div(I110F18::saturating_from_num(u64::MAX)); - let next_value: I110F18 = alpha - .saturating_mul(I110F18::saturating_from_num(current_difficulty)) + let alpha: U110F18 = U110F18::saturating_from_num(Self::get_adjustment_alpha(netuid)) + .safe_div(U110F18::saturating_from_num(u64::MAX)); + let next_value: U110F18 = alpha + .saturating_mul(U110F18::saturating_from_num(current_difficulty)) .saturating_add( - I110F18::saturating_from_num(1.0) + U110F18::saturating_from_num(1.0) .saturating_sub(alpha) .saturating_mul(updated_difficulty), ); - if next_value >= I110F18::saturating_from_num(Self::get_max_difficulty(netuid)) { + if next_value >= U110F18::saturating_from_num(Self::get_max_difficulty(netuid)) { Self::get_max_difficulty(netuid) - } else if next_value <= I110F18::saturating_from_num(Self::get_min_difficulty(netuid)) { + } else if next_value <= U110F18::saturating_from_num(Self::get_min_difficulty(netuid)) { return Self::get_min_difficulty(netuid); } else { return next_value.saturating_to_num::(); @@ -225,7 +225,7 @@ impl Pallet { } /// Calculates the upgraded burn by multiplying the current burn by the ratio ( reg_actual + reg_target / reg_target + reg_target ) - /// We use I110F18 to avoid any overflows on u64. Also min_burn and max_burn bound the range. + /// We use U110F18 to avoid any overflows on u64. Also min_burn and max_burn bound the range. /// pub fn upgraded_burn( netuid: u16, @@ -233,25 +233,25 @@ impl Pallet { registrations_this_interval: u16, target_registrations_per_interval: u16, ) -> u64 { - let updated_burn: I110F18 = I110F18::saturating_from_num(current_burn) - .saturating_mul(I110F18::saturating_from_num( + let updated_burn: U110F18 = U110F18::saturating_from_num(current_burn) + .saturating_mul(U110F18::saturating_from_num( registrations_this_interval.saturating_add(target_registrations_per_interval), )) - .safe_div(I110F18::saturating_from_num( + .safe_div(U110F18::saturating_from_num( target_registrations_per_interval.saturating_add(target_registrations_per_interval), )); - let alpha: I110F18 = I110F18::saturating_from_num(Self::get_adjustment_alpha(netuid)) - .safe_div(I110F18::saturating_from_num(u64::MAX)); - let next_value: I110F18 = alpha - .saturating_mul(I110F18::saturating_from_num(current_burn)) + let alpha: U110F18 = U110F18::saturating_from_num(Self::get_adjustment_alpha(netuid)) + .safe_div(U110F18::saturating_from_num(u64::MAX)); + let next_value: U110F18 = alpha + .saturating_mul(U110F18::saturating_from_num(current_burn)) .saturating_add( - I110F18::saturating_from_num(1.0) + U110F18::saturating_from_num(1.0) .saturating_sub(alpha) .saturating_mul(updated_burn), ); - if next_value >= I110F18::saturating_from_num(Self::get_max_burn_as_u64(netuid)) { + if next_value >= U110F18::saturating_from_num(Self::get_max_burn_as_u64(netuid)) { Self::get_max_burn_as_u64(netuid) - } else if next_value <= I110F18::saturating_from_num(Self::get_min_burn_as_u64(netuid)) { + } else if next_value <= U110F18::saturating_from_num(Self::get_min_burn_as_u64(netuid)) { return Self::get_min_burn_as_u64(netuid); } else { return next_value.saturating_to_num::(); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 1ff8b2760d..df08a6d2a5 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -1,7 +1,7 @@ use super::*; use alloc::collections::BTreeMap; use safe_math::*; -use substrate_fixed::types::I96F32; +use substrate_fixed::types::U96F32; use tle::stream_ciphers::AESGCMStreamCipherProvider; use tle::tlock::tld; @@ -21,7 +21,7 @@ pub struct WeightsTlockPayload { // Distribute dividends to each hotkey macro_rules! asfloat { ($val:expr) => { - I96F32::saturating_from_num($val) + U96F32::saturating_from_num($val) }; } @@ -32,7 +32,7 @@ macro_rules! tou64 { } impl Pallet { - pub fn run_coinbase(block_emission: I96F32) { + pub fn run_coinbase(block_emission: U96F32) { // --- 0. Get current block. let current_block: u64 = Self::get_current_block_as_u64(); log::debug!("Current block: {:?}", current_block); @@ -52,7 +52,7 @@ impl Pallet { log::debug!("Subnets to emit to: {:?}", subnets_to_emit_to); // --- 2. Get sum of tao reserves ( in a later version we will switch to prices. ) - let mut total_moving_prices: I96F32 = I96F32::saturating_from_num(0.0); + let mut total_moving_prices: U96F32 = U96F32::saturating_from_num(0.0); // Only get price EMA for subnets that we emit to. for netuid_i in subnets_to_emit_to.iter() { // Get and update the moving price of each subnet adding the total together. @@ -63,31 +63,31 @@ impl Pallet { // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) // Computation is described in detail in the dtao whitepaper. - let mut tao_in: BTreeMap = BTreeMap::new(); - let mut alpha_in: BTreeMap = BTreeMap::new(); - let mut alpha_out: BTreeMap = BTreeMap::new(); + let mut tao_in: BTreeMap = BTreeMap::new(); + let mut alpha_in: BTreeMap = BTreeMap::new(); + let mut alpha_out: BTreeMap = BTreeMap::new(); // Only calculate for subnets that we are emitting to. for netuid_i in subnets_to_emit_to.iter() { // Get subnet price. - let price_i: I96F32 = Self::get_alpha_price(*netuid_i); + let price_i: U96F32 = Self::get_alpha_price(*netuid_i); log::debug!("price_i: {:?}", price_i); // Get subnet TAO. - let moving_price_i: I96F32 = Self::get_moving_alpha_price(*netuid_i); + let moving_price_i: U96F32 = Self::get_moving_alpha_price(*netuid_i); log::debug!("moving_price_i: {:?}", moving_price_i); // Emission is price over total. - let mut tao_in_i: I96F32 = block_emission + let mut tao_in_i: U96F32 = block_emission .saturating_mul(moving_price_i) .checked_div(total_moving_prices) .unwrap_or(asfloat!(0.0)); log::debug!("tao_in_i: {:?}", tao_in_i); // Get alpha_emission total - let alpha_emission_i: I96F32 = asfloat!( + let alpha_emission_i: U96F32 = asfloat!( Self::get_block_emission_for_issuance(Self::get_alpha_issuance(*netuid_i)) .unwrap_or(0) ); log::debug!("alpha_emission_i: {:?}", alpha_emission_i); // Get initial alpha_in - let alpha_in_i: I96F32 = tao_in_i + let alpha_in_i: U96F32 = tao_in_i .checked_div(price_i) .unwrap_or(alpha_emission_i) .min(alpha_emission_i); @@ -142,14 +142,14 @@ impl Pallet { // --- 5. Compute owner cuts and remove them from alpha_out remaining. // Remove owner cuts here so that we can properly seperate root dividends in the next step. // Owner cuts are accumulated and then fed to the drain at the end of this func. - let cut_percent: I96F32 = Self::get_float_subnet_owner_cut(); - let mut owner_cuts: BTreeMap = BTreeMap::new(); + let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); + let mut owner_cuts: BTreeMap = BTreeMap::new(); for netuid_i in subnets_to_emit_to.iter() { // Get alpha out. - let alpha_out_i: I96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0)); + let alpha_out_i: U96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0)); log::debug!("alpha_out_i: {:?}", alpha_out_i); // Calculate the owner cut. - let owner_cut_i: I96F32 = alpha_out_i.saturating_mul(cut_percent); + let owner_cut_i: U96F32 = alpha_out_i.saturating_mul(cut_percent); log::debug!("owner_cut_i: {:?}", owner_cut_i); // Save owner cut. *owner_cuts.entry(*netuid_i).or_insert(asfloat!(0)) = owner_cut_i; @@ -165,30 +165,30 @@ impl Pallet { // Then accumulate those dividends for later. for netuid_i in subnets_to_emit_to.iter() { // Get remaining alpha out. - let alpha_out_i: I96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0.0)); + let alpha_out_i: U96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0.0)); log::debug!("alpha_out_i: {:?}", alpha_out_i); // Get total TAO on root. - let root_tao: I96F32 = asfloat!(SubnetTAO::::get(0)); + let root_tao: U96F32 = asfloat!(SubnetTAO::::get(0)); log::debug!("root_tao: {:?}", root_tao); // Get total ALPHA on subnet. - let alpha_issuance: I96F32 = asfloat!(Self::get_alpha_issuance(*netuid_i)); + let alpha_issuance: U96F32 = asfloat!(Self::get_alpha_issuance(*netuid_i)); log::debug!("alpha_issuance: {:?}", alpha_issuance); // Get tao_weight - let tao_weight: I96F32 = root_tao.saturating_mul(Self::get_tao_weight()); + let tao_weight: U96F32 = root_tao.saturating_mul(Self::get_tao_weight()); log::debug!("tao_weight: {:?}", tao_weight); // Get root proportional dividends. - let root_proportion: I96F32 = tao_weight + let root_proportion: U96F32 = tao_weight .checked_div(tao_weight.saturating_add(alpha_issuance)) .unwrap_or(asfloat!(0.0)); log::debug!("root_proportion: {:?}", root_proportion); // Get root proportion of alpha_out dividends. - let root_alpha: I96F32 = root_proportion + let root_alpha: U96F32 = root_proportion .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. .saturating_mul(asfloat!(0.5)); // 50% to validators. // Remove root alpha from alpha_out. log::debug!("root_alpha: {:?}", root_alpha); // Get pending alpha as original alpha_out - root_alpha. - let pending_alpha: I96F32 = alpha_out_i.saturating_sub(root_alpha); + let pending_alpha: U96F32 = alpha_out_i.saturating_sub(root_alpha); log::debug!("pending_alpha: {:?}", pending_alpha); // Sell root emission through the pool. let root_tao: u64 = Self::swap_alpha_for_tao(*netuid_i, tou64!(root_alpha)); @@ -265,10 +265,10 @@ impl Pallet { pub fn calculate_dividends_and_incentives( netuid: u16, hotkey_emission: Vec<(T::AccountId, u64, u64)>, - ) -> (BTreeMap, BTreeMap) { + ) -> (BTreeMap, BTreeMap) { // Accumulate emission of dividends and incentive per hotkey. let mut incentives: BTreeMap = BTreeMap::new(); - let mut dividends: BTreeMap = BTreeMap::new(); + let mut dividends: BTreeMap = BTreeMap::new(); for (hotkey, incentive, dividend) in hotkey_emission { // Accumulate incentives to miners. incentives @@ -295,12 +295,12 @@ impl Pallet { pub fn calculate_dividend_distribution( pending_alpha: u64, pending_tao: u64, - tao_weight: I96F32, + tao_weight: U96F32, stake_map: BTreeMap, - dividends: BTreeMap, + dividends: BTreeMap, ) -> ( - BTreeMap, - BTreeMap, + BTreeMap, + BTreeMap, ) { log::debug!("dividends: {:?}", dividends); log::debug!("stake_map: {:?}", stake_map); @@ -309,31 +309,31 @@ impl Pallet { log::debug!("tao_weight: {:?}", tao_weight); // Setup. - let zero: I96F32 = asfloat!(0.0); + let zero: U96F32 = asfloat!(0.0); // Accumulate root divs and alpha_divs. For each hotkey we compute their // local and root dividend proportion based on their alpha_stake/root_stake - let mut total_root_divs: I96F32 = asfloat!(0); - let mut total_alpha_divs: I96F32 = asfloat!(0); - let mut root_dividends: BTreeMap = BTreeMap::new(); - let mut alpha_dividends: BTreeMap = BTreeMap::new(); + let mut total_root_divs: U96F32 = asfloat!(0); + let mut total_alpha_divs: U96F32 = asfloat!(0); + let mut root_dividends: BTreeMap = BTreeMap::new(); + let mut alpha_dividends: BTreeMap = BTreeMap::new(); for (hotkey, dividend) in dividends { if let Some((alpha_stake_u64, root_stake_u64)) = stake_map.get(&hotkey) { // Get hotkey ALPHA on subnet. - let alpha_stake: I96F32 = asfloat!(*alpha_stake_u64); + let alpha_stake: U96F32 = asfloat!(*alpha_stake_u64); // Get hotkey TAO on root. - let root_stake: I96F32 = asfloat!(*root_stake_u64); + let root_stake: U96F32 = asfloat!(*root_stake_u64); // Convert TAO to alpha with weight. - let root_alpha: I96F32 = root_stake.saturating_mul(tao_weight); + let root_alpha: U96F32 = root_stake.saturating_mul(tao_weight); // Get total from root and local - let total_alpha: I96F32 = alpha_stake.saturating_add(root_alpha); + let total_alpha: U96F32 = alpha_stake.saturating_add(root_alpha); // Compute root prop. - let root_prop: I96F32 = root_alpha.checked_div(total_alpha).unwrap_or(zero); + let root_prop: U96F32 = root_alpha.checked_div(total_alpha).unwrap_or(zero); // Compute root dividends - let root_divs: I96F32 = dividend.saturating_mul(root_prop); + let root_divs: U96F32 = dividend.saturating_mul(root_prop); // Compute alpha dividends - let alpha_divs: I96F32 = dividend.saturating_sub(root_divs); + let alpha_divs: U96F32 = dividend.saturating_sub(root_divs); // Record the alpha dividends. alpha_dividends .entry(hotkey.clone()) @@ -356,13 +356,13 @@ impl Pallet { log::debug!("total_alpha_divs: {:?}", total_alpha_divs); // Compute root divs as TAO. Here we take - let mut tao_dividends: BTreeMap = BTreeMap::new(); + let mut tao_dividends: BTreeMap = BTreeMap::new(); for (hotkey, root_divs) in root_dividends { // Root proportion. - let root_share: I96F32 = root_divs.checked_div(total_root_divs).unwrap_or(zero); + let root_share: U96F32 = root_divs.checked_div(total_root_divs).unwrap_or(zero); log::debug!("hotkey: {:?}, root_share: {:?}", hotkey, root_share); // Root proportion in TAO - let root_tao: I96F32 = asfloat!(pending_tao).saturating_mul(root_share); + let root_tao: U96F32 = asfloat!(pending_tao).saturating_mul(root_share); log::debug!("hotkey: {:?}, root_tao: {:?}", hotkey, root_tao); // Record root dividends as TAO. tao_dividends @@ -373,14 +373,14 @@ impl Pallet { log::debug!("tao_dividends: {:?}", tao_dividends); // Compute proportional alpha divs using the pending alpha and total alpha divs from the epoch. - let mut prop_alpha_dividends: BTreeMap = BTreeMap::new(); + let mut prop_alpha_dividends: BTreeMap = BTreeMap::new(); for (hotkey, alpha_divs) in alpha_dividends { // Alpha proportion. - let alpha_share: I96F32 = alpha_divs.checked_div(total_alpha_divs).unwrap_or(zero); + let alpha_share: U96F32 = alpha_divs.checked_div(total_alpha_divs).unwrap_or(zero); log::debug!("hotkey: {:?}, alpha_share: {:?}", hotkey, alpha_share); // Compute the proportional pending_alpha to this hotkey. - let prop_alpha: I96F32 = asfloat!(pending_alpha).saturating_mul(alpha_share); + let prop_alpha: U96F32 = asfloat!(pending_alpha).saturating_mul(alpha_share); log::debug!("hotkey: {:?}, prop_alpha: {:?}", hotkey, prop_alpha); // Record the proportional alpha dividends. prop_alpha_dividends @@ -397,8 +397,8 @@ impl Pallet { netuid: u16, owner_cut: u64, incentives: BTreeMap, - alpha_dividends: BTreeMap, - tao_dividends: BTreeMap, + alpha_dividends: BTreeMap, + tao_dividends: BTreeMap, ) { // Distribute the owner cut. if let Ok(owner_coldkey) = SubnetOwner::::try_get(netuid) { @@ -446,7 +446,7 @@ impl Pallet { let _ = AlphaDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); for (hotkey, mut alpha_divs) in alpha_dividends { // Get take prop - let alpha_take: I96F32 = + let alpha_take: U96F32 = Self::get_hotkey_take_float(&hotkey).saturating_mul(alpha_divs); // Remove take prop from alpha_divs alpha_divs = alpha_divs.saturating_sub(alpha_take); @@ -471,7 +471,7 @@ impl Pallet { let _ = TaoDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); for (hotkey, mut root_tao) in tao_dividends { // Get take prop - let tao_take: I96F32 = Self::get_hotkey_take_float(&hotkey).saturating_mul(root_tao); + let tao_take: U96F32 = Self::get_hotkey_take_float(&hotkey).saturating_mul(root_tao); // Remove take prop from root_tao root_tao = root_tao.saturating_sub(tao_take); // Give the validator their take. @@ -517,12 +517,12 @@ impl Pallet { pending_tao: u64, pending_validator_alpha: u64, hotkey_emission: Vec<(T::AccountId, u64, u64)>, - tao_weight: I96F32, + tao_weight: U96F32, ) -> ( BTreeMap, ( - BTreeMap, - BTreeMap, + BTreeMap, + BTreeMap, ), ) { let (incentives, dividends) = @@ -609,31 +609,31 @@ impl Pallet { pub fn get_self_contribution(hotkey: &T::AccountId, netuid: u16) -> u64 { // Get all childkeys for this hotkey. let childkeys = Self::get_children(hotkey, netuid); - let mut remaining_proportion: I96F32 = I96F32::saturating_from_num(1.0); + let mut remaining_proportion: U96F32 = U96F32::saturating_from_num(1.0); for (proportion, _) in childkeys { remaining_proportion = remaining_proportion.saturating_sub( - I96F32::saturating_from_num(proportion) // Normalize - .safe_div(I96F32::saturating_from_num(u64::MAX)), + U96F32::saturating_from_num(proportion) // Normalize + .safe_div(U96F32::saturating_from_num(u64::MAX)), ); } // Get TAO weight - let tao_weight: I96F32 = Self::get_tao_weight(); + let tao_weight: U96F32 = Self::get_tao_weight(); // Get the hotkey's stake including weight - let root_stake: I96F32 = I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( + let root_stake: U96F32 = U96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( hotkey, Self::get_root_netuid(), )); - let alpha_stake: I96F32 = - I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + let alpha_stake: U96F32 = + U96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); // Calculate the - let alpha_contribution: I96F32 = alpha_stake.saturating_mul(remaining_proportion); - let root_contribution: I96F32 = root_stake + let alpha_contribution: U96F32 = alpha_stake.saturating_mul(remaining_proportion); + let root_contribution: U96F32 = root_stake .saturating_mul(remaining_proportion) .saturating_mul(tao_weight); - let combined_contribution: I96F32 = alpha_contribution.saturating_add(root_contribution); + let combined_contribution: U96F32 = alpha_contribution.saturating_add(root_contribution); // Return the combined contribution as a u64 combined_contribution.saturating_to_num::() @@ -661,11 +661,11 @@ impl Pallet { let mut dividend_tuples: Vec<(T::AccountId, u64)> = vec![]; // Calculate the hotkey's share of the validator emission based on its childkey take - let validating_emission: I96F32 = I96F32::saturating_from_num(dividends); - let mut remaining_emission: I96F32 = validating_emission; - let childkey_take_proportion: I96F32 = - I96F32::saturating_from_num(Self::get_childkey_take(hotkey, netuid)) - .safe_div(I96F32::saturating_from_num(u16::MAX)); + let validating_emission: U96F32 = U96F32::saturating_from_num(dividends); + let mut remaining_emission: U96F32 = validating_emission; + let childkey_take_proportion: U96F32 = + U96F32::saturating_from_num(Self::get_childkey_take(hotkey, netuid)) + .safe_div(U96F32::saturating_from_num(u16::MAX)); log::debug!( "Childkey take proportion: {:?} for hotkey {:?}", childkey_take_proportion, @@ -678,14 +678,14 @@ impl Pallet { // Initialize variables to track emission distribution let mut to_parents: u64 = 0; - let mut total_child_emission_take: I96F32 = I96F32::saturating_from_num(0); + let mut total_child_emission_take: U96F32 = U96F32::saturating_from_num(0); // Initialize variables to calculate total stakes from parents - let mut total_contribution: I96F32 = I96F32::saturating_from_num(0); - let mut parent_contributions: Vec<(T::AccountId, I96F32)> = Vec::new(); + let mut total_contribution: U96F32 = U96F32::saturating_from_num(0); + let mut parent_contributions: Vec<(T::AccountId, U96F32)> = Vec::new(); // Get the weights for root and alpha stakes in emission distribution - let tao_weight: I96F32 = Self::get_tao_weight(); + let tao_weight: U96F32 = Self::get_tao_weight(); // Get self contribution, removing any childkey proportions. let self_contribution = Self::get_self_contribution(hotkey, netuid); @@ -697,27 +697,27 @@ impl Pallet { ); // Add self contribution to total contribution but not to the parent contributions. total_contribution = - total_contribution.saturating_add(I96F32::saturating_from_num(self_contribution)); + total_contribution.saturating_add(U96F32::saturating_from_num(self_contribution)); // Calculate total root and alpha (subnet-specific) stakes from all parents for (proportion, parent) in Self::get_parents(hotkey, netuid) { // Convert the parent's stake proportion to a fractional value - let parent_proportion: I96F32 = I96F32::saturating_from_num(proportion) - .safe_div(I96F32::saturating_from_num(u64::MAX)); + let parent_proportion: U96F32 = U96F32::saturating_from_num(proportion) + .safe_div(U96F32::saturating_from_num(u64::MAX)); // Get the parent's root and subnet-specific (alpha) stakes - let parent_root: I96F32 = I96F32::saturating_from_num( + let parent_root: U96F32 = U96F32::saturating_from_num( Self::get_stake_for_hotkey_on_subnet(&parent, Self::get_root_netuid()), ); - let parent_alpha: I96F32 = - I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); + let parent_alpha: U96F32 = + U96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); // Calculate the parent's contribution to the hotkey's stakes - let parent_alpha_contribution: I96F32 = parent_alpha.saturating_mul(parent_proportion); - let parent_root_contribution: I96F32 = parent_root + let parent_alpha_contribution: U96F32 = parent_alpha.saturating_mul(parent_proportion); + let parent_root_contribution: U96F32 = parent_root .saturating_mul(parent_proportion) .saturating_mul(tao_weight); - let combined_contribution: I96F32 = + let combined_contribution: U96F32 = parent_alpha_contribution.saturating_add(parent_root_contribution); // Add to the total stakes @@ -738,22 +738,22 @@ impl Pallet { let parent_owner = Self::get_owning_coldkey_for_hotkey(&parent); // Get the stake contribution of this parent key of the total stake. - let emission_factor: I96F32 = contribution + let emission_factor: U96F32 = contribution .checked_div(total_contribution) - .unwrap_or(I96F32::saturating_from_num(0)); + .unwrap_or(U96F32::saturating_from_num(0)); // Get the parent's portion of the validating emission based on their contribution. - let mut parent_emission: I96F32 = validating_emission.saturating_mul(emission_factor); + let mut parent_emission: U96F32 = validating_emission.saturating_mul(emission_factor); // Remove this emission from the remaining emission. remaining_emission = remaining_emission.saturating_sub(parent_emission); // Get the childkey take for this parent. - let child_emission_take: I96F32 = if parent_owner == childkey_owner { + let child_emission_take: U96F32 = if parent_owner == childkey_owner { // The parent is from the same coldkey, so we don't remove any childkey take. - I96F32::saturating_from_num(0) + U96F32::saturating_from_num(0) } else { childkey_take_proportion - .saturating_mul(I96F32::saturating_from_num(parent_emission)) + .saturating_mul(U96F32::saturating_from_num(parent_emission)) }; // Remove the childkey take from the parent's emission. diff --git a/pallets/subtensor/src/rpc_info/stake_info.rs b/pallets/subtensor/src/rpc_info/stake_info.rs index 7ac5b6b0c6..8a3888061f 100644 --- a/pallets/subtensor/src/rpc_info/stake_info.rs +++ b/pallets/subtensor/src/rpc_info/stake_info.rs @@ -2,7 +2,7 @@ use super::*; use frame_support::pallet_prelude::{Decode, Encode}; extern crate alloc; use codec::Compact; -use substrate_fixed::types::I96F32; +use substrate_fixed::types::U96F32; #[freeze_struct("5cfb3c84c3af3116")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] @@ -139,7 +139,7 @@ impl Pallet { &origin_coldkey_account, destination_, &destination_coldkey_account, - I96F32::saturating_from_num(amount), + U96F32::saturating_from_num(amount), ) } } diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 5aff56ea28..9ee04f36a8 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -1,5 +1,6 @@ use super::*; -use substrate_fixed::types::I96F32; +use safe_math::*; +use substrate_fixed::types::U96F32; use frame_support::traits::{ Imbalance, @@ -46,10 +47,10 @@ impl Pallet { Self::get_all_subnet_netuids() .iter() .map(|netuid| { - let alpha: I96F32 = I96F32::saturating_from_num( + let alpha: U96F32 = U96F32::saturating_from_num( Self::get_stake_for_hotkey_on_subnet(hotkey, *netuid), ); - let tao_price: I96F32 = Self::get_alpha_price(*netuid); + let tao_price: U96F32 = Self::get_alpha_price(*netuid); alpha.saturating_mul(tao_price).saturating_to_num::() }) .sum() @@ -66,9 +67,9 @@ impl Pallet { for (netuid, _) in Alpha::::iter_prefix((hotkey, coldkey)) { let alpha_stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid); - let tao_price: I96F32 = Self::get_alpha_price(netuid); + let tao_price: U96F32 = Self::get_alpha_price(netuid); total_stake = total_stake.saturating_add( - I96F32::saturating_from_num(alpha_stake) + U96F32::saturating_from_num(alpha_stake) .saturating_mul(tao_price) .saturating_to_num::(), ); @@ -128,10 +129,9 @@ impl Pallet { pub fn get_hotkey_take(hotkey: &T::AccountId) -> u16 { Delegates::::get(hotkey) } - pub fn get_hotkey_take_float(hotkey: &T::AccountId) -> I96F32 { - I96F32::saturating_from_num(Self::get_hotkey_take(hotkey)) - .checked_div(I96F32::saturating_from_num(u16::MAX)) - .unwrap_or(I96F32::saturating_from_num(0.0)) + pub fn get_hotkey_take_float(hotkey: &T::AccountId) -> U96F32 { + U96F32::saturating_from_num(Self::get_hotkey_take(hotkey)) + .safe_div(U96F32::saturating_from_num(u16::MAX)) } /// Returns true if the hotkey account has been created. diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 1ccc932157..4198d29efc 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -1,7 +1,7 @@ use super::*; use safe_math::*; use sp_core::Get; -use substrate_fixed::types::{I96F32, U64F64}; +use substrate_fixed::types::{U64F64, U96F32}; impl Pallet { /// Moves stake from one hotkey to another across subnets. @@ -343,7 +343,7 @@ impl Pallet { origin_coldkey, Some((destination_hotkey, destination_netuid)), destination_coldkey, - I96F32::saturating_from_num(alpha_amount), + U96F32::saturating_from_num(alpha_amount), ) .safe_div(2); diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 96c04c9456..b902cb6641 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -1,5 +1,5 @@ use super::*; -use substrate_fixed::types::I96F32; +use substrate_fixed::types::U96F32; impl Pallet { /// ---- The implementation for the extrinsic remove_stake: Removes stake from a hotkey account and adds it onto a coldkey. @@ -63,7 +63,7 @@ impl Pallet { &coldkey, None, &coldkey, - I96F32::saturating_from_num(alpha_unstaked), + U96F32::saturating_from_num(alpha_unstaked), ); let tao_unstaked: u64 = Self::unstake_from_subnet(&hotkey, &coldkey, netuid, alpha_unstaked, fee); @@ -139,7 +139,7 @@ impl Pallet { &coldkey, None, &coldkey, - I96F32::saturating_from_num(alpha_unstaked), + U96F32::saturating_from_num(alpha_unstaked), ); if alpha_unstaked > 0 { @@ -216,7 +216,7 @@ impl Pallet { &coldkey, None, &coldkey, - I96F32::saturating_from_num(alpha_unstaked), + U96F32::saturating_from_num(alpha_unstaked), ); if alpha_unstaked > 0 { @@ -325,7 +325,7 @@ impl Pallet { &coldkey, None, &coldkey, - I96F32::saturating_from_num(alpha_unstaked), + U96F32::saturating_from_num(alpha_unstaked), ); let tao_unstaked = Self::unstake_from_subnet(&hotkey, &coldkey, netuid, possible_alpha, fee); diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 7757851649..e54541d12e 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -2,7 +2,7 @@ use super::*; use safe_math::*; use share_pool::{SharePool, SharePoolDataOperations}; use sp_std::ops::Neg; -use substrate_fixed::types::{I64F64, I96F32, I110F18, U64F64}; +use substrate_fixed::types::{I64F64, I96F32, U64F64, U96F32, U110F18}; impl Pallet { /// Retrieves the total alpha issuance for a given subnet. @@ -30,34 +30,35 @@ impl Pallet { /// /// # Returns /// * `I96F32` - The price of alpha for the specified subnet. - pub fn get_alpha_price(netuid: u16) -> I96F32 { + pub fn get_alpha_price(netuid: u16) -> U96F32 { if netuid == Self::get_root_netuid() { - return I96F32::saturating_from_num(1.0); // Root. + return U96F32::saturating_from_num(1.0); // Root. } if SubnetMechanism::::get(netuid) == 0 { - return I96F32::saturating_from_num(1.0); // Stable + return U96F32::saturating_from_num(1.0); // Stable } if SubnetAlphaIn::::get(netuid) == 0 { - I96F32::saturating_from_num(0) + U96F32::saturating_from_num(0) } else { - I96F32::saturating_from_num(SubnetTAO::::get(netuid)) - .checked_div(I96F32::saturating_from_num(SubnetAlphaIn::::get(netuid))) - .unwrap_or(I96F32::saturating_from_num(0)) + U96F32::saturating_from_num(SubnetTAO::::get(netuid)) + .checked_div(U96F32::saturating_from_num(SubnetAlphaIn::::get(netuid))) + .unwrap_or(U96F32::saturating_from_num(0)) } } - pub fn get_moving_alpha_price(netuid: u16) -> I96F32 { + pub fn get_moving_alpha_price(netuid: u16) -> U96F32 { + let one = U96F32::saturating_from_num(1.0); if netuid == Self::get_root_netuid() { // Root. - I96F32::saturating_from_num(1.0) + one } else if SubnetMechanism::::get(netuid) == 0 { // Stable - I96F32::saturating_from_num(1.0) + one } else { - SubnetMovingPrice::::get(netuid) + U96F32::saturating_from_num(SubnetMovingPrice::::get(netuid)) } } pub fn update_moving_price(netuid: u16) { - let blocks_since_registration = I96F32::saturating_from_num( + let blocks_since_registration = U96F32::saturating_from_num( Self::get_current_block_as_u64().saturating_sub(NetworkRegisteredAt::::get(netuid)), ); @@ -66,16 +67,20 @@ impl Pallet { // will take in order for the distance between current EMA of price and current price to shorten // by half. let halving_time = EMAPriceHalvingBlocks::::get(netuid); - let alpha: I96F32 = - SubnetMovingAlpha::::get().saturating_mul(blocks_since_registration.safe_div( - blocks_since_registration.saturating_add(I96F32::saturating_from_num(halving_time)), - )); - let minus_alpha: I96F32 = I96F32::saturating_from_num(1.0).saturating_sub(alpha); - let current_price: I96F32 = alpha - .saturating_mul(Self::get_alpha_price(netuid).min(I96F32::saturating_from_num(1.0))); - let current_moving: I96F32 = - minus_alpha.saturating_mul(Self::get_moving_alpha_price(netuid)); - let new_moving: I96F32 = current_price.saturating_add(current_moving); + let current_ma_unsigned = U96F32::saturating_from_num(SubnetMovingAlpha::::get()); + let alpha: U96F32 = current_ma_unsigned.saturating_mul(blocks_since_registration.safe_div( + blocks_since_registration.saturating_add(U96F32::saturating_from_num(halving_time)), + )); + // Because alpha = b / (b + h), where b and h > 0, alpha < 1, so 1 - alpha > 0. + // We can use unsigned type here: U96F32 + let one_minus_alpha: U96F32 = U96F32::saturating_from_num(1.0).saturating_sub(alpha); + let current_price: U96F32 = alpha + .saturating_mul(Self::get_alpha_price(netuid).min(U96F32::saturating_from_num(1.0))); + let current_moving: U96F32 = + one_minus_alpha.saturating_mul(Self::get_moving_alpha_price(netuid)); + // Convert batch to signed I96F32 to avoid migration of SubnetMovingPrice for now`` + let new_moving: I96F32 = + I96F32::saturating_from_num(current_price.saturating_add(current_moving)); SubnetMovingPrice::::insert(netuid, new_moving); } @@ -83,28 +88,28 @@ impl Pallet { /// /// This function performs the following steps: /// 1. Fetches the global weight from storage using the TaoWeight storage item. - /// 2. Converts the retrieved u64 value to a fixed-point number (I96F32). + /// 2. Converts the retrieved u64 value to a fixed-point number (U96F32). /// 3. Normalizes the weight by dividing it by the maximum possible u64 value. - /// 4. Returns the normalized weight as an I96F32 fixed-point number. + /// 4. Returns the normalized weight as an U96F32 fixed-point number. /// /// The normalization ensures that the returned value is always between 0 and 1, /// regardless of the actual stored weight value. /// /// # Returns - /// * `I96F32` - The normalized global global weight as a fixed-point number between 0 and 1. + /// * `U96F32` - The normalized global global weight as a fixed-point number between 0 and 1. /// /// # Note /// This function uses saturating division to prevent potential overflow errors. - pub fn get_tao_weight() -> I96F32 { + pub fn get_tao_weight() -> U96F32 { // Step 1: Fetch the global weight from storage let stored_weight = TaoWeight::::get(); - // Step 2: Convert the u64 weight to I96F32 - let weight_fixed = I96F32::saturating_from_num(stored_weight); + // Step 2: Convert the u64 weight to U96F32 + let weight_fixed = U96F32::saturating_from_num(stored_weight); // Step 3: Normalize the weight by dividing by u64::MAX // This ensures the result is always between 0 and 1 - weight_fixed.safe_div(I96F32::saturating_from_num(u64::MAX)) + weight_fixed.safe_div(U96F32::saturating_from_num(u64::MAX)) } /// Sets the global global weight in storage. @@ -237,13 +242,13 @@ impl Pallet { /// # Note /// This function uses saturating arithmetic to prevent overflows. pub fn get_tao_inherited_for_hotkey_on_subnet(hotkey: &T::AccountId, netuid: u16) -> u64 { - let initial_tao: I96F32 = I96F32::saturating_from_num( + let initial_tao: U96F32 = U96F32::saturating_from_num( Self::get_stake_for_hotkey_on_subnet(hotkey, Self::get_root_netuid()), ); // Initialize variables to track alpha allocated to children and inherited from parents. - let mut tao_to_children: I96F32 = I96F32::saturating_from_num(0); - let mut tao_from_parents: I96F32 = I96F32::saturating_from_num(0); + let mut tao_to_children: U96F32 = U96F32::saturating_from_num(0); + let mut tao_from_parents: U96F32 = U96F32::saturating_from_num(0); // Step 2: Retrieve the lists of parents and children for the hotkey on the subnet. let parents: Vec<(u64, T::AccountId)> = Self::get_parents(hotkey, netuid); @@ -264,16 +269,16 @@ impl Pallet { // Step 3: Calculate the total tao allocated to children. for (proportion, _) in children { // Convert the proportion to a normalized value between 0 and 1. - let normalized_proportion: I96F32 = I96F32::saturating_from_num(proportion) - .safe_div(I96F32::saturating_from_num(u64::MAX)); + let normalized_proportion: U96F32 = U96F32::saturating_from_num(proportion) + .safe_div(U96F32::saturating_from_num(u64::MAX)); log::trace!( "Normalized proportion for child: {:?}", normalized_proportion ); // Calculate the amount of tao to be allocated to this child. - let tao_proportion_to_child: I96F32 = - I96F32::saturating_from_num(initial_tao).saturating_mul(normalized_proportion); + let tao_proportion_to_child: U96F32 = + U96F32::saturating_from_num(initial_tao).saturating_mul(normalized_proportion); log::trace!("Tao proportion to child: {:?}", tao_proportion_to_child); // Add this child's allocation to the total tao allocated to children. @@ -284,7 +289,7 @@ impl Pallet { // Step 4: Calculate the total tao inherited from parents. for (proportion, parent) in parents { // Retrieve the parent's total stake on this subnet. - let parent_tao: I96F32 = I96F32::saturating_from_num( + let parent_tao: U96F32 = U96F32::saturating_from_num( Self::get_stake_for_hotkey_on_subnet(&parent, Self::get_root_netuid()), ); log::trace!( @@ -295,16 +300,16 @@ impl Pallet { ); // Convert the proportion to a normalized value between 0 and 1. - let normalized_proportion: I96F32 = I96F32::saturating_from_num(proportion) - .safe_div(I96F32::saturating_from_num(u64::MAX)); + let normalized_proportion: U96F32 = U96F32::saturating_from_num(proportion) + .safe_div(U96F32::saturating_from_num(u64::MAX)); log::trace!( "Normalized proportion from parent: {:?}", normalized_proportion ); // Calculate the amount of tao to be inherited from this parent. - let tao_proportion_from_parent: I96F32 = - I96F32::saturating_from_num(parent_tao).saturating_mul(normalized_proportion); + let tao_proportion_from_parent: U96F32 = + U96F32::saturating_from_num(parent_tao).saturating_mul(normalized_proportion); log::trace!( "Tao proportion from parent: {:?}", tao_proportion_from_parent @@ -316,7 +321,7 @@ impl Pallet { log::trace!("Total tao inherited from parents: {:?}", tao_from_parents); // Step 5: Calculate the final inherited tao for the hotkey. - let finalized_tao: I96F32 = initial_tao + let finalized_tao: U96F32 = initial_tao .saturating_sub(tao_to_children) // Subtract tao allocated to children .saturating_add(tao_from_parents); // Add tao inherited from parents log::trace!( @@ -332,8 +337,8 @@ impl Pallet { pub fn get_inherited_for_hotkey_on_subnet(hotkey: &T::AccountId, netuid: u16) -> u64 { // Step 1: Retrieve the initial total stake (alpha) for the hotkey on the specified subnet. - let initial_alpha: I96F32 = - I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + let initial_alpha: U96F32 = + U96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); log::debug!( "Initial alpha for hotkey {:?} on subnet {}: {:?}", hotkey, @@ -345,8 +350,8 @@ impl Pallet { } // Initialize variables to track alpha allocated to children and inherited from parents. - let mut alpha_to_children: I96F32 = I96F32::saturating_from_num(0); - let mut alpha_from_parents: I96F32 = I96F32::saturating_from_num(0); + let mut alpha_to_children: U96F32 = U96F32::saturating_from_num(0); + let mut alpha_from_parents: U96F32 = U96F32::saturating_from_num(0); // Step 2: Retrieve the lists of parents and children for the hotkey on the subnet. let parents: Vec<(u64, T::AccountId)> = Self::get_parents(hotkey, netuid); @@ -367,16 +372,16 @@ impl Pallet { // Step 3: Calculate the total alpha allocated to children. for (proportion, _) in children { // Convert the proportion to a normalized value between 0 and 1. - let normalized_proportion: I96F32 = I96F32::saturating_from_num(proportion) - .safe_div(I96F32::saturating_from_num(u64::MAX)); + let normalized_proportion: U96F32 = U96F32::saturating_from_num(proportion) + .safe_div(U96F32::saturating_from_num(u64::MAX)); log::trace!( "Normalized proportion for child: {:?}", normalized_proportion ); // Calculate the amount of alpha to be allocated to this child. - let alpha_proportion_to_child: I96F32 = - I96F32::saturating_from_num(initial_alpha).saturating_mul(normalized_proportion); + let alpha_proportion_to_child: U96F32 = + U96F32::saturating_from_num(initial_alpha).saturating_mul(normalized_proportion); log::trace!("Alpha proportion to child: {:?}", alpha_proportion_to_child); // Add this child's allocation to the total alpha allocated to children. @@ -387,8 +392,8 @@ impl Pallet { // Step 4: Calculate the total alpha inherited from parents. for (proportion, parent) in parents { // Retrieve the parent's total stake on this subnet. - let parent_alpha: I96F32 = - I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); + let parent_alpha: U96F32 = + U96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); log::trace!( "Parent alpha for parent {:?} on subnet {}: {:?}", parent, @@ -397,16 +402,16 @@ impl Pallet { ); // Convert the proportion to a normalized value between 0 and 1. - let normalized_proportion: I96F32 = I96F32::saturating_from_num(proportion) - .safe_div(I96F32::saturating_from_num(u64::MAX)); + let normalized_proportion: U96F32 = U96F32::saturating_from_num(proportion) + .safe_div(U96F32::saturating_from_num(u64::MAX)); log::trace!( "Normalized proportion from parent: {:?}", normalized_proportion ); // Calculate the amount of alpha to be inherited from this parent. - let alpha_proportion_from_parent: I96F32 = - I96F32::saturating_from_num(parent_alpha).saturating_mul(normalized_proportion); + let alpha_proportion_from_parent: U96F32 = + U96F32::saturating_from_num(parent_alpha).saturating_mul(normalized_proportion); log::trace!( "Alpha proportion from parent: {:?}", alpha_proportion_from_parent @@ -421,7 +426,7 @@ impl Pallet { ); // Step 5: Calculate the final inherited alpha for the hotkey. - let finalized_alpha: I96F32 = initial_alpha + let finalized_alpha: U96F32 = initial_alpha .saturating_sub(alpha_to_children) // Subtract alpha allocated to children .saturating_add(alpha_from_parents); // Add alpha inherited from parents log::trace!( @@ -620,15 +625,15 @@ impl Pallet { // Step 2: Initialized vars. if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I110F18 = I110F18::saturating_from_num(SubnetTAO::::get(netuid)); - let alpha_reserves: I110F18 = - I110F18::saturating_from_num(SubnetAlphaIn::::get(netuid)); + let tao_reserves: U110F18 = U110F18::saturating_from_num(SubnetTAO::::get(netuid)); + let alpha_reserves: U110F18 = + U110F18::saturating_from_num(SubnetAlphaIn::::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao - let k: I110F18 = alpha_reserves.saturating_mul(tao_reserves); + let k: U110F18 = alpha_reserves.saturating_mul(tao_reserves); // Calculate new alpha reserve - let new_alpha_reserves: I110F18 = - k.safe_div(tao_reserves.saturating_add(I110F18::saturating_from_num(tao))); + let new_alpha_reserves: U110F18 = + k.safe_div(tao_reserves.saturating_add(U110F18::saturating_from_num(tao))); // Step 3.a.3: Calculate alpha staked using the constant product formula // alpha_stake_recieved = current_alpha - (k / (current_tao + new_tao)) @@ -659,16 +664,16 @@ impl Pallet { // Step 2: Swap alpha and attain tao if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I110F18 = I110F18::saturating_from_num(SubnetTAO::::get(netuid)); - let alpha_reserves: I110F18 = - I110F18::saturating_from_num(SubnetAlphaIn::::get(netuid)); + let tao_reserves: U110F18 = U110F18::saturating_from_num(SubnetTAO::::get(netuid)); + let alpha_reserves: U110F18 = + U110F18::saturating_from_num(SubnetAlphaIn::::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao - let k: I110F18 = alpha_reserves.saturating_mul(tao_reserves); + let k: U110F18 = alpha_reserves.saturating_mul(tao_reserves); // Calculate new tao reserve - let new_tao_reserves: I110F18 = k - .checked_div(alpha_reserves.saturating_add(I110F18::saturating_from_num(alpha))) - .unwrap_or(I110F18::saturating_from_num(0)); + let new_tao_reserves: U110F18 = k + .checked_div(alpha_reserves.saturating_add(U110F18::saturating_from_num(alpha))) + .unwrap_or(U110F18::saturating_from_num(0)); // Step 3.a.3: Calculate alpha staked using the constant product formula // tao_recieved = tao_reserves - (k / (alpha_reserves + new_tao)) @@ -1081,7 +1086,7 @@ impl Pallet { _origin_coldkey: &T::AccountId, destination: Option<(&T::AccountId, u16)>, _destination_coldkey: &T::AccountId, - alpha_estimate: I96F32, + alpha_estimate: U96F32, ) -> u64 { match origin { // If origin is defined, we are removing/moving stake @@ -1103,11 +1108,11 @@ impl Pallet { // Otherwise, calculate the fee based on the alpha estimate let mut fee = alpha_estimate .saturating_mul( - I96F32::saturating_from_num(AlphaDividendsPerSubnet::::get( + U96F32::saturating_from_num(AlphaDividendsPerSubnet::::get( origin_netuid, &origin_hotkey, )) - .safe_div(I96F32::saturating_from_num( + .safe_div(U96F32::saturating_from_num( TotalHotkeyAlpha::::get(&origin_hotkey, origin_netuid), )), ) @@ -1116,7 +1121,7 @@ impl Pallet { // 0.005% per epoch matches to 44% annual in compound interest. Do not allow the fee // to be lower than that. (1.00005^(365*20) ~= 1.44) - let apr_20_percent = I96F32::saturating_from_num(0.00005); + let apr_20_percent = U96F32::saturating_from_num(0.00005); fee = fee.max( alpha_estimate .saturating_mul(apr_20_percent) diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index faf48a8366..34c7b5459b 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -4,7 +4,7 @@ use super::mock::*; use approx::assert_abs_diff_eq; use frame_support::{assert_err, assert_noop, assert_ok}; -use substrate_fixed::types::{I64F64, I96F32}; +use substrate_fixed::types::{I64F64, I96F32, U96F32}; use crate::{utils::rate_limiting::TransactionType, *}; use sp_core::U256; @@ -961,7 +961,7 @@ fn test_childkey_take_rate_limiting() { last_block, limit, passes, - current_block.saturating_sub(last_block) + current_block - last_block ); }; @@ -2764,9 +2764,7 @@ fn test_set_weights_no_parent() { // Set a minimum stake to set weights SubtensorModule::set_stake_threshold( - curr_stake_weight - .saturating_sub(I64F64::saturating_from_num(5)) - .saturating_to_num::(), + (curr_stake_weight - I64F64::from_num(5)).to_num::(), ); // Check if the stake for the hotkey is above @@ -3029,7 +3027,7 @@ fn test_parent_child_chain_emission() { // Set the weight of root TAO to be 0%, so only alpha is effective. SubtensorModule::set_tao_weight(0); - let emission: I96F32 = I96F32::from_num(SubtensorModule::get_block_emission().unwrap_or(0)); + let emission: U96F32 = U96F32::from_num(SubtensorModule::get_block_emission().unwrap_or(0)); // Set pending emission to 0 PendingEmission::::insert(netuid, 0); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 0a95f6dd60..eaa1997556 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -6,8 +6,7 @@ use alloc::collections::BTreeMap; use approx::assert_abs_diff_eq; use frame_support::assert_ok; use sp_core::U256; -use substrate_fixed::types::I64F64; -use substrate_fixed::types::I96F32; +use substrate_fixed::types::{I64F64, I96F32, U96F32}; #[allow(clippy::arithmetic_side_effects)] fn close(value: u64, target: u64, eps: u64) { @@ -74,7 +73,7 @@ fn test_dynamic_function_various_values() { #[test] fn test_coinbase_basecase() { new_test_ext(1).execute_with(|| { - SubtensorModule::run_coinbase(I96F32::from_num(0.0)); + SubtensorModule::run_coinbase(U96F32::from_num(0.0)); }); } @@ -91,7 +90,7 @@ fn test_coinbase_tao_issuance_base() { let emission: u64 = 1_234_567; add_network(netuid, 1, 0); assert_eq!(SubnetTAO::::get(netuid), 0); - SubtensorModule::run_coinbase(I96F32::from_num(emission)); + SubtensorModule::run_coinbase(U96F32::from_num(emission)); assert_eq!(SubnetTAO::::get(netuid), emission); assert_eq!(TotalIssuance::::get(), emission); assert_eq!(TotalStake::::get(), emission); @@ -106,7 +105,7 @@ fn test_coinbase_tao_issuance_base_low() { let emission: u64 = 1; add_network(netuid, 1, 0); assert_eq!(SubnetTAO::::get(netuid), 0); - SubtensorModule::run_coinbase(I96F32::from_num(emission)); + SubtensorModule::run_coinbase(U96F32::from_num(emission)); assert_eq!(SubnetTAO::::get(netuid), emission); assert_eq!(TotalIssuance::::get(), emission); assert_eq!(TotalStake::::get(), emission); @@ -133,7 +132,7 @@ fn test_coinbase_tao_issuance_multiple() { assert_eq!(SubnetTAO::::get(netuid1), 0); assert_eq!(SubnetTAO::::get(netuid2), 0); assert_eq!(SubnetTAO::::get(netuid3), 0); - SubtensorModule::run_coinbase(I96F32::from_num(emission)); + SubtensorModule::run_coinbase(U96F32::from_num(emission)); assert_eq!(SubnetTAO::::get(netuid1), emission / 3); assert_eq!(SubnetTAO::::get(netuid2), emission / 3); assert_eq!(SubnetTAO::::get(netuid3), emission / 3); @@ -166,7 +165,7 @@ fn test_coinbase_tao_issuance_different_prices() { assert_eq!(SubnetTAO::::get(netuid1), 0); assert_eq!(SubnetTAO::::get(netuid2), 0); // Run the coinbase with the emission amount. - SubtensorModule::run_coinbase(I96F32::from_num(emission)); + SubtensorModule::run_coinbase(U96F32::from_num(emission)); // Assert tao emission is split evenly. assert_eq!(SubnetTAO::::get(netuid1), emission / 3); assert_eq!(SubnetTAO::::get(netuid2), emission / 3 + emission / 3); @@ -213,7 +212,7 @@ fn test_coinbase_moving_prices() { // Run moving 1 times. SubtensorModule::update_moving_price(netuid); // Assert price is ~ 100% of the real price. - assert!(I96F32::from_num(1.0) - SubtensorModule::get_moving_alpha_price(netuid) < 0.05); + assert!(U96F32::from_num(1.0) - SubtensorModule::get_moving_alpha_price(netuid) < 0.05); // Set price to zero. SubnetMovingPrice::::insert(netuid, I96F32::from_num(0)); SubnetMovingAlpha::::set(I96F32::from_num(0.1)); @@ -227,9 +226,10 @@ fn test_coinbase_moving_prices() { } // Assert price is > 50% of the real price. - assert!( - (I96F32::from_num(0.512325) - SubtensorModule::get_moving_alpha_price(netuid)).abs() - < 0.001 + assert_abs_diff_eq!( + 0.512325, + SubtensorModule::get_moving_alpha_price(netuid).to_num::(), + epsilon = 0.001 ); }); } @@ -305,7 +305,7 @@ fn test_coinbase_alpha_issuance_base() { SubnetTAO::::insert(netuid2, initial); SubnetAlphaIn::::insert(netuid2, initial); // Check initial - SubtensorModule::run_coinbase(I96F32::from_num(emission)); + SubtensorModule::run_coinbase(U96F32::from_num(emission)); // tao_in = 500_000 // alpha_in = 500_000/price = 500_000 assert_eq!(SubnetAlphaIn::::get(netuid1), initial + emission / 2); @@ -340,7 +340,7 @@ fn test_coinbase_alpha_issuance_different() { SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); // Run coinbase - SubtensorModule::run_coinbase(I96F32::from_num(emission)); + SubtensorModule::run_coinbase(U96F32::from_num(emission)); // tao_in = 333_333 // alpha_in = 333_333/price = 333_333 + initial assert_eq!(SubnetAlphaIn::::get(netuid1), initial + emission / 3); @@ -376,7 +376,7 @@ fn test_coinbase_alpha_issuance_with_cap_trigger() { SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); // Run coinbase - SubtensorModule::run_coinbase(I96F32::from_num(emission)); + SubtensorModule::run_coinbase(U96F32::from_num(emission)); // tao_in = 333_333 // alpha_in = 333_333/price > 1_000_000_000 --> 1_000_000_000 + initial_alpha assert_eq!( @@ -420,7 +420,7 @@ fn test_coinbase_alpha_issuance_with_cap_trigger_and_block_emission() { SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); // Run coinbase - SubtensorModule::run_coinbase(I96F32::from_num(emission)); + SubtensorModule::run_coinbase(U96F32::from_num(emission)); // tao_in = 333_333 // alpha_in = 333_333/price > 1_000_000_000 --> 0 + initial_alpha assert_eq!(SubnetAlphaIn::::get(netuid1), initial_alpha); @@ -441,10 +441,10 @@ fn test_owner_cut_base() { add_network(netuid, 1, 0); SubtensorModule::set_tempo(netuid, 10000); // Large number (dont drain) SubtensorModule::set_subnet_owner_cut(0); - SubtensorModule::run_coinbase(I96F32::from_num(0)); + SubtensorModule::run_coinbase(U96F32::from_num(0)); assert_eq!(PendingOwnerCut::::get(netuid), 0); // No cut SubtensorModule::set_subnet_owner_cut(u16::MAX); - SubtensorModule::run_coinbase(I96F32::from_num(0)); + SubtensorModule::run_coinbase(U96F32::from_num(0)); assert_eq!(PendingOwnerCut::::get(netuid), 1_000_000_000); // Full cut. }); } @@ -456,14 +456,14 @@ fn test_pending_swapped() { let netuid: u16 = 1; let emission: u64 = 1_000_000; add_network(netuid, 1, 0); - SubtensorModule::run_coinbase(I96F32::from_num(0)); + SubtensorModule::run_coinbase(U96F32::from_num(0)); assert_eq!(PendingAlphaSwapped::::get(netuid), 0); // Zero tao weight and no root. SubnetTAO::::insert(0, 1_000_000_000); // Add root weight. - SubtensorModule::run_coinbase(I96F32::from_num(0)); + SubtensorModule::run_coinbase(U96F32::from_num(0)); assert_eq!(PendingAlphaSwapped::::get(netuid), 0); // Zero tao weight with 1 root. SubtensorModule::set_tempo(netuid, 10000); // Large number (dont drain) SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 - SubtensorModule::run_coinbase(I96F32::from_num(0)); + SubtensorModule::run_coinbase(U96F32::from_num(0)); assert_eq!(PendingAlphaSwapped::::get(netuid), 125000000); // 1 TAO / ( 1 + 3 ) = 0.25 * 1 / 2 = 125000000 assert_eq!( PendingEmission::::get(netuid), @@ -1484,19 +1484,19 @@ fn test_incentive_to_subnet_owner_is_burned() { fn test_calculate_dividend_distribution_totals() { new_test_ext(1).execute_with(|| { let mut stake_map: BTreeMap = BTreeMap::new(); - let mut dividends: BTreeMap = BTreeMap::new(); + let mut dividends: BTreeMap = BTreeMap::new(); let pending_validator_alpha: u64 = 183_123_567_452; let pending_tao: u64 = 837_120_949_872; - let tao_weight: I96F32 = I96F32::saturating_from_num(0.18); // 18% + let tao_weight: U96F32 = U96F32::saturating_from_num(0.18); // 18% let hotkeys = [U256::from(0), U256::from(1)]; // Stake map and dividends shouldn't matter for this test. stake_map.insert(hotkeys[0], (4_859_302, 2_342_352)); stake_map.insert(hotkeys[1], (23_423, 859_273)); - dividends.insert(hotkeys[0], 77_783_738.into()); - dividends.insert(hotkeys[1], 19_283_940.into()); + dividends.insert(hotkeys[0], 77_783_738_u64.into()); + dividends.insert(hotkeys[1], 19_283_940_u64.into()); let (alpha_dividends, tao_dividends) = SubtensorModule::calculate_dividend_distribution( pending_validator_alpha, @@ -1507,8 +1507,8 @@ fn test_calculate_dividend_distribution_totals() { ); // Verify the total of each dividends type is close to the inputs. - let total_alpha_dividends = alpha_dividends.values().sum::(); - let total_tao_dividends = tao_dividends.values().sum::(); + let total_alpha_dividends = alpha_dividends.values().sum::(); + let total_tao_dividends = tao_dividends.values().sum::(); assert_abs_diff_eq!( total_alpha_dividends.saturating_to_num::(), @@ -1527,19 +1527,19 @@ fn test_calculate_dividend_distribution_totals() { fn test_calculate_dividend_distribution_total_only_tao() { new_test_ext(1).execute_with(|| { let mut stake_map: BTreeMap = BTreeMap::new(); - let mut dividends: BTreeMap = BTreeMap::new(); + let mut dividends: BTreeMap = BTreeMap::new(); let pending_validator_alpha: u64 = 0; let pending_tao: u64 = 837_120_949_872; - let tao_weight: I96F32 = I96F32::saturating_from_num(0.18); // 18% + let tao_weight: U96F32 = U96F32::saturating_from_num(0.18); // 18% let hotkeys = [U256::from(0), U256::from(1)]; // Stake map and dividends shouldn't matter for this test. stake_map.insert(hotkeys[0], (4_859_302, 2_342_352)); stake_map.insert(hotkeys[1], (23_423, 859_273)); - dividends.insert(hotkeys[0], 77_783_738.into()); - dividends.insert(hotkeys[1], 19_283_940.into()); + dividends.insert(hotkeys[0], 77_783_738_u64.into()); + dividends.insert(hotkeys[1], 19_283_940_u64.into()); let (alpha_dividends, tao_dividends) = SubtensorModule::calculate_dividend_distribution( pending_validator_alpha, @@ -1550,8 +1550,8 @@ fn test_calculate_dividend_distribution_total_only_tao() { ); // Verify the total of each dividends type is close to the inputs. - let total_alpha_dividends = alpha_dividends.values().sum::(); - let total_tao_dividends = tao_dividends.values().sum::(); + let total_alpha_dividends = alpha_dividends.values().sum::(); + let total_tao_dividends = tao_dividends.values().sum::(); assert_abs_diff_eq!( total_alpha_dividends.saturating_to_num::(), @@ -1570,19 +1570,19 @@ fn test_calculate_dividend_distribution_total_only_tao() { fn test_calculate_dividend_distribution_total_no_tao_weight() { new_test_ext(1).execute_with(|| { let mut stake_map: BTreeMap = BTreeMap::new(); - let mut dividends: BTreeMap = BTreeMap::new(); + let mut dividends: BTreeMap = BTreeMap::new(); let pending_validator_alpha: u64 = 183_123_567_452; let pending_tao: u64 = 0; // If tao weight is 0, then only alpha dividends should be input. - let tao_weight: I96F32 = I96F32::saturating_from_num(0.0); // 0% + let tao_weight: U96F32 = U96F32::saturating_from_num(0.0); // 0% let hotkeys = [U256::from(0), U256::from(1)]; // Stake map and dividends shouldn't matter for this test. stake_map.insert(hotkeys[0], (4_859_302, 2_342_352)); stake_map.insert(hotkeys[1], (23_423, 859_273)); - dividends.insert(hotkeys[0], 77_783_738.into()); - dividends.insert(hotkeys[1], 19_283_940.into()); + dividends.insert(hotkeys[0], 77_783_738_u64.into()); + dividends.insert(hotkeys[1], 19_283_940_u64.into()); let (alpha_dividends, tao_dividends) = SubtensorModule::calculate_dividend_distribution( pending_validator_alpha, @@ -1593,8 +1593,8 @@ fn test_calculate_dividend_distribution_total_no_tao_weight() { ); // Verify the total of each dividends type is close to the inputs. - let total_alpha_dividends = alpha_dividends.values().sum::(); - let total_tao_dividends = tao_dividends.values().sum::(); + let total_alpha_dividends = alpha_dividends.values().sum::(); + let total_tao_dividends = tao_dividends.values().sum::(); assert_abs_diff_eq!( total_alpha_dividends.saturating_to_num::(), @@ -1613,19 +1613,19 @@ fn test_calculate_dividend_distribution_total_no_tao_weight() { fn test_calculate_dividend_distribution_total_only_alpha() { new_test_ext(1).execute_with(|| { let mut stake_map: BTreeMap = BTreeMap::new(); - let mut dividends: BTreeMap = BTreeMap::new(); + let mut dividends: BTreeMap = BTreeMap::new(); let pending_validator_alpha: u64 = 183_123_567_452; let pending_tao: u64 = 0; - let tao_weight: I96F32 = I96F32::saturating_from_num(0.18); // 18% + let tao_weight: U96F32 = U96F32::saturating_from_num(0.18); // 18% let hotkeys = [U256::from(0), U256::from(1)]; // Stake map and dividends shouldn't matter for this test. stake_map.insert(hotkeys[0], (4_859_302, 2_342_352)); stake_map.insert(hotkeys[1], (23_423, 859_273)); - dividends.insert(hotkeys[0], 77_783_738.into()); - dividends.insert(hotkeys[1], 19_283_940.into()); + dividends.insert(hotkeys[0], 77_783_738_u64.into()); + dividends.insert(hotkeys[1], 19_283_940_u64.into()); let (alpha_dividends, tao_dividends) = SubtensorModule::calculate_dividend_distribution( pending_validator_alpha, @@ -1636,8 +1636,8 @@ fn test_calculate_dividend_distribution_total_only_alpha() { ); // Verify the total of each dividends type is close to the inputs. - let total_alpha_dividends = alpha_dividends.values().sum::(); - let total_tao_dividends = tao_dividends.values().sum::(); + let total_alpha_dividends = alpha_dividends.values().sum::(); + let total_tao_dividends = tao_dividends.values().sum::(); assert_abs_diff_eq!( total_alpha_dividends.saturating_to_num::(), @@ -1672,7 +1672,7 @@ fn test_calculate_dividend_and_incentive_distribution() { let pending_validator_alpha = pending_alpha / 2; // Pay half to validators. let pending_tao: u64 = 0; let pending_swapped = 0; // Only alpha output. - let tao_weight: I96F32 = I96F32::saturating_from_num(0.0); // 0% + let tao_weight: U96F32 = U96F32::saturating_from_num(0.0); // 0% // Hotkey, Incentive, Dividend let hotkey_emission = vec![(hotkey, pending_alpha / 2, pending_alpha / 2)]; @@ -1689,7 +1689,7 @@ fn test_calculate_dividend_and_incentive_distribution() { let incentives_total = incentives.values().sum::(); let dividends_total = alpha_dividends .values() - .sum::() + .sum::() .saturating_to_num::(); assert_abs_diff_eq!( @@ -1719,7 +1719,7 @@ fn test_calculate_dividend_and_incentive_distribution_all_to_validators() { let pending_alpha = 123_456_789; let pending_validator_alpha = pending_alpha; // Pay all to validators. let pending_tao: u64 = 0; - let tao_weight: I96F32 = I96F32::saturating_from_num(0.0); // 0% + let tao_weight: U96F32 = U96F32::saturating_from_num(0.0); // 0% // Hotkey, Incentive, Dividend let hotkey_emission = vec![(hotkey, 0, pending_alpha)]; @@ -1736,7 +1736,7 @@ fn test_calculate_dividend_and_incentive_distribution_all_to_validators() { let incentives_total = incentives.values().sum::(); let dividends_total = alpha_dividends .values() - .sum::() + .sum::() .saturating_to_num::(); assert_eq!( @@ -1775,7 +1775,7 @@ fn test_calculate_dividends_and_incentives() { let incentives_total = incentives.values().sum::(); let dividends_total = dividends .values() - .sum::() + .sum::() .saturating_to_num::(); assert_eq!( @@ -1813,7 +1813,7 @@ fn test_calculate_dividends_and_incentives_only_validators() { let incentives_total = incentives.values().sum::(); let dividends_total = dividends .values() - .sum::() + .sum::() .saturating_to_num::(); assert_eq!(dividends_total, divdends); @@ -1849,7 +1849,7 @@ fn test_calculate_dividends_and_incentives_only_miners() { let incentives_total = incentives.values().sum::(); let dividends_total = dividends .values() - .sum::() + .sum::() .saturating_to_num::(); assert_eq!(incentives_total, incentive); @@ -2018,7 +2018,7 @@ fn test_run_coinbase_not_started() { assert!(SubtensorModule::should_run_epoch(netuid, current_block)); // Run coinbase with emission. - SubtensorModule::run_coinbase(I96F32::saturating_from_num(100_000_000)); + SubtensorModule::run_coinbase(U96F32::saturating_from_num(100_000_000)); // We expect that the epoch ran. assert_eq!(BlocksSinceLastStep::::get(netuid), 0); @@ -2100,7 +2100,7 @@ fn test_run_coinbase_not_started_start_after() { assert!(SubtensorModule::should_run_epoch(netuid, current_block)); // Run coinbase with emission. - SubtensorModule::run_coinbase(I96F32::saturating_from_num(100_000_000)); + SubtensorModule::run_coinbase(U96F32::saturating_from_num(100_000_000)); // We expect that the epoch ran. assert_eq!(BlocksSinceLastStep::::get(netuid), 0); @@ -2120,7 +2120,7 @@ fn test_run_coinbase_not_started_start_after() { ); // Run coinbase with emission. - SubtensorModule::run_coinbase(I96F32::saturating_from_num(100_000_000)); + SubtensorModule::run_coinbase(U96F32::saturating_from_num(100_000_000)); // We expect that the epoch ran. assert_eq!(BlocksSinceLastStep::::get(netuid), 0); diff --git a/pallets/subtensor/src/tests/delegate_info.rs b/pallets/subtensor/src/tests/delegate_info.rs index 6fbcbbfb87..fa9aa942f7 100644 --- a/pallets/subtensor/src/tests/delegate_info.rs +++ b/pallets/subtensor/src/tests/delegate_info.rs @@ -22,9 +22,8 @@ fn test_return_per_1000_tao() { // We expect 82 TAO per day with 10% of total_stake let expected_return_per_1000 = U64F64::from_num(82.0); - let diff_from_expected: f64 = (return_per_1000 / U64F64::from_num(1e9)) - .saturating_sub(expected_return_per_1000) - .to_num::(); + let diff_from_expected: f64 = + ((return_per_1000 / U64F64::from_num(1e9)) - expected_return_per_1000).to_num::(); let eps: f64 = 0.0005e9; // Precision within 0.0005 TAO assert!( diff --git a/pallets/subtensor/src/tests/emission.rs b/pallets/subtensor/src/tests/emission.rs index 61a08ccd32..c4d8d51e3c 100644 --- a/pallets/subtensor/src/tests/emission.rs +++ b/pallets/subtensor/src/tests/emission.rs @@ -102,7 +102,7 @@ fn test_consecutive_blocks() { let mut last_result = SubtensorModule::blocks_until_next_epoch(netuid, tempo, 0); for i in 1..tempo - 1 { let current_result = SubtensorModule::blocks_until_next_epoch(netuid, tempo, i as u64); - assert_eq!(current_result, last_result.saturating_sub(1)); + assert_eq!(current_result, last_result - 1); last_result = current_result; } }); diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index afaee9b980..0b7584a4f0 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -3,7 +3,7 @@ use crate::*; use approx::assert_abs_diff_eq; use frame_support::{assert_err, assert_noop, assert_ok}; use sp_core::{Get, U256}; -use substrate_fixed::types::{I96F32, U64F64}; +use substrate_fixed::types::{U64F64, U96F32}; // 1. test_do_move_success // Description: Test a successful move of stake between two hotkeys in the same subnet @@ -112,9 +112,9 @@ fn test_do_move_different_subnets() { ), 0 ); - let alpha_fee: I96F32 = - I96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); - let expected_value = I96F32::from_num(alpha) + let alpha_fee: U96F32 = + U96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); + let expected_value = U96F32::from_num(alpha) * SubtensorModule::get_alpha_price(origin_netuid) / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( @@ -709,8 +709,8 @@ fn test_do_move_storage_updates() { 0 ); let alpha_fee = - I96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); - let alpha2 = I96F32::from_num(alpha) * SubtensorModule::get_alpha_price(origin_netuid) + U96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); + let alpha2 = U96F32::from_num(alpha) * SubtensorModule::get_alpha_price(origin_netuid) / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1080,7 +1080,7 @@ fn test_do_transfer_different_subnets() { &destination_coldkey, destination_netuid, ); - let expected_value = I96F32::from_num(stake_amount - fee) + let expected_value = U96F32::from_num(stake_amount - fee) / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( dest_stake, @@ -1134,8 +1134,8 @@ fn test_do_swap_success() { destination_netuid, ); let alpha_fee = - I96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); - let expected_value = I96F32::from_num(alpha_before) + U96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); + let expected_value = U96F32::from_num(alpha_before) * SubtensorModule::get_alpha_price(origin_netuid) / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( @@ -1353,8 +1353,8 @@ fn test_do_swap_partial_stake() { ); let alpha_fee = - I96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); - let expected_value = I96F32::from_num(swap_amount) + U96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); + let expected_value = U96F32::from_num(swap_amount) * SubtensorModule::get_alpha_price(origin_netuid) / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( @@ -1408,8 +1408,8 @@ fn test_do_swap_storage_updates() { ); let alpha_fee = - I96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); - let expected_value = I96F32::from_num(alpha) + U96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); + let expected_value = U96F32::from_num(alpha) * SubtensorModule::get_alpha_price(origin_netuid) / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( @@ -1543,7 +1543,7 @@ fn test_swap_stake_limit_validate() { // Setup limit price so that it doesn't allow much slippage at all let limit_price = ((SubtensorModule::get_alpha_price(origin_netuid) / SubtensorModule::get_alpha_price(destination_netuid)) - * I96F32::from_num(1_000_000_000)) + * U96F32::from_num(1_000_000_000)) .to_num::() - 1_u64; @@ -1737,8 +1737,8 @@ fn test_move_stake_specific_stake_into_subnet_fail() { 0 ); let fee = DefaultStakingFee::::get(); - let alpha_fee: I96F32 = I96F32::from_num(fee) / SubtensorModule::get_alpha_price(netuid); - let expected_value = I96F32::from_num(alpha_to_move) + let alpha_fee: U96F32 = U96F32::from_num(fee) / SubtensorModule::get_alpha_price(netuid); + let expected_value = U96F32::from_num(alpha_to_move) * SubtensorModule::get_alpha_price(origin_netuid) / SubtensorModule::get_alpha_price(netuid); assert_abs_diff_eq!( diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 1fc4e7b590..b4506c65c6 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -387,7 +387,7 @@ fn test_remove_stake_ok_no_emission() { // Add subnet TAO for the equivalent amount added at price let amount_tao = - I96F32::saturating_from_num(amount) * SubtensorModule::get_alpha_price(netuid); + U96F32::saturating_from_num(amount) * SubtensorModule::get_alpha_price(netuid); SubnetTAO::::mutate(netuid, |v| *v += amount_tao.saturating_to_num::()); TotalStake::::mutate(|v| *v += amount_tao.saturating_to_num::()); @@ -404,7 +404,7 @@ fn test_remove_stake_ok_no_emission() { &coldkey_account_id, None, &coldkey_account_id, - I96F32::saturating_from_num(amount), + U96F32::saturating_from_num(amount), ); // we do not expect the exact amount due to slippage @@ -568,7 +568,7 @@ fn test_remove_stake_total_balance_no_change() { // Add subnet TAO for the equivalent amount added at price let amount_tao = - I96F32::saturating_from_num(amount) * SubtensorModule::get_alpha_price(netuid); + U96F32::saturating_from_num(amount) * SubtensorModule::get_alpha_price(netuid); SubnetTAO::::mutate(netuid, |v| *v += amount_tao.saturating_to_num::()); TotalStake::::mutate(|v| *v += amount_tao.saturating_to_num::()); @@ -585,7 +585,7 @@ fn test_remove_stake_total_balance_no_change() { &coldkey_account_id, None, &coldkey_account_id, - I96F32::saturating_from_num(amount), + U96F32::saturating_from_num(amount), ); assert_abs_diff_eq!( SubtensorModule::get_coldkey_balance(&coldkey_account_id), @@ -3526,8 +3526,11 @@ fn test_add_stake_limit_ok() { // Check that price has updated to ~24 = (150+450) / (100 - 75) let exp_price = U96F32::from_num(24.0); let current_price: U96F32 = U96F32::from_num(SubtensorModule::get_alpha_price(netuid)); - assert!(exp_price.saturating_sub(current_price) < 0.0001); - assert!(current_price.saturating_sub(exp_price) < 0.0001); + assert_abs_diff_eq!( + exp_price.to_num::(), + current_price.to_num::(), + epsilon = 0.0001, + ); }); } diff --git a/pallets/subtensor/src/tests/staking2.rs b/pallets/subtensor/src/tests/staking2.rs index 57f880ffba..6fbabf83b2 100644 --- a/pallets/subtensor/src/tests/staking2.rs +++ b/pallets/subtensor/src/tests/staking2.rs @@ -6,7 +6,7 @@ use frame_support::{ weights::Weight, }; use sp_core::U256; -use substrate_fixed::types::I96F32; +use substrate_fixed::types::{I96F32, U96F32}; // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --workspace --test staking2 -- test_swap_tao_for_alpha_dynamic_mechanism --exact --nocapture #[test] @@ -673,7 +673,7 @@ fn test_stake_fee_api() { &coldkey1, Some((&hotkey1, netuid0)), &coldkey1, - I96F32::saturating_from_num(stake_amount), + U96F32::saturating_from_num(stake_amount), ); assert_eq!(stake_fee_0, dynamic_fee_0); @@ -690,7 +690,7 @@ fn test_stake_fee_api() { &coldkey1, None, &coldkey1, - I96F32::saturating_from_num(stake_amount), + U96F32::saturating_from_num(stake_amount), ); assert_eq!(stake_fee_1, dynamic_fee_1); @@ -707,7 +707,7 @@ fn test_stake_fee_api() { &coldkey1, Some((&hotkey1, netuid0)), &coldkey1, - I96F32::saturating_from_num(stake_amount), + U96F32::saturating_from_num(stake_amount), ); assert_eq!(stake_fee_2, dynamic_fee_2); @@ -724,7 +724,7 @@ fn test_stake_fee_api() { &coldkey1, Some((&hotkey2, root_netuid)), &coldkey1, - I96F32::saturating_from_num(stake_amount), + U96F32::saturating_from_num(stake_amount), ); assert_eq!(stake_fee_3, dynamic_fee_3); @@ -741,7 +741,7 @@ fn test_stake_fee_api() { &coldkey1, Some((&hotkey1, root_netuid)), &coldkey2, - I96F32::saturating_from_num(stake_amount), + U96F32::saturating_from_num(stake_amount), ); assert_eq!(stake_fee_4, dynamic_fee_4); @@ -758,7 +758,7 @@ fn test_stake_fee_api() { &coldkey1, Some((&hotkey1, root_netuid)), &coldkey1, - I96F32::saturating_from_num(stake_amount), + U96F32::saturating_from_num(stake_amount), ); assert_eq!(stake_fee_5, dynamic_fee_5); @@ -775,7 +775,7 @@ fn test_stake_fee_api() { &coldkey1, Some((&hotkey2, netuid0)), &coldkey1, - I96F32::saturating_from_num(stake_amount), + U96F32::saturating_from_num(stake_amount), ); assert_eq!(stake_fee_6, dynamic_fee_6); @@ -792,7 +792,7 @@ fn test_stake_fee_api() { &coldkey1, Some((&hotkey1, netuid0)), &coldkey2, - I96F32::saturating_from_num(stake_amount), + U96F32::saturating_from_num(stake_amount), ); assert_eq!(stake_fee_7, dynamic_fee_7); @@ -809,7 +809,7 @@ fn test_stake_fee_api() { &coldkey1, Some((&hotkey1, netuid1)), &coldkey1, - I96F32::saturating_from_num(stake_amount), + U96F32::saturating_from_num(stake_amount), ); assert_eq!(stake_fee_8, dynamic_fee_8); }); @@ -861,7 +861,7 @@ fn test_stake_fee_calculation() { &coldkey1, Some((&hotkey1, netuid0)), &coldkey1, - I96F32::from_num(stake_amount), + U96F32::from_num(stake_amount), ); // Default for adding stake assert_eq!(stake_fee_0, default_fee); @@ -871,7 +871,7 @@ fn test_stake_fee_calculation() { &coldkey1, None, &coldkey1, - I96F32::from_num(stake_amount), + U96F32::from_num(stake_amount), ); // Default for removing stake from root assert_eq!(stake_fee_1, default_fee); @@ -881,7 +881,7 @@ fn test_stake_fee_calculation() { &coldkey1, Some((&hotkey1, netuid0)), &coldkey1, - I96F32::from_num(stake_amount), + U96F32::from_num(stake_amount), ); // Default for moving stake from root to non-root assert_eq!(stake_fee_2, default_fee); @@ -891,7 +891,7 @@ fn test_stake_fee_calculation() { &coldkey1, Some((&hotkey2, root_netuid)), &coldkey1, - I96F32::from_num(stake_amount), + U96F32::from_num(stake_amount), ); // Default for moving stake between hotkeys on root assert_eq!(stake_fee_3, default_fee); @@ -901,7 +901,7 @@ fn test_stake_fee_calculation() { &coldkey1, Some((&hotkey1, root_netuid)), &coldkey2, - I96F32::from_num(stake_amount), + U96F32::from_num(stake_amount), ); // Default for moving stake between coldkeys on root assert_eq!(stake_fee_4, default_fee); @@ -911,7 +911,7 @@ fn test_stake_fee_calculation() { &coldkey1, Some((&hotkey1, root_netuid)), &coldkey1, - I96F32::from_num(stake_amount), + U96F32::from_num(stake_amount), ); // Charged a dynamic fee assert_ne!(stake_fee_5, default_fee); @@ -921,7 +921,7 @@ fn test_stake_fee_calculation() { &coldkey1, Some((&hotkey2, netuid0)), &coldkey1, - I96F32::from_num(stake_amount), + U96F32::from_num(stake_amount), ); // Charge the default fee assert_eq!(stake_fee_6, default_fee); @@ -931,7 +931,7 @@ fn test_stake_fee_calculation() { &coldkey1, Some((&hotkey1, netuid0)), &coldkey2, - I96F32::from_num(stake_amount), + U96F32::from_num(stake_amount), ); // Charge the default fee; stake did not leave the subnet. assert_eq!(stake_fee_7, default_fee); @@ -941,7 +941,7 @@ fn test_stake_fee_calculation() { &coldkey1, Some((&hotkey1, netuid1)), &coldkey1, - I96F32::from_num(stake_amount), + U96F32::from_num(stake_amount), ); // Charged a dynamic fee assert_ne!(stake_fee_8, default_fee); }); diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 96394743a4..beb4df59a5 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -14,7 +14,7 @@ use frame_support::traits::schedule::DispatchTime; use frame_support::traits::schedule::v3::Named as ScheduleNamed; use sp_core::{Get, H256, U256}; use sp_runtime::DispatchError; -use substrate_fixed::types::I96F32; +use substrate_fixed::types::U96F32; // // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_total_hotkey_coldkey_stakes_this_interval --exact --nocapture // #[test] @@ -539,7 +539,7 @@ fn test_swap_concurrent_modifications() { let initial_stake = 1_000_000_000_000; let additional_stake = 500_000_000_000; let initial_stake_alpha = - I96F32::from(initial_stake).saturating_mul(SubtensorModule::get_alpha_price(netuid)); + U96F32::from(initial_stake).saturating_mul(SubtensorModule::get_alpha_price(netuid)); let fee = SubtensorModule::calculate_staking_fee( None, &new_coldkey, diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index 5fcbbcb698..14b80a0310 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -3110,7 +3110,7 @@ fn test_reveal_at_exact_block() { let current_block = SubtensorModule::get_current_block_as_u64(); if current_block < reveal_epoch_start_block { // Advance to one block before the reveal epoch starts - let blocks_to_advance = reveal_epoch_start_block.saturating_sub(current_block); + let blocks_to_advance = reveal_epoch_start_block - current_block; if blocks_to_advance > 1 { // Advance to one block before the reveal epoch let new_block_number = current_block + blocks_to_advance - 1; @@ -3181,9 +3181,7 @@ fn test_reveal_at_exact_block() { let commit_epoch = SubtensorModule::get_epoch_index(netuid, commit_block); let reveal_epoch = commit_epoch.saturating_add(reveal_period); let expiration_epoch = reveal_epoch.saturating_add(1); - let expiration_epoch_start_block = expiration_epoch - .saturating_mul(tempo_plus_one) - .saturating_sub(netuid_plus_one); + let expiration_epoch_start_block = expiration_epoch * tempo_plus_one - netuid_plus_one; let current_block = SubtensorModule::get_current_block_as_u64(); if current_block < expiration_epoch_start_block { diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 19d07248d2..c46cf8b36f 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -7,7 +7,7 @@ use safe_math::*; use sp_core::Get; use sp_core::U256; use sp_runtime::Saturating; -use substrate_fixed::types::{I32F32, I96F32}; +use substrate_fixed::types::{I32F32, U96F32}; impl Pallet { pub fn ensure_subnet_owner_or_root( @@ -598,9 +598,9 @@ impl Pallet { pub fn get_subnet_owner_cut() -> u16 { SubnetOwnerCut::::get() } - pub fn get_float_subnet_owner_cut() -> I96F32 { - I96F32::saturating_from_num(SubnetOwnerCut::::get()) - .safe_div(I96F32::saturating_from_num(u16::MAX)) + pub fn get_float_subnet_owner_cut() -> U96F32 { + U96F32::saturating_from_num(SubnetOwnerCut::::get()) + .safe_div(U96F32::saturating_from_num(u16::MAX)) } pub fn set_subnet_owner_cut(subnet_owner_cut: u16) { SubnetOwnerCut::::set(subnet_owner_cut); diff --git a/pallets/utility/src/tests.rs b/pallets/utility/src/tests.rs index 16ae0a3a51..34ed8d323e 100644 --- a/pallets/utility/src/tests.rs +++ b/pallets/utility/src/tests.rs @@ -18,6 +18,7 @@ // Tests for Utility Pallet #![cfg(test)] +#![allow(clippy::arithmetic_side_effects)] use super::*; @@ -689,7 +690,7 @@ fn batch_all_handles_weight_refund() { assert_err_ignore_postinfo!(result, "The cake is a lie."); assert_eq!( extract_actual_weight(&result, &info), - info.weight.saturating_sub(diff.saturating_mul(batch_len)) + info.weight - diff * batch_len ); // Partial batch completion From 7ec94772135f533af099fefc9c908cc858892fb9 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 31 Mar 2025 12:35:07 -0400 Subject: [PATCH 33/45] spec version bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index dc00e1d2fa..96e4306002 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -207,7 +207,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: 258, + spec_version: 259, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From cec883ea841dd137fc864b593a5c4f3028d6f85b Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Mon, 31 Mar 2025 11:08:52 -0700 Subject: [PATCH 34/45] add new events --- pallets/subtensor/src/macros/events.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index e756145f3a..34e5a92581 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -293,5 +293,23 @@ mod events { /// Parameters: /// (coldkey, hotkey, amount, subnet_id) AlphaBurned(T::AccountId, T::AccountId, u64, u16), + + /// CRV3 Weights have been successfully revealed. + /// + /// - **netuid**: The network identifier. + /// - **who**: The account ID of the user revealing the weights. + CRV3WeightsRevealed(u16, T::AccountId), + + /// Commit-Reveal periods has been successfully set. + /// + /// - **netuid**: The network identifier. + /// - **periods**: The number of epochs before the reveal. + CommitRevealPeriodsSet(u16, u64), + + /// Commit-Reveal has been successfully toggled. + /// + /// - **netuid**: The network identifier. + /// - **Enabled**: Is Commit-Reveal enabled. + CommitRevealEnabled(u16, bool) } } From d0d01f8fa09db47a20a917b9476cd9c63a728e79 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Mon, 31 Mar 2025 11:09:21 -0700 Subject: [PATCH 35/45] emit new events --- pallets/subtensor/src/coinbase/run_coinbase.rs | 2 ++ pallets/subtensor/src/subnets/weights.rs | 1 + pallets/subtensor/src/utils/misc.rs | 1 + 3 files changed, 4 insertions(+) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 1f4b5284bd..6d1b2edff0 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -961,6 +961,8 @@ impl Pallet { e ); continue; + } else { + Self::deposit_event(Event::CRV3WeightsRevealed(netuid, who)); }; } diff --git a/pallets/subtensor/src/subnets/weights.rs b/pallets/subtensor/src/subnets/weights.rs index a122e7b985..06a6e4be03 100644 --- a/pallets/subtensor/src/subnets/weights.rs +++ b/pallets/subtensor/src/subnets/weights.rs @@ -1092,6 +1092,7 @@ impl Pallet { pub fn set_reveal_period(netuid: u16, reveal_period: u64) { RevealPeriodEpochs::::insert(netuid, reveal_period); + Self::deposit_event(Event::CommitRevealPeriodsSet(netuid, reveal_period)); } pub fn get_reveal_period(netuid: u16) -> u64 { RevealPeriodEpochs::::get(netuid) diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 19d07248d2..c367e0d8d8 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -475,6 +475,7 @@ impl Pallet { } pub fn set_commit_reveal_weights_enabled(netuid: u16, enabled: bool) { CommitRevealWeightsEnabled::::set(netuid, enabled); + Self::deposit_event(Event::CommitRevealEnabled(netuid, enabled)); } pub fn get_rho(netuid: u16) -> u16 { From b6b45318463d81ae43f6f6da8c0500332c14422e Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Mon, 31 Mar 2025 11:13:12 -0700 Subject: [PATCH 36/45] fmt --- pallets/subtensor/src/macros/events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 34e5a92581..6f3e02dc3a 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -310,6 +310,6 @@ mod events { /// /// - **netuid**: The network identifier. /// - **Enabled**: Is Commit-Reveal enabled. - CommitRevealEnabled(u16, bool) + CommitRevealEnabled(u16, bool), } } From f4d1a25ae5111560d0a2ee7a868db0361f178b5c Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 31 Mar 2025 17:36:15 -0400 Subject: [PATCH 37/45] Use last epoch hotkey alpha for fee calculation --- .../subtensor/src/coinbase/run_coinbase.rs | 10 +++-- pallets/subtensor/src/lib.rs | 11 ++++++ pallets/subtensor/src/staking/stake_utils.rs | 4 +- pallets/subtensor/src/swap/swap_hotkey.rs | 39 +++++++++++++++++++ pallets/subtensor/src/tests/swap_hotkey.rs | 21 ++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 1f4b5284bd..5f1890e0ec 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -454,17 +454,21 @@ impl Pallet { log::debug!("hotkey: {:?} alpha_take: {:?}", hotkey, alpha_take); Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, - &Owner::::get(hotkey.clone()), + &Owner::::get(&hotkey), netuid, tou64!(alpha_take), ); // Give all other nominators. log::debug!("hotkey: {:?} alpha_divs: {:?}", hotkey, alpha_divs); - Self::increase_stake_for_hotkey_on_subnet(&hotkey.clone(), netuid, tou64!(alpha_divs)); + Self::increase_stake_for_hotkey_on_subnet(&hotkey, netuid, tou64!(alpha_divs)); // Record dividends for this hotkey. - AlphaDividendsPerSubnet::::mutate(netuid, hotkey.clone(), |divs| { + AlphaDividendsPerSubnet::::mutate(netuid, &hotkey, |divs| { *divs = divs.saturating_add(tou64!(alpha_divs)); }); + // Record total hotkey alpha based on which this value of AlphaDividendsPerSubnet + // was calculated + let total_hotkey_alpha = TotalHotkeyAlpha::::get(&hotkey, netuid); + TotalHotkeyAlphaLastEpoch::::insert(hotkey, netuid, total_hotkey_alpha); } // Distribute root tao divs. diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 1ec9cadb0a..0024b1587c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1024,6 +1024,17 @@ pub mod pallet { ValueQuery, DefaultZeroU64, >; + #[pallet::storage] // --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owned in the last epoch. + pub type TotalHotkeyAlphaLastEpoch = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, + Identity, + u16, + u64, + ValueQuery, + DefaultZeroU64, + >; #[pallet::storage] /// DMAP ( hot, netuid ) --> total_alpha_shares | Returns the number of alpha shares for a hotkey on a subnet. pub type TotalHotkeyShares = StorageDoubleMap< diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 7757851649..b807a18e5f 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1101,6 +1101,8 @@ impl Pallet { DefaultStakingFee::::get() } else { // Otherwise, calculate the fee based on the alpha estimate + // Here we are using TotalHotkeyAlphaLastEpoch, which is exactly the value that + // was used to calculate AlphaDividendsPerSubnet let mut fee = alpha_estimate .saturating_mul( I96F32::saturating_from_num(AlphaDividendsPerSubnet::::get( @@ -1108,7 +1110,7 @@ impl Pallet { &origin_hotkey, )) .safe_div(I96F32::saturating_from_num( - TotalHotkeyAlpha::::get(&origin_hotkey, origin_netuid), + TotalHotkeyAlphaLastEpoch::::get(&origin_hotkey, origin_netuid), )), ) .saturating_mul(Self::get_alpha_price(origin_netuid)) // fee needs to be in TAO diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index fa336c687b..54c7c01d8e 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -434,6 +434,45 @@ impl Pallet { } } + // 16. Swap dividend records + TotalHotkeyAlphaLastEpoch::::iter_prefix(old_hotkey) + .drain() + .for_each(|(netuid, old_alpha)| { + // 16.1 Swap TotalHotkeyAlphaLastEpoch + let new_total_hotkey_alpha = + TotalHotkeyAlphaLastEpoch::::get(new_hotkey, netuid); + TotalHotkeyAlphaLastEpoch::::insert( + new_hotkey, + netuid, + old_alpha.saturating_add(new_total_hotkey_alpha), + ); + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + + // 16.2 Swap AlphaDividendsPerSubnet + let old_hotkey_alpha_dividends = + AlphaDividendsPerSubnet::::get(netuid, old_hotkey); + let new_hotkey_alpha_dividends = + AlphaDividendsPerSubnet::::get(netuid, new_hotkey); + AlphaDividendsPerSubnet::::remove(netuid, old_hotkey); + AlphaDividendsPerSubnet::::insert( + netuid, + new_hotkey, + old_hotkey_alpha_dividends.saturating_add(new_hotkey_alpha_dividends), + ); + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + + // 16.3 Swap TaoDividendsPerSubnet + let old_hotkey_tao_dividends = TaoDividendsPerSubnet::::get(netuid, old_hotkey); + let new_hotkey_tao_dividends = TaoDividendsPerSubnet::::get(netuid, new_hotkey); + TaoDividendsPerSubnet::::remove(netuid, old_hotkey); + TaoDividendsPerSubnet::::insert( + netuid, + new_hotkey, + old_hotkey_tao_dividends.saturating_add(new_hotkey_tao_dividends), + ); + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + }); + // Return successful after swapping all the relevant terms. Ok(()) } diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index 63413b0667..a82972c2f7 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -897,8 +897,11 @@ fn test_swap_stake_success() { // Initialize staking variables for old_hotkey TotalHotkeyAlpha::::insert(old_hotkey, netuid, amount); + TotalHotkeyAlphaLastEpoch::::insert(old_hotkey, netuid, amount * 2); TotalHotkeyShares::::insert(old_hotkey, netuid, U64F64::from_num(shares)); Alpha::::insert((old_hotkey, coldkey, netuid), U64F64::from_num(amount)); + AlphaDividendsPerSubnet::::insert(netuid, old_hotkey, amount); + TaoDividendsPerSubnet::::insert(netuid, old_hotkey, amount); // Perform the swap SubtensorModule::perform_hotkey_swap(&old_hotkey, &new_hotkey, &coldkey, &mut weight); @@ -906,6 +909,14 @@ fn test_swap_stake_success() { // Verify the swap assert_eq!(TotalHotkeyAlpha::::get(old_hotkey, netuid), 0); assert_eq!(TotalHotkeyAlpha::::get(new_hotkey, netuid), amount); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(old_hotkey, netuid), + 0 + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(new_hotkey, netuid), + amount * 2 + ); assert_eq!( TotalHotkeyShares::::get(old_hotkey, netuid), U64F64::from_num(0) @@ -922,6 +933,16 @@ fn test_swap_stake_success() { Alpha::::get((new_hotkey, coldkey, netuid)), U64F64::from_num(amount) ); + assert_eq!(AlphaDividendsPerSubnet::::get(netuid, old_hotkey), 0); + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, new_hotkey), + amount + ); + assert_eq!(TaoDividendsPerSubnet::::get(netuid, old_hotkey), 0); + assert_eq!( + TaoDividendsPerSubnet::::get(netuid, new_hotkey), + amount + ); }); } From ee4941d7d8374e6c588de22b12671a735ca03eea Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 31 Mar 2025 17:58:05 -0400 Subject: [PATCH 38/45] Merge devnet-ready --- pallets/subtensor/src/staking/stake_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index b058007335..30c9f25071 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1114,7 +1114,7 @@ impl Pallet { origin_netuid, &origin_hotkey, )) - .safe_div(I96F32::saturating_from_num( + .safe_div(U96F32::saturating_from_num( TotalHotkeyAlphaLastEpoch::::get(&origin_hotkey, origin_netuid), )), ) From cfe38a5cd2a112b8e555118a76be291f99efc0d5 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 1 Apr 2025 13:20:25 -0400 Subject: [PATCH 39/45] More tests for dividend variables --- pallets/subtensor/src/staking/stake_utils.rs | 12 +- pallets/subtensor/src/tests/staking.rs | 181 ++++++++++++++++++- 2 files changed, 182 insertions(+), 11 deletions(-) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 30c9f25071..ae07ad760a 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1108,7 +1108,14 @@ impl Pallet { // Otherwise, calculate the fee based on the alpha estimate // Here we are using TotalHotkeyAlphaLastEpoch, which is exactly the value that // was used to calculate AlphaDividendsPerSubnet - let mut fee = alpha_estimate + let tao_estimate = U96F32::saturating_from_num( + Self::sim_swap_alpha_for_tao( + origin_netuid, + alpha_estimate.saturating_to_num::(), + ) + .unwrap_or(0), + ); + let mut fee = tao_estimate .saturating_mul( U96F32::saturating_from_num(AlphaDividendsPerSubnet::::get( origin_netuid, @@ -1118,14 +1125,13 @@ impl Pallet { TotalHotkeyAlphaLastEpoch::::get(&origin_hotkey, origin_netuid), )), ) - .saturating_mul(Self::get_alpha_price(origin_netuid)) // fee needs to be in TAO .saturating_to_num::(); // 0.005% per epoch matches to 44% annual in compound interest. Do not allow the fee // to be lower than that. (1.00005^(365*20) ~= 1.44) let apr_20_percent = U96F32::saturating_from_num(0.00005); fee = fee.max( - alpha_estimate + tao_estimate .saturating_mul(apr_20_percent) .saturating_to_num::(), ); diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index b4506c65c6..fe6113548c 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -771,6 +771,170 @@ fn test_remove_stake_total_issuance_no_change() { }); } +// cargo test --package pallet-subtensor --lib -- tests::staking::test_remove_prev_epoch_stake --exact --show-output --nocapture +#[test] +fn test_remove_prev_epoch_stake() { + new_test_ext(1).execute_with(|| { + let def_fee = DefaultStakingFee::::get(); + + // Test case: (amount_to_stake, AlphaDividendsPerSubnet, TotalHotkeyAlphaLastEpoch, expected_fee) + [ + // No previous epoch stake and low hotkey stake + ( + DefaultMinStake::::get() * 10, + 0_u64, + 1000_u64, + def_fee * 2, + ), + // Same, but larger amount to stake - we get 0.005% for unstake + ( + 1_000_000_000, + 0_u64, + 1000_u64, + (1_000_000_000_f64 * 0.00005) as u64 + def_fee, + ), + ( + 100_000_000_000, + 0_u64, + 1000_u64, + (100_000_000_000_f64 * 0.00005) as u64 + def_fee, + ), + // Lower previous epoch stake than current stake + // Staking/unstaking 100 TAO, divs / total = 0.1 => fee is 1 TAO + ( + 100_000_000_000, + 1_000_000_000_u64, + 10_000_000_000_u64, + (100_000_000_000_f64 * 0.1) as u64 + def_fee, + ), + // Staking/unstaking 100 TAO, divs / total = 0.001 => fee is 0.01 TAO + ( + 100_000_000_000, + 10_000_000_u64, + 10_000_000_000_u64, + (100_000_000_000_f64 * 0.001) as u64 + def_fee, + ), + // Higher previous epoch stake than current stake + ( + 1_000_000_000, + 100_000_000_000_u64, + 100_000_000_000_000_u64, + (1_000_000_000_f64 * 0.001) as u64 + def_fee, + ), + ] + .iter() + .for_each( + |(amount_to_stake, alpha_divs, hotkey_alpha, expected_fee)| { + let subnet_owner_coldkey = U256::from(1); + let subnet_owner_hotkey = U256::from(2); + let hotkey_account_id = U256::from(581337); + let coldkey_account_id = U256::from(81337); + let amount = *amount_to_stake; + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); + + // Give it some $$$ in his coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + AlphaDividendsPerSubnet::::insert(netuid, hotkey_account_id, *alpha_divs); + TotalHotkeyAlphaLastEpoch::::insert(hotkey_account_id, netuid, *hotkey_alpha); + let balance_before = SubtensorModule::get_coldkey_balance(&coldkey_account_id); + + // Stake to hotkey account, and check if the result is ok + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount + )); + + // Remove all stake + let stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid, + ); + + assert_ok!(SubtensorModule::remove_stake( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + stake + )); + + // Measure actual fee + let balance_after = SubtensorModule::get_coldkey_balance(&coldkey_account_id); + let actual_fee = balance_before - balance_after; + + assert_abs_diff_eq!(actual_fee, *expected_fee, epsilon = *expected_fee / 100,); + }, + ); + }); +} + +// cargo test --package pallet-subtensor --lib -- tests::staking::test_staking_sets_div_variables --exact --show-output --nocapture +#[test] +fn test_staking_sets_div_variables() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1); + let subnet_owner_hotkey = U256::from(2); + let hotkey_account_id = U256::from(581337); + let coldkey_account_id = U256::from(81337); + let amount = 100_000_000_000; + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let tempo = 10; + Tempo::::insert(netuid, tempo); + register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); + + // Give it some $$$ in his coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + + // Verify that divident variables are clear in the beginning + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, hotkey_account_id), + 0 + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(hotkey_account_id, netuid), + 0 + ); + + // Stake to hotkey account, and check if the result is ok + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount + )); + + // Verify that divident variables are still clear in the beginning + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, hotkey_account_id), + 0 + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(hotkey_account_id, netuid), + 0 + ); + + // Wait for 1 epoch + step_block(tempo + 1); + + // Verify that divident variables have been set + let stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid, + ); + + assert!(AlphaDividendsPerSubnet::::get(netuid, hotkey_account_id) > 0); + assert_abs_diff_eq!( + TotalHotkeyAlphaLastEpoch::::get(hotkey_account_id, netuid), + stake, + epsilon = stake / 100_000 + ); + }); +} + /*********************************************************** staking::get_coldkey_balance() tests ************************************************************/ @@ -2300,7 +2464,7 @@ fn test_remove_stake_fee_realistic_values() { SubnetTAO::::insert(netuid, tao_reserve.to_num::()); SubnetAlphaIn::::insert(netuid, alpha_in.to_num::()); AlphaDividendsPerSubnet::::insert(netuid, hotkey, alpha_divs); - let current_price = SubtensorModule::get_alpha_price(netuid).to_num::(); + TotalHotkeyAlphaLastEpoch::::insert(hotkey, netuid, alpha_to_unstake); // Add stake first time to init TotalHotkeyAlpha SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( @@ -2310,17 +2474,18 @@ fn test_remove_stake_fee_realistic_values() { alpha_to_unstake, ); - // Estimate fees - let mut expected_fee: f64 = current_price * alpha_divs as f64; - if expected_fee < alpha_to_unstake as f64 * 0.00005 { - expected_fee = alpha_to_unstake as f64 * 0.00005; - } - // Remove stake to measure fee let balance_before = SubtensorModule::get_coldkey_balance(&coldkey); let expected_tao_no_fee = SubtensorModule::sim_swap_alpha_for_tao(netuid, alpha_to_unstake).unwrap(); + // Estimate fees + let mut expected_fee = + expected_tao_no_fee as f64 * alpha_divs as f64 / alpha_to_unstake as f64; + if expected_fee < expected_tao_no_fee as f64 * 0.00005 { + expected_fee = expected_tao_no_fee as f64 * 0.00005; + } + assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey), hotkey, @@ -3942,7 +4107,7 @@ fn test_remove_99_9991_per_cent_stake_removes_all() { assert_abs_diff_eq!( SubtensorModule::get_coldkey_balance(&coldkey_account_id), amount - fee, - epsilon = 10000, + epsilon = 100000, ); assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), From 3fc43c5f90211d909b30f144b6f1eb6a9f61c228 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:48:06 -0700 Subject: [PATCH 40/45] add min commitment size --- pallets/commitments/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/commitments/src/lib.rs b/pallets/commitments/src/lib.rs index c59b32437b..2c0122f7ff 100644 --- a/pallets/commitments/src/lib.rs +++ b/pallets/commitments/src/lib.rs @@ -225,7 +225,8 @@ pub mod pallet { .fields .iter() .map(|field| field.len_for_rate_limit()) - .sum(); + .sum::() + .max(100); let mut usage = UsedSpaceOf::::get(netuid, &who).unwrap_or_default(); let cur_block_u64 = cur_block.saturated_into::(); From f4055152c6eb21d4b9a233d9483881bf57544b01 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:48:13 -0700 Subject: [PATCH 41/45] add test --- pallets/commitments/src/tests.rs | 75 +++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index 4a0f80be5d..d667db4bbb 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -4,7 +4,7 @@ use sp_std::prelude::*; #[cfg(test)] use crate::{ CommitmentInfo, CommitmentOf, Config, Data, Error, Event, MaxSpace, Pallet, RateLimit, - Registration, RevealedCommitments, TimelockedIndex, + Registration, RevealedCommitments, TimelockedIndex, UsedSpaceOf, mock::{ Balances, DRAND_QUICKNET_SIG_2000_HEX, DRAND_QUICKNET_SIG_HEX, RuntimeEvent, RuntimeOrigin, Test, TestMaxFields, insert_drand_pulse, new_test_ext, produce_ciphertext, @@ -1716,3 +1716,76 @@ fn revealed_commitments_keeps_only_10_newest_with_individual_single_field_commit } }); } + +#[test] +fn usage_respects_minimum_of_100_bytes() { + new_test_ext().execute_with(|| { + MaxSpace::::set(1000); + + let netuid = 1; + let who = 99; + + System::::set_block_number(1); + + let small_data = Data::Raw(vec![0u8; 50].try_into().expect("<=128 bytes for Raw")); + let info_small = Box::new(CommitmentInfo { + fields: BoundedVec::try_from(vec![small_data]).expect("Must not exceed MaxFields"), + }); + + let usage_before = UsedSpaceOf::::get(netuid, &who).unwrap_or_default(); + assert_eq!(usage_before.used_space, 0); + + assert_ok!(Pallet::::set_commitment( + RuntimeOrigin::signed(who), + netuid, + info_small + )); + + let usage_after_small = UsedSpaceOf::::get(netuid, &who).unwrap(); + assert_eq!( + usage_after_small.used_space, 100, + "Usage must jump to 100 even though we only used 50 bytes" + ); + + let big_data = Data::Raw(vec![0u8; 110].try_into().expect("<=128 bytes for Raw")); + let info_big = Box::new(CommitmentInfo { + fields: BoundedVec::try_from(vec![big_data]).expect("Must not exceed MaxFields"), + }); + + assert_ok!(Pallet::::set_commitment( + RuntimeOrigin::signed(who), + netuid, + info_big + )); + + let usage_after_big = UsedSpaceOf::::get(netuid, &who).unwrap(); + assert_eq!( + usage_after_big.used_space, 210, + "Usage should be 100 + 110 = 210 in this epoch" + ); + + UsedSpaceOf::::remove(netuid, &who); + let usage_after_wipe = UsedSpaceOf::::get(netuid, &who); + assert!( + usage_after_wipe.is_none(), + "Expected `UsedSpaceOf` entry to be removed" + ); + + let bigger_data = Data::Raw(vec![0u8; 120].try_into().expect("<=128 bytes for Raw")); + let info_bigger = Box::new(CommitmentInfo { + fields: BoundedVec::try_from(vec![bigger_data]).expect("Must not exceed MaxFields"), + }); + + assert_ok!(Pallet::::set_commitment( + RuntimeOrigin::signed(who), + netuid, + info_bigger + )); + + let usage_after_reset = UsedSpaceOf::::get(netuid, &who).unwrap(); + assert_eq!( + usage_after_reset.used_space, 120, + "After wiping old usage, the new usage should be exactly 120" + ); + }); +} From 7540223cb49c9d80be9fc88dbb89a0995e699d2c Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:49:55 -0700 Subject: [PATCH 42/45] clippy --- pallets/commitments/src/tests.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index d667db4bbb..bde31ff9e9 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -1732,7 +1732,7 @@ fn usage_respects_minimum_of_100_bytes() { fields: BoundedVec::try_from(vec![small_data]).expect("Must not exceed MaxFields"), }); - let usage_before = UsedSpaceOf::::get(netuid, &who).unwrap_or_default(); + let usage_before = UsedSpaceOf::::get(netuid, who).unwrap_or_default(); assert_eq!(usage_before.used_space, 0); assert_ok!(Pallet::::set_commitment( @@ -1741,7 +1741,8 @@ fn usage_respects_minimum_of_100_bytes() { info_small )); - let usage_after_small = UsedSpaceOf::::get(netuid, &who).unwrap(); + let usage_after_small = + UsedSpaceOf::::get(netuid, who).expect("expected to not panic"); assert_eq!( usage_after_small.used_space, 100, "Usage must jump to 100 even though we only used 50 bytes" @@ -1758,14 +1759,14 @@ fn usage_respects_minimum_of_100_bytes() { info_big )); - let usage_after_big = UsedSpaceOf::::get(netuid, &who).unwrap(); + let usage_after_big = UsedSpaceOf::::get(netuid, who).expect("expected to not panic"); assert_eq!( usage_after_big.used_space, 210, "Usage should be 100 + 110 = 210 in this epoch" ); - UsedSpaceOf::::remove(netuid, &who); - let usage_after_wipe = UsedSpaceOf::::get(netuid, &who); + UsedSpaceOf::::remove(netuid, who); + let usage_after_wipe = UsedSpaceOf::::get(netuid, who); assert!( usage_after_wipe.is_none(), "Expected `UsedSpaceOf` entry to be removed" @@ -1782,7 +1783,8 @@ fn usage_respects_minimum_of_100_bytes() { info_bigger )); - let usage_after_reset = UsedSpaceOf::::get(netuid, &who).unwrap(); + let usage_after_reset = + UsedSpaceOf::::get(netuid, who).expect("expected to not panic"); assert_eq!( usage_after_reset.used_space, 120, "After wiping old usage, the new usage should be exactly 120" From 13a5bc6ecad15d989940c44c32d5d896a89e81ce Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:30:28 -0700 Subject: [PATCH 43/45] fix new errors in old tests --- pallets/commitments/src/tests.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index bde31ff9e9..c9b14d188b 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -887,7 +887,7 @@ fn tempo_based_space_limit_accumulates_in_same_window() { new_test_ext().execute_with(|| { let netuid = 1; let who = 100; - let space_limit = 50; + let space_limit = 150; MaxSpace::::set(space_limit); System::::set_block_number(0); @@ -921,7 +921,7 @@ fn tempo_based_space_limit_resets_after_tempo() { let netuid = 2; let who = 101; - MaxSpace::::set(40); + MaxSpace::::set(250); System::::set_block_number(1); let commit_small = Box::new(CommitmentInfo { @@ -979,7 +979,7 @@ fn tempo_based_space_limit_does_not_affect_different_netuid() { let netuid_a = 10; let netuid_b = 20; let who = 111; - let space_limit = 50; + let space_limit = 199; MaxSpace::::set(space_limit); let commit_large = Box::new(CommitmentInfo { @@ -1029,7 +1029,7 @@ fn tempo_based_space_limit_does_not_affect_different_user() { let netuid = 10; let user1 = 123; let user2 = 456; - let space_limit = 50; + let space_limit = 199; MaxSpace::::set(space_limit); let commit_large = Box::new(CommitmentInfo { @@ -1078,7 +1078,7 @@ fn tempo_based_space_limit_sudo_set_max_space() { new_test_ext().execute_with(|| { let netuid = 3; let who = 15; - MaxSpace::::set(30); + MaxSpace::::set(100); System::::set_block_number(1); let commit_25 = Box::new(CommitmentInfo { @@ -1098,7 +1098,7 @@ fn tempo_based_space_limit_sudo_set_max_space() { Error::::SpaceLimitExceeded ); - assert_ok!(Pallet::::set_max_space(RuntimeOrigin::root(), 100)); + assert_ok!(Pallet::::set_max_space(RuntimeOrigin::root(), 300)); assert_ok!(Pallet::::set_commitment( RuntimeOrigin::signed(who), From 76b3fd8942cc6ef11a307d06026661a90f06033f Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:31:16 -0700 Subject: [PATCH 44/45] magic number => variable --- pallets/commitments/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/commitments/src/lib.rs b/pallets/commitments/src/lib.rs index 2c0122f7ff..11e1ae76ee 100644 --- a/pallets/commitments/src/lib.rs +++ b/pallets/commitments/src/lib.rs @@ -221,12 +221,13 @@ pub mod pallet { let cur_block = >::block_number(); + let min_used_space: u64 = 100; let required_space: u64 = info .fields .iter() .map(|field| field.len_for_rate_limit()) .sum::() - .max(100); + .max(min_used_space); let mut usage = UsedSpaceOf::::get(netuid, &who).unwrap_or_default(); let cur_block_u64 = cur_block.saturated_into::(); From 2d4781286b49ffe82d7fd3654f2c2dadcbf644b0 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 1 Apr 2025 16:36:27 -0400 Subject: [PATCH 45/45] Add transferToggle precompile call, deprecate setMinBurn and setWeightsRateLimit --- precompiles/src/solidity/subnet.abi | 18 +++++++++++ precompiles/src/subnet.rs | 46 ++++++++++++++++------------- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/precompiles/src/solidity/subnet.abi b/precompiles/src/solidity/subnet.abi index 3cc16d9df7..e2a3e569da 100644 --- a/precompiles/src/solidity/subnet.abi +++ b/precompiles/src/solidity/subnet.abi @@ -883,5 +883,23 @@ "outputs": [], "stateMutability": "payable", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "toggle", + "type": "bool" + } + ], + "name": "toggleTransfers", + "outputs": [], + "stateMutability": "payable", + "type": "function" } ] diff --git a/precompiles/src/subnet.rs b/precompiles/src/subnet.rs index cffe82ab78..e9bfc0c5f9 100644 --- a/precompiles/src/subnet.rs +++ b/precompiles/src/subnet.rs @@ -200,19 +200,12 @@ where #[precompile::public("setWeightsSetRateLimit(uint16,uint64)")] #[precompile::payable] fn set_weights_set_rate_limit( - handle: &mut impl PrecompileHandle, - netuid: u16, - weights_set_rate_limit: u64, + _handle: &mut impl PrecompileHandle, + _netuid: u16, + _weights_set_rate_limit: u64, ) -> EvmResult<()> { - let call = pallet_admin_utils::Call::::sudo_set_weights_set_rate_limit { - netuid, - weights_set_rate_limit, - }; - - handle.try_dispatch_runtime_call::( - call, - RawOrigin::Signed(handle.caller_account_id::()), - ) + // DEPRECATED. Subnet owner cannot set weight setting rate limits + Ok(()) } #[precompile::public("getAdjustmentAlpha(uint16)")] @@ -436,16 +429,12 @@ where #[precompile::public("setMinBurn(uint16,uint64)")] #[precompile::payable] fn set_min_burn( - handle: &mut impl PrecompileHandle, - netuid: u16, - min_burn: u64, + _handle: &mut impl PrecompileHandle, + _netuid: u16, + _min_burn: u64, ) -> EvmResult<()> { - let call = pallet_admin_utils::Call::::sudo_set_min_burn { netuid, min_burn }; - - handle.try_dispatch_runtime_call::( - call, - RawOrigin::Signed(handle.caller_account_id::()), - ) + // DEPRECATED. The subnet owner cannot set the min burn anymore. + Ok(()) } #[precompile::public("getMaxBurn(uint16)")] @@ -616,4 +605,19 @@ where RawOrigin::Signed(handle.caller_account_id::()), ) } + + #[precompile::public("toggleTransfers(uint16,bool)")] + #[precompile::payable] + fn toggle_transfers( + handle: &mut impl PrecompileHandle, + netuid: u16, + toggle: bool, + ) -> EvmResult<()> { + let call = pallet_admin_utils::Call::::sudo_set_toggle_transfer { netuid, toggle }; + + handle.try_dispatch_runtime_call::( + call, + RawOrigin::Signed(handle.caller_account_id::()), + ) + } }