diff --git a/Cargo.lock b/Cargo.lock index e86ef3d698..1d5fad7da2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6456,6 +6456,7 @@ dependencies = [ "parity-util-mem", "rand", "rand_chacha", + "safe-math", "scale-info", "serde", "serde-tuple-vec-map", @@ -8032,6 +8033,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "safe-math" +version = "0.1.0" +dependencies = [ + "num-traits", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", + "substrate-fixed", +] + [[package]] name = "safe-mix" version = "1.0.1" @@ -9568,6 +9578,7 @@ dependencies = [ name = "share-pool" version = "0.1.0" dependencies = [ + "safe-math", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", "substrate-fixed", ] diff --git a/pallets/collective/src/lib.rs b/pallets/collective/src/lib.rs index 823e926634..707f49deda 100644 --- a/pallets/collective/src/lib.rs +++ b/pallets/collective/src/lib.rs @@ -549,7 +549,8 @@ pub mod pallet { ); let threshold = T::GetVotingMembers::get_count() - .saturating_div(2) + .checked_div(2) + .unwrap_or(0) .saturating_add(1); let members = Self::members(); diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index fe1bb7e2dd..f22f855fc1 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -41,6 +41,7 @@ pallet-utility = { workspace = true } ndarray = { workspace = true } hex = { workspace = true } share-pool = { default-features = false, path = "../../primitives/share-pool" } +safe-math = { default-features = false, path = "../../primitives/safe-math" } approx = { workspace = true } pallet-collective = { version = "4.0.0-dev", default-features = false, path = "../collective" } @@ -104,6 +105,7 @@ std = [ "ark-serialize/std", "w3f-bls/std", "rand_chacha/std", + "safe-math/std", "sha2/std", "share-pool/std" ] diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index a19651f8cb..6d2c57b742 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -1,5 +1,6 @@ use super::*; use frame_support::traits::Get; +use safe_math::*; use substrate_fixed::{transcendental::log2, types::I96F32}; impl Pallet { @@ -30,15 +31,15 @@ impl Pallet { alpha_block_emission: u64, ) -> (u64, u64, u64) { // Init terms. - let mut tao_in_emission: I96F32 = I96F32::from_num(tao_emission); - let float_alpha_block_emission: I96F32 = I96F32::from_num(alpha_block_emission); + 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); // Get alpha price for subnet. let alpha_price: I96F32 = Self::get_alpha_price(netuid); log::debug!("{:?} - alpha_price: {:?}", netuid, alpha_price); // Get initial alpha_in - let mut alpha_in_emission: I96F32 = I96F32::from_num(tao_emission) + let mut alpha_in_emission: I96F32 = I96F32::saturating_from_num(tao_emission) .checked_div(alpha_price) .unwrap_or(float_alpha_block_emission); @@ -59,9 +60,11 @@ impl Pallet { } // Avoid rounding errors. - if tao_in_emission < I96F32::from_num(1) || alpha_in_emission < I96F32::from_num(1) { - alpha_in_emission = I96F32::from_num(0); - tao_in_emission = I96F32::from_num(0); + if tao_in_emission < I96F32::saturating_from_num(1) + || alpha_in_emission < I96F32::saturating_from_num(1) + { + alpha_in_emission = I96F32::saturating_from_num(0); + tao_in_emission = I96F32::saturating_from_num(0); } // Set Alpha in emission. @@ -78,9 +81,9 @@ impl Pallet { // Return result. ( - tao_in_emission.to_num::(), - alpha_in_emission.to_num::(), - alpha_out_emission.to_num::(), + tao_in_emission.saturating_to_num::(), + alpha_in_emission.saturating_to_num::(), + alpha_out_emission.saturating_to_num::(), ) } @@ -103,23 +106,22 @@ impl Pallet { /// Returns the block emission for an issuance value. pub fn get_block_emission_for_issuance(issuance: u64) -> Result { // Convert issuance to a float for calculations below. - let total_issuance: I96F32 = I96F32::from_num(issuance); + let total_issuance: I96F32 = I96F32::saturating_from_num(issuance); // Check to prevent division by zero when the total supply is reached // and creating an issuance greater than the total supply. - if total_issuance >= I96F32::from_num(TotalSupply::::get()) { + if total_issuance >= I96F32::saturating_from_num(TotalSupply::::get()) { return Ok(0); } // Calculate the logarithmic residual of the issuance against half the total supply. let residual: I96F32 = log2( - I96F32::from_num(1.0) + I96F32::saturating_from_num(1.0) .checked_div( - I96F32::from_num(1.0) + I96F32::saturating_from_num(1.0) .checked_sub( total_issuance - .checked_div( - I96F32::from_num(2.0) - .saturating_mul(I96F32::from_num(10_500_000_000_000_000.0)), - ) + .checked_div(I96F32::saturating_from_num(2.0).saturating_mul( + I96F32::saturating_from_num(10_500_000_000_000_000.0), + )) .ok_or("Logarithm calculation failed")?, ) .ok_or("Logarithm calculation failed")?, @@ -131,18 +133,19 @@ impl Pallet { let floored_residual: I96F32 = residual.floor(); // Calculate the final emission rate using the floored residual. // Convert floored_residual to an integer - let floored_residual_int: u64 = floored_residual.to_num::(); + let floored_residual_int: u64 = floored_residual.saturating_to_num::(); // Multiply 2.0 by itself floored_residual times to calculate the power of 2. - let mut multiplier: I96F32 = I96F32::from_num(1.0); + let mut multiplier: I96F32 = I96F32::saturating_from_num(1.0); for _ in 0..floored_residual_int { - multiplier = multiplier.saturating_mul(I96F32::from_num(2.0)); + multiplier = multiplier.saturating_mul(I96F32::saturating_from_num(2.0)); } - let block_emission_percentage: I96F32 = I96F32::from_num(1.0).saturating_div(multiplier); + let block_emission_percentage: I96F32 = + I96F32::saturating_from_num(1.0).safe_div(multiplier); // Calculate the actual emission based on the emission rate let block_emission: I96F32 = block_emission_percentage - .saturating_mul(I96F32::from_num(DefaultBlockEmission::::get())); + .saturating_mul(I96F32::saturating_from_num(DefaultBlockEmission::::get())); // Convert to u64 - let block_emission_u64: u64 = block_emission.to_num::(); + let block_emission_u64: u64 = block_emission.saturating_to_num::(); if BlockEmission::::get() != block_emission_u64 { BlockEmission::::put(block_emission_u64); } diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index 23218ed50c..bcfd1a37bc 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -1,5 +1,6 @@ use super::*; use frame_support::storage::IterableStorageMap; +use safe_math::*; use substrate_fixed::types::{I110F18, I96F32}; impl Pallet { @@ -10,7 +11,8 @@ impl Pallet { // --- 1. Adjust difficulties. Self::adjust_registration_terms_for_networks(); // --- 2. Get the current coinbase emission. - let block_emission: I96F32 = I96F32::from_num(Self::get_block_emission().unwrap_or(0)); + let block_emission: I96F32 = + I96F32::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); @@ -198,28 +200,28 @@ impl Pallet { registrations_this_interval: u16, target_registrations_per_interval: u16, ) -> u64 { - let updated_difficulty: I110F18 = I110F18::from_num(current_difficulty) - .saturating_mul(I110F18::from_num( + let updated_difficulty: I110F18 = I110F18::saturating_from_num(current_difficulty) + .saturating_mul(I110F18::saturating_from_num( registrations_this_interval.saturating_add(target_registrations_per_interval), )) - .saturating_div(I110F18::from_num( + .safe_div(I110F18::saturating_from_num( target_registrations_per_interval.saturating_add(target_registrations_per_interval), )); - let alpha: I110F18 = I110F18::from_num(Self::get_adjustment_alpha(netuid)) - .saturating_div(I110F18::from_num(u64::MAX)); + 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::from_num(current_difficulty)) + .saturating_mul(I110F18::saturating_from_num(current_difficulty)) .saturating_add( - I110F18::from_num(1.0) + I110F18::saturating_from_num(1.0) .saturating_sub(alpha) .saturating_mul(updated_difficulty), ); - if next_value >= I110F18::from_num(Self::get_max_difficulty(netuid)) { + if next_value >= I110F18::saturating_from_num(Self::get_max_difficulty(netuid)) { Self::get_max_difficulty(netuid) - } else if next_value <= I110F18::from_num(Self::get_min_difficulty(netuid)) { + } else if next_value <= I110F18::saturating_from_num(Self::get_min_difficulty(netuid)) { return Self::get_min_difficulty(netuid); } else { - return next_value.to_num::(); + return next_value.saturating_to_num::(); } } @@ -232,28 +234,28 @@ impl Pallet { registrations_this_interval: u16, target_registrations_per_interval: u16, ) -> u64 { - let updated_burn: I110F18 = I110F18::from_num(current_burn) - .saturating_mul(I110F18::from_num( + let updated_burn: I110F18 = I110F18::saturating_from_num(current_burn) + .saturating_mul(I110F18::saturating_from_num( registrations_this_interval.saturating_add(target_registrations_per_interval), )) - .saturating_div(I110F18::from_num( + .safe_div(I110F18::saturating_from_num( target_registrations_per_interval.saturating_add(target_registrations_per_interval), )); - let alpha: I110F18 = I110F18::from_num(Self::get_adjustment_alpha(netuid)) - .saturating_div(I110F18::from_num(u64::MAX)); + 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::from_num(current_burn)) + .saturating_mul(I110F18::saturating_from_num(current_burn)) .saturating_add( - I110F18::from_num(1.0) + I110F18::saturating_from_num(1.0) .saturating_sub(alpha) .saturating_mul(updated_burn), ); - if next_value >= I110F18::from_num(Self::get_max_burn_as_u64(netuid)) { + if next_value >= I110F18::saturating_from_num(Self::get_max_burn_as_u64(netuid)) { Self::get_max_burn_as_u64(netuid) - } else if next_value <= I110F18::from_num(Self::get_min_burn_as_u64(netuid)) { + } else if next_value <= I110F18::saturating_from_num(Self::get_min_burn_as_u64(netuid)) { return Self::get_min_burn_as_u64(netuid); } else { - return next_value.to_num::(); + return next_value.saturating_to_num::(); } } } diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 112c887853..3f83f934f1 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -19,6 +19,7 @@ use super::*; use frame_support::dispatch::Pays; use frame_support::storage::IterableStorageDoubleMap; use frame_support::weights::Weight; +use safe_math::*; use sp_core::Get; use sp_std::vec; use substrate_fixed::types::I64F64; @@ -112,7 +113,7 @@ impl Pallet { // --- 2. Initialize a 2D vector with zeros to store the weights. The dimensions are determined // by `n` (number of validators) and `k` (total number of subnets). - let mut weights: Vec> = vec![vec![I64F64::from_num(0.0); k]; n]; + let mut weights: Vec> = vec![vec![I64F64::saturating_from_num(0.0); k]; n]; log::debug!("weights:\n{:?}\n", weights); let subnet_list = Self::get_all_subnet_netuids(); @@ -134,7 +135,7 @@ impl Pallet { .zip(&subnet_list) .find(|(_, subnet)| *subnet == netuid) { - *w = I64F64::from_num(*weight_ij); + *w = I64F64::saturating_from_num(*weight_ij); } } } @@ -624,7 +625,7 @@ impl Pallet { let mut lock_cost = last_lock.saturating_mul(mult).saturating_sub( last_lock - .saturating_div(lock_reduction_interval) + .safe_div(lock_reduction_interval) .saturating_mul(current_block.saturating_sub(last_lock_block)), ); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 2e9fe44b48..c810e5c505 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -1,5 +1,6 @@ use super::*; use alloc::collections::BTreeMap; +use safe_math::*; use substrate_fixed::types::I96F32; use tle::stream_ciphers::AESGCMStreamCipherProvider; use tle::tlock::tld; @@ -24,15 +25,16 @@ impl Pallet { validator_proportion: I96F32, ) -> I96F32 { // Get total TAO on root. - let total_root_tao: I96F32 = I96F32::from_num(SubnetTAO::::get(0)); + let total_root_tao: I96F32 = I96F32::saturating_from_num(SubnetTAO::::get(0)); // Get total ALPHA on subnet. - let total_alpha_issuance: I96F32 = I96F32::from_num(Self::get_alpha_issuance(netuid)); + let total_alpha_issuance: I96F32 = + I96F32::saturating_from_num(Self::get_alpha_issuance(netuid)); // Get tao_weight let tao_weight: I96F32 = total_root_tao.saturating_mul(Self::get_tao_weight()); // Get root proportional dividends. let root_proportion: I96F32 = tao_weight .checked_div(tao_weight.saturating_add(total_alpha_issuance)) - .unwrap_or(I96F32::from_num(0.0)); + .unwrap_or(I96F32::saturating_from_num(0.0)); // Get root proportion of alpha_out dividends. let root_divs_in_alpha: I96F32 = root_proportion .saturating_mul(alpha_out_emission) @@ -53,17 +55,22 @@ impl Pallet { // --- 2. Sum all the SubnetTAO associated with the same mechanism. // Mechanisms get emission based on the proportion of TAO across all their subnets - let mut total_active_tao: I96F32 = I96F32::from_num(0); + let mut total_active_tao: I96F32 = I96F32::saturating_from_num(0); let mut mechanism_tao: BTreeMap = BTreeMap::new(); for netuid in subnets.iter() { if *netuid == 0 { continue; } // Skip root network let mechid = SubnetMechanism::::get(*netuid); - let subnet_tao = I96F32::from_num(SubnetTAO::::get(*netuid)); - let new_subnet_tao = subnet_tao - .saturating_add(*mechanism_tao.entry(mechid).or_insert(I96F32::from_num(0))); - *mechanism_tao.entry(mechid).or_insert(I96F32::from_num(0)) = new_subnet_tao; + let subnet_tao = I96F32::saturating_from_num(SubnetTAO::::get(*netuid)); + let new_subnet_tao = subnet_tao.saturating_add( + *mechanism_tao + .entry(mechid) + .or_insert(I96F32::saturating_from_num(0)), + ); + *mechanism_tao + .entry(mechid) + .or_insert(I96F32::saturating_from_num(0)) = new_subnet_tao; total_active_tao = total_active_tao.saturating_add(subnet_tao); } log::debug!("Mechanism TAO sums: {:?}", mechanism_tao); @@ -79,10 +86,12 @@ impl Pallet { let mechid: u16 = SubnetMechanism::::get(*netuid); log::debug!("Netuid: {:?}, Mechanism ID: {:?}", netuid, mechid); // 3.2: Get subnet TAO (T_s) - let subnet_tao: I96F32 = I96F32::from_num(SubnetTAO::::get(*netuid)); + let subnet_tao: I96F32 = I96F32::saturating_from_num(SubnetTAO::::get(*netuid)); log::debug!("Subnet TAO (T_s) for netuid {:?}: {:?}", netuid, subnet_tao); // 3.3: Get the denominator as the sum of all TAO associated with a specific mechanism (T_m) - let mech_tao: I96F32 = *mechanism_tao.get(&mechid).unwrap_or(&I96F32::from_num(0)); + let mech_tao: I96F32 = *mechanism_tao + .get(&mechid) + .unwrap_or(&I96F32::saturating_from_num(0)); log::debug!( "Mechanism TAO (T_m) for mechanism ID {:?}: {:?}", mechid, @@ -91,7 +100,7 @@ impl Pallet { // 3.4: Compute the mechanism emission proportion: P_m = T_m / T_total let mech_proportion: I96F32 = mech_tao .checked_div(total_active_tao) - .unwrap_or(I96F32::from_num(0)); + .unwrap_or(I96F32::saturating_from_num(0)); log::debug!( "Mechanism proportion (P_m) for mechanism ID {:?}: {:?}", mechid, @@ -107,7 +116,7 @@ impl Pallet { // 3.6: Calculate subnet's proportion of mechanism TAO: P_s = T_s / T_m let subnet_proportion: I96F32 = subnet_tao .checked_div(mech_tao) - .unwrap_or(I96F32::from_num(0)); + .unwrap_or(I96F32::saturating_from_num(0)); log::debug!( "Subnet proportion (P_s) for netuid {:?}: {:?}", netuid, @@ -116,8 +125,8 @@ impl Pallet { // 3.7: Calculate subnet's TAO emission: E_s = P_s * E_m let tao_in: u64 = mech_emission .checked_mul(subnet_proportion) - .unwrap_or(I96F32::from_num(0)) - .to_num::(); + .unwrap_or(I96F32::saturating_from_num(0)) + .saturating_to_num::(); log::debug!( "Subnet TAO emission (E_s) for netuid {:?}: {:?}", netuid, @@ -198,9 +207,9 @@ impl Pallet { }); // Calculate the owner cut. - let owner_cut: u64 = I96F32::from_num(alpha_out_emission) + let owner_cut: u64 = I96F32::saturating_from_num(alpha_out_emission) .saturating_mul(Self::get_float_subnet_owner_cut()) - .to_num::(); + .saturating_to_num::(); log::debug!("Owner cut for netuid {:?}: {:?}", netuid, owner_cut); // Store the owner cut for this subnet. *owner_cuts.entry(*netuid).or_insert(0) = owner_cut; @@ -213,31 +222,36 @@ impl Pallet { ); // Validators get 50% of remaining emission. - let validator_proportion: I96F32 = I96F32::from_num(0.5); + let validator_proportion: I96F32 = I96F32::saturating_from_num(0.5); // Get proportion of alpha out emission as root divs. let root_emission_in_alpha: I96F32 = Self::get_root_divs_in_alpha( *netuid, - I96F32::from_num(remaining_emission), + I96F32::saturating_from_num(remaining_emission), validator_proportion, ); // Subtract root divs from alpha divs. - let pending_alpha_emission: I96F32 = - I96F32::from_num(remaining_emission).saturating_sub(root_emission_in_alpha); + let pending_alpha_emission: I96F32 = I96F32::saturating_from_num(remaining_emission) + .saturating_sub(root_emission_in_alpha); // Sell root emission through the pool. - let root_emission_in_tao: u64 = - Self::swap_alpha_for_tao(*netuid, root_emission_in_alpha.to_num::()); - SubnetAlphaEmissionSell::::insert(*netuid, root_emission_in_alpha.to_num::()); + let root_emission_in_tao: u64 = Self::swap_alpha_for_tao( + *netuid, + root_emission_in_alpha.saturating_to_num::(), + ); + SubnetAlphaEmissionSell::::insert( + *netuid, + root_emission_in_alpha.saturating_to_num::(), + ); // Accumulate root divs for subnet. PendingRootDivs::::mutate(*netuid, |total| { *total = total.saturating_add(root_emission_in_tao); }); // Accumulate alpha that was swapped for the pending root divs. PendingAlphaSwapped::::mutate(*netuid, |total| { - *total = total.saturating_add(root_emission_in_alpha.to_num::()); + *total = total.saturating_add(root_emission_in_alpha.saturating_to_num::()); }); // Accumulate alpha emission in pending. PendingEmission::::mutate(*netuid, |total| { - *total = total.saturating_add(pending_alpha_emission.to_num::()); + *total = total.saturating_add(pending_alpha_emission.saturating_to_num::()); }); // Accumulate the owner cut in pending. PendingOwnerCut::::mutate(*netuid, |total| { @@ -372,29 +386,29 @@ impl Pallet { ); // 2.1 --- Get the local alpha and root alpha. - let hotkey_tao: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( - hotkey_j, - Self::get_root_netuid(), - )); + let hotkey_tao: I96F32 = I96F32::saturating_from_num( + Self::get_stake_for_hotkey_on_subnet(hotkey_j, Self::get_root_netuid()), + ); let hotkey_tao_as_alpha: I96F32 = hotkey_tao.saturating_mul(Self::get_tao_weight()); - let hotkey_alpha = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey_j, netuid)); + let hotkey_alpha = I96F32::saturating_from_num( + Self::get_stake_for_hotkey_on_subnet(hotkey_j, netuid), + ); log::debug!("Hotkey tao for hotkey {:?} on root netuid: {:?}, hotkey tao as alpha: {:?}, hotkey alpha: {:?}", hotkey_j, hotkey_tao, hotkey_tao_as_alpha, hotkey_alpha); // 2.2 --- Compute alpha and root proportions. let alpha_prop: I96F32 = hotkey_alpha .checked_div(hotkey_alpha.saturating_add(hotkey_tao_as_alpha)) - .unwrap_or(I96F32::from_num(0.0)); + .unwrap_or(I96F32::saturating_from_num(0.0)); let root_prop: I96F32 = hotkey_tao_as_alpha .checked_div(hotkey_alpha.saturating_add(hotkey_tao_as_alpha)) - .unwrap_or(I96F32::from_num(0.0)); + .unwrap_or(I96F32::saturating_from_num(0.0)); log::debug!( "Alpha proportion: {:?}, root proportion: {:?}", alpha_prop, root_prop ); - let divs_j: I96F32 = I96F32::from_num(*divs_j); + let divs_j: I96F32 = I96F32::saturating_from_num(*divs_j); // 2.3.1 --- Compute root dividends let root_alpha_divs_j: I96F32 = divs_j.saturating_mul(root_prop); // 2.3.2 --- Compute alpha dividends @@ -406,9 +420,10 @@ impl Pallet { ); // 2.4.1 --- Remove the hotkey take from both alpha and root divs. - let take_prop: I96F32 = I96F32::from_num(Self::get_hotkey_take(hotkey_j)) - .checked_div(I96F32::from_num(u16::MAX)) - .unwrap_or(I96F32::from_num(0.0)); + let take_prop: I96F32 = + I96F32::saturating_from_num(Self::get_hotkey_take(hotkey_j)) + .checked_div(I96F32::saturating_from_num(u16::MAX)) + .unwrap_or(I96F32::saturating_from_num(0.0)); let validator_alpha_take: I96F32 = take_prop.saturating_mul(alpha_divs_j); let validator_root_alpha_take: I96F32 = take_prop.saturating_mul(root_alpha_divs_j); @@ -428,41 +443,47 @@ impl Pallet { // 2.4.2 --- Store the validator takes. validator_alpha_takes .entry(hotkey_j.clone()) - .and_modify(|e| *e = e.saturating_add(validator_alpha_take.to_num::())) - .or_insert(validator_alpha_take.to_num::()); + .and_modify(|e| { + *e = e.saturating_add(validator_alpha_take.saturating_to_num::()) + }) + .or_insert(validator_alpha_take.saturating_to_num::()); validator_root_alpha_takes .entry(hotkey_j.clone()) .and_modify(|e| { - *e = e.saturating_add(validator_root_alpha_take.to_num::()) + *e = e.saturating_add(validator_root_alpha_take.saturating_to_num::()) }) - .or_insert(validator_root_alpha_take.to_num::()); + .or_insert(validator_root_alpha_take.saturating_to_num::()); log::debug!( "Stored validator take for hotkey {:?}: alpha take: {:?}, root-alpha take: {:?}", hotkey_j, - validator_alpha_take.to_num::(), - validator_root_alpha_take.to_num::() + validator_alpha_take.saturating_to_num::(), + validator_root_alpha_take.saturating_to_num::() ); // 2.5.1 --- Store the root divs under hotkey_j root_alpha_divs .entry(hotkey_j.clone()) - .and_modify(|e| *e = e.saturating_add(root_alpha_divs_j.to_num::())) - .or_insert(root_alpha_divs_j.to_num::()); + .and_modify(|e| { + *e = e.saturating_add(root_alpha_divs_j.saturating_to_num::()) + }) + .or_insert(root_alpha_divs_j.saturating_to_num::()); log::debug!( "Stored root alpha dividends for hotkey {:?}: {:?}", hotkey_j, - root_alpha_divs_j.to_num::() + root_alpha_divs_j.saturating_to_num::() ); // 2.5.2 --- Store the alpha dividends alpha_divs .entry(hotkey_j.clone()) - .and_modify(|e| *e = e.saturating_add(rem_alpha_divs_j.to_num::())) - .or_insert(rem_alpha_divs_j.to_num::()); + .and_modify(|e| { + *e = e.saturating_add(rem_alpha_divs_j.saturating_to_num::()) + }) + .or_insert(rem_alpha_divs_j.saturating_to_num::()); log::debug!( "Stored alpha dividends for hotkey {:?}: {:?}", hotkey_j, - rem_alpha_divs_j.to_num::() + rem_alpha_divs_j.saturating_to_num::() ); } } @@ -537,13 +558,13 @@ impl Pallet { // 3.3.2 --- Distribute validator root-alpha takes for (validator_j, validator_take) in validator_root_alpha_takes { // 3.3.2a --- Calculate the proportion of root divs to pay out to this validator's take. - let proportion: I96F32 = I96F32::from_num(validator_take) - .checked_div(I96F32::from_num(total_root_alpha_divs)) - .unwrap_or(I96F32::from_num(0)); + let proportion: I96F32 = I96F32::saturating_from_num(validator_take) + .checked_div(I96F32::saturating_from_num(total_root_alpha_divs)) + .unwrap_or(I96F32::saturating_from_num(0)); // 3.3.2b --- Get the proportion of root divs from the pending root divs. let take_as_root_divs: u64 = proportion - .saturating_mul(I96F32::from_num(pending_root_divs)) - .to_num::(); + .saturating_mul(I96F32::saturating_from_num(pending_root_divs)) + .saturating_to_num::(); log::debug!( "Root div proportion for validator take {:?}: {:?}, take_as_root_divs: {:?}", validator_take, @@ -604,13 +625,13 @@ impl Pallet { // For all the root-alpha divs give this proportion of the swapped tao to the root participants. for (hotkey_j, root_alpha_divs_j) in root_alpha_divs.iter() { // 3.5.1 --- Calculate the proportion of root divs to pay out to this hotkey. - let proportion: I96F32 = I96F32::from_num(*root_alpha_divs_j) - .checked_div(I96F32::from_num(total_root_alpha_divs)) - .unwrap_or(I96F32::from_num(0)); + let proportion: I96F32 = I96F32::saturating_from_num(*root_alpha_divs_j) + .checked_div(I96F32::saturating_from_num(total_root_alpha_divs)) + .unwrap_or(I96F32::saturating_from_num(0)); // 3.5.2 --- Get the proportion of root divs from the pending root divs. let root_divs_to_pay: u64 = proportion - .saturating_mul(I96F32::from_num(pending_root_divs)) - .to_num::(); + .saturating_mul(I96F32::saturating_from_num(pending_root_divs)) + .saturating_to_num::(); log::debug!( "Proportion for hotkey {:?}: {:?}, root_divs_to_pay: {:?}", hotkey_j, @@ -649,11 +670,11 @@ 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::from_num(1.0); + let mut remaining_proportion: I96F32 = I96F32::saturating_from_num(1.0); for (proportion, _) in childkeys { remaining_proportion = remaining_proportion.saturating_sub( - I96F32::from_num(proportion) // Normalize - .saturating_div(I96F32::from_num(u64::MAX)), + I96F32::saturating_from_num(proportion) // Normalize + .safe_div(I96F32::saturating_from_num(u64::MAX)), ); } @@ -661,12 +682,12 @@ impl Pallet { let tao_weight: I96F32 = Self::get_tao_weight(); // Get the hotkey's stake including weight - let root_stake: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( + let root_stake: I96F32 = I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( hotkey, Self::get_root_netuid(), )); let alpha_stake: I96F32 = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); // Calculate the let alpha_contribution: I96F32 = alpha_stake.saturating_mul(remaining_proportion); @@ -676,7 +697,7 @@ impl Pallet { let combined_contribution: I96F32 = alpha_contribution.saturating_add(root_contribution); // Return the combined contribution as a u64 - combined_contribution.to_num::() + combined_contribution.saturating_to_num::() } /// Returns a list of tuples for each parent associated with this hotkey including self @@ -701,10 +722,10 @@ 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::from_num(dividends); + let validating_emission: I96F32 = I96F32::saturating_from_num(dividends); let childkey_take_proportion: I96F32 = - I96F32::from_num(Self::get_childkey_take(hotkey, netuid)) - .saturating_div(I96F32::from_num(u16::MAX)); + I96F32::saturating_from_num(Self::get_childkey_take(hotkey, netuid)) + .safe_div(I96F32::saturating_from_num(u16::MAX)); log::debug!( "Childkey take proportion: {:?} for hotkey {:?}", childkey_take_proportion, @@ -713,8 +734,8 @@ impl Pallet { // NOTE: Only the validation emission should be split amongst parents. // Reserve childkey take - let child_emission_take: I96F32 = - childkey_take_proportion.saturating_mul(I96F32::from_num(validating_emission)); + let child_emission_take: I96F32 = childkey_take_proportion + .saturating_mul(I96F32::saturating_from_num(validating_emission)); let remaining_emission: I96F32 = validating_emission.saturating_sub(child_emission_take); log::debug!( "Child emission take: {:?} for hotkey {:?}", @@ -731,7 +752,7 @@ impl Pallet { let mut to_parents: u64 = 0; // Initialize variables to calculate total stakes from parents - let mut total_contribution: I96F32 = I96F32::from_num(0); + let mut total_contribution: I96F32 = I96F32::saturating_from_num(0); let mut parent_contributions: Vec<(T::AccountId, I96F32)> = Vec::new(); // Get the weights for root and alpha stakes in emission distribution @@ -746,21 +767,21 @@ impl Pallet { self_contribution ); // Add self contribution to total contribution but not to the parent contributions. - total_contribution = total_contribution.saturating_add(I96F32::from_num(self_contribution)); + total_contribution = + total_contribution.saturating_add(I96F32::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::from_num(proportion).saturating_div(I96F32::from_num(u64::MAX)); + let parent_proportion: I96F32 = I96F32::saturating_from_num(proportion) + .safe_div(I96F32::saturating_from_num(u64::MAX)); // Get the parent's root and subnet-specific (alpha) stakes - let parent_root: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( - &parent, - Self::get_root_netuid(), - )); + let parent_root: I96F32 = I96F32::saturating_from_num( + Self::get_stake_for_hotkey_on_subnet(&parent, Self::get_root_netuid()), + ); let parent_alpha: I96F32 = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); + I96F32::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); @@ -788,9 +809,9 @@ impl Pallet { // Sum up the total emission for this parent let emission_factor: I96F32 = contribution .checked_div(total_contribution) - .unwrap_or(I96F32::from_num(0)); + .unwrap_or(I96F32::saturating_from_num(0)); let parent_emission: u64 = - (remaining_emission.saturating_mul(emission_factor)).to_num::(); + (remaining_emission.saturating_mul(emission_factor)).saturating_to_num::(); // Add the parent's emission to the distribution list dividend_tuples.push((parent, parent_emission)); @@ -802,7 +823,7 @@ impl Pallet { // This includes the take left from the parents and the self contribution. let child_emission = remaining_emission .saturating_add(child_emission_take) - .to_num::() + .saturating_to_num::() .saturating_sub(to_parents); // Add the hotkey's own emission to the distribution list diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 616a9b78be..9818b06a48 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -3,6 +3,7 @@ use crate::alloc::borrow::ToOwned; #[allow(unused)] use num_traits::float::Float; +use safe_math::*; use sp_runtime::traits::{CheckedAdd, Saturating}; use sp_std::cmp::Ordering; @@ -16,47 +17,47 @@ use sp_std::vec::Vec; #[allow(dead_code)] pub fn fixed(val: f32) -> I32F32 { - I32F32::from_num(val) + I32F32::saturating_from_num(val) } #[allow(dead_code)] pub fn fixed_to_u16(x: I32F32) -> u16 { - x.to_num::() + x.saturating_to_num::() } #[allow(dead_code)] pub fn fixed_to_u64(x: I32F32) -> u64 { - x.to_num::() + x.saturating_to_num::() } #[allow(dead_code)] pub fn fixed64_to_u64(x: I64F64) -> u64 { - x.to_num::() + x.saturating_to_num::() } #[allow(dead_code)] pub fn fixed64_to_fixed32(x: I64F64) -> I32F32 { - I32F32::from_num(x) + I32F32::saturating_from_num(x) } #[allow(dead_code)] pub fn fixed32_to_fixed64(x: I32F32) -> I64F64 { - I64F64::from_num(x) + I64F64::saturating_from_num(x) } #[allow(dead_code)] pub fn u16_to_fixed(x: u16) -> I32F32 { - I32F32::from_num(x) + I32F32::saturating_from_num(x) } #[allow(dead_code)] pub fn u16_proportion_to_fixed(x: u16) -> I32F32 { - I32F32::from_num(x).saturating_div(I32F32::from_num(u16::MAX)) + I32F32::saturating_from_num(x).safe_div(I32F32::saturating_from_num(u16::MAX)) } #[allow(dead_code)] pub fn fixed_proportion_to_u16(x: I32F32) -> u16 { - fixed_to_u16(x.saturating_mul(I32F32::from_num(u16::MAX))) + fixed_to_u16(x.saturating_mul(I32F32::saturating_from_num(u16::MAX))) } #[allow(dead_code)] @@ -92,33 +93,33 @@ pub fn vec_fixed_proportions_to_u16(vec: Vec) -> Vec { #[allow(dead_code)] // Max-upscale vector and convert to u16 so max_value = u16::MAX. Assumes non-negative normalized input. pub fn vec_max_upscale_to_u16(vec: &[I32F32]) -> Vec { - let u16_max: I32F32 = I32F32::from_num(u16::MAX); - let threshold: I32F32 = I32F32::from_num(32768); + let u16_max: I32F32 = I32F32::saturating_from_num(u16::MAX); + let threshold: I32F32 = I32F32::saturating_from_num(32768); let max_value: Option<&I32F32> = vec.iter().max(); match max_value { Some(val) => { - if *val == I32F32::from_num(0) { + if *val == I32F32::saturating_from_num(0) { return vec .iter() - .map(|e: &I32F32| e.saturating_mul(u16_max).to_num::()) + .map(|e: &I32F32| e.saturating_mul(u16_max).saturating_to_num::()) .collect(); } if *val > threshold { return vec .iter() .map(|e: &I32F32| { - e.saturating_mul(u16_max.saturating_div(*val)) + e.saturating_mul(u16_max.safe_div(*val)) .round() - .to_num::() + .saturating_to_num::() }) .collect(); } vec.iter() .map(|e: &I32F32| { e.saturating_mul(u16_max) - .saturating_div(*val) + .safe_div(*val) .round() - .to_num::() + .saturating_to_num::() }) .collect() } @@ -127,8 +128,8 @@ pub fn vec_max_upscale_to_u16(vec: &[I32F32]) -> Vec { vec.iter() .map(|e: &I32F32| { e.saturating_mul(u16_max) - .saturating_div(sum) - .to_num::() + .safe_div(sum) + .saturating_to_num::() }) .collect() } @@ -138,7 +139,10 @@ pub fn vec_max_upscale_to_u16(vec: &[I32F32]) -> Vec { #[allow(dead_code)] // Max-upscale u16 vector and convert to u16 so max_value = u16::MAX. Assumes u16 vector input. pub fn vec_u16_max_upscale_to_u16(vec: &[u16]) -> Vec { - let vec_fixed: Vec = vec.iter().map(|e: &u16| I32F32::from_num(*e)).collect(); + let vec_fixed: Vec = vec + .iter() + .map(|e: &u16| I32F32::saturating_from_num(*e)) + .collect(); vec_max_upscale_to_u16(&vec_fixed) } @@ -146,8 +150,11 @@ pub fn vec_u16_max_upscale_to_u16(vec: &[u16]) -> Vec { // Checks if u16 vector, when normalized, has a max value not greater than a u16 ratio max_limit. pub fn check_vec_max_limited(vec: &[u16], max_limit: u16) -> bool { let max_limit_fixed: I32F32 = - I32F32::from_num(max_limit).saturating_div(I32F32::from_num(u16::MAX)); - let mut vec_fixed: Vec = vec.iter().map(|e: &u16| I32F32::from_num(*e)).collect(); + I32F32::saturating_from_num(max_limit).safe_div(I32F32::saturating_from_num(u16::MAX)); + let mut vec_fixed: Vec = vec + .iter() + .map(|e: &u16| I32F32::saturating_from_num(*e)) + .collect(); inplace_normalize(&mut vec_fixed); let max_value: Option<&I32F32> = vec_fixed.iter().max(); max_value.is_none_or(|v| *v <= max_limit_fixed) @@ -180,14 +187,14 @@ where #[allow(dead_code)] pub fn is_zero(vector: &[I32F32]) -> bool { let vector_sum: I32F32 = sum(vector); - vector_sum == I32F32::from_num(0) + vector_sum == I32F32::saturating_from_num(0) } // Exp safe function with I32F32 output of I32F32 input. #[allow(dead_code)] pub fn exp_safe(input: I32F32) -> I32F32 { - let min_input: I32F32 = I32F32::from_num(-20); // <= 1/exp(-20) = 485 165 195,4097903 - let max_input: I32F32 = I32F32::from_num(20); // <= exp(20) = 485 165 195,4097903 + let min_input: I32F32 = I32F32::saturating_from_num(-20); // <= 1/exp(-20) = 485 165 195,4097903 + let max_input: I32F32 = I32F32::saturating_from_num(20); // <= exp(20) = 485 165 195,4097903 let mut safe_input: I32F32 = input; if input < min_input { safe_input = min_input; @@ -201,7 +208,7 @@ pub fn exp_safe(input: I32F32) -> I32F32 { } Err(_err) => { if safe_input <= 0 { - output = I32F32::from_num(0); + output = I32F32::saturating_from_num(0); } else { output = I32F32::max_value(); } @@ -213,13 +220,13 @@ pub fn exp_safe(input: I32F32) -> I32F32 { // Sigmoid safe function with I32F32 output of I32F32 input with offset kappa and (recommended) scaling 0 < rho <= 40. #[allow(dead_code)] pub fn sigmoid_safe(input: I32F32, rho: I32F32, kappa: I32F32) -> I32F32 { - let one: I32F32 = I32F32::from_num(1); + let one: I32F32 = I32F32::saturating_from_num(1); let offset: I32F32 = input.saturating_sub(kappa); // (input - kappa) let neg_rho: I32F32 = rho.saturating_mul(one.saturating_neg()); // -rho let exp_input: I32F32 = neg_rho.saturating_mul(offset); // -rho*(input-kappa) let exp_output: I32F32 = exp_safe(exp_input); // exp(-rho*(input-kappa)) let denominator: I32F32 = exp_output.saturating_add(one); // 1 + exp(-rho*(input-kappa)) - let sigmoid_output: I32F32 = one.saturating_div(denominator); // 1 / (1 + exp(-rho*(input-kappa))) + let sigmoid_output: I32F32 = one.safe_div(denominator); // 1 / (1 + exp(-rho*(input-kappa))) sigmoid_output } @@ -243,8 +250,8 @@ pub fn is_topk(vector: &[I32F32], k: usize) -> Vec { #[allow(dead_code)] pub fn normalize(x: &[I32F32]) -> Vec { let x_sum: I32F32 = sum(x); - if x_sum != I32F32::from_num(0.0_f32) { - x.iter().map(|xi| xi.saturating_div(x_sum)).collect() + if x_sum != I32F32::saturating_from_num(0.0_f32) { + x.iter().map(|xi| xi.safe_div(x_sum)).collect() } else { x.to_vec() } @@ -254,32 +261,32 @@ pub fn normalize(x: &[I32F32]) -> Vec { #[allow(dead_code)] pub fn inplace_normalize(x: &mut [I32F32]) { let x_sum: I32F32 = x.iter().sum(); - if x_sum == I32F32::from_num(0.0_f32) { + if x_sum == I32F32::saturating_from_num(0.0_f32) { return; } x.iter_mut() - .for_each(|value| *value = value.saturating_div(x_sum)); + .for_each(|value| *value = value.safe_div(x_sum)); } // Normalizes (sum to 1 except 0) the input vector directly in-place, using the sum arg. #[allow(dead_code)] pub fn inplace_normalize_using_sum(x: &mut [I32F32], x_sum: I32F32) { - if x_sum == I32F32::from_num(0.0_f32) { + if x_sum == I32F32::saturating_from_num(0.0_f32) { return; } x.iter_mut() - .for_each(|value| *value = value.saturating_div(x_sum)); + .for_each(|value| *value = value.safe_div(x_sum)); } // Normalizes (sum to 1 except 0) the I64F64 input vector directly in-place. #[allow(dead_code)] pub fn inplace_normalize_64(x: &mut [I64F64]) { let x_sum: I64F64 = x.iter().sum(); - if x_sum == I64F64::from_num(0) { + if x_sum == I64F64::saturating_from_num(0) { return; } x.iter_mut() - .for_each(|value| *value = value.saturating_div(x_sum)); + .for_each(|value| *value = value.safe_div(x_sum)); } /// Normalizes (sum to 1 except 0) each row (dim=0) of a I64F64 matrix in-place. @@ -287,9 +294,9 @@ pub fn inplace_normalize_64(x: &mut [I64F64]) { pub fn inplace_row_normalize_64(x: &mut [Vec]) { for row in x { let row_sum: I64F64 = row.iter().sum(); - if row_sum > I64F64::from_num(0.0_f64) { + if row_sum > I64F64::saturating_from_num(0.0_f64) { row.iter_mut() - .for_each(|x_ij: &mut I64F64| *x_ij = x_ij.saturating_div(row_sum)); + .for_each(|x_ij: &mut I64F64| *x_ij = x_ij.safe_div(row_sum)); } } } @@ -302,9 +309,9 @@ pub fn vecdiv(x: &[I32F32], y: &[I32F32]) -> Vec { .zip(y) .map(|(x_i, y_i)| { if *y_i != 0 { - x_i.saturating_div(*y_i) + x_i.safe_div(*y_i) } else { - I32F32::from_num(0) + I32F32::saturating_from_num(0) } }) .collect() @@ -315,9 +322,9 @@ pub fn vecdiv(x: &[I32F32], y: &[I32F32]) -> Vec { pub fn inplace_row_normalize(x: &mut [Vec]) { for row in x { let row_sum: I32F32 = row.iter().sum(); - if row_sum > I32F32::from_num(0.0_f32) { + if row_sum > I32F32::saturating_from_num(0.0_f32) { row.iter_mut() - .for_each(|x_ij: &mut I32F32| *x_ij = x_ij.saturating_div(row_sum)); + .for_each(|x_ij: &mut I32F32| *x_ij = x_ij.safe_div(row_sum)); } } } @@ -327,10 +334,10 @@ pub fn inplace_row_normalize(x: &mut [Vec]) { pub fn inplace_row_normalize_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>]) { for sparse_row in sparse_matrix.iter_mut() { let row_sum: I32F32 = sparse_row.iter().map(|(_j, value)| *value).sum(); - if row_sum > I32F32::from_num(0.0) { + if row_sum > I32F32::saturating_from_num(0.0) { sparse_row .iter_mut() - .for_each(|(_j, value)| *value = value.saturating_div(row_sum)); + .for_each(|(_j, value)| *value = value.safe_div(row_sum)); } } } @@ -365,19 +372,21 @@ pub fn col_sum(x: &[Vec]) -> Vec { if cols == 0 { return vec![]; } - x.iter() - .fold(vec![I32F32::from_num(0); cols], |acc, next_row| { + x.iter().fold( + vec![I32F32::saturating_from_num(0); cols], + |acc, next_row| { acc.into_iter() .zip(next_row) .map(|(acc_elem, next_elem)| acc_elem.saturating_add(*next_elem)) .collect() - }) + }, + ) } // Sum across each column (dim=1) of a sparse matrix. #[allow(dead_code, clippy::indexing_slicing)] pub fn col_sum_sparse(sparse_matrix: &[Vec<(u16, I32F32)>], columns: u16) -> Vec { - let mut result: Vec = vec![I32F32::from_num(0); columns as usize]; + let mut result: Vec = vec![I32F32::saturating_from_num(0); columns as usize]; for sparse_row in sparse_matrix { for (j, value) in sparse_row { result[*j as usize] = result[*j as usize].saturating_add(*value); @@ -389,7 +398,7 @@ pub fn col_sum_sparse(sparse_matrix: &[Vec<(u16, I32F32)>], columns: u16) -> Vec // Normalizes (sum to 1 except 0) each column (dim=1) of a sparse matrix in-place. #[allow(dead_code, clippy::indexing_slicing)] pub fn inplace_col_normalize_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>], columns: u16) { - let mut col_sum: Vec = vec![I32F32::from_num(0.0); columns as usize]; // assume square matrix, rows=cols + let mut col_sum: Vec = vec![I32F32::saturating_from_num(0.0); columns as usize]; // assume square matrix, rows=cols for sparse_row in sparse_matrix.iter() { for (j, value) in sparse_row.iter() { col_sum[*j as usize] = col_sum[*j as usize].saturating_add(*value); @@ -397,10 +406,10 @@ pub fn inplace_col_normalize_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>], co } for sparse_row in sparse_matrix { for (j, value) in sparse_row { - if col_sum[*j as usize] == I32F32::from_num(0.0_f32) { + if col_sum[*j as usize] == I32F32::saturating_from_num(0.0_f32) { continue; } - *value = value.saturating_div(col_sum[*j as usize]); + *value = value.safe_div(col_sum[*j as usize]); } } } @@ -417,7 +426,7 @@ pub fn inplace_col_normalize(x: &mut [Vec]) { let cols = first_row.len(); let col_sums = x .iter_mut() - .fold(vec![I32F32::from_num(0.0); cols], |acc, row| { + .fold(vec![I32F32::saturating_from_num(0.0); cols], |acc, row| { row.iter_mut() .zip(acc) .map(|(&mut m_val, acc_val)| acc_val.saturating_add(m_val)) @@ -426,9 +435,9 @@ pub fn inplace_col_normalize(x: &mut [Vec]) { x.iter_mut().for_each(|row| { row.iter_mut() .zip(&col_sums) - .filter(|(_, col_sum)| **col_sum != I32F32::from_num(0_f32)) + .filter(|(_, col_sum)| **col_sum != I32F32::saturating_from_num(0_f32)) .for_each(|(m_val, col_sum)| { - *m_val = m_val.saturating_div(*col_sum); + *m_val = m_val.safe_div(*col_sum); }); }); } @@ -436,7 +445,7 @@ pub fn inplace_col_normalize(x: &mut [Vec]) { // Max-upscale each column (dim=1) of a sparse matrix in-place. #[allow(dead_code, clippy::indexing_slicing)] pub fn inplace_col_max_upscale_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>], columns: u16) { - let mut col_max: Vec = vec![I32F32::from_num(0.0); columns as usize]; // assume square matrix, rows=cols + let mut col_max: Vec = vec![I32F32::saturating_from_num(0.0); columns as usize]; // assume square matrix, rows=cols for sparse_row in sparse_matrix.iter() { for (j, value) in sparse_row.iter() { if col_max[*j as usize] < *value { @@ -446,10 +455,10 @@ pub fn inplace_col_max_upscale_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>], } for sparse_row in sparse_matrix { for (j, value) in sparse_row { - if col_max[*j as usize] == I32F32::from_num(0.0_f32) { + if col_max[*j as usize] == I32F32::saturating_from_num(0.0_f32) { continue; } - *value = value.saturating_div(col_max[*j as usize]); + *value = value.safe_div(col_max[*j as usize]); } } } @@ -464,20 +473,21 @@ pub fn inplace_col_max_upscale(x: &mut [Vec]) { return; } let cols = first_row.len(); - let col_maxes = x - .iter_mut() - .fold(vec![I32F32::from_num(0_f32); cols], |acc, row| { + let col_maxes = x.iter_mut().fold( + vec![I32F32::saturating_from_num(0_f32); cols], + |acc, row| { row.iter_mut() .zip(acc) .map(|(m_val, acc_val)| acc_val.max(*m_val)) .collect() - }); + }, + ); x.iter_mut().for_each(|row| { row.iter_mut() .zip(&col_maxes) - .filter(|(_, col_max)| **col_max != I32F32::from_num(0)) + .filter(|(_, col_max)| **col_max != I32F32::saturating_from_num(0)) .for_each(|(m_val, col_max)| { - *m_val = m_val.saturating_div(*col_max); + *m_val = m_val.safe_div(*col_max); }); }); } @@ -489,7 +499,7 @@ pub fn inplace_mask_vector(mask: &[bool], vector: &mut [I32F32]) { return; } assert_eq!(mask.len(), vector.len()); - let zero: I32F32 = I32F32::from_num(0.0); + let zero: I32F32 = I32F32::saturating_from_num(0.0); mask.iter() .zip(vector) .filter(|(m, _)| **m) @@ -508,7 +518,7 @@ pub fn inplace_mask_matrix(mask: &[Vec], matrix: &mut Vec>) { return; } assert_eq!(mask.len(), matrix.len()); - let zero: I32F32 = I32F32::from_num(0.0); + let zero: I32F32 = I32F32::saturating_from_num(0.0); mask.iter().zip(matrix).for_each(|(mask_row, matrix_row)| { mask_row .iter() @@ -528,7 +538,7 @@ pub fn inplace_mask_rows(mask: &[bool], matrix: &mut [Vec]) { }; let cols = first_row.len(); assert_eq!(mask.len(), matrix.len()); - let zero: I32F32 = I32F32::from_num(0); + let zero: I32F32 = I32F32::saturating_from_num(0); matrix .iter_mut() .zip(mask) @@ -549,7 +559,7 @@ pub fn inplace_mask_diag(matrix: &mut [Vec]) { return; } assert_eq!(matrix.len(), first_row.len()); - let zero: I32F32 = I32F32::from_num(0.0); + let zero: I32F32 = I32F32::saturating_from_num(0.0); matrix.iter_mut().enumerate().for_each(|(idx, row)| { let Some(elem) = row.get_mut(idx) else { // Should not happen since matrix is square @@ -664,7 +674,7 @@ pub fn matmul(matrix: &[Vec], vector: &[I32F32]) -> Vec { } assert!(matrix.len() == vector.len()); matrix.iter().zip(vector).fold( - vec![I32F32::from_num(0_f32); cols], + vec![I32F32::saturating_from_num(0_f32); cols], |acc, (row, vec_val)| { row.iter() .zip(acc) @@ -690,10 +700,9 @@ pub fn matmul_64(matrix: &[Vec], vector: &[I64F64]) -> Vec { return vec![]; } assert!(matrix.len() == vector.len()); - matrix - .iter() - .zip(vector) - .fold(vec![I64F64::from_num(0.0); cols], |acc, (row, vec_val)| { + matrix.iter().zip(vector).fold( + vec![I64F64::saturating_from_num(0.0); cols], + |acc, (row, vec_val)| { row.iter() .zip(acc) .map(|(m_val, acc_val)| { @@ -703,7 +712,8 @@ pub fn matmul_64(matrix: &[Vec], vector: &[I64F64]) -> Vec { acc_val.saturating_add(vec_val.saturating_mul(*m_val)) }) .collect() - }) + }, + ) } // Column-wise matrix-vector product, row-wise sum: result_i = SUM(j) vector_j * matrix_ij. @@ -721,7 +731,7 @@ pub fn matmul_transpose(matrix: &[Vec], vector: &[I32F32]) -> Vec Vec { - let mut result: Vec = vec![I32F32::from_num(0.0); columns as usize]; + let mut result: Vec = vec![I32F32::saturating_from_num(0.0); columns as usize]; for (i, sparse_row) in sparse_matrix.iter().enumerate() { for (j, value) in sparse_row.iter() { // Compute ranks: r_j = SUM(i) w_ij * s_i @@ -757,7 +767,7 @@ pub fn matmul_transpose_sparse( sparse_matrix: &[Vec<(u16, I32F32)>], vector: &[I32F32], ) -> Vec { - let mut result: Vec = vec![I32F32::from_num(0.0); sparse_matrix.len()]; + let mut result: Vec = vec![I32F32::saturating_from_num(0.0); sparse_matrix.len()]; for (i, sparse_row) in sparse_matrix.iter().enumerate() { for (j, value) in sparse_row.iter() { // Compute dividends: d_j = SUM(i) b_ji * inc_i @@ -892,16 +902,16 @@ pub fn weighted_median( ) -> I32F32 { let n = partition_idx.len(); if n == 0 { - return I32F32::from_num(0); + return I32F32::saturating_from_num(0); } if n == 1 { return score[partition_idx[0]]; } assert!(stake.len() == score.len()); - let mid_idx: usize = n.saturating_div(2); + let mid_idx: usize = n.safe_div(2); let pivot: I32F32 = score[partition_idx[mid_idx]]; - let mut lo_stake: I32F32 = I32F32::from_num(0); - let mut hi_stake: I32F32 = I32F32::from_num(0); + let mut lo_stake: I32F32 = I32F32::saturating_from_num(0); + let mut hi_stake: I32F32 = I32F32::saturating_from_num(0); let mut lower: Vec = vec![]; let mut upper: Vec = vec![]; for &idx in partition_idx { @@ -951,7 +961,7 @@ pub fn weighted_median_col( ) -> Vec { let rows = stake.len(); let columns = score[0].len(); - let zero: I32F32 = I32F32::from_num(0); + let zero: I32F32 = I32F32::saturating_from_num(0); let mut median: Vec = vec![zero; columns]; #[allow(clippy::needless_range_loop)] @@ -991,7 +1001,7 @@ pub fn weighted_median_col_sparse( majority: I32F32, ) -> Vec { let rows = stake.len(); - let zero: I32F32 = I32F32::from_num(0); + let zero: I32F32 = I32F32::saturating_from_num(0); let mut use_stake: Vec = stake.iter().copied().filter(|&s| s > zero).collect(); inplace_normalize(&mut use_stake); let stake_sum: I32F32 = use_stake.iter().sum(); @@ -1028,10 +1038,10 @@ pub fn weighted_median_col_sparse( // ratio=1: Result = B #[allow(dead_code)] pub fn interpolate(mat1: &[Vec], mat2: &[Vec], ratio: I32F32) -> Vec> { - if ratio == I32F32::from_num(0) { + if ratio == I32F32::saturating_from_num(0) { return mat1.to_owned(); } - if ratio == I32F32::from_num(1) { + if ratio == I32F32::saturating_from_num(1) { return mat2.to_owned(); } assert!(mat1.len() == mat2.len()); @@ -1042,7 +1052,10 @@ pub fn interpolate(mat1: &[Vec], mat2: &[Vec], ratio: I32F32) -> return vec![vec![]; 1]; } let mut result: Vec> = - vec![vec![I32F32::from_num(0); mat1.first().unwrap_or(&vec![]).len()]; mat1.len()]; + vec![ + vec![I32F32::saturating_from_num(0); mat1.first().unwrap_or(&vec![]).len()]; + mat1.len() + ]; for (i, (row1, row2)) in mat1.iter().zip(mat2.iter()).enumerate() { assert!(row1.len() == row2.len()); for (j, (&v1, &v2)) in row1.iter().zip(row2.iter()).enumerate() { @@ -1065,15 +1078,15 @@ pub fn interpolate_sparse( columns: u16, ratio: I32F32, ) -> Vec> { - if ratio == I32F32::from_num(0) { + if ratio == I32F32::saturating_from_num(0) { return mat1.to_owned(); } - if ratio == I32F32::from_num(1) { + if ratio == I32F32::saturating_from_num(1) { return mat2.to_owned(); } assert!(mat1.len() == mat2.len()); let rows = mat1.len(); - let zero: I32F32 = I32F32::from_num(0); + let zero: I32F32 = I32F32::saturating_from_num(0); let mut result: Vec> = vec![vec![]; rows]; for i in 0..rows { let mut row1: Vec = vec![zero; columns as usize]; @@ -1137,7 +1150,7 @@ pub fn hadamard_sparse( ) -> Vec> { assert!(mat1.len() == mat2.len()); let rows = mat1.len(); - let zero: I32F32 = I32F32::from_num(0); + let zero: I32F32 = I32F32::saturating_from_num(0); let mut result: Vec> = vec![vec![]; rows]; for i in 0..rows { let mut row1: Vec = vec![zero; columns as usize]; @@ -1169,7 +1182,7 @@ pub fn mat_ema(new: &[Vec], old: &[Vec], alpha: I32F32) -> Vec Vec> { assert!(new.len() == old.len()); let n = new.len(); // assume square matrix, rows=cols - let zero: I32F32 = I32F32::from_num(0.0); - let one_minus_alpha: I32F32 = I32F32::from_num(1.0).saturating_sub(alpha); + let zero: I32F32 = I32F32::saturating_from_num(0.0); + let one_minus_alpha: I32F32 = I32F32::saturating_from_num(1.0).saturating_sub(alpha); let mut result: Vec> = vec![vec![]; n]; for i in 0..new.len() { let mut row: Vec = vec![zero; n]; @@ -1241,7 +1254,7 @@ pub fn mat_ema_alpha_vec_sparse( // Ensure the new and old matrices have the same number of rows. assert!(new.len() == old.len()); let n = new.len(); // Assume square matrix, rows=cols - let zero: I32F32 = I32F32::from_num(0.0); + let zero: I32F32 = I32F32::saturating_from_num(0.0); let mut result: Vec> = vec![vec![]; n]; // Iterate over each row of the matrices. @@ -1273,7 +1286,8 @@ pub fn mat_ema_alpha_vec_sparse( // Retrieve the alpha value for the current column. let alpha_val: I32F32 = alpha.get(*j as usize).copied().unwrap_or(zero); // Calculate the complement of the alpha value using saturating subtraction. - let one_minus_alpha: I32F32 = I32F32::from_num(1.0).saturating_sub(alpha_val); + let one_minus_alpha: I32F32 = + I32F32::saturating_from_num(1.0).saturating_sub(alpha_val); // Compute the EMA component for the old value and add it to the row using saturating operations. if let Some(row_val) = row.get_mut(*j as usize) { *row_val = row_val.saturating_add(one_minus_alpha.saturating_mul(*value)); @@ -1323,7 +1337,10 @@ pub fn mat_ema_alpha_vec( // Initialize the result matrix with zeros, having the same dimensions as the new matrix. let mut result: Vec> = - vec![vec![I32F32::from_num(0.0); new.first().map_or(0, |row| row.len())]; new.len()]; + vec![ + vec![I32F32::saturating_from_num(0.0); new.first().map_or(0, |row| row.len())]; + new.len() + ]; // Iterate over each row of the matrices. for (i, (new_row, old_row)) in new.iter().zip(old).enumerate() { @@ -1333,7 +1350,7 @@ pub fn mat_ema_alpha_vec( // Iterate over each column of the current row. for (j, &alpha_val) in alpha.iter().enumerate().take(new_row.len()) { // Calculate the complement of the alpha value using saturating subtraction. - let one_minus_alpha = I32F32::from_num(1.0).saturating_sub(alpha_val); + let one_minus_alpha = I32F32::saturating_from_num(1.0).saturating_sub(alpha_val); // Compute the EMA for the current element using saturating operations. if let (Some(new_val), Some(old_val), Some(result_val)) = ( @@ -1365,7 +1382,7 @@ pub fn quantile(data: &[I32F32], quantile: f64) -> I32F32 { // If the data is empty, return 0 as the quantile value. if len == 0 { - return I32F32::from_num(0); + return I32F32::saturating_from_num(0); } // Calculate the position in the sorted array corresponding to the quantile. @@ -1382,20 +1399,20 @@ pub fn quantile(data: &[I32F32], quantile: f64) -> I32F32 { sorted_data .get(low) .copied() - .unwrap_or_else(|| I32F32::from_num(0)) + .unwrap_or_else(|| I32F32::saturating_from_num(0)) } else { // Otherwise, perform linear interpolation between the low and high values. let low_value = sorted_data .get(low) .copied() - .unwrap_or_else(|| I32F32::from_num(0)); + .unwrap_or_else(|| I32F32::saturating_from_num(0)); let high_value = sorted_data .get(high) .copied() - .unwrap_or_else(|| I32F32::from_num(0)); + .unwrap_or_else(|| I32F32::saturating_from_num(0)); // Calculate the weight for interpolation. - let weight = I32F32::from_num(pos - low as f64); + let weight = I32F32::saturating_from_num(pos - low as f64); // Return the interpolated value using saturating operations. low_value.saturating_add((high_value.saturating_sub(low_value)).saturating_mul(weight)) @@ -1404,10 +1421,10 @@ pub fn quantile(data: &[I32F32], quantile: f64) -> I32F32 { /// Safe ln function, returns 0 if value is 0. pub fn safe_ln(value: I32F32) -> I32F32 { - ln(value).unwrap_or(I32F32::from_num(0.0)) + ln(value).unwrap_or(I32F32::saturating_from_num(0.0)) } /// Safe exp function, returns 0 if value is 0. pub fn safe_exp(value: I32F32) -> I32F32 { - exp(value).unwrap_or(I32F32::from_num(0.0)) + exp(value).unwrap_or(I32F32::saturating_from_num(0.0)) } diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index e6edd25857..9a7e4f6abd 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -1,6 +1,7 @@ use super::*; use crate::epoch::math::*; use frame_support::IterableStorageDoubleMap; +use safe_math::*; use sp_std::vec; use substrate_fixed::types::{I32F32, I64F64, I96F32}; @@ -230,34 +231,34 @@ impl Pallet { } // Compute rao based emission scores. range: I96F32(0, rao_emission) - let float_rao_emission: I96F32 = I96F32::from_num(rao_emission); + let float_rao_emission: I96F32 = I96F32::saturating_from_num(rao_emission); let server_emission: Vec = normalized_server_emission .iter() - .map(|se: &I32F32| I96F32::from_num(*se).saturating_mul(float_rao_emission)) + .map(|se: &I32F32| I96F32::saturating_from_num(*se).saturating_mul(float_rao_emission)) .collect(); let server_emission: Vec = server_emission .iter() - .map(|e: &I96F32| e.to_num::()) + .map(|e: &I96F32| e.saturating_to_num::()) .collect(); let validator_emission: Vec = normalized_validator_emission .iter() - .map(|ve: &I32F32| I96F32::from_num(*ve).saturating_mul(float_rao_emission)) + .map(|ve: &I32F32| I96F32::saturating_from_num(*ve).saturating_mul(float_rao_emission)) .collect(); let validator_emission: Vec = validator_emission .iter() - .map(|e: &I96F32| e.to_num::()) + .map(|e: &I96F32| e.saturating_to_num::()) .collect(); // Used only to track combined emission in the storage. let combined_emission: Vec = normalized_combined_emission .iter() - .map(|ce: &I32F32| I96F32::from_num(*ce).saturating_mul(float_rao_emission)) + .map(|ce: &I32F32| I96F32::saturating_from_num(*ce).saturating_mul(float_rao_emission)) .collect(); let combined_emission: Vec = combined_emission .iter() - .map(|e: &I96F32| e.to_num::()) + .map(|e: &I96F32| e.saturating_to_num::()) .collect(); log::trace!("nSE: {:?}", &normalized_server_emission); @@ -601,34 +602,34 @@ impl Pallet { } // Compute rao based emission scores. range: I96F32(0, rao_emission) - let float_rao_emission: I96F32 = I96F32::from_num(rao_emission); + let float_rao_emission: I96F32 = I96F32::saturating_from_num(rao_emission); let server_emission: Vec = normalized_server_emission .iter() - .map(|se: &I32F32| I96F32::from_num(*se).saturating_mul(float_rao_emission)) + .map(|se: &I32F32| I96F32::saturating_from_num(*se).saturating_mul(float_rao_emission)) .collect(); let server_emission: Vec = server_emission .iter() - .map(|e: &I96F32| e.to_num::()) + .map(|e: &I96F32| e.saturating_to_num::()) .collect(); let validator_emission: Vec = normalized_validator_emission .iter() - .map(|ve: &I32F32| I96F32::from_num(*ve).saturating_mul(float_rao_emission)) + .map(|ve: &I32F32| I96F32::saturating_from_num(*ve).saturating_mul(float_rao_emission)) .collect(); let validator_emission: Vec = validator_emission .iter() - .map(|e: &I96F32| e.to_num::()) + .map(|e: &I96F32| e.saturating_to_num::()) .collect(); // Only used to track emission in storage. let combined_emission: Vec = normalized_combined_emission .iter() - .map(|ce: &I32F32| I96F32::from_num(*ce).saturating_mul(float_rao_emission)) + .map(|ce: &I32F32| I96F32::saturating_from_num(*ce).saturating_mul(float_rao_emission)) .collect(); let combined_emission: Vec = combined_emission .iter() - .map(|e: &I96F32| e.to_num::()) + .map(|e: &I96F32| e.saturating_to_num::()) .collect(); log::trace!( @@ -732,13 +733,15 @@ impl Pallet { } pub fn get_float_rho(netuid: u16) -> I32F32 { - I32F32::from_num(Self::get_rho(netuid)) + I32F32::saturating_from_num(Self::get_rho(netuid)) } pub fn get_float_kappa(netuid: u16) -> I32F32 { - I32F32::from_num(Self::get_kappa(netuid)).saturating_div(I32F32::from_num(u16::MAX)) + I32F32::saturating_from_num(Self::get_kappa(netuid)) + .safe_div(I32F32::saturating_from_num(u16::MAX)) } pub fn get_float_bonds_penalty(netuid: u16) -> I32F32 { - I32F32::from_num(Self::get_bonds_penalty(netuid)).saturating_div(I32F32::from_num(u16::MAX)) + I32F32::saturating_from_num(Self::get_bonds_penalty(netuid)) + .safe_div(I32F32::saturating_from_num(u16::MAX)) } pub fn get_block_at_registration(netuid: u16) -> Vec { @@ -767,7 +770,7 @@ impl Pallet { weights .get_mut(uid_i as usize) .expect("uid_i is filtered to be less than n; qed") - .push((*uid_j, I32F32::from_num(*weight_ij))); + .push((*uid_j, I32F32::saturating_from_num(*weight_ij))); } } weights @@ -776,7 +779,7 @@ impl Pallet { /// Output unnormalized weights in [n, n] matrix, input weights are assumed to be row max-upscaled in u16. pub fn get_weights(netuid: u16) -> Vec> { let n: usize = Self::get_subnetwork_n(netuid) as usize; - let mut weights: Vec> = vec![vec![I32F32::from_num(0.0); n]; n]; + let mut weights: Vec> = vec![vec![I32F32::saturating_from_num(0.0); n]; n]; for (uid_i, weights_vec) in as IterableStorageDoubleMap>>::iter_prefix(netuid) .filter(|(uid_i, _)| *uid_i < n as u16) @@ -790,7 +793,7 @@ impl Pallet { .expect("uid_i is filtered to be less than n; qed") .get_mut(uid_j as usize) .expect("uid_j is filtered to be less than n; qed") = - I32F32::from_num(weight_ij); + I32F32::saturating_from_num(weight_ij); } } weights @@ -808,7 +811,7 @@ impl Pallet { bonds .get_mut(uid_i as usize) .expect("uid_i is filtered to be less than n; qed") - .push((uid_j, I32F32::from_num(bonds_ij))); + .push((uid_j, I32F32::saturating_from_num(bonds_ij))); } } bonds @@ -817,7 +820,7 @@ impl Pallet { /// Output unnormalized bonds in [n, n] matrix, input bonds are assumed to be column max-upscaled in u16. pub fn get_bonds(netuid: u16) -> Vec> { let n: usize = Self::get_subnetwork_n(netuid) as usize; - let mut bonds: Vec> = vec![vec![I32F32::from_num(0.0); n]; n]; + let mut bonds: Vec> = vec![vec![I32F32::saturating_from_num(0.0); n]; n]; for (uid_i, bonds_vec) in as IterableStorageDoubleMap>>::iter_prefix(netuid) .filter(|(uid_i, _)| *uid_i < n as u16) @@ -828,7 +831,7 @@ impl Pallet { .expect("uid_i has been filtered to be less than n; qed") .get_mut(uid_j as usize) .expect("uid_j has been filtered to be less than n; qed") = - I32F32::from_num(bonds_ij); + I32F32::saturating_from_num(bonds_ij); } } bonds @@ -858,25 +861,30 @@ impl Pallet { // extra caution to ensure we never divide by zero if consensus_high <= consensus_low || alpha_low == 0 || alpha_high == 0 { // Return 0 for both 'a' and 'b' when consensus values are equal - return (I32F32::from_num(0.0), I32F32::from_num(0.0)); + return ( + I32F32::saturating_from_num(0.0), + I32F32::saturating_from_num(0.0), + ); } // Calculate the slope 'a' of the logistic function. // a = (ln((1 / alpha_high - 1)) - ln((1 / alpha_low - 1))) / (consensus_low - consensus_high) let a = (safe_ln( - (I32F32::from_num(1.0).saturating_div(alpha_high)) - .saturating_sub(I32F32::from_num(1.0)), + (I32F32::saturating_from_num(1.0).safe_div(alpha_high)) + .saturating_sub(I32F32::saturating_from_num(1.0)), ) .saturating_sub(safe_ln( - (I32F32::from_num(1.0).saturating_div(alpha_low)).saturating_sub(I32F32::from_num(1.0)), + (I32F32::saturating_from_num(1.0).safe_div(alpha_low)) + .saturating_sub(I32F32::saturating_from_num(1.0)), ))) - .saturating_div(consensus_low.saturating_sub(consensus_high)); + .safe_div(consensus_low.saturating_sub(consensus_high)); log::trace!("a: {:?}", a); // Calculate the intercept 'b' of the logistic function. // b = ln((1 / alpha_low - 1)) + a * consensus_low let b = safe_ln( - (I32F32::from_num(1.0).saturating_div(alpha_low)).saturating_sub(I32F32::from_num(1.0)), + (I32F32::saturating_from_num(1.0).safe_div(alpha_low)) + .saturating_sub(I32F32::saturating_from_num(1.0)), ) .saturating_add(a.saturating_mul(consensus_low)); log::trace!("b: {:?}", b); @@ -905,7 +913,8 @@ impl Pallet { // Compute the alpha value using the logistic function formula. // alpha = 1 / (1 + exp_val) - I32F32::from_num(1.0).saturating_div(I32F32::from_num(1.0).saturating_add(exp_val)) + I32F32::saturating_from_num(1.0) + .safe_div(I32F32::saturating_from_num(1.0).saturating_add(exp_val)) }) .collect(); @@ -1019,13 +1028,14 @@ impl Pallet { netuid: u16, ) -> Vec> { // Retrieve the bonds moving average for the given network ID and scale it down. - let bonds_moving_average: I64F64 = I64F64::from_num(Self::get_bonds_moving_average(netuid)) - .saturating_div(I64F64::from_num(1_000_000)); + let bonds_moving_average: I64F64 = + I64F64::saturating_from_num(Self::get_bonds_moving_average(netuid)) + .safe_div(I64F64::saturating_from_num(1_000_000)); // Calculate the alpha value for the EMA calculation. // Alpha is derived by subtracting the scaled bonds moving average from 1. - let alpha: I32F32 = - I32F32::from_num(1).saturating_sub(I32F32::from_num(bonds_moving_average)); + let alpha: I32F32 = I32F32::saturating_from_num(1) + .saturating_sub(I32F32::saturating_from_num(bonds_moving_average)); // Compute the Exponential Moving Average (EMA) of bonds using the calculated alpha value. let ema_bonds = mat_ema_sparse(bonds_delta, bonds, alpha); @@ -1052,13 +1062,14 @@ impl Pallet { netuid: u16, ) -> Vec> { // Retrieve the bonds moving average for the given network ID and scale it down. - let bonds_moving_average: I64F64 = I64F64::from_num(Self::get_bonds_moving_average(netuid)) - .saturating_div(I64F64::from_num(1_000_000)); + let bonds_moving_average: I64F64 = + I64F64::saturating_from_num(Self::get_bonds_moving_average(netuid)) + .safe_div(I64F64::saturating_from_num(1_000_000)); // Calculate the alpha value for the EMA calculation. // Alpha is derived by subtracting the scaled bonds moving average from 1. - let alpha: I32F32 = - I32F32::from_num(1).saturating_sub(I32F32::from_num(bonds_moving_average)); + let alpha: I32F32 = I32F32::saturating_from_num(1) + .saturating_sub(I32F32::saturating_from_num(bonds_moving_average)); // Compute the Exponential Moving Average (EMA) of bonds using the calculated alpha value. let ema_bonds = mat_ema(bonds_delta, bonds, alpha); @@ -1090,7 +1101,9 @@ impl Pallet { // This way we avoid the quantil function panic. if LiquidAlphaOn::::get(netuid) && !consensus.is_empty() - && consensus.iter().any(|&c| c != I32F32::from_num(0)) + && consensus + .iter() + .any(|&c| c != I32F32::saturating_from_num(0)) { // Calculate the 75th percentile (high) and 25th percentile (low) of the consensus values. let consensus_high = quantile(&consensus, 0.75); @@ -1158,7 +1171,9 @@ impl Pallet { // Check if Liquid Alpha is enabled, consensus is not empty, and contains non-zero values. if LiquidAlphaOn::::get(netuid) && !consensus.is_empty() - && consensus.iter().any(|&c| c != I32F32::from_num(0)) + && consensus + .iter() + .any(|&c| c != I32F32::saturating_from_num(0)) { // Calculate the 75th percentile (high) and 25th percentile (low) of the consensus values. let consensus_high = quantile(&consensus, 0.75); @@ -1221,7 +1236,7 @@ impl Pallet { ); let max_u16: u32 = u16::MAX as u32; // 65535 - let min_alpha_high: u16 = (max_u16.saturating_mul(4).saturating_div(5)) as u16; // 52428 + let min_alpha_high: u16 = (max_u16.saturating_mul(4).safe_div(5)) as u16; // 52428 // --- 4. Ensure alpha high is greater than the minimum ensure!(alpha_high >= min_alpha_high, Error::::AlphaHighTooLow); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index cbe394037f..3fd67e0d8e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -730,7 +730,7 @@ pub mod pallet { #[pallet::type_value] /// Default value for Share Pool variables pub fn DefaultSharePoolZero() -> U64F64 { - U64F64::from_num(0) + U64F64::saturating_from_num(0) } #[pallet::storage] diff --git a/pallets/subtensor/src/macros/genesis.rs b/pallets/subtensor/src/macros/genesis.rs index 96c4db62e6..f1288c5a63 100644 --- a/pallets/subtensor/src/macros/genesis.rs +++ b/pallets/subtensor/src/macros/genesis.rs @@ -66,13 +66,13 @@ mod genesis { Alpha::::insert( // Lock the initial funds making this key the owner. (hotkey.clone(), hotkey.clone(), netuid), - U64F64::from_num(1_000_000_000), + U64F64::saturating_from_num(1_000_000_000), ); TotalHotkeyAlpha::::insert(hotkey.clone(), netuid, 1_000_000_000); TotalHotkeyShares::::insert( hotkey.clone(), netuid, - U64F64::from_num(1_000_000_000), + U64F64::saturating_from_num(1_000_000_000), ); // TotalColdkeyAlpha::::insert(hotkey.clone(), netuid, 1_000_000_000); SubnetAlphaOut::::insert(netuid, 1_000_000_000); diff --git a/pallets/subtensor/src/migrations/migrate_rao.rs b/pallets/subtensor/src/migrations/migrate_rao.rs index 13ff02c118..3c034b7dad 100644 --- a/pallets/subtensor/src/migrations/migrate_rao.rs +++ b/pallets/subtensor/src/migrations/migrate_rao.rs @@ -45,10 +45,10 @@ pub fn migrate_rao() -> Weight { }); // Set all the stake on root 0 subnet. Alpha::::mutate((hotkey.clone(), coldkey.clone(), 0), |total| { - *total = total.saturating_add(U64F64::from_num(stake)) + *total = total.saturating_add(U64F64::saturating_from_num(stake)) }); TotalHotkeyShares::::mutate(hotkey.clone(), 0, |total| { - *total = total.saturating_add(U64F64::from_num(stake)) + *total = total.saturating_add(U64F64::saturating_from_num(stake)) }); // Set the total stake on the hotkey TotalHotkeyAlpha::::mutate(hotkey.clone(), 0, |total| { diff --git a/pallets/subtensor/src/rpc_info/delegate_info.rs b/pallets/subtensor/src/rpc_info/delegate_info.rs index 5776c48af4..f8d6418232 100644 --- a/pallets/subtensor/src/rpc_info/delegate_info.rs +++ b/pallets/subtensor/src/rpc_info/delegate_info.rs @@ -2,6 +2,7 @@ use super::*; use frame_support::pallet_prelude::{Decode, Encode}; use frame_support::storage::IterableStorageMap; use frame_support::IterableStorageDoubleMap; +use safe_math::*; use substrate_fixed::types::U64F64; extern crate alloc; use codec::Compact; @@ -27,16 +28,16 @@ impl Pallet { emissions_per_day: U64F64, ) -> U64F64 { // Get the take as a percentage and subtract it from 1 for remainder. - let without_take: U64F64 = U64F64::from_num(1) - .saturating_sub(U64F64::from_num(take.0).saturating_div(u16::MAX.into())); + let without_take: U64F64 = U64F64::saturating_from_num(1) + .saturating_sub(U64F64::saturating_from_num(take.0).safe_div(u16::MAX.into())); - if total_stake > U64F64::from_num(0) { + if total_stake > U64F64::saturating_from_num(0) { emissions_per_day .saturating_mul(without_take) // Divide by 1000 TAO for return per 1k - .saturating_div(total_stake.saturating_div(U64F64::from_num(1000.0 * 1e9))) + .safe_div(total_stake.safe_div(U64F64::saturating_from_num(1000.0 * 1e9))) } else { - U64F64::from_num(0) + U64F64::saturating_from_num(0) } } @@ -66,7 +67,7 @@ impl Pallet { let registrations = Self::get_registered_networks_for_hotkey(&delegate.clone()); let mut validator_permits = Vec::>::new(); - let mut emissions_per_day: U64F64 = U64F64::from_num(0); + let mut emissions_per_day: U64F64 = U64F64::saturating_from_num(0); for netuid in registrations.iter() { if let Ok(uid) = Self::get_uid_for_net_and_hotkey(*netuid, &delegate.clone()) { @@ -77,8 +78,8 @@ impl Pallet { let emission: U64F64 = Self::get_emission_for_uid(*netuid, uid).into(); let tempo: U64F64 = Self::get_tempo(*netuid).into(); - if tempo > U64F64::from_num(0) { - let epochs_per_day: U64F64 = U64F64::from_num(7200).saturating_div(tempo); + if tempo > U64F64::saturating_from_num(0) { + let epochs_per_day: U64F64 = U64F64::saturating_from_num(7200).safe_div(tempo); emissions_per_day = emissions_per_day.saturating_add(emission.saturating_mul(epochs_per_day)); } @@ -101,8 +102,8 @@ impl Pallet { owner_ss58: owner.clone(), registrations: registrations.iter().map(|x| x.into()).collect(), validator_permits, - return_per_1000: U64F64::to_num::(return_per_1000).into(), - total_daily_return: U64F64::to_num::(emissions_per_day).into(), + return_per_1000: return_per_1000.saturating_to_num::().into(), + total_daily_return: emissions_per_day.saturating_to_num::().into(), } } diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 1a1a2d00dd..0cbe3bccf4 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -46,10 +46,11 @@ impl Pallet { Self::get_all_subnet_netuids() .iter() .map(|netuid| { - let alpha: I96F32 = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, *netuid)); + 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).to_num::() + alpha.saturating_mul(tao_price).saturating_to_num::() }) .sum() } @@ -65,9 +66,9 @@ impl Pallet { for (netuid, alpha) in Alpha::::iter_prefix((hotkey, coldkey)) { let tao_price: I96F32 = Self::get_alpha_price(netuid); total_stake = total_stake.saturating_add( - I96F32::from_num(alpha) + I96F32::saturating_from_num(alpha) .saturating_mul(tao_price) - .to_num::(), + .saturating_to_num::(), ); } total_stake diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 4644b32134..75f9331356 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -1,4 +1,5 @@ use super::*; +use safe_math::*; use sp_core::Get; impl Pallet { @@ -223,7 +224,7 @@ impl Pallet { )?; // Unstake from the origin subnet, returning TAO (or a 1:1 equivalent). - let fee = DefaultStakingFee::::get().saturating_div(2); + let fee = DefaultStakingFee::::get().safe_div(2); let tao_unstaked = Self::unstake_from_subnet( origin_hotkey, origin_coldkey, diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index f34a5ace00..1bd6aa1af5 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1,4 +1,5 @@ use super::*; +use safe_math::*; use share_pool::{SharePool, SharePoolDataOperations}; use sp_std::ops::Neg; use substrate_fixed::types::{I64F64, I96F32, U64F64}; @@ -31,17 +32,17 @@ impl Pallet { /// * `I96F32` - The price of alpha for the specified subnet. pub fn get_alpha_price(netuid: u16) -> I96F32 { if netuid == Self::get_root_netuid() { - return I96F32::from_num(1.0); // Root. + return I96F32::saturating_from_num(1.0); // Root. } if SubnetMechanism::::get(netuid) == 0 { - return I96F32::from_num(1.0); // Stable + return I96F32::saturating_from_num(1.0); // Stable } if SubnetAlphaIn::::get(netuid) == 0 { - I96F32::from_num(0) + I96F32::saturating_from_num(0) } else { - I96F32::from_num(SubnetTAO::::get(netuid)) - .checked_div(I96F32::from_num(SubnetAlphaIn::::get(netuid))) - .unwrap_or(I96F32::from_num(0)) + I96F32::saturating_from_num(SubnetTAO::::get(netuid)) + .checked_div(I96F32::saturating_from_num(SubnetAlphaIn::::get(netuid))) + .unwrap_or(I96F32::saturating_from_num(0)) } } @@ -66,11 +67,11 @@ impl Pallet { let stored_weight = TaoWeight::::get(); // Step 2: Convert the u64 weight to I96F32 - let weight_fixed = I96F32::from_num(stored_weight); + let weight_fixed = I96F32::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.saturating_div(I96F32::from_num(u64::MAX)) + weight_fixed.safe_div(I96F32::saturating_from_num(u64::MAX)) } /// Sets the global global weight in storage. @@ -101,16 +102,17 @@ impl Pallet { netuid: u16, ) -> (I64F64, I64F64, I64F64) { // Retrieve the global tao weight. - let tao_weight = I64F64::from_num(Self::get_tao_weight()); + let tao_weight = I64F64::saturating_from_num(Self::get_tao_weight()); log::debug!("tao_weight: {:?}", tao_weight); // Step 1: Get stake of hotkey (neuron) let alpha_stake = - I64F64::from_num(Self::get_inherited_for_hotkey_on_subnet(hotkey, netuid)); + I64F64::saturating_from_num(Self::get_inherited_for_hotkey_on_subnet(hotkey, netuid)); log::trace!("alpha_stake: {:?}", alpha_stake); // Step 2: Get the global tao stake for the hotkey - let tao_stake = I64F64::from_num(Self::get_inherited_for_hotkey_on_subnet(hotkey, 0)); + let tao_stake = + I64F64::saturating_from_num(Self::get_inherited_for_hotkey_on_subnet(hotkey, 0)); log::trace!("tao_stake: {:?}", tao_stake); // Step 3: Combine alpha and tao stakes @@ -124,7 +126,7 @@ impl Pallet { /// pub fn get_stake_weights_for_network(netuid: u16) -> (Vec, Vec, Vec) { // Retrieve the global tao weight. - let tao_weight: I64F64 = I64F64::from_num(Self::get_tao_weight()); + let tao_weight: I64F64 = I64F64::saturating_from_num(Self::get_tao_weight()); log::debug!("tao_weight: {:?}", tao_weight); // Step 1: Get subnetwork size @@ -135,9 +137,11 @@ impl Pallet { .map(|uid| { if Keys::::contains_key(netuid, uid) { let hotkey: T::AccountId = Keys::::get(netuid, uid); - I64F64::from_num(Self::get_inherited_for_hotkey_on_subnet(&hotkey, netuid)) + I64F64::saturating_from_num(Self::get_inherited_for_hotkey_on_subnet( + &hotkey, netuid, + )) } else { - I64F64::from_num(0) + I64F64::saturating_from_num(0) } }) .collect(); @@ -149,9 +153,11 @@ impl Pallet { .map(|uid| { if Keys::::contains_key(netuid, uid) { let hotkey: T::AccountId = Keys::::get(netuid, uid); - I64F64::from_num(Self::get_inherited_for_hotkey_on_subnet(&hotkey, 0)) + I64F64::saturating_from_num(Self::get_inherited_for_hotkey_on_subnet( + &hotkey, 0, + )) } else { - I64F64::from_num(0) + I64F64::saturating_from_num(0) } }) .collect(); @@ -199,7 +205,7 @@ 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::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); log::trace!( "Initial alpha for hotkey {:?} on subnet {}: {:?}", hotkey, @@ -207,12 +213,12 @@ impl Pallet { initial_alpha ); if netuid == 0 { - return initial_alpha.to_num::(); + return initial_alpha.saturating_to_num::(); } // Initialize variables to track alpha allocated to children and inherited from parents. - let mut alpha_to_children: I96F32 = I96F32::from_num(0); - let mut alpha_from_parents: I96F32 = I96F32::from_num(0); + let mut alpha_to_children: I96F32 = I96F32::saturating_from_num(0); + let mut alpha_from_parents: I96F32 = I96F32::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); @@ -233,8 +239,8 @@ 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::from_num(proportion).saturating_div(I96F32::from_num(u64::MAX)); + let normalized_proportion: I96F32 = I96F32::saturating_from_num(proportion) + .safe_div(I96F32::saturating_from_num(u64::MAX)); log::trace!( "Normalized proportion for child: {:?}", normalized_proportion @@ -242,7 +248,7 @@ impl Pallet { // Calculate the amount of alpha to be allocated to this child. let alpha_proportion_to_child: I96F32 = - I96F32::from_num(initial_alpha).saturating_mul(normalized_proportion); + I96F32::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. @@ -254,7 +260,7 @@ impl Pallet { for (proportion, parent) in parents { // Retrieve the parent's total stake on this subnet. let parent_alpha: I96F32 = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); log::trace!( "Parent alpha for parent {:?} on subnet {}: {:?}", parent, @@ -263,8 +269,8 @@ impl Pallet { ); // Convert the proportion to a normalized value between 0 and 1. - let normalized_proportion: I96F32 = - I96F32::from_num(proportion).saturating_div(I96F32::from_num(u64::MAX)); + let normalized_proportion: I96F32 = I96F32::saturating_from_num(proportion) + .safe_div(I96F32::saturating_from_num(u64::MAX)); log::trace!( "Normalized proportion from parent: {:?}", normalized_proportion @@ -272,7 +278,7 @@ impl Pallet { // Calculate the amount of alpha to be inherited from this parent. let alpha_proportion_from_parent: I96F32 = - I96F32::from_num(parent_alpha).saturating_mul(normalized_proportion); + I96F32::saturating_from_num(parent_alpha).saturating_mul(normalized_proportion); log::trace!( "Alpha proportion from parent: {:?}", alpha_proportion_from_parent @@ -298,7 +304,7 @@ impl Pallet { ); // Step 6: Return the final inherited alpha value. - finalized_alpha.to_num::() + finalized_alpha.saturating_to_num::() } /// Checks if a specific hotkey-coldkey pair has enough stake on a subnet to fulfill a given decrement. @@ -460,22 +466,23 @@ impl Pallet { // Step 2: Initialized vars. let alpha: I96F32 = if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::from_num(SubnetTAO::::get(netuid)); - let alpha_reserves: I96F32 = I96F32::from_num(SubnetAlphaIn::::get(netuid)); + let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::::get(netuid)); + let alpha_reserves: I96F32 = + I96F32::saturating_from_num(SubnetAlphaIn::::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); // Step 3.a.3: Calculate alpha staked using the constant product formula // alpha_stake_recieved = current_alpha - (k / (current_tao + new_tao)) alpha_reserves.saturating_sub( - k.checked_div(tao_reserves.saturating_add(I96F32::from_num(tao))) - .unwrap_or(I96F32::from_num(0)), + k.checked_div(tao_reserves.saturating_add(I96F32::saturating_from_num(tao))) + .unwrap_or(I96F32::saturating_from_num(0)), ) } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::from_num(tao) + I96F32::saturating_from_num(tao) }; // Return simulated amount. - alpha.to_num::() + alpha.saturating_to_num::() } /// Swaps a subnet's Alpba token for TAO. @@ -487,21 +494,22 @@ impl Pallet { // Step 2: Swap alpha and attain tao let tao: I96F32 = if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::from_num(SubnetTAO::::get(netuid)); - let alpha_reserves: I96F32 = I96F32::from_num(SubnetAlphaIn::::get(netuid)); + let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::::get(netuid)); + let alpha_reserves: I96F32 = + I96F32::saturating_from_num(SubnetAlphaIn::::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); // Step 3.a.3: Calculate alpha staked using the constant product formula // tao_recieved = tao_reserves - (k / (alpha_reserves + new_tao)) tao_reserves.saturating_sub( - k.checked_div(alpha_reserves.saturating_add(I96F32::from_num(alpha))) - .unwrap_or(I96F32::from_num(0)), + k.checked_div(alpha_reserves.saturating_add(I96F32::saturating_from_num(alpha))) + .unwrap_or(I96F32::saturating_from_num(0)), ) } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::from_num(alpha) + I96F32::saturating_from_num(alpha) }; - tao.to_num::() + tao.saturating_to_num::() } /// Swaps TAO for the alpha token on the subnet. @@ -513,27 +521,28 @@ impl Pallet { // Step 2: Initialized vars. let alpha: I96F32 = if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::from_num(SubnetTAO::::get(netuid)); - let alpha_reserves: I96F32 = I96F32::from_num(SubnetAlphaIn::::get(netuid)); + let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::::get(netuid)); + let alpha_reserves: I96F32 = + I96F32::saturating_from_num(SubnetAlphaIn::::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); // Step 3.a.3: Calculate alpha staked using the constant product formula // alpha_stake_recieved = current_alpha - (k / (current_tao + new_tao)) alpha_reserves.saturating_sub( - k.checked_div(tao_reserves.saturating_add(I96F32::from_num(tao))) - .unwrap_or(I96F32::from_num(0)), + k.checked_div(tao_reserves.saturating_add(I96F32::saturating_from_num(tao))) + .unwrap_or(I96F32::saturating_from_num(0)), ) } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::from_num(tao) + I96F32::saturating_from_num(tao) }; // Step 4. Decrease Alpha reserves. SubnetAlphaIn::::mutate(netuid, |total| { - *total = total.saturating_sub(alpha.to_num::()); + *total = total.saturating_sub(alpha.saturating_to_num::()); }); // Step 5: Increase Alpha outstanding. SubnetAlphaOut::::mutate(netuid, |total| { - *total = total.saturating_add(alpha.to_num::()); + *total = total.saturating_add(alpha.saturating_to_num::()); }); // Step 6: Increase Tao reserves. SubnetTAO::::mutate(netuid, |total| { @@ -548,7 +557,7 @@ impl Pallet { *total = total.saturating_sub(tao); }); // Step 9. Return the alpha received. - alpha.to_num::() + alpha.saturating_to_num::() } /// Swaps a subnet's Alpba token for TAO. @@ -560,19 +569,20 @@ impl Pallet { // Step 2: Swap alpha and attain tao let tao: I96F32 = if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::from_num(SubnetTAO::::get(netuid)); - let alpha_reserves: I96F32 = I96F32::from_num(SubnetAlphaIn::::get(netuid)); + let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::::get(netuid)); + let alpha_reserves: I96F32 = + I96F32::saturating_from_num(SubnetAlphaIn::::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); // Step 3.a.3: Calculate alpha staked using the constant product formula // tao_recieved = tao_reserves - (k / (alpha_reserves + new_tao)) tao_reserves.saturating_sub( - k.checked_div(alpha_reserves.saturating_add(I96F32::from_num(alpha))) - .unwrap_or(I96F32::from_num(0)), + k.checked_div(alpha_reserves.saturating_add(I96F32::saturating_from_num(alpha))) + .unwrap_or(I96F32::saturating_from_num(0)), ) } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::from_num(alpha) + I96F32::saturating_from_num(alpha) }; // Step 4: Increase Alpha reserves. SubnetAlphaIn::::mutate(netuid, |total| { @@ -584,18 +594,18 @@ impl Pallet { }); // Step 6: Decrease tao reserves. SubnetTAO::::mutate(netuid, |total| { - *total = total.saturating_sub(tao.to_num::()); + *total = total.saturating_sub(tao.saturating_to_num::()); }); // Step 7: Reduce total TAO reserves. TotalStake::::mutate(|total| { - *total = total.saturating_sub(tao.to_num::()); + *total = total.saturating_sub(tao.saturating_to_num::()); }); // Step 8. Decrease Alpha reserves. SubnetVolume::::mutate(netuid, |total| { - *total = total.saturating_sub(tao.to_num::()); + *total = total.saturating_sub(tao.saturating_to_num::()); }); // Step 9. Return the tao received. - tao.to_num::() + tao.saturating_to_num::() } /// Unstakes alpha from a subnet for a given hotkey and coldkey pair. @@ -870,7 +880,7 @@ impl SharePoolDataOperations> for HotkeyAlphaSharePoolDataOperations { fn get_shared_value(&self) -> U64F64 { - U64F64::from_num(crate::TotalHotkeyAlpha::::get(&self.hotkey, self.netuid)) + U64F64::saturating_from_num(crate::TotalHotkeyAlpha::::get(&self.hotkey, self.netuid)) } fn get_share(&self, key: &AlphaShareKey) -> U64F64 { @@ -890,7 +900,7 @@ impl SharePoolDataOperations> crate::TotalHotkeyAlpha::::insert( &(self.hotkey), self.netuid, - value.to_num::(), + value.saturating_to_num::(), ); } else { crate::TotalHotkeyAlpha::::remove(&(self.hotkey), self.netuid); diff --git a/pallets/subtensor/src/subnets/weights.rs b/pallets/subtensor/src/subnets/weights.rs index fca0e5999e..ad868c52dc 100644 --- a/pallets/subtensor/src/subnets/weights.rs +++ b/pallets/subtensor/src/subnets/weights.rs @@ -1,6 +1,7 @@ use super::*; use crate::epoch::math::*; use codec::Compact; +use safe_math::*; use sp_core::{ConstU32, H256}; use sp_runtime::{ traits::{BlakeTwo256, Hash}, @@ -1001,9 +1002,7 @@ impl Pallet { return weights; } weights.iter_mut().for_each(|x| { - *x = (*x as u64) - .saturating_mul(u16::MAX as u64) - .saturating_div(sum) as u16; + *x = (*x as u64).saturating_mul(u16::MAX as u64).safe_div(sum) as u16; }); weights } diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index a179fc5aa3..7e30a22514 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3357,7 +3357,7 @@ fn test_parent_child_chain_emission() { let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( netuid, - total_tao.saturating_to_num::(), + total_tao.to_num::(), )); // Set the stakes directly @@ -4046,7 +4046,7 @@ fn test_dynamic_parent_child_relationships() { let expected_parent_stake = ((I96F32::from_num(stake_parent_0) + total_emission * rel_stake_parent_0) * I96F32::from_num(5)) - .saturating_div(I96F32::from_num(12)); + / I96F32::from_num(12); assert!( (I96F32::from_num(stake_parent_2) - expected_parent_stake).abs() / expected_parent_stake diff --git a/pallets/subtensor/src/tests/delegate_info.rs b/pallets/subtensor/src/tests/delegate_info.rs index 78ea484829..0dcb69d78d 100644 --- a/pallets/subtensor/src/tests/delegate_info.rs +++ b/pallets/subtensor/src/tests/delegate_info.rs @@ -6,7 +6,7 @@ use super::mock::*; #[test] fn test_return_per_1000_tao() { let take = // 18% take to the Validator - Compact::::from((U64F64::from_num(0.18 * u16::MAX as f64)).saturating_to_num::()); + Compact::::from((U64F64::from_num(0.18 * u16::MAX as f64)).to_num::()); // 10_000 TAO total validator stake let total_stake = U64F64::from_num(10_000.0 * 1e9); diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index ae0c8f6699..a353ee5b92 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -2285,22 +2285,19 @@ fn test_compute_alpha_values() { // exp_val = exp(0.0 - 1.0 * 0.1) = exp(-0.1) // alpha[0] = 1 / (1 + exp(-0.1)) ~ 0.9048374180359595 let exp_val_0 = I32F32::from_num(0.9048374180359595); - let expected_alpha_0 = - I32F32::from_num(1.0).saturating_div(I32F32::from_num(1.0).saturating_add(exp_val_0)); + let expected_alpha_0 = I32F32::from_num(1.0) / I32F32::from_num(1.0).saturating_add(exp_val_0); // For consensus[1] = 0.5: // exp_val = exp(0.0 - 1.0 * 0.5) = exp(-0.5) // alpha[1] = 1 / (1 + exp(-0.5)) ~ 0.6065306597126334 let exp_val_1 = I32F32::from_num(0.6065306597126334); - let expected_alpha_1 = - I32F32::from_num(1.0).saturating_div(I32F32::from_num(1.0).saturating_add(exp_val_1)); + let expected_alpha_1 = I32F32::from_num(1.0) / I32F32::from_num(1.0).saturating_add(exp_val_1); // For consensus[2] = 0.9: // exp_val = exp(0.0 - 1.0 * 0.9) = exp(-0.9) // alpha[2] = 1 / (1 + exp(-0.9)) ~ 0.4065696597405991 let exp_val_2 = I32F32::from_num(0.4065696597405991); - let expected_alpha_2 = - I32F32::from_num(1.0).saturating_div(I32F32::from_num(1.0).saturating_add(exp_val_2)); + let expected_alpha_2 = I32F32::from_num(1.0) / I32F32::from_num(1.0).saturating_add(exp_val_2); // Define an epsilon for approximate equality checks. let epsilon = I32F32::from_num(1e-6); @@ -2338,8 +2335,7 @@ fn test_compute_alpha_values_256_miners() { let exp_val = safe_exp(exponent); // Use saturating addition and division - let expected_alpha = - I32F32::from_num(1.0).saturating_div(I32F32::from_num(1.0).saturating_add(exp_val)); + let expected_alpha = I32F32::from_num(1.0) / I32F32::from_num(1.0).saturating_add(exp_val); // Assert that the computed alpha values match the expected values within the epsilon. assert_approx_eq(alpha[i], expected_alpha, epsilon); diff --git a/pallets/subtensor/src/tests/math.rs b/pallets/subtensor/src/tests/math.rs index 84fcc4aac1..59d826ab33 100644 --- a/pallets/subtensor/src/tests/math.rs +++ b/pallets/subtensor/src/tests/math.rs @@ -2264,16 +2264,17 @@ fn test_math_fixed_to_u64() { } #[test] -#[should_panic(expected = "-1 overflows")] -fn test_math_fixed_to_u64_panics() { +fn test_math_fixed_to_u64_saturates() { let bad_input = I32F32::from_num(-1); - fixed_to_u64(bad_input); + let expected = 0; + assert_eq!(fixed_to_u64(bad_input), expected); } #[test] fn test_math_fixed64_to_u64() { let expected = u64::MIN; - assert_eq!(fixed64_to_u64(I64F64::from_num(expected)), expected); + let input = I64F64::from_num(expected); + assert_eq!(fixed64_to_u64(input), expected); let input = i64::MAX / 2; let expected = u64::try_from(input).unwrap(); @@ -2285,10 +2286,10 @@ fn test_math_fixed64_to_u64() { } #[test] -#[should_panic(expected = "-1 overflows")] -fn test_math_fixed64_to_u64_panics() { +fn test_math_fixed64_to_u64_saturates() { let bad_input = I64F64::from_num(-1); - fixed64_to_u64(bad_input); + let expected = 0; + assert_eq!(fixed64_to_u64(bad_input), expected); } /* @TODO: find the _true_ max, and half, input values */ @@ -2304,10 +2305,9 @@ fn test_math_fixed64_to_fixed32() { } #[test] -#[should_panic(expected = "overflow")] -fn test_math_fixed64_to_fixed32_panics() { +fn test_math_fixed64_to_fixed32_saturates() { let bad_input = I64F64::from_num(u32::MAX); - fixed64_to_fixed32(bad_input); + assert_eq!(fixed64_to_fixed32(bad_input), I32F32::max_value()); } #[test] @@ -2340,14 +2340,15 @@ fn test_fixed_proportion_to_u16() { } #[test] -#[should_panic(expected = "overflow")] -fn test_fixed_proportion_to_u16_panics() { +fn test_fixed_proportion_to_u16_saturates() { let expected = u16::MAX; let input = I32F32::from_num(expected); log::trace!("Testing with input: {:?}", input); // Debug output let result = fixed_proportion_to_u16(input); log::trace!("Testing with result: {:?}", result); // Debug output + assert_eq!(result, expected); } + #[test] fn test_vec_fixed64_to_fixed32() { let input = vec![I64F64::from_num(i32::MIN)]; @@ -2360,10 +2361,9 @@ fn test_vec_fixed64_to_fixed32() { } #[test] -#[should_panic(expected = "overflow")] -fn test_vec_fixed64_to_fixed32_panics() { +fn test_vec_fixed64_to_fixed32_saturates() { let bad_input = vec![I64F64::from_num(i64::MAX)]; - vec_fixed64_to_fixed32(bad_input); + assert_eq!(vec_fixed64_to_fixed32(bad_input), [I32F32::max_value()]); } #[test] diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index f1e0c7eb40..b27921f27c 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -3,6 +3,7 @@ use crate::{ system::{ensure_root, ensure_signed_or_root, pallet_prelude::BlockNumberFor}, Error, }; +use safe_math::*; use sp_core::Get; use sp_core::U256; use sp_runtime::Saturating; @@ -592,7 +593,8 @@ impl Pallet { SubnetOwnerCut::::get() } pub fn get_float_subnet_owner_cut() -> I96F32 { - I96F32::from_num(SubnetOwnerCut::::get()).saturating_div(I96F32::from_num(u16::MAX)) + I96F32::saturating_from_num(SubnetOwnerCut::::get()) + .safe_div(I96F32::saturating_from_num(u16::MAX)) } pub fn set_subnet_owner_cut(subnet_owner_cut: u16) { SubnetOwnerCut::::set(subnet_owner_cut); @@ -664,9 +666,10 @@ impl Pallet { pub fn get_alpha_values_32(netuid: u16) -> (I32F32, I32F32) { let (alpha_low, alpha_high): (u16, u16) = AlphaValues::::get(netuid); - let converted_low = I32F32::from_num(alpha_low).saturating_div(I32F32::from_num(u16::MAX)); + let converted_low = + I32F32::saturating_from_num(alpha_low).safe_div(I32F32::saturating_from_num(u16::MAX)); let converted_high = - I32F32::from_num(alpha_high).saturating_div(I32F32::from_num(u16::MAX)); + I32F32::saturating_from_num(alpha_high).safe_div(I32F32::saturating_from_num(u16::MAX)); (converted_low, converted_high) } diff --git a/primitives/safe-math/Cargo.toml b/primitives/safe-math/Cargo.toml new file mode 100644 index 0000000000..f67d53c5df --- /dev/null +++ b/primitives/safe-math/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "safe-math" +version = "0.1.0" +edition = "2021" + +[dependencies] +substrate-fixed = { workspace = true } +sp-std = { workspace = true } +num-traits = { version = "0.2.19", default-features = false, features = ["libm"] } + +[lints] +workspace = true + +[features] +default = ["std"] +std = [ + "substrate-fixed/std", + "sp-std/std", + "num-traits/std", +] diff --git a/primitives/safe-math/src/lib.rs b/primitives/safe-math/src/lib.rs new file mode 100644 index 0000000000..83c27b07c4 --- /dev/null +++ b/primitives/safe-math/src/lib.rs @@ -0,0 +1,71 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::result_unit_err)] + +use substrate_fixed::types::{I110F18, I32F32, I64F64, I96F32, U64F64}; + +/// Safe division trait +pub trait SafeDiv { + /// Safe division that returns supplied default value for division by zero + fn safe_div_or(self, rhs: Self, def: Self) -> Self; + /// Safe division that returns default value for division by zero + fn safe_div(self, rhs: Self) -> Self; +} + +/// Implementation of safe division trait for primitive types +macro_rules! impl_safe_div_for_primitive { + ($($t:ty),*) => { + $( + impl SafeDiv for $t { + fn safe_div_or(self, rhs: Self, def: Self) -> Self { + self.checked_div(rhs).unwrap_or(def) + } + + fn safe_div(self, rhs: Self) -> Self { + self.checked_div(rhs).unwrap_or_default() + } + } + )* + }; +} +impl_safe_div_for_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, usize); + +/// Implementation of safe division trait for substrate fixed types +macro_rules! impl_safe_div_for_fixed { + ($($t:ty),*) => { + $( + impl SafeDiv for $t { + fn safe_div_or(self, rhs: Self, def: Self) -> Self { + self.checked_div(rhs).unwrap_or(def) + } + + fn safe_div(self, rhs: Self) -> Self { + self.checked_div(rhs).unwrap_or_default() + } + } + )* + }; +} +impl_safe_div_for_fixed!(I96F32, I32F32, I64F64, I110F18, U64F64); + +// /// Trait for safe conversion to primitive type P +// pub trait SafeToNum { +// /// Safe conversion to primitive type P +// fn safe_to_num

(self) -> P +// where +// P: num_traits::Bounded + substrate_fixed::prelude::ToFixed + substrate_fixed::prelude::FromFixed; +// } + +// impl SafeToNum for T +// where +// T: substrate_fixed::traits::Fixed, +// { +// fn safe_to_num

(self) -> P +// where +// P: num_traits::Bounded + substrate_fixed::prelude::ToFixed + substrate_fixed::prelude::FromFixed +// { +// match self.try_into() { +// Ok(value) => value, +// Err(_) => P::max_value(), +// } +// } +// } diff --git a/primitives/share-pool/Cargo.toml b/primitives/share-pool/Cargo.toml index 2191232699..e0696ee306 100644 --- a/primitives/share-pool/Cargo.toml +++ b/primitives/share-pool/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] substrate-fixed = { workspace = true } sp-std = { workspace = true } +safe-math = { default-features = false, path = "../safe-math" } [lints] workspace = true @@ -15,4 +16,5 @@ default = ["std"] std = [ "substrate-fixed/std", "sp-std/std", + "safe-math/std", ] diff --git a/primitives/share-pool/src/lib.rs b/primitives/share-pool/src/lib.rs index 2f4d25ef9f..f3e00fca9a 100644 --- a/primitives/share-pool/src/lib.rs +++ b/primitives/share-pool/src/lib.rs @@ -52,9 +52,9 @@ where shared_value .checked_div(denominator) - .unwrap_or(U64F64::from_num(0)) + .unwrap_or(U64F64::saturating_from_num(0)) .saturating_mul(current_share) - .to_num::() + .saturating_to_num::() } pub fn try_get_value(&self, key: &K) -> Result { @@ -69,9 +69,9 @@ where pub fn update_value_for_all(&mut self, update: i64) { let shared_value: U64F64 = self.state_ops.get_shared_value(); self.state_ops.set_shared_value(if update >= 0 { - shared_value.saturating_add(U64F64::from_num(update)) + shared_value.saturating_add(U64F64::saturating_from_num(update)) } else { - shared_value.saturating_sub(U64F64::from_num(update.neg())) + shared_value.saturating_sub(U64F64::saturating_from_num(update.neg())) }); } @@ -92,31 +92,33 @@ where self.state_ops.set_share(key, new_shared_value); } else { // There are already keys in the pool, set or update this key - let value_per_share: I64F64 = I64F64::from_num( + let value_per_share: I64F64 = I64F64::saturating_from_num( shared_value .checked_div(denominator) // denominator is never 0 here - .unwrap_or(U64F64::from_num(0)), + .unwrap_or(U64F64::saturating_from_num(0)), ); - let shares_per_update: I64F64 = I64F64::from_num(update) + let shares_per_update: I64F64 = I64F64::saturating_from_num(update) .checked_div(value_per_share) - .unwrap_or(I64F64::from_num(0)); + .unwrap_or(I64F64::saturating_from_num(0)); if shares_per_update >= 0 { self.state_ops.set_denominator( - denominator.saturating_add(U64F64::from_num(shares_per_update)), + denominator.saturating_add(U64F64::saturating_from_num(shares_per_update)), ); self.state_ops.set_share( key, - current_share.saturating_add(U64F64::from_num(shares_per_update)), + current_share.saturating_add(U64F64::saturating_from_num(shares_per_update)), ); } else { self.state_ops.set_denominator( - denominator.saturating_sub(U64F64::from_num(shares_per_update.neg())), + denominator + .saturating_sub(U64F64::saturating_from_num(shares_per_update.neg())), ); self.state_ops.set_share( key, - current_share.saturating_sub(U64F64::from_num(shares_per_update.neg())), + current_share + .saturating_sub(U64F64::saturating_from_num(shares_per_update.neg())), ); } } @@ -137,9 +139,9 @@ mod tests { impl MockSharePoolDataOperations { fn new() -> Self { MockSharePoolDataOperations { - shared_value: U64F64::from_num(0), + shared_value: U64F64::saturating_from_num(0), share: BTreeMap::new(), - denominator: U64F64::from_num(0), + denominator: U64F64::saturating_from_num(0), } } } @@ -150,7 +152,10 @@ mod tests { } fn get_share(&self, key: &u16) -> U64F64 { - *self.share.get(key).unwrap_or(&U64F64::from_num(0)) + *self + .share + .get(key) + .unwrap_or(&U64F64::saturating_from_num(0)) } fn try_get_share(&self, key: &u16) -> Result { @@ -180,10 +185,10 @@ mod tests { #[test] fn test_get_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_denominator(U64F64::from_num(10)); - mock_ops.set_share(&1_u16, U64F64::from_num(3)); - mock_ops.set_share(&2_u16, U64F64::from_num(7)); - mock_ops.set_shared_value(U64F64::from_num(100)); + mock_ops.set_denominator(U64F64::saturating_from_num(10)); + mock_ops.set_share(&1_u16, U64F64::saturating_from_num(3)); + mock_ops.set_share(&2_u16, U64F64::saturating_from_num(7)); + mock_ops.set_shared_value(U64F64::saturating_from_num(100)); let share_pool = SharePool::new(mock_ops); let result1 = share_pool.get_value(&1); let result2 = share_pool.get_value(&2); @@ -194,7 +199,7 @@ mod tests { #[test] fn test_division_by_zero() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_denominator(U64F64::from_num(0)); // Zero denominator + mock_ops.set_denominator(U64F64::saturating_from_num(0)); // Zero denominator let pool = SharePool::::new(mock_ops); let value = pool.get_value(&1); @@ -204,10 +209,10 @@ mod tests { #[test] fn test_max_shared_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_shared_value(U64F64::from_num(u64::MAX)); - mock_ops.set_share(&1, U64F64::from_num(3)); // Use a neutral value for share - mock_ops.set_share(&2, U64F64::from_num(7)); // Use a neutral value for share - mock_ops.set_denominator(U64F64::from_num(10)); // Neutral value to see max effect + mock_ops.set_shared_value(U64F64::saturating_from_num(u64::MAX)); + mock_ops.set_share(&1, U64F64::saturating_from_num(3)); // Use a neutral value for share + mock_ops.set_share(&2, U64F64::saturating_from_num(7)); // Use a neutral value for share + mock_ops.set_denominator(U64F64::saturating_from_num(10)); // Neutral value to see max effect let pool = SharePool::::new(mock_ops); let max_value = pool.get_value(&1) + pool.get_value(&2); @@ -217,10 +222,10 @@ mod tests { #[test] fn test_max_share_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_shared_value(U64F64::from_num(1_000_000_000)); // Use a neutral value for shared value - mock_ops.set_share(&1, U64F64::from_num(u64::MAX / 2)); - mock_ops.set_share(&2, U64F64::from_num(u64::MAX / 2)); - mock_ops.set_denominator(U64F64::from_num(u64::MAX)); + mock_ops.set_shared_value(U64F64::saturating_from_num(1_000_000_000)); // Use a neutral value for shared value + mock_ops.set_share(&1, U64F64::saturating_from_num(u64::MAX / 2)); + mock_ops.set_share(&2, U64F64::saturating_from_num(u64::MAX / 2)); + mock_ops.set_denominator(U64F64::saturating_from_num(u64::MAX)); let pool = SharePool::::new(mock_ops); let value1 = pool.get_value(&1) as i128; @@ -268,6 +273,9 @@ mod tests { let mut pool = SharePool::::new(mock_ops); pool.update_value_for_all(1000); - assert_eq!(pool.state_ops.shared_value, U64F64::from_num(1000)); + assert_eq!( + pool.state_ops.shared_value, + U64F64::saturating_from_num(1000) + ); } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index f8491dd57a..372c9a9815 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1179,7 +1179,9 @@ const BLOCK_GAS_LIMIT: u64 = 75_000_000; /// `WeightPerGas` is an approximate ratio of the amount of Weight per Gas. /// fn weight_per_gas() -> Weight { - (NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT).saturating_div(BLOCK_GAS_LIMIT) + (NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT) + .checked_div(BLOCK_GAS_LIMIT) + .unwrap_or_default() } parameter_types! {