From e522f1e44907a39bd8dfbf4c9d50f9de0b2c92bc Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 5 Nov 2025 12:13:46 -0500 Subject: [PATCH 1/2] Code cleanup, no functional changes --- .../src/coinbase/subnet_emissions.rs | 33 - pallets/subtensor/src/tests/coinbase.rs | 200 +++--- .../subtensor/src/tests/subnet_emissions.rs | 654 +++++++++--------- 3 files changed, 427 insertions(+), 460 deletions(-) diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 184f27a4b..2d9c3b147 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -51,7 +51,6 @@ impl Pallet { // Update SubnetEmaTaoFlow if needed and return its value for // the current block - #[allow(dead_code)] fn get_ema_flow(netuid: NetUid) -> I64F64 { let current_block: u64 = Self::get_current_block_as_u64(); @@ -89,7 +88,6 @@ impl Pallet { // Either the minimal EMA flow L = min{Si}, or an artificial // cut off at some higher value A (TaoFlowCutoff) // L = max {A, min{min{S[i], 0}}} - #[allow(dead_code)] fn get_lower_limit(ema_flows: &BTreeMap) -> I64F64 { let zero = I64F64::saturating_from_num(0); let min_flow = ema_flows @@ -181,7 +179,6 @@ impl Pallet { } // Implementation of shares that uses TAO flow - #[allow(dead_code)] fn get_shares_flow(subnets_to_emit_to: &[NetUid]) -> BTreeMap { // Get raw flows let ema_flows = subnets_to_emit_to @@ -217,35 +214,5 @@ impl Pallet { // Combines ema price method and tao flow method linearly over FlowHalfLife blocks pub(crate) fn get_shares(subnets_to_emit_to: &[NetUid]) -> BTreeMap { Self::get_shares_flow(subnets_to_emit_to) - // Self::get_shares_price_ema(subnets_to_emit_to) - } - - // DEPRECATED: Implementation of shares that uses EMA prices will be gradually deprecated - #[allow(dead_code)] - fn get_shares_price_ema(subnets_to_emit_to: &[NetUid]) -> BTreeMap { - // Get sum of alpha moving prices - let total_moving_prices = subnets_to_emit_to - .iter() - .map(|netuid| U64F64::saturating_from_num(Self::get_moving_alpha_price(*netuid))) - .fold(U64F64::saturating_from_num(0.0), |acc, ema| { - acc.saturating_add(ema) - }); - log::debug!("total_moving_prices: {total_moving_prices:?}"); - - // Calculate shares. - subnets_to_emit_to - .iter() - .map(|netuid| { - let moving_price = - U64F64::saturating_from_num(Self::get_moving_alpha_price(*netuid)); - log::debug!("moving_price_i: {moving_price:?}"); - - let share = moving_price - .checked_div(total_moving_prices) - .unwrap_or(U64F64::saturating_from_num(0)); - - (*netuid, share) - }) - .collect::>() } } diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 60644b2a2..2d0376c89 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -115,28 +115,28 @@ fn test_coinbase_tao_issuance_base_low() { } // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_tao_issuance_base_low_flow --exact --show-output --nocapture -// #[test] -// fn test_coinbase_tao_issuance_base_low_flow() { -// new_test_ext(1).execute_with(|| { -// let emission = TaoCurrency::from(1_234_567); -// let subnet_owner_ck = U256::from(1001); -// let subnet_owner_hk = U256::from(1002); -// let netuid = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); -// let emission = TaoCurrency::from(1); - -// // 100% tao flow method -// let block_num = FlowHalfLife::::get(); -// SubnetEmaTaoFlow::::insert(netuid, (block_num, I64F64::from_num(1_000_000_000))); -// System::set_block_number(block_num); - -// let tao_in_before = SubnetTAO::::get(netuid); -// let total_stake_before = TotalStake::::get(); -// SubtensorModule::run_coinbase(U96F32::from_num(emission)); -// assert_eq!(SubnetTAO::::get(netuid), tao_in_before + emission); -// assert_eq!(TotalIssuance::::get(), emission); -// assert_eq!(TotalStake::::get(), total_stake_before + emission); -// }); -// } +#[test] +fn test_coinbase_tao_issuance_base_low_flow() { + new_test_ext(1).execute_with(|| { + let emission = TaoCurrency::from(1_234_567); + let subnet_owner_ck = U256::from(1001); + let subnet_owner_hk = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); + let emission = TaoCurrency::from(1); + + // 100% tao flow method + let block_num = FlowHalfLife::::get(); + SubnetEmaTaoFlow::::insert(netuid, (block_num, I64F64::from_num(1_000_000_000))); + System::set_block_number(block_num); + + let tao_in_before = SubnetTAO::::get(netuid); + let total_stake_before = TotalStake::::get(); + SubtensorModule::run_coinbase(U96F32::from_num(emission)); + assert_eq!(SubnetTAO::::get(netuid), tao_in_before + emission); + assert_eq!(TotalIssuance::::get(), emission); + assert_eq!(TotalStake::::get(), total_stake_before + emission); + }); +} // Test emission distribution across multiple subnets. // This test verifies that: @@ -260,85 +260,85 @@ fn test_coinbase_tao_issuance_different_prices() { } // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_tao_issuance_different_flows --exact --show-output --nocapture -// #[test] -// fn test_coinbase_tao_issuance_different_flows() { -// new_test_ext(1).execute_with(|| { -// let subnet_owner_ck = U256::from(1001); -// let subnet_owner_hk = U256::from(1002); -// let netuid1 = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); -// let netuid2 = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); -// let emission = 100_000_000; - -// // Setup prices 0.1 and 0.2 -// let initial_tao: u64 = 100_000_u64; -// let initial_alpha1: u64 = initial_tao * 10; -// let initial_alpha2: u64 = initial_tao * 5; -// mock::setup_reserves(netuid1, initial_tao.into(), initial_alpha1.into()); -// mock::setup_reserves(netuid2, initial_tao.into(), initial_alpha2.into()); - -// // Force the swap to initialize -// SubtensorModule::swap_tao_for_alpha( -// netuid1, -// TaoCurrency::ZERO, -// 1_000_000_000_000.into(), -// false, -// ) -// .unwrap(); -// SubtensorModule::swap_tao_for_alpha( -// netuid2, -// TaoCurrency::ZERO, -// 1_000_000_000_000.into(), -// false, -// ) -// .unwrap(); - -// // Set subnet prices to reversed proportion to ensure they don't affect emissions. -// SubnetMovingPrice::::insert(netuid1, I96F32::from_num(2)); -// SubnetMovingPrice::::insert(netuid2, I96F32::from_num(1)); - -// // Set subnet tao flow ema. -// let block_num = FlowHalfLife::::get(); -// SubnetEmaTaoFlow::::insert(netuid1, (block_num, I64F64::from_num(1))); -// SubnetEmaTaoFlow::::insert(netuid2, (block_num, I64F64::from_num(2))); -// System::set_block_number(block_num); - -// // Set normalization exponent to 1 for simplicity -// FlowNormExponent::::set(U64F64::from(1_u64)); - -// // Assert initial TAO reserves. -// assert_eq!(SubnetTAO::::get(netuid1), initial_tao.into()); -// assert_eq!(SubnetTAO::::get(netuid2), initial_tao.into()); -// let total_stake_before = TotalStake::::get(); - -// // Run the coinbase with the emission amount. -// SubtensorModule::run_coinbase(U96F32::from_num(emission)); - -// // Assert tao emission is split evenly. -// assert_abs_diff_eq!( -// SubnetTAO::::get(netuid1), -// TaoCurrency::from(initial_tao + emission / 3), -// epsilon = 10.into(), -// ); -// assert_abs_diff_eq!( -// SubnetTAO::::get(netuid2), -// TaoCurrency::from(initial_tao + 2 * emission / 3), -// epsilon = 10.into(), -// ); +#[test] +fn test_coinbase_tao_issuance_different_flows() { + new_test_ext(1).execute_with(|| { + let subnet_owner_ck = U256::from(1001); + let subnet_owner_hk = U256::from(1002); + let netuid1 = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); + let netuid2 = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); + let emission = 100_000_000; -// // Prices are low => we limit tao issued (buy alpha with it) -// let tao_issued = TaoCurrency::from(((0.1 + 0.2) * emission as f64) as u64); -// assert_abs_diff_eq!( -// TotalIssuance::::get(), -// tao_issued, -// epsilon = 10.into() -// ); -// assert_abs_diff_eq!( -// TotalStake::::get(), -// total_stake_before + emission.into(), -// epsilon = 10.into() -// ); -// }); -// } + // Setup prices 0.1 and 0.2 + let initial_tao: u64 = 100_000_u64; + let initial_alpha1: u64 = initial_tao * 10; + let initial_alpha2: u64 = initial_tao * 5; + mock::setup_reserves(netuid1, initial_tao.into(), initial_alpha1.into()); + mock::setup_reserves(netuid2, initial_tao.into(), initial_alpha2.into()); + + // Force the swap to initialize + SubtensorModule::swap_tao_for_alpha( + netuid1, + TaoCurrency::ZERO, + 1_000_000_000_000.into(), + false, + ) + .unwrap(); + SubtensorModule::swap_tao_for_alpha( + netuid2, + TaoCurrency::ZERO, + 1_000_000_000_000.into(), + false, + ) + .unwrap(); + + // Set subnet prices to reversed proportion to ensure they don't affect emissions. + SubnetMovingPrice::::insert(netuid1, I96F32::from_num(2)); + SubnetMovingPrice::::insert(netuid2, I96F32::from_num(1)); + + // Set subnet tao flow ema. + let block_num = FlowHalfLife::::get(); + SubnetEmaTaoFlow::::insert(netuid1, (block_num, I64F64::from_num(1))); + SubnetEmaTaoFlow::::insert(netuid2, (block_num, I64F64::from_num(2))); + System::set_block_number(block_num); + + // Set normalization exponent to 1 for simplicity + FlowNormExponent::::set(U64F64::from(1_u64)); + + // Assert initial TAO reserves. + assert_eq!(SubnetTAO::::get(netuid1), initial_tao.into()); + assert_eq!(SubnetTAO::::get(netuid2), initial_tao.into()); + let total_stake_before = TotalStake::::get(); + + // Run the coinbase with the emission amount. + SubtensorModule::run_coinbase(U96F32::from_num(emission)); + + // Assert tao emission is split evenly. + assert_abs_diff_eq!( + SubnetTAO::::get(netuid1), + TaoCurrency::from(initial_tao + emission / 3), + epsilon = 10.into(), + ); + assert_abs_diff_eq!( + SubnetTAO::::get(netuid2), + TaoCurrency::from(initial_tao + 2 * emission / 3), + epsilon = 10.into(), + ); + + // Prices are low => we limit tao issued (buy alpha with it) + let tao_issued = TaoCurrency::from(((0.1 + 0.2) * emission as f64) as u64); + assert_abs_diff_eq!( + TotalIssuance::::get(), + tao_issued, + epsilon = 10.into() + ); + assert_abs_diff_eq!( + TotalStake::::get(), + total_stake_before + emission.into(), + epsilon = 10.into() + ); + }); +} // Test moving price updates with different alpha values. // This test verifies that: diff --git a/pallets/subtensor/src/tests/subnet_emissions.rs b/pallets/subtensor/src/tests/subnet_emissions.rs index 311a93064..9f29b3fee 100644 --- a/pallets/subtensor/src/tests/subnet_emissions.rs +++ b/pallets/subtensor/src/tests/subnet_emissions.rs @@ -151,137 +151,137 @@ fn inplace_pow_normalize_fractional_exponent() { }) } -// /// Normal (moderate, non-zero) EMA flows across 3 subnets. -// /// Expect: shares sum to ~1 and are monotonic with flows. -// #[test] -// fn get_shares_normal_flows_three_subnets() { -// new_test_ext(1).execute_with(|| { -// let owner_hotkey = U256::from(10); -// let owner_coldkey = U256::from(20); - -// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); -// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); -// let n3 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - -// let block_num = FlowHalfLife::::get(); -// System::set_block_number(block_num); - -// // Set (block_number, flow) with reasonable positive flows -// SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(1_000.0))); -// SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(3_000.0))); -// SubnetEmaTaoFlow::::insert(n3, (block_num, i64f64(6_000.0))); - -// let subnets = vec![n1, n2, n3]; -// let shares = SubtensorModule::get_shares(&subnets); - -// // Sum ≈ 1 -// let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); -// assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-9); - -// // Each share in [0,1] and finite -// for (k, v) in &shares { -// let f = v.to_num::(); -// assert!(f.is_finite(), "share for {k:?} not finite"); -// assert!( -// (0.0..=1.0).contains(&f), -// "share for {k:?} out of [0,1]: {f}" -// ); -// } - -// // Monotonicity with the flows: share(n3) > share(n2) > share(n1) -// let s1 = shares.get(&n1).unwrap().to_num::(); -// let s2 = shares.get(&n2).unwrap().to_num::(); -// let s3 = shares.get(&n3).unwrap().to_num::(); -// assert!( -// s3 > s2 && s2 > s1, -// "expected s3 > s2 > s1; got {s1}, {s2}, {s3}" -// ); -// }); -// } - -// /// Very low (but non-zero) EMA flows across 2 subnets. -// /// Expect: shares sum to ~1 and higher-flow subnet gets higher share. -// #[test] -// fn get_shares_low_flows_sum_one_and_ordering() { -// new_test_ext(1).execute_with(|| { -// let owner_hotkey = U256::from(11); -// let owner_coldkey = U256::from(21); - -// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); -// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - -// let block_num = FlowHalfLife::::get(); -// System::set_block_number(block_num); - -// // Tiny flows to exercise precision/scaling path -// SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(1e-9))); -// SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(2e-9))); - -// let subnets = vec![n1, n2]; -// let shares = SubtensorModule::get_shares(&subnets); - -// let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); -// assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-8); - -// for (k, v) in &shares { -// let f = v.to_num::(); -// assert!(f.is_finite(), "share for {k:?} not finite"); -// assert!( -// (0.0..=1.0).contains(&f), -// "share for {k:?} out of [0,1]: {f}" -// ); -// } - -// let s1 = shares.get(&n1).unwrap().to_num::(); -// let s2 = shares.get(&n2).unwrap().to_num::(); -// assert!( -// s2 > s1, -// "expected s2 > s1 with higher flow; got s1={s1}, s2={s2}" -// ); -// }); -// } - -// /// High EMA flows across 2 subnets. -// /// Expect: no overflow, shares sum to ~1, and ordering follows flows. -// #[test] -// fn get_shares_high_flows_sum_one_and_ordering() { -// new_test_ext(1).execute_with(|| { -// let owner_hotkey = U256::from(12); -// let owner_coldkey = U256::from(22); - -// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); -// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - -// let block_num = FlowHalfLife::::get(); -// System::set_block_number(block_num); - -// // Large but safe flows for I64F64 -// SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(9.0e11))); -// SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(1.8e12))); - -// let subnets = vec![n1, n2]; -// let shares = SubtensorModule::get_shares(&subnets); - -// let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); -// assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-9); - -// for (k, v) in &shares { -// let f = v.to_num::(); -// assert!(f.is_finite(), "share for {k:?} not finite"); -// assert!( -// (0.0..=1.0).contains(&f), -// "share for {k:?} out of [0,1]: {f}" -// ); -// } - -// let s1 = shares.get(&n1).unwrap().to_num::(); -// let s2 = shares.get(&n2).unwrap().to_num::(); -// assert!( -// s2 > s1, -// "expected s2 > s1 with higher flow; got s1={s1}, s2={s2}" -// ); -// }); -// } +/// Normal (moderate, non-zero) EMA flows across 3 subnets. +/// Expect: shares sum to ~1 and are monotonic with flows. +#[test] +fn get_shares_normal_flows_three_subnets() { + new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(10); + let owner_coldkey = U256::from(20); + + let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + let n3 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + + let block_num = FlowHalfLife::::get(); + System::set_block_number(block_num); + + // Set (block_number, flow) with reasonable positive flows + SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(1_000.0))); + SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(3_000.0))); + SubnetEmaTaoFlow::::insert(n3, (block_num, i64f64(6_000.0))); + + let subnets = vec![n1, n2, n3]; + let shares = SubtensorModule::get_shares(&subnets); + + // Sum ≈ 1 + let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); + assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-9); + + // Each share in [0,1] and finite + for (k, v) in &shares { + let f = v.to_num::(); + assert!(f.is_finite(), "share for {k:?} not finite"); + assert!( + (0.0..=1.0).contains(&f), + "share for {k:?} out of [0,1]: {f}" + ); + } + + // Monotonicity with the flows: share(n3) > share(n2) > share(n1) + let s1 = shares.get(&n1).unwrap().to_num::(); + let s2 = shares.get(&n2).unwrap().to_num::(); + let s3 = shares.get(&n3).unwrap().to_num::(); + assert!( + s3 > s2 && s2 > s1, + "expected s3 > s2 > s1; got {s1}, {s2}, {s3}" + ); + }); +} + +/// Very low (but non-zero) EMA flows across 2 subnets. +/// Expect: shares sum to ~1 and higher-flow subnet gets higher share. +#[test] +fn get_shares_low_flows_sum_one_and_ordering() { + new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(11); + let owner_coldkey = U256::from(21); + + let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + + let block_num = FlowHalfLife::::get(); + System::set_block_number(block_num); + + // Tiny flows to exercise precision/scaling path + SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(1e-9))); + SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(2e-9))); + + let subnets = vec![n1, n2]; + let shares = SubtensorModule::get_shares(&subnets); + + let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); + assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-8); + + for (k, v) in &shares { + let f = v.to_num::(); + assert!(f.is_finite(), "share for {k:?} not finite"); + assert!( + (0.0..=1.0).contains(&f), + "share for {k:?} out of [0,1]: {f}" + ); + } + + let s1 = shares.get(&n1).unwrap().to_num::(); + let s2 = shares.get(&n2).unwrap().to_num::(); + assert!( + s2 > s1, + "expected s2 > s1 with higher flow; got s1={s1}, s2={s2}" + ); + }); +} + +/// High EMA flows across 2 subnets. +/// Expect: no overflow, shares sum to ~1, and ordering follows flows. +#[test] +fn get_shares_high_flows_sum_one_and_ordering() { + new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(12); + let owner_coldkey = U256::from(22); + + let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + + let block_num = FlowHalfLife::::get(); + System::set_block_number(block_num); + + // Large but safe flows for I64F64 + SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(9.0e11))); + SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(1.8e12))); + + let subnets = vec![n1, n2]; + let shares = SubtensorModule::get_shares(&subnets); + + let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); + assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-9); + + for (k, v) in &shares { + let f = v.to_num::(); + assert!(f.is_finite(), "share for {k:?} not finite"); + assert!( + (0.0..=1.0).contains(&f), + "share for {k:?} out of [0,1]: {f}" + ); + } + + let s1 = shares.get(&n1).unwrap().to_num::(); + let s2 = shares.get(&n2).unwrap().to_num::(); + assert!( + s2 > s1, + "expected s2 > s1 with higher flow; got s1={s1}, s2={s2}" + ); + }); +} /// Helper to (re)seed EMA price & flow at the *current* block. fn seed_price_and_flow(n1: NetUid, n2: NetUid, price1: f64, price2: f64, flow1: f64, flow2: f64) { @@ -292,199 +292,199 @@ fn seed_price_and_flow(n1: NetUid, n2: NetUid, price1: f64, price2: f64, flow1: SubnetEmaTaoFlow::::insert(n2, (now, i64f64(flow2))); } -// /// If one subnet has a negative EMA flow and the other positive, -// /// the negative one should contribute no weight (treated as zero), -// /// so the positive-flow subnet gets the full share. -// #[test] -// fn get_shares_negative_vs_positive_flow() { -// new_test_ext(1).execute_with(|| { -// // 2 subnets -// let owner_hotkey = U256::from(60); -// let owner_coldkey = U256::from(61); -// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); -// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - -// // Configure blending window and current block -// let half_life: u64 = FlowHalfLife::::get(); -// FlowNormExponent::::set(u64f64(1.0)); -// frame_system::Pallet::::set_block_number(half_life); -// TaoFlowCutoff::::set(I64F64::from_num(0)); - -// // Equal EMA prices so price side doesn't bias -// SubnetMovingPrice::::insert(n1, i96f32(1.0)); -// SubnetMovingPrice::::insert(n2, i96f32(1.0)); - -// // Set flows: n1 negative, n2 positive -// let now = frame_system::Pallet::::block_number(); -// SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); -// SubnetEmaTaoFlow::::insert(n2, (now, i64f64(500.0))); - -// let shares = SubtensorModule::get_shares(&[n1, n2]); -// let s1 = shares.get(&n1).unwrap().to_num::(); -// let s2 = shares.get(&n2).unwrap().to_num::(); - -// // Sum ~ 1 -// assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); -// // Negative flow subnet should not get weight from flow; with equal prices mid-window, -// // positive-flow subnet should dominate and get all the allocation. -// assert!( -// s2 > 0.999_999 && s1 < 1e-6, -// "expected s2≈1, s1≈0; got s1={s1}, s2={s2}" -// ); -// }); -// } - -// /// If both subnets have negative EMA flows, flows should contribute zero weight -// #[test] -// fn get_shares_both_negative_flows_zero_emission() { -// new_test_ext(1).execute_with(|| { -// // 2 subnets -// let owner_hotkey = U256::from(60); -// let owner_coldkey = U256::from(61); -// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); -// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - -// // Configure blending window and current block -// let half_life: u64 = FlowHalfLife::::get(); -// FlowNormExponent::::set(u64f64(1.0)); -// frame_system::Pallet::::set_block_number(half_life); -// TaoFlowCutoff::::set(I64F64::from_num(0)); - -// // Equal EMA prices so price side doesn't bias -// SubnetMovingPrice::::insert(n1, i96f32(1.0)); -// SubnetMovingPrice::::insert(n2, i96f32(1.0)); - -// // Set flows -// let now = frame_system::Pallet::::block_number(); -// SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); -// SubnetEmaTaoFlow::::insert(n2, (now, i64f64(-200.0))); - -// let shares = SubtensorModule::get_shares(&[n1, n2]); -// let s1 = shares.get(&n1).unwrap().to_num::(); -// let s2 = shares.get(&n2).unwrap().to_num::(); - -// assert!( -// s1 < 1e-20 && s2 < 1e-20, -// "expected s2≈0, s1≈0; got s1={s1}, s2={s2}" -// ); -// }); -// } - -// /// If both subnets have positive EMA flows lower than or equal to cutoff, flows should contribute zero weight -// #[test] -// fn get_shares_both_below_cutoff_zero_emission() { -// new_test_ext(1).execute_with(|| { -// // 2 subnets -// let owner_hotkey = U256::from(60); -// let owner_coldkey = U256::from(61); -// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); -// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - -// // Configure blending window and current block -// let half_life: u64 = FlowHalfLife::::get(); -// FlowNormExponent::::set(u64f64(1.0)); -// frame_system::Pallet::::set_block_number(half_life); -// TaoFlowCutoff::::set(I64F64::from_num(2_000)); - -// // Equal EMA prices so price side doesn't bias -// SubnetMovingPrice::::insert(n1, i96f32(1.0)); -// SubnetMovingPrice::::insert(n2, i96f32(1.0)); - -// // Set flows -// let now = frame_system::Pallet::::block_number(); -// SubnetEmaTaoFlow::::insert(n1, (now, i64f64(1000.0))); -// SubnetEmaTaoFlow::::insert(n2, (now, i64f64(2000.0))); - -// let shares = SubtensorModule::get_shares(&[n1, n2]); -// let s1 = shares.get(&n1).unwrap().to_num::(); -// let s2 = shares.get(&n2).unwrap().to_num::(); - -// assert!( -// s1 < 1e-20 && s2 < 1e-20, -// "expected s2≈0, s1≈0; got s1={s1}, s2={s2}" -// ); -// }); -// } - -// /// If one subnet has positive EMA flow lower than cutoff, the other gets full emission -// #[test] -// fn get_shares_one_below_cutoff_other_full_emission() { -// new_test_ext(1).execute_with(|| { -// [(1000.0, 2000.00001), (1000.0, 2000.001), (1000.0, 5000.0)] -// .into_iter() -// .for_each(|(flow1, flow2)| { -// // 2 subnets -// let owner_hotkey = U256::from(60); -// let owner_coldkey = U256::from(61); -// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); -// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - -// // Configure blending window and current block -// let half_life: u64 = FlowHalfLife::::get(); -// FlowNormExponent::::set(u64f64(1.0)); -// frame_system::Pallet::::set_block_number(half_life); -// TaoFlowCutoff::::set(I64F64::from_num(2_000)); - -// // Equal EMA prices (price side doesn't bias) -// SubnetMovingPrice::::insert(n1, i96f32(1.0)); -// SubnetMovingPrice::::insert(n2, i96f32(1.0)); - -// // Set flows -// let now = frame_system::Pallet::::block_number(); -// SubnetEmaTaoFlow::::insert(n1, (now, i64f64(flow1))); -// SubnetEmaTaoFlow::::insert(n2, (now, i64f64(flow2))); - -// let shares = SubtensorModule::get_shares(&[n1, n2]); -// let s1 = shares.get(&n1).unwrap().to_num::(); -// let s2 = shares.get(&n2).unwrap().to_num::(); - -// // Sum ~ 1 -// assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); -// assert!( -// s2 > 0.999_999 && s1 < 1e-6, -// "expected s2≈1, s1≈0; got s1={s1}, s2={s2}" -// ); -// }); -// }); -// } - -// /// If subnets have negative EMA flows, but they are above the cut-off, emissions are proportional -// /// for all except the bottom one, which gets nothing -// #[test] -// fn get_shares_both_negative_above_cutoff() { -// new_test_ext(1).execute_with(|| { -// // 2 subnets -// let owner_hotkey = U256::from(60); -// let owner_coldkey = U256::from(61); -// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); -// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); -// let n3 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - -// // Configure blending window and current block -// let half_life: u64 = FlowHalfLife::::get(); -// FlowNormExponent::::set(u64f64(1.0)); -// frame_system::Pallet::::set_block_number(half_life); -// TaoFlowCutoff::::set(I64F64::from_num(-1000.0)); - -// // Equal EMA prices so price side doesn't bias -// SubnetMovingPrice::::insert(n1, i96f32(1.0)); -// SubnetMovingPrice::::insert(n2, i96f32(1.0)); -// SubnetMovingPrice::::insert(n3, i96f32(1.0)); - -// // Set flows -// let now = frame_system::Pallet::::block_number(); -// SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); -// SubnetEmaTaoFlow::::insert(n2, (now, i64f64(-300.0))); -// SubnetEmaTaoFlow::::insert(n3, (now, i64f64(-400.0))); - -// let shares = SubtensorModule::get_shares(&[n1, n2, n3]); -// let s1 = shares.get(&n1).unwrap().to_num::(); -// let s2 = shares.get(&n2).unwrap().to_num::(); -// let s3 = shares.get(&n3).unwrap().to_num::(); - -// assert_abs_diff_eq!(s1, 0.75, epsilon = s1 / 100.0); -// assert_abs_diff_eq!(s2, 0.25, epsilon = s2 / 100.0); -// assert_abs_diff_eq!(s3, 0.0, epsilon = 1e-9); -// assert_abs_diff_eq!(s1 + s2 + s3, 1.0, epsilon = 1e-9); -// }); -// } +/// If one subnet has a negative EMA flow and the other positive, +/// the negative one should contribute no weight (treated as zero), +/// so the positive-flow subnet gets the full share. +#[test] +fn get_shares_negative_vs_positive_flow() { + new_test_ext(1).execute_with(|| { + // 2 subnets + let owner_hotkey = U256::from(60); + let owner_coldkey = U256::from(61); + let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + + // Configure blending window and current block + let half_life: u64 = FlowHalfLife::::get(); + FlowNormExponent::::set(u64f64(1.0)); + frame_system::Pallet::::set_block_number(half_life); + TaoFlowCutoff::::set(I64F64::from_num(0)); + + // Equal EMA prices so price side doesn't bias + SubnetMovingPrice::::insert(n1, i96f32(1.0)); + SubnetMovingPrice::::insert(n2, i96f32(1.0)); + + // Set flows: n1 negative, n2 positive + let now = frame_system::Pallet::::block_number(); + SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); + SubnetEmaTaoFlow::::insert(n2, (now, i64f64(500.0))); + + let shares = SubtensorModule::get_shares(&[n1, n2]); + let s1 = shares.get(&n1).unwrap().to_num::(); + let s2 = shares.get(&n2).unwrap().to_num::(); + + // Sum ~ 1 + assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); + // Negative flow subnet should not get weight from flow; with equal prices mid-window, + // positive-flow subnet should dominate and get all the allocation. + assert!( + s2 > 0.999_999 && s1 < 1e-6, + "expected s2≈1, s1≈0; got s1={s1}, s2={s2}" + ); + }); +} + +/// If both subnets have negative EMA flows, flows should contribute zero weight +#[test] +fn get_shares_both_negative_flows_zero_emission() { + new_test_ext(1).execute_with(|| { + // 2 subnets + let owner_hotkey = U256::from(60); + let owner_coldkey = U256::from(61); + let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + + // Configure blending window and current block + let half_life: u64 = FlowHalfLife::::get(); + FlowNormExponent::::set(u64f64(1.0)); + frame_system::Pallet::::set_block_number(half_life); + TaoFlowCutoff::::set(I64F64::from_num(0)); + + // Equal EMA prices so price side doesn't bias + SubnetMovingPrice::::insert(n1, i96f32(1.0)); + SubnetMovingPrice::::insert(n2, i96f32(1.0)); + + // Set flows + let now = frame_system::Pallet::::block_number(); + SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); + SubnetEmaTaoFlow::::insert(n2, (now, i64f64(-200.0))); + + let shares = SubtensorModule::get_shares(&[n1, n2]); + let s1 = shares.get(&n1).unwrap().to_num::(); + let s2 = shares.get(&n2).unwrap().to_num::(); + + assert!( + s1 < 1e-20 && s2 < 1e-20, + "expected s2≈0, s1≈0; got s1={s1}, s2={s2}" + ); + }); +} + +/// If both subnets have positive EMA flows lower than or equal to cutoff, flows should contribute zero weight +#[test] +fn get_shares_both_below_cutoff_zero_emission() { + new_test_ext(1).execute_with(|| { + // 2 subnets + let owner_hotkey = U256::from(60); + let owner_coldkey = U256::from(61); + let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + + // Configure blending window and current block + let half_life: u64 = FlowHalfLife::::get(); + FlowNormExponent::::set(u64f64(1.0)); + frame_system::Pallet::::set_block_number(half_life); + TaoFlowCutoff::::set(I64F64::from_num(2_000)); + + // Equal EMA prices so price side doesn't bias + SubnetMovingPrice::::insert(n1, i96f32(1.0)); + SubnetMovingPrice::::insert(n2, i96f32(1.0)); + + // Set flows + let now = frame_system::Pallet::::block_number(); + SubnetEmaTaoFlow::::insert(n1, (now, i64f64(1000.0))); + SubnetEmaTaoFlow::::insert(n2, (now, i64f64(2000.0))); + + let shares = SubtensorModule::get_shares(&[n1, n2]); + let s1 = shares.get(&n1).unwrap().to_num::(); + let s2 = shares.get(&n2).unwrap().to_num::(); + + assert!( + s1 < 1e-20 && s2 < 1e-20, + "expected s2≈0, s1≈0; got s1={s1}, s2={s2}" + ); + }); +} + +/// If one subnet has positive EMA flow lower than cutoff, the other gets full emission +#[test] +fn get_shares_one_below_cutoff_other_full_emission() { + new_test_ext(1).execute_with(|| { + [(1000.0, 2000.00001), (1000.0, 2000.001), (1000.0, 5000.0)] + .into_iter() + .for_each(|(flow1, flow2)| { + // 2 subnets + let owner_hotkey = U256::from(60); + let owner_coldkey = U256::from(61); + let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + + // Configure blending window and current block + let half_life: u64 = FlowHalfLife::::get(); + FlowNormExponent::::set(u64f64(1.0)); + frame_system::Pallet::::set_block_number(half_life); + TaoFlowCutoff::::set(I64F64::from_num(2_000)); + + // Equal EMA prices (price side doesn't bias) + SubnetMovingPrice::::insert(n1, i96f32(1.0)); + SubnetMovingPrice::::insert(n2, i96f32(1.0)); + + // Set flows + let now = frame_system::Pallet::::block_number(); + SubnetEmaTaoFlow::::insert(n1, (now, i64f64(flow1))); + SubnetEmaTaoFlow::::insert(n2, (now, i64f64(flow2))); + + let shares = SubtensorModule::get_shares(&[n1, n2]); + let s1 = shares.get(&n1).unwrap().to_num::(); + let s2 = shares.get(&n2).unwrap().to_num::(); + + // Sum ~ 1 + assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); + assert!( + s2 > 0.999_999 && s1 < 1e-6, + "expected s2≈1, s1≈0; got s1={s1}, s2={s2}" + ); + }); + }); +} + +/// If subnets have negative EMA flows, but they are above the cut-off, emissions are proportional +/// for all except the bottom one, which gets nothing +#[test] +fn get_shares_both_negative_above_cutoff() { + new_test_ext(1).execute_with(|| { + // 2 subnets + let owner_hotkey = U256::from(60); + let owner_coldkey = U256::from(61); + let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + let n3 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + + // Configure blending window and current block + let half_life: u64 = FlowHalfLife::::get(); + FlowNormExponent::::set(u64f64(1.0)); + frame_system::Pallet::::set_block_number(half_life); + TaoFlowCutoff::::set(I64F64::from_num(-1000.0)); + + // Equal EMA prices so price side doesn't bias + SubnetMovingPrice::::insert(n1, i96f32(1.0)); + SubnetMovingPrice::::insert(n2, i96f32(1.0)); + SubnetMovingPrice::::insert(n3, i96f32(1.0)); + + // Set flows + let now = frame_system::Pallet::::block_number(); + SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); + SubnetEmaTaoFlow::::insert(n2, (now, i64f64(-300.0))); + SubnetEmaTaoFlow::::insert(n3, (now, i64f64(-400.0))); + + let shares = SubtensorModule::get_shares(&[n1, n2, n3]); + let s1 = shares.get(&n1).unwrap().to_num::(); + let s2 = shares.get(&n2).unwrap().to_num::(); + let s3 = shares.get(&n3).unwrap().to_num::(); + + assert_abs_diff_eq!(s1, 0.75, epsilon = s1 / 100.0); + assert_abs_diff_eq!(s2, 0.25, epsilon = s2 / 100.0); + assert_abs_diff_eq!(s3, 0.0, epsilon = 1e-9); + assert_abs_diff_eq!(s1 + s2 + s3, 1.0, epsilon = 1e-9); + }); +} From 23b856e96d6beaf923ff52b989fa1d295cd7551e Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 5 Nov 2025 15:34:01 -0500 Subject: [PATCH 2/2] Spec bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 266a75570..d95eb45ee 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 338, + spec_version: 339, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,