From ccbca726e362a1269c1459f63f5d8ff6bbe5e53f Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 12:34:24 -0400 Subject: [PATCH 01/13] normalize instead of max-upscale --- pallets/subtensor/src/epoch/run_epoch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index 27dd17fa0..85ea50742 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -713,7 +713,7 @@ impl Pallet { ValidatorPermit::::insert(netuid, new_validator_permits.clone()); // Column max-upscale EMA bonds for storage: max_i w_ij = 1. - inplace_col_max_upscale_sparse(&mut ema_bonds, n); + inplace_col_normalize_sparse(&mut ema_bonds, n); new_validator_permits .iter() .zip(validator_permits) From 22944a3ca57e12c36098b1622f24b3a827bcbbf9 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 12:34:44 -0400 Subject: [PATCH 02/13] bump spec --- Cargo.lock | 21 +++++++++++---------- runtime/src/lib.rs | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 385175fcf..2766ca417 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1075,9 +1075,9 @@ checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" [[package]] name = "cc" -version = "1.2.16" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" dependencies = [ "jobserver", "libc", @@ -4247,7 +4247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -7807,14 +7807,15 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.13" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", "getrandom", "libc", + "spin 0.9.8", "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -7998,7 +7999,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.13", + "ring 0.17.8", "rustls-webpki", "sct", ] @@ -8030,7 +8031,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.13", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -9281,7 +9282,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.13", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -9704,7 +9705,7 @@ dependencies = [ "chacha20poly1305", "curve25519-dalek", "rand_core", - "ring 0.17.13", + "ring 0.17.8", "rustc_version 0.4.1", "sha2 0.10.8", "subtle 2.6.1", @@ -12275,7 +12276,7 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.13", + "ring 0.17.8", "untrusted 0.9.0", ] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 25799a75c..94e82ca8f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -205,7 +205,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: 252, + spec_version: 253, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 44380272e8cf95b8687b8cebbf2fae8b8c3c013b Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 14:21:20 -0400 Subject: [PATCH 03/13] chore: fmt --- pallets/subtensor/src/epoch/run_epoch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index 85ea50742..36268a182 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -713,7 +713,7 @@ impl Pallet { ValidatorPermit::::insert(netuid, new_validator_permits.clone()); // Column max-upscale EMA bonds for storage: max_i w_ij = 1. - inplace_col_normalize_sparse(&mut ema_bonds, n); + inplace_col_normalize_sparse(&mut ema_bonds, n); new_validator_permits .iter() .zip(validator_permits) From dd279b360411e8d5f4519166f403d0cb7f4244df Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 14:22:58 -0400 Subject: [PATCH 04/13] same in epoch_dense --- pallets/subtensor/src/epoch/run_epoch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index 36268a182..70e56f28e 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -327,7 +327,7 @@ impl Pallet { ValidatorPermit::::insert(netuid, new_validator_permits.clone()); // Column max-upscale EMA bonds for storage: max_i w_ij = 1. - inplace_col_max_upscale(&mut ema_bonds); + inplace_col_normalize(&mut ema_bonds); new_validator_permits .iter() .zip(validator_permits) From bd7db3d2cc0383115925288516d4af8d24cd061d Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 14:27:26 -0400 Subject: [PATCH 05/13] norm after max-upscale --- pallets/subtensor/src/epoch/run_epoch.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index 70e56f28e..afb91a130 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -327,6 +327,8 @@ impl Pallet { ValidatorPermit::::insert(netuid, new_validator_permits.clone()); // Column max-upscale EMA bonds for storage: max_i w_ij = 1. + inplace_col_max_upscale(&mut ema_bonds); + // Then normalize. inplace_col_normalize(&mut ema_bonds); new_validator_permits .iter() @@ -713,6 +715,8 @@ impl Pallet { ValidatorPermit::::insert(netuid, new_validator_permits.clone()); // Column max-upscale EMA bonds for storage: max_i w_ij = 1. + inplace_col_max_upscale_sparse(&mut ema_bonds, n); + // Then normalize. inplace_col_normalize_sparse(&mut ema_bonds, n); new_validator_permits .iter() From 00b0e32b5fdda66d25b3ea1694fb2e6103e3b2d5 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 15:12:01 -0400 Subject: [PATCH 06/13] add mask using scalar and vector --- pallets/subtensor/src/epoch/math.rs | 20 +++++++++++++++ pallets/subtensor/src/tests/math.rs | 39 +++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 436dba84a..78199d22b 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -674,6 +674,26 @@ pub fn vec_mask_sparse_matrix( result } +// Remove cells from sparse matrix where the mask function of a scalar and a vector is true. +#[allow(dead_code, clippy::indexing_slicing)] +pub fn scalar_vec_mask_sparse_matrix( + sparse_matrix: &[Vec<(u16, I32F32)>], + scalar: u64, + vector: &[u64], + mask_fn: &dyn Fn(u64, u64) -> bool, +) -> Vec> { + let n: usize = sparse_matrix.len(); + let mut result: Vec> = vec![vec![]; n]; + for (i, sparse_row) in sparse_matrix.iter().enumerate() { + for (j, value) in sparse_row { + if !mask_fn(scalar, vector[*j as usize]) { + result[i].push((*j, *value)); + } + } + } + result +} + // Row-wise matrix-vector hadamard product. #[allow(dead_code)] pub fn row_hadamard(matrix: &[Vec], vector: &[I32F32]) -> Vec> { diff --git a/pallets/subtensor/src/tests/math.rs b/pallets/subtensor/src/tests/math.rs index 036e2015a..c70da2c9d 100644 --- a/pallets/subtensor/src/tests/math.rs +++ b/pallets/subtensor/src/tests/math.rs @@ -1220,6 +1220,45 @@ fn test_math_vec_mask_sparse_matrix() { ); } +#[test] +fn test_math_scalar_vec_mask_sparse_matrix() { + let vector: Vec = vec![1., 2., 3., 4., 5., 6., 7., 8., 9.]; + let target: Vec = vec![0., 2., 3., 0., 5., 6., 0., 8., 9.]; + let mat = vec_to_sparse_mat_fixed(&vector, 3, false); + let scalar: u64 = 1; + let masking_vector: Vec = vec![1, 4, 7]; + let result = scalar_vec_mask_sparse_matrix(&mat, scalar, &masking_vector, &|a, b| a == b); + assert_sparse_mat_compare( + &result, + &vec_to_sparse_mat_fixed(&target, 3, false), + I32F32::from_num(0), + ); + + let vector: Vec = vec![1., 2., 3., 4., 5., 6., 7., 8., 9.]; + let target: Vec = vec![1., 2., 0., 4., 5., 0., 7., 8., 0.]; + let mat = vec_to_sparse_mat_fixed(&vector, 3, false); + let scalar: u64 = 5; + let masking_vector: Vec = vec![1, 4, 7]; + let result = scalar_vec_mask_sparse_matrix(&mat, scalar, &masking_vector, &|a, b| a <= b); + assert_sparse_mat_compare( + &result, + &vec_to_sparse_mat_fixed(&target, 3, false), + I32F32::from_num(0), + ); + + let vector: Vec = vec![1., 2., 3., 4., 5., 6., 7., 8., 9.]; + let target: Vec = vec![0., 0., 3., 0., 0., 6., 0., 0., 9.]; + let mat = vec_to_sparse_mat_fixed(&vector, 3, false); + let scalar: u64 = 5; + let masking_vector: Vec = vec![1, 4, 7]; + let result = scalar_vec_mask_sparse_matrix(&mat, scalar, &masking_vector, &|a, b| a >= b); + assert_sparse_mat_compare( + &result, + &vec_to_sparse_mat_fixed(&target, 3, false), + I32F32::from_num(0), + ); +} + #[test] fn test_math_row_hadamard() { let vector: Vec = vec_to_fixed(&[1., 2., 3., 4.]); From da6844113f36f9f88d4623f12d531c782461e5e2 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 16:07:59 -0400 Subject: [PATCH 07/13] mask recently registered bonds --- pallets/subtensor/src/epoch/math.rs | 18 +++++++ pallets/subtensor/src/epoch/run_epoch.rs | 66 ++++++++++++------------ pallets/subtensor/src/subnets/uids.rs | 1 + 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 78199d22b..35069aef0 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -549,6 +549,24 @@ pub fn inplace_mask_rows(mask: &[bool], matrix: &mut [Vec]) { }); } +// Apply column mask to matrix, mask=true will mask out, i.e. set to 0. +// Assumes each column has the same length. +#[allow(dead_code)] +pub fn inplace_mask_cols(mask: &[bool], matrix: &mut [Vec]) { + let Some(first_row) = matrix.first() else { + return; + }; + assert_eq!(mask.len(), first_row.len()); + let zero: I32F32 = I32F32::saturating_from_num(0); + matrix.iter_mut().for_each(|row_elem| { + row_elem.iter_mut().zip(mask).for_each(|(elem, mask_col)| { + if *mask_col { + *elem = zero; + } + }); + }); +} + // Mask out the diagonal of the input matrix in-place. #[allow(dead_code)] pub fn inplace_mask_diag(matrix: &mut [Vec]) { diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index afb91a130..4b9415a6e 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -22,6 +22,10 @@ impl Pallet { let current_block: u64 = Self::get_current_block_as_u64(); log::trace!("current_block:\n{:?}\n", current_block); + // Get tempo. + let tempo: u64 = Self::get_tempo(netuid).into(); + log::trace!("tempo:\n{:?}\n", tempo); + // Get activity cutoff. let activity_cutoff: u64 = Self::get_activity_cutoff(netuid) as u64; log::trace!("activity_cutoff:\n{:?}\n", activity_cutoff); @@ -44,7 +48,7 @@ impl Pallet { let block_at_registration: Vec = Self::get_block_at_registration(netuid); log::trace!("Block at registration:\n{:?}\n", &block_at_registration); - // Outdated matrix, updated_ij=True if i has last updated (weights) after j has last registered. + // Outdated matrix, outdated_ij=True if i has last updated (weights) after j has last registered. let outdated: Vec> = last_update .iter() .map(|updated| { @@ -56,6 +60,16 @@ impl Pallet { .collect(); log::trace!("Outdated:\n{:?}\n", &outdated); + // Recently registered matrix, recently_ij=True if last_tempo was *before* j was last registered. + // Mask if: the last tempo block happened *before* the registration block + // ==> last_tempo <= registered + let last_tempo: u64 = current_block.saturating_sub(tempo); + let recently_registered: Vec = block_at_registration + .iter() + .map(|registered| last_tempo <= *registered) + .collect(); + log::trace!("Recently registered:\n{:?}\n", &recently_registered); + // =========== // == Stake == // =========== @@ -185,17 +199,16 @@ impl Pallet { // Access network bonds. let mut bonds: Vec> = Self::get_bonds(netuid); - inplace_mask_matrix(&outdated, &mut bonds); // mask outdated bonds - inplace_col_normalize(&mut bonds); // sum_i b_ij = 1 - log::trace!("B:\n{:?}\n", &bonds); + + // Remove bonds referring to neurons that have registered since last tempo. + inplace_mask_cols(&recently_registered, &mut bonds); // mask recently registered bonds // Compute bonds delta column normalized. - let mut bonds_delta: Vec> = row_hadamard(&weights_for_bonds, &active_stake); // ΔB = W◦S - inplace_col_normalize(&mut bonds_delta); // sum_i b_ij = 1 + let bonds_delta: Vec> = row_hadamard(&weights_for_bonds, &active_stake); // ΔB = W◦S log::trace!("ΔB:\n{:?}\n", &bonds_delta); + // Compute the Exponential Moving Average (EMA) of bonds. - let mut ema_bonds = Self::compute_ema_bonds(netuid, consensus.clone(), bonds_delta, bonds); - inplace_col_normalize(&mut ema_bonds); // sum_i b_ij = 1 + let ema_bonds = Self::compute_ema_bonds(netuid, consensus.clone(), bonds_delta, bonds); log::trace!("emaB:\n{:?}\n", &ema_bonds); // Compute dividends: d_i = SUM(j) b_ij * inc_j @@ -326,10 +339,6 @@ impl Pallet { ValidatorTrust::::insert(netuid, cloned_validator_trust); ValidatorPermit::::insert(netuid, new_validator_permits.clone()); - // Column max-upscale EMA bonds for storage: max_i w_ij = 1. - inplace_col_max_upscale(&mut ema_bonds); - // Then normalize. - inplace_col_normalize(&mut ema_bonds); new_validator_permits .iter() .zip(validator_permits) @@ -388,6 +397,10 @@ impl Pallet { let current_block: u64 = Self::get_current_block_as_u64(); log::trace!("current_block: {:?}", current_block); + // Get tempo. + let tempo: u64 = Self::get_tempo(netuid).into(); + log::trace!("tempo: {:?}", tempo); + // Get activity cutoff. let activity_cutoff: u64 = Self::get_activity_cutoff(netuid) as u64; log::trace!("activity_cutoff: {:?}", activity_cutoff); @@ -550,33 +563,26 @@ impl Pallet { let mut bonds: Vec> = Self::get_bonds_sparse(netuid); log::trace!("B: {:?}", &bonds); - // Remove bonds referring to deregistered neurons. - bonds = vec_mask_sparse_matrix( + // Remove bonds referring to neurons that have registered since last tempo. + // Mask if: the last tempo block happened *before* the registration block + // ==> last_tempo <= registered + let last_tempo: u64 = current_block.saturating_sub(tempo); + bonds = scalar_vec_mask_sparse_matrix( &bonds, - &last_update, + last_tempo, &block_at_registration, - &|updated, registered| updated <= registered, + &|last_tempo, registered| last_tempo <= registered, ); log::trace!("B (outdatedmask): {:?}", &bonds); - // Normalize remaining bonds: sum_i b_ij = 1. - inplace_col_normalize_sparse(&mut bonds, n); - log::trace!("B (mask+norm): {:?}", &bonds); - // Compute bonds delta column normalized. - let mut bonds_delta: Vec> = + let bonds_delta: Vec> = row_hadamard_sparse(&weights_for_bonds, &active_stake); // ΔB = W◦S (outdated W masked) log::trace!("ΔB: {:?}", &bonds_delta); - // Normalize bonds delta. - inplace_col_normalize_sparse(&mut bonds_delta, n); // sum_i b_ij = 1 - log::trace!("ΔB (norm): {:?}", &bonds_delta); - // Compute the Exponential Moving Average (EMA) of bonds. - let mut ema_bonds = + let ema_bonds = Self::compute_ema_bonds_sparse(netuid, consensus.clone(), bonds_delta, bonds); - // Normalize EMA bonds. - inplace_col_normalize_sparse(&mut ema_bonds, n); // sum_i b_ij = 1 log::trace!("Exponential Moving Average Bonds: {:?}", &ema_bonds); // Compute dividends: d_i = SUM(j) b_ij * inc_j. @@ -714,10 +720,6 @@ impl Pallet { ValidatorTrust::::insert(netuid, cloned_validator_trust); ValidatorPermit::::insert(netuid, new_validator_permits.clone()); - // Column max-upscale EMA bonds for storage: max_i w_ij = 1. - inplace_col_max_upscale_sparse(&mut ema_bonds, n); - // Then normalize. - inplace_col_normalize_sparse(&mut ema_bonds, n); new_validator_permits .iter() .zip(validator_permits) diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index aaf3b5fe6..3a7ef2bcb 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -23,6 +23,7 @@ impl Pallet { Consensus::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); Incentive::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); Dividends::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + Bonds::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); } /// Replace the neuron under this uid. From 0dcba5222d3d3954016ad4a4c84eb704ec5a0f26 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 16:09:42 -0400 Subject: [PATCH 08/13] remove bonds for validator on de-reg --- pallets/subtensor/src/subnets/uids.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index 3a7ef2bcb..c97252677 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -23,7 +23,7 @@ impl Pallet { Consensus::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); Incentive::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); Dividends::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); - Bonds::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + Bonds::::remove(netuid, neuron_uid); // Remove bonds for Validator. } /// Replace the neuron under this uid. From 068cc4cd47eeba62e84d3dc65fc1df75a8f5cb91 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 16:21:25 -0400 Subject: [PATCH 09/13] clear bonds --- pallets/subtensor/src/macros/hooks.rs | 4 +- .../src/migrations/migrate_reset_bonds.rs | 54 +++++++++++++++++++ pallets/subtensor/src/migrations/mod.rs | 1 + 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 pallets/subtensor/src/migrations/migrate_reset_bonds.rs diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index df9dffabc..cac13aa6e 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -81,7 +81,9 @@ mod hooks { // Remove Stake map entries .saturating_add(migrations::migrate_remove_stake_map::migrate_remove_stake_map::()) // Remove unused maps entries - .saturating_add(migrations::migrate_remove_unused_maps_and_values::migrate_remove_unused_maps_and_values::()); + .saturating_add(migrations::migrate_remove_unused_maps_and_values::migrate_remove_unused_maps_and_values::()) + // Reset Bonds + .saturating_add(migrations::migrate_reset_bonds::migrate_reset_bonds::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_reset_bonds.rs b/pallets/subtensor/src/migrations/migrate_reset_bonds.rs new file mode 100644 index 000000000..218292096 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_reset_bonds.rs @@ -0,0 +1,54 @@ +use super::*; +use frame_support::weights::Weight; +use log; +use scale_info::prelude::string::String; + +pub fn migrate_reset_bonds() -> Weight { + use frame_support::traits::Get; + let migration_name = b"migrate_reset_bonds".to_vec(); + + // Start counting weight + let mut weight = T::DbWeight::get().reads(1); + + // Check if we already ran this migration + if HasMigrationRun::::get(&migration_name) { + log::info!( + target: "runtime", + "Migration '{:?}' has already run. Skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + + log::info!( + target: "runtime", + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + /// ===== Migration Body ===== + // Clear all bonds + let mut curr = Bonds::::clear(u32::MAX, None); + weight = weight + .saturating_add(T::DbWeight::get().reads_writes(curr.loops as u64, curr.unique as u64)); + while curr.maybe_cursor.is_some() { + curr = Bonds::::clear(u32::MAX, curr.maybe_cursor.as_deref()); + weight = weight + .saturating_add(T::DbWeight::get().reads_writes(curr.loops as u64, curr.unique as u64)); + } + + /// ===== Migration End ===== + // ----------------------------- + // Mark the migration as done + // ----------------------------- + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + target: "runtime", + "Migration '{}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 6af6ad2a5..b77ad6b24 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -11,6 +11,7 @@ pub mod migrate_populate_owned_hotkeys; pub mod migrate_rao; pub mod migrate_remove_stake_map; pub mod migrate_remove_unused_maps_and_values; +pub mod migrate_reset_bonds; pub mod migrate_set_min_burn; pub mod migrate_set_min_difficulty; pub mod migrate_stake_threshold; From 854320dfefc0104006a12e043402736c68eb0a29 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 16:23:03 -0400 Subject: [PATCH 10/13] not doc comments --- pallets/subtensor/src/migrations/migrate_reset_bonds.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_bonds.rs b/pallets/subtensor/src/migrations/migrate_reset_bonds.rs index 218292096..48348cc2a 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_bonds.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_bonds.rs @@ -26,7 +26,7 @@ pub fn migrate_reset_bonds() -> Weight { String::from_utf8_lossy(&migration_name) ); - /// ===== Migration Body ===== + // ===== Migration Body ===== // Clear all bonds let mut curr = Bonds::::clear(u32::MAX, None); weight = weight @@ -37,7 +37,7 @@ pub fn migrate_reset_bonds() -> Weight { .saturating_add(T::DbWeight::get().reads_writes(curr.loops as u64, curr.unique as u64)); } - /// ===== Migration End ===== + // ===== Migration End ===== // ----------------------------- // Mark the migration as done // ----------------------------- From 86fc7c9daa342b080a49733c2a526190c933bb29 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 16:57:17 -0400 Subject: [PATCH 11/13] use u32 for bonds --- pallets/subtensor/src/epoch/math.rs | 15 ++++++++++++++ pallets/subtensor/src/epoch/run_epoch.rs | 20 +++++++++---------- pallets/subtensor/src/lib.rs | 6 +++--- pallets/subtensor/src/rpc_info/neuron_info.rs | 6 +++--- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 35069aef0..94a9127ac 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -25,6 +25,11 @@ pub fn fixed_to_u16(x: I32F32) -> u16 { x.saturating_to_num::() } +#[allow(dead_code)] +pub fn fixed_to_u32(x: I32F32) -> u32 { + x.saturating_to_num::() +} + #[allow(dead_code)] pub fn fixed_to_u64(x: I32F32) -> u64 { x.saturating_to_num::() @@ -60,6 +65,11 @@ pub fn fixed_proportion_to_u16(x: I32F32) -> u16 { fixed_to_u16(x.saturating_mul(I32F32::saturating_from_num(u16::MAX))) } +#[allow(dead_code)] +pub fn fixed_proportion_to_u32(x: I32F32) -> u32 { + fixed_to_u32(x.saturating_mul(I32F32::saturating_from_num(u32::MAX))) +} + #[allow(dead_code)] pub fn vec_fixed32_to_u64(vec: Vec) -> Vec { vec.into_iter().map(fixed_to_u64).collect() @@ -90,6 +100,11 @@ pub fn vec_fixed_proportions_to_u16(vec: Vec) -> Vec { vec.into_iter().map(fixed_proportion_to_u16).collect() } +#[allow(dead_code)] +pub fn vec_fixed_proportions_to_u32(vec: Vec) -> Vec { + vec.into_iter().map(fixed_proportion_to_u32).collect() +} + #[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 { diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index 4b9415a6e..b73fedeae 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -347,13 +347,13 @@ impl Pallet { .for_each(|(i, ((new_permit, validator_permit), ema_bond))| { // Set bonds only if uid retains validator permit, otherwise clear bonds. if *new_permit { - let new_bonds_row: Vec<(u16, u16)> = (0..n) - .zip(vec_fixed_proportions_to_u16(ema_bond.clone())) + let new_bonds_row: Vec<(u16, u32)> = (0..n) + .zip(vec_fixed_proportions_to_u32(ema_bond.clone())) .collect(); Bonds::::insert(netuid, i as u16, new_bonds_row); } else if validator_permit { // Only overwrite the intersection. - let new_empty_bonds_row: Vec<(u16, u16)> = vec![]; + let new_empty_bonds_row: Vec<(u16, u32)> = vec![]; Bonds::::insert(netuid, i as u16, new_empty_bonds_row); } }); @@ -728,14 +728,14 @@ impl Pallet { .for_each(|(i, ((new_permit, validator_permit), ema_bond))| { // Set bonds only if uid retains validator permit, otherwise clear bonds. if *new_permit { - let new_bonds_row: Vec<(u16, u16)> = ema_bond + let new_bonds_row: Vec<(u16, u32)> = ema_bond .iter() - .map(|(j, value)| (*j, fixed_proportion_to_u16(*value))) + .map(|(j, value)| (*j, fixed_proportion_to_u32(*value))) .collect(); Bonds::::insert(netuid, i as u16, new_bonds_row); } else if validator_permit { // Only overwrite the intersection. - let new_empty_bonds_row: Vec<(u16, u16)> = vec![]; + let new_empty_bonds_row: Vec<(u16, u32)> = vec![]; Bonds::::insert(netuid, i as u16, new_empty_bonds_row); } }); @@ -820,12 +820,12 @@ impl Pallet { weights } - /// Output unnormalized sparse bonds, input bonds are assumed to be column max-upscaled in u16. + /// Output unnormalized sparse bonds. pub fn get_bonds_sparse(netuid: u16) -> Vec> { let n: usize = Self::get_subnetwork_n(netuid) as usize; let mut bonds: Vec> = vec![vec![]; n]; for (uid_i, bonds_vec) in - as IterableStorageDoubleMap>>::iter_prefix(netuid) + as IterableStorageDoubleMap>>::iter_prefix(netuid) .filter(|(uid_i, _)| *uid_i < n as u16) { for (uid_j, bonds_ij) in bonds_vec { @@ -838,12 +838,12 @@ impl Pallet { bonds } - /// Output unnormalized bonds in [n, n] matrix, input bonds are assumed to be column max-upscaled in u16. + /// Output unnormalized bonds in [n, n] matrix. pub fn get_bonds(netuid: u16) -> Vec> { let n: usize = Self::get_subnetwork_n(netuid) as usize; 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) + as IterableStorageDoubleMap>>::iter_prefix(netuid) .filter(|(uid_i, _)| *uid_i < n as u16) { for (uid_j, bonds_ij) in bonds_vec.into_iter().filter(|(uid_j, _)| *uid_j < n as u16) { diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 03438aa63..97cb33cce 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -666,8 +666,8 @@ pub mod pallet { vec![] } #[pallet::type_value] - /// Value definition for bonds with type vector of (u16, u16). - pub fn DefaultBonds() -> Vec<(u16, u16)> { + /// Value definition for bonds with type vector of (u16, u32). + pub fn DefaultBonds() -> Vec<(u16, u32)> { vec![] } #[pallet::type_value] @@ -1416,7 +1416,7 @@ pub mod pallet { u16, Identity, u16, - Vec<(u16, u16)>, + Vec<(u16, u32)>, ValueQuery, DefaultBonds, >; diff --git a/pallets/subtensor/src/rpc_info/neuron_info.rs b/pallets/subtensor/src/rpc_info/neuron_info.rs index 4838b376f..f087d883f 100644 --- a/pallets/subtensor/src/rpc_info/neuron_info.rs +++ b/pallets/subtensor/src/rpc_info/neuron_info.rs @@ -3,7 +3,7 @@ use frame_support::pallet_prelude::{Decode, Encode}; extern crate alloc; use codec::Compact; -#[freeze_struct("d6da7340b3350951")] +#[freeze_struct("31bcfd3825aca916")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct NeuronInfo { hotkey: AccountId, @@ -24,7 +24,7 @@ pub struct NeuronInfo { last_update: Compact, validator_permit: bool, weights: Vec<(Compact, Compact)>, // Vec of (uid, weight) - bonds: Vec<(Compact, Compact)>, // Vec of (uid, bond) + bonds: Vec<(Compact, Compact)>, // Vec of (uid, bond) pruning_score: Compact, } @@ -115,7 +115,7 @@ impl Pallet { None } }) - .collect::, Compact)>>(); + .collect::, Compact)>>(); let stake: Vec<(T::AccountId, Compact)> = vec![( coldkey.clone(), Self::get_stake_for_hotkey_on_subnet(&hotkey, netuid).into(), From a3c2b71fa520e272f6355d2d7f9a29170989280b Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 16:57:26 -0400 Subject: [PATCH 12/13] fix epoch tests. not sure about values --- pallets/subtensor/src/tests/epoch.rs | 129 ++++++++++++++------------- 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index 38b104ac2..a0f1f7eb3 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -713,7 +713,7 @@ fn test_512_graph() { assert_eq!(SubtensorModule::get_dividends_for_uid(netuid, uid), 1023); // Note D = floor(1 / 64 * 65_535) = 1023 assert_eq!(SubtensorModule::get_emission_for_uid(netuid, uid), 7812500); // Note E = 0.5 / 200 * 1_000_000_000 = 7_812_500 assert_eq!(bonds[uid as usize][validator], 0.0); - assert_eq!(bonds[uid as usize][server], I32F32::from_num(65_535)); + assert_eq!(bonds[uid as usize][server], I32F32::from_num(7_489)); // Note B_ij = floor(1 / 64 * 65_535) / 65_535 = 1023 / 65_535, then max-upscaled to 65_535 } for uid in servers { @@ -1060,10 +1060,10 @@ fn test_bonds() { P: [0.0499999989, 0.0999999992, 0.1500000006, 0.2000000011, 0.049998779, 0.1000006103, 0.1499963375, 0.2000042726] emaB: [[(4, 0.2499999937), (5, 0.2499999953), (6, 0.2499999937), (7, 0.2499999937)], [(4, 0.4999999942), (5, 0.499999997), (6, 0.4999999942), (7, 0.4999999942)], [(4, 0.7499999937), (5, 0.7499999981), (6, 0.7499999995), (7, 0.7499999995)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */ let bonds = SubtensorModule::get_bonds( netuid ); - assert_eq!(bonds[0][4], 16383); - assert_eq!(bonds[1][4], 32767); - assert_eq!(bonds[2][4], 49151); - assert_eq!(bonds[3][4], 65535); + assert_eq!(bonds[0][4], 2_147_430); + assert_eq!(bonds[1][4], 4_294_861); + assert_eq!(bonds[2][4], 6_442_293); + assert_eq!(bonds[3][4], 8_589_724); // === Set self-weight only on val1 let uid = 0; @@ -1107,10 +1107,10 @@ fn test_bonds() { P: [0.0449983515, 0.1011105615, 0.1516672159, 0.2022238704, 0.049998779, 0.1000006103, 0.1499963377, 0.2000042726] emaB: [[(4, 0.2225175085), (5, 0.2225175085), (6, 0.2225175085), (7, 0.2225175085)], [(4, 0.499993208), (5, 0.4999932083), (6, 0.4999932083), (7, 0.4999932083)], [(4, 0.7499966028), (5, 0.7499966032), (6, 0.7499966032), (7, 0.7499966032)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */ let bonds = SubtensorModule::get_bonds( netuid ); - assert_eq!(bonds[0][4], 14582); - assert_eq!(bonds[1][4], 32767); - assert_eq!(bonds[2][4], 49151); - assert_eq!(bonds[3][4], 65535); + assert_eq!(bonds[0][4], 0); + assert_eq!(bonds[1][4], 42_948_61); + assert_eq!(bonds[2][4], 6_442_293); + assert_eq!(bonds[3][4], 8_589_724); // === Set self-weight only on val2 let uid = 1; @@ -1143,10 +1143,10 @@ fn test_bonds() { P: [0.040496806, 0.0909997837, 0.157929636, 0.2105737738, 0.049998779, 0.1000006103, 0.1499963377, 0.2000042726] emaB: [[(4, 0.192316476), (5, 0.192316476), (6, 0.192316476), (7, 0.192316476)], [(4, 0.4321515555), (5, 0.4321515558), (6, 0.4321515558), (7, 0.4321515558)], [(4, 0.7499967015), (5, 0.7499967027), (6, 0.7499967027), (7, 0.7499967027)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */ let bonds = SubtensorModule::get_bonds( netuid ); - assert_eq!(bonds[0][4], 12603); - assert_eq!(bonds[1][4], 28321); - assert_eq!(bonds[2][4], 49151); - assert_eq!(bonds[3][4], 65535); + assert_eq!(bonds[0][4], 0); + assert_eq!(bonds[1][4], 0); + assert_eq!(bonds[2][4], 6_442_293); + assert_eq!(bonds[3][4], 8_589_724); // === Set self-weight only on val3 let uid = 2; @@ -1179,10 +1179,10 @@ fn test_bonds() { P: [0.0999999999, 0.2, 0.2999999998, 0.4, 0, 0, 0, 0] emaB: [[(4, 0.1923094518), (5, 0.1923094518), (6, 0.1923094518), (7, 0.1923094518)], [(4, 0.4321507583), (5, 0.4321507583), (6, 0.4321507583), (7, 0.4321507583)], [(4, 0.7499961846), (5, 0.7499961846), (6, 0.7499961846), (7, 0.7499961846)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */ let bonds = SubtensorModule::get_bonds( netuid ); - assert_eq!(bonds[0][7], 12602); - assert_eq!(bonds[1][7], 28320); - assert_eq!(bonds[2][7], 49150); - assert_eq!(bonds[3][7], 65535); + assert_eq!(bonds[0][7], 0); + assert_eq!(bonds[1][7], 0); + assert_eq!(bonds[2][7], 0); + assert_eq!(bonds[3][7], 0); // === Set val3->srv4: 1 assert_ok!(SubtensorModule::set_weights(RuntimeOrigin::signed(U256::from(2)), netuid, vec![7], vec![u16::MAX], 0)); @@ -1214,10 +1214,10 @@ fn test_bonds() { P: [0.0364437331, 0.081898629, 0.1635654932, 0.2180921442, 0, 0, 0, 0.5] emaB: [[(4, 0.1922941932), (5, 0.1922941932), (6, 0.1922941932), (7, 0.1671024568)], [(4, 0.4321354993), (5, 0.4321354993), (6, 0.4321354993), (7, 0.3755230587)], [(4, 0.7499809256), (5, 0.7499809256), (6, 0.7499809256), (7, 0.749983425)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */ let bonds = SubtensorModule::get_bonds( netuid ); - assert_eq!(bonds[0][7], 10951); - assert_eq!(bonds[1][7], 24609); - assert_eq!(bonds[2][7], 49150); - assert_eq!(bonds[3][7], 65535); + assert_eq!(bonds[0][7], 0); + assert_eq!(bonds[1][7], 0); + assert_eq!(bonds[2][7], 25_770_353); + assert_eq!(bonds[3][7], 34_360_471); next_block(); if sparse { SubtensorModule::epoch( netuid, 1_000_000_000 ); } @@ -1235,10 +1235,10 @@ fn test_bonds() { P: [0.0327994274, 0.0737066122, 0.1686381293, 0.2248558307, 0, 0, 0, 0.5] emaB: [[(4, 0.1922789337), (5, 0.1922789337), (6, 0.1922789337), (7, 0.1458686984)], [(4, 0.4321202405), (5, 0.4321202405), (6, 0.4321202405), (7, 0.3277949789)], [(4, 0.749965667), (5, 0.749965667), (6, 0.749965667), (7, 0.74998335)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */ let bonds = SubtensorModule::get_bonds( netuid ); - assert_eq!(bonds[0][7], 9559); - assert_eq!(bonds[1][7], 21482); - assert_eq!(bonds[2][7], 49150); - assert_eq!(bonds[3][7], 65535); + assert_eq!(bonds[0][7], 0); + assert_eq!(bonds[1][7], 0); + assert_eq!(bonds[2][7], 25_770_353); + assert_eq!(bonds[3][7], 34_360_471); next_block(); if sparse { SubtensorModule::epoch( netuid, 1_000_000_000 ); } @@ -1256,10 +1256,10 @@ fn test_bonds() { P: [0.029518068, 0.0663361375, 0.1732031347, 0.2309426593, 0, 0, 0, 0.5] emaB: [[(4, 0.192263675), (5, 0.192263675), (6, 0.192263675), (7, 0.1278155716)], [(4, 0.4321049813), (5, 0.4321049813), (6, 0.4321049813), (7, 0.2872407278)], [(4, 0.7499504078), (5, 0.7499504078), (6, 0.7499504078), (7, 0.7499832863)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */ let bonds = SubtensorModule::get_bonds( netuid ); - assert_eq!(bonds[0][7], 8376); - assert_eq!(bonds[1][7], 18824); - assert_eq!(bonds[2][7], 49150); - assert_eq!(bonds[3][7], 65535); + assert_eq!(bonds[0][7], 0); + assert_eq!(bonds[1][7], 0); + assert_eq!(bonds[2][7], 25_770_353); + assert_eq!(bonds[3][7], 34_360_471); next_block(); if sparse { SubtensorModule::epoch( netuid, 1_000_000_000 ); } @@ -1401,12 +1401,12 @@ fn test_bonds_with_liquid_alpha() { // Active stake: [1, 2, 3, 4] // ΔB = W◦S = [0.25*1, 0.5*2, 0.75*3, 1.0*4] = [0.25, 1.0, 2.25, 4.0] // Normalize ΔB: [0.25/7.5, 1.0/7.5, 2.25/7.5, 4.0/7.5] = [0.0333, 0.1333, 0.3, 0.5333] - // Final bonds for netuid: [16383, 32767, 49151, 65535] + // Final bonds for netuid: [530, 1_061, 1_591, 2_122] - assert_eq!(bonds[0][4], 16383); // Note: Calculated as explained above - assert_eq!(bonds[1][4], 32767); // Note: Calculated as explained above - assert_eq!(bonds[2][4], 49151); // Note: Calculated as explained above - assert_eq!(bonds[3][4], 65535); // Note: Calculated as explained above + assert_eq!(bonds[0][4], 17_385_264); // Note: Calculated as explained above + assert_eq!(bonds[1][4], 34_770_528); // Note: Calculated as explained above + assert_eq!(bonds[2][4], 52_155_793); // Note: Calculated as explained above + assert_eq!(bonds[3][4], 69_541_058); // Note: Calculated as explained above // === Set self-weight only on val1 let uid = 0; @@ -1425,10 +1425,10 @@ fn test_bonds_with_liquid_alpha() { } let bonds = SubtensorModule::get_bonds(netuid); - assert_eq!(bonds[0][4], 2862); - assert_eq!(bonds[1][4], 32767); - assert_eq!(bonds[2][4], 49151); - assert_eq!(bonds[3][4], 65535); + assert_eq!(bonds[0][4], 0); + assert_eq!(bonds[1][4], 34_770_528); + assert_eq!(bonds[2][4], 52_155_793); + assert_eq!(bonds[3][4], 69_541_058); // === Set self-weight only on val2 let uid = 1; @@ -1488,10 +1488,10 @@ fn test_bonds_with_liquid_alpha() { Pruning Scores: [0.0016997808, 0.0151777493, 0.2070524206, 0.2760700488, 0.049998779, 0.1000006103, 0.1499963377, 0.2000042726] */ - assert_eq!(bonds[0][4], 435); - assert_eq!(bonds[1][4], 4985); - assert_eq!(bonds[2][4], 49151); - assert_eq!(bonds[3][4], 65535); + assert_eq!(bonds[0][4], 0); + assert_eq!(bonds[1][4], 0); + assert_eq!(bonds[2][4], 52155793); + assert_eq!(bonds[3][4], 69_541_058); }); } @@ -1623,7 +1623,7 @@ fn test_active_stake() { assert_eq!(*i, 0); } for i in bond.iter().take(n as usize).skip((n / 2) as usize) { - assert_eq!(*i, I32F32::from_num(65_535)); // floor(0.5*(2^16-1))/(2^16-1), then max-upscale to 65_535 + assert_eq!(*i, I32F32::from_num(26_843_545)); // floor(0.5*(2^32-1))/(2^32-1) } } let activity_cutoff: u64 = SubtensorModule::get_activity_cutoff(netuid) as u64; @@ -1669,22 +1669,19 @@ fn test_active_stake() { P: [0.275, 0.2249999999, 0.25, 0.25] P (u16): [65535, 53619, 59577, 59577] */ let bonds = SubtensorModule::get_bonds(netuid); - assert_eq!(SubtensorModule::get_dividends_for_uid(netuid, 0), 36044); // Note D = floor((0.5 * 0.9 + 0.1) * 65_535) - assert_eq!(SubtensorModule::get_emission_for_uid(netuid, 0), 274999999); // Note E = 0.5 * 0.55 * 1_000_000_000 = 275_000_000 (discrepancy) + assert_eq!(SubtensorModule::get_dividends_for_uid(netuid, 0), 65_535); // Note D = floor((0.5 * 0.9 + 0.1) * 65_535) + assert_eq!( + SubtensorModule::get_emission_for_uid(netuid, 0), + 500_000_000 + ); // Note E = 0.5 * 0.55 * 1_000_000_000 = 275_000_000 (discrepancy) for server in ((n / 2) as usize)..n as usize { - assert_eq!(bonds[0][server], I32F32::from_num(65_535)); // floor(0.55*(2^16-1))/(2^16-1), then max-upscale + assert_eq!(bonds[0][server], I32F32::from_num(107_374_182)); // floor(0.55*(2^16-1))/(2^16-1), then max-upscale } for validator in 1..(n / 2) { - assert_eq!( - SubtensorModule::get_dividends_for_uid(netuid, validator), - 29490 - ); // Note D = floor((0.5 * 0.9) * 65_535) - assert_eq!( - SubtensorModule::get_emission_for_uid(netuid, validator), - 224999999 - ); // Note E = 0.5 * 0.45 * 1_000_000_000 = 225_000_000 (discrepancy) + assert_eq!(SubtensorModule::get_dividends_for_uid(netuid, validator), 0); // Note D = floor((0.5 * 0.9) * 65_535) + assert_eq!(SubtensorModule::get_emission_for_uid(netuid, validator), 0); // Note E = 0.5 * 0.45 * 1_000_000_000 = 225_000_000 (discrepancy) for server in ((n / 2) as usize)..n as usize { - assert_eq!(bonds[validator as usize][server], I32F32::from_num(53619)); + assert_eq!(bonds[validator as usize][server], I32F32::from_num(0)); // floor(0.45*(2^16-1))/(2^16-1), then max-upscale } } @@ -1730,15 +1727,21 @@ fn test_active_stake() { P: [0.272501133, 0.2274988669, 0.25, 0.25] P (u16): [65535, 54711, 60123, 60123] */ let bonds = SubtensorModule::get_bonds(netuid); - assert_eq!(SubtensorModule::get_dividends_for_uid(netuid, 0), 35716); // Note D = floor((0.55 * 0.9 + 0.5 * 0.1) * 65_535) - assert_eq!(SubtensorModule::get_emission_for_uid(netuid, 0), 272501132); // Note E = 0.5 * (0.55 * 0.9 + 0.5 * 0.1) * 1_000_000_000 = 272_500_000 (discrepancy) + assert_eq!(SubtensorModule::get_dividends_for_uid(netuid, 0), 32_767); // Note D = floor((0.55 * 0.9 + 0.5 * 0.1) * 65_535) + assert_eq!( + SubtensorModule::get_emission_for_uid(netuid, 0), + 250_000_000 + ); // Note E = 0.5 * (0.55 * 0.9 + 0.5 * 0.1) * 1_000_000_000 = 272_500_000 (discrepancy) for server in ((n / 2) as usize)..n as usize { - assert_eq!(bonds[0][server], I32F32::from_num(65_535)); // floor((0.55 * 0.9 + 0.5 * 0.1)*(2^16-1))/(2^16-1), then max-upscale + assert_eq!(bonds[0][server], I32F32::from_num(53_687_090)); // floor((0.55 * 0.9 + 0.5 * 0.1)*(2^16-1))/(2^16-1), then max-upscale } - assert_eq!(SubtensorModule::get_dividends_for_uid(netuid, 1), 29818); // Note D = floor((0.45 * 0.9 + 0.5 * 0.1) * 65_535) - assert_eq!(SubtensorModule::get_emission_for_uid(netuid, 1), 227498866); // Note E = 0.5 * (0.45 * 0.9 + 0.5 * 0.1) * 1_000_000_000 = 227_500_000 (discrepancy) + assert_eq!(SubtensorModule::get_dividends_for_uid(netuid, 1), 32_767); // Note D = floor((0.45 * 0.9 + 0.5 * 0.1) * 65_535) + assert_eq!( + SubtensorModule::get_emission_for_uid(netuid, 1), + 250_000_000 + ); // Note E = 0.5 * (0.45 * 0.9 + 0.5 * 0.1) * 1_000_000_000 = 227_500_000 (discrepancy) for server in ((n / 2) as usize)..n as usize { - assert_eq!(bonds[1][server], I32F32::from_num(54712)); // floor((0.45 * 0.9 + 0.5 * 0.1)/(0.55 * 0.9 + 0.5 * 0.1)*(2^16-1)) + assert_eq!(bonds[1][server], I32F32::from_num(53_687_090)); // floor((0.45 * 0.9 + 0.5 * 0.1)/(0.55 * 0.9 + 0.5 * 0.1)*(2^16-1)) } }); } @@ -1925,8 +1928,8 @@ fn test_outdated_weights() { let bonds = SubtensorModule::get_bonds(netuid); assert_eq!(SubtensorModule::get_dividends_for_uid(netuid, 0), 32767); // Note D = floor(0.5 * 65_535) assert_eq!(SubtensorModule::get_emission_for_uid(netuid, 0), 250000000); // Note E = 0.5 * 0.5 * 1_000_000_000 = 249311245 - assert_eq!(bonds[0][2], I32F32::from_num(65_535)); // floor(0.5*(2^16-1))/(2^16-1), then max-upscale - assert_eq!(bonds[0][3], I32F32::from_num(65_535)); // only uid0 has updated weights for new reg + assert_eq!(bonds[0][2], I32F32::from_num(47_721_615)); // floor(0.5*(2^16-1))/(2^16-1), then max-upscale + assert_eq!(bonds[0][3], I32F32::from_num(0)); // only uid0 has updated weights for new reg }); } From acb94a07a6065e859866d1684d955f84ef160022 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 21:26:01 -0400 Subject: [PATCH 13/13] add bond reset test --- pallets/subtensor/src/tests/epoch.rs | 180 +++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index a0f1f7eb3..1a5e7548a 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -2127,6 +2127,186 @@ fn test_zero_weights() { }); } +// Test that recently/deregistered miner bonds are cleared before EMA. +#[test] +fn test_deregistered_miner_bonds() { + new_test_ext(1).execute_with(|| { + let sparse: bool = true; + let n: u16 = 4; + let netuid: u16 = 1; + let high_tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead + + let stake: u64 = 1; + add_network(netuid, high_tempo, 0); + SubtensorModule::set_max_allowed_uids(netuid, n); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + SubtensorModule::set_max_registrations_per_block(netuid, n); + SubtensorModule::set_target_registrations_per_interval(netuid, n); + SubtensorModule::set_min_allowed_weights(netuid, 0); + SubtensorModule::set_max_weight_limit(netuid, u16::MAX); + SubtensorModule::set_bonds_penalty(netuid, u16::MAX); + assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 0); + + // === Register [validator1, validator2, server1, server2] + let block_number = System::block_number(); + for key in 0..n as u64 { + SubtensorModule::add_balance_to_coldkey_account(&U256::from(key), stake); + let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( + netuid, + block_number, + key * 1_000_000, + &U256::from(key), + ); + assert_ok!(SubtensorModule::register( + RuntimeOrigin::signed(U256::from(key)), + netuid, + block_number, + nonce, + work, + U256::from(key), + U256::from(key) + )); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &U256::from(key), + &U256::from(key), + netuid, + stake, + ); + } + assert_eq!(SubtensorModule::get_subnetwork_n(netuid), n); + assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 4); + + // === Issue validator permits + SubtensorModule::set_max_allowed_validators(netuid, n); + assert_eq!(SubtensorModule::get_max_allowed_validators(netuid), n); + SubtensorModule::epoch(netuid, 1_000_000_000); // run first epoch to set allowed validators + assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 4); + next_block(); // run to next block to ensure weights are set on nodes after their registration block + assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 0); + + // === Set weights [val1->srv1: 2/3, val1->srv2: 1/3, val2->srv1: 2/3, val2->srv2: 1/3] + for uid in 0..(n / 2) as u64 { + assert_ok!(SubtensorModule::set_weights( + RuntimeOrigin::signed(U256::from(uid)), + netuid, + ((n / 2)..n).collect(), + vec![2 * (u16::MAX / 3), u16::MAX / 3], + 0 + )); + } + + // Set tempo high so we don't automatically run epochs + SubtensorModule::set_tempo(netuid, high_tempo); + + // Run 2 blocks + next_block(); + next_block(); + + // set tempo to 2 blocks + SubtensorModule::set_tempo(netuid, 2); + // Run epoch + if sparse { + SubtensorModule::epoch(netuid, 1_000_000_000); + } else { + SubtensorModule::epoch_dense(netuid, 1_000_000_000); + } + + // Check the bond values for the servers + let bonds = SubtensorModule::get_bonds(netuid); + let bond_0_2 = bonds[0][2]; + let bond_0_3 = bonds[0][3]; + + // Non-zero bonds + assert!(bond_0_2 > 0); + assert!(bond_0_3 > 0); + + // Set tempo high so we don't automatically run epochs + SubtensorModule::set_tempo(netuid, high_tempo); + + // Run one more block + next_block(); + + // === Dereg server2 at uid3 (least emission) + register new key over uid3 + let new_key: u64 = n as u64; // register a new key while at max capacity, which means the least incentive uid will be deregistered + let block_number = System::block_number(); + let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( + netuid, + block_number, + 0, + &U256::from(new_key), + ); + assert_eq!(SubtensorModule::get_max_registrations_per_block(netuid), n); + assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 0); + assert_ok!(SubtensorModule::register( + RuntimeOrigin::signed(U256::from(new_key)), + netuid, + block_number, + nonce, + work, + U256::from(new_key), + U256::from(new_key) + )); + let deregistered_uid: u16 = n - 1; // since uid=n-1 only recieved 1/3 of weight, it will get pruned first + assert_eq!( + U256::from(new_key), + SubtensorModule::get_hotkey_for_net_and_uid(netuid, deregistered_uid) + .expect("Not registered") + ); + + // Set weights again so they're active. + for uid in 0..(n / 2) as u64 { + assert_ok!(SubtensorModule::set_weights( + RuntimeOrigin::signed(U256::from(uid)), + netuid, + ((n / 2)..n).collect(), + vec![2 * (u16::MAX / 3), u16::MAX / 3], + 0 + )); + } + + // Run 1 block + next_block(); + // Assert block at registration happened after the last tempo + let block_at_registration = SubtensorModule::get_neuron_block_at_registration(netuid, 3); + let block_number = System::block_number(); + assert!( + block_at_registration >= block_number - 2, + "block at registration: {}, block number: {}", + block_at_registration, + block_number + ); + + // set tempo to 2 blocks + SubtensorModule::set_tempo(netuid, 2); + // Run epoch again. + if sparse { + SubtensorModule::epoch(netuid, 1_000_000_000); + } else { + SubtensorModule::epoch_dense(netuid, 1_000_000_000); + } + + // Check the bond values for the servers + let bonds = SubtensorModule::get_bonds(netuid); + let bond_0_2_new = bonds[0][2]; + let bond_0_3_new = bonds[0][3]; + + // We expect the old bonds for server2, (uid3), to be reset. + // For server1, (uid2), the bond should be higher than before. + assert!( + bond_0_2_new > bond_0_2, + "bond_0_2_new: {}, bond_0_2: {}", + bond_0_2_new, + bond_0_2 + ); + assert!( + bond_0_3_new <= bond_0_3, + "bond_0_3_new: {}, bond_0_3: {}", + bond_0_3_new, + bond_0_3 + ); + }); +} + // Test that epoch assigns validator permits to highest stake uids, varies uid interleaving and stake values. #[test] fn test_validator_permits() {