From 2b674c71abb17c63a495924289429010751c1fe0 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 22:07:52 -0400 Subject: [PATCH 1/5] reset bonds for miners recently deregistered --- pallets/subtensor/src/epoch/math.rs | 38 +++++ pallets/subtensor/src/epoch/run_epoch.rs | 34 ++++- pallets/subtensor/src/tests/epoch.rs | 180 +++++++++++++++++++++++ pallets/subtensor/src/tests/math.rs | 39 +++++ 4 files changed, 285 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 436dba84a..b4f23ced8 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]) { @@ -569,6 +587,26 @@ pub fn inplace_mask_diag(matrix: &mut [Vec]) { }); } +// 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 +} + // Mask out the diagonal of the input matrix in-place, except for the diagonal entry at except_index. #[allow(dead_code)] pub fn inplace_mask_diag_except_index(matrix: &mut [Vec], except_index: u16) { diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index 27dd17fa0..62027f963 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: {:?}", 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,7 +199,8 @@ impl Pallet { // Access network bonds. let mut bonds: Vec> = Self::get_bonds(netuid); - inplace_mask_matrix(&outdated, &mut bonds); // mask outdated bonds + // Remove bonds referring to neurons that have registered since last tempo. + inplace_mask_cols(&recently_registered, &mut bonds); // mask recently registered bonds inplace_col_normalize(&mut bonds); // sum_i b_ij = 1 log::trace!("B:\n{:?}\n", &bonds); @@ -386,6 +401,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:\n{:?}\n", tempo); + // Get activity cutoff. let activity_cutoff: u64 = Self::get_activity_cutoff(netuid) as u64; log::trace!("activity_cutoff: {:?}", activity_cutoff); @@ -548,12 +567,15 @@ 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); diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index 38b104ac2..e7066feee 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -2124,6 +2124,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() { 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 4725f550d0d7905cb4a1f4e8cc6ef292f91e0740 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 22:08:05 -0400 Subject: [PATCH 2/5] reset bonds from deregistered validator --- pallets/subtensor/src/subnets/uids.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index aaf3b5fe6..c97252677 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::::remove(netuid, neuron_uid); // Remove bonds for Validator. } /// Replace the neuron under this uid. From f7e153105eea32d5cd9b15d2d0bacc3501eb9115 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 20 Mar 2025 22:14:28 -0400 Subject: [PATCH 3/5] add tests --- pallets/subtensor/src/tests/uids.rs | 71 +++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/pallets/subtensor/src/tests/uids.rs b/pallets/subtensor/src/tests/uids.rs index 178613fbb..92a8a6404 100644 --- a/pallets/subtensor/src/tests/uids.rs +++ b/pallets/subtensor/src/tests/uids.rs @@ -68,6 +68,7 @@ fn test_replace_neuron() { Dividends::::mutate(netuid, |v| { SubtensorModule::set_element_at(v, neuron_uid as usize, 5u16) }); + Bonds::::insert(netuid, neuron_uid, vec![(0, 1)]); // serve axon mock address let ip: u128 = 1676056785; @@ -138,6 +139,76 @@ fn test_replace_neuron() { assert_eq!(axon_info.ip, 0); assert_eq!(axon_info.port, 0); assert_eq!(axon_info.ip_type, 0); + + // Check bonds are cleared. + assert_eq!(Bonds::::get(netuid, neuron_uid), vec![]); + }); +} + +#[test] +fn test_bonds_cleared_on_replace() { + new_test_ext(1).execute_with(|| { + let block_number: u64 = 0; + let netuid: u16 = 1; + let tempo: u16 = 13; + let hotkey_account_id = U256::from(1); + let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( + netuid, + block_number, + 111111, + &hotkey_account_id, + ); + let coldkey_account_id = U256::from(1234); + + let new_hotkey_account_id = U256::from(2); + let _new_colkey_account_id = U256::from(12345); + + //add network + add_network(netuid, tempo, 0); + + // Register a neuron. + assert_ok!(SubtensorModule::register( + <::RuntimeOrigin>::signed(hotkey_account_id), + netuid, + block_number, + nonce, + work, + hotkey_account_id, + coldkey_account_id + )); + + // Get UID + let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id); + assert_ok!(neuron_uid); + let neuron_uid = neuron_uid.unwrap(); + + // set non-default bonds + Bonds::::insert(netuid, neuron_uid, vec![(0, 1)]); + + // Replace the neuron. + SubtensorModule::replace_neuron(netuid, neuron_uid, &new_hotkey_account_id, block_number); + + // Check old hotkey is not registered on any network. + assert!(SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).is_err()); + assert!(!SubtensorModule::is_hotkey_registered_on_any_network( + &hotkey_account_id + )); + + let curr_hotkey = SubtensorModule::get_hotkey_for_net_and_uid(netuid, neuron_uid); + assert_ok!(curr_hotkey); + assert_ne!(curr_hotkey.unwrap(), hotkey_account_id); + + // Check new hotkey is registered on the network. + assert!( + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &new_hotkey_account_id).is_ok() + ); + assert!(SubtensorModule::is_hotkey_registered_on_any_network( + &new_hotkey_account_id + )); + assert_eq!(curr_hotkey.unwrap(), new_hotkey_account_id); + + // Check bonds are cleared. + assert_eq!(Bonds::::get(netuid, neuron_uid), vec![]); }); } From 213b7b1198a6144e622afa3d4ce83eaf39f2df18 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Fri, 21 Mar 2025 01:30:17 -0400 Subject: [PATCH 4/5] use next_block/run_to_block _no_epoch --- pallets/subtensor/src/tests/epoch.rs | 71 ++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index e7066feee..c11271017 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -982,6 +982,28 @@ fn test_512_graph_random_weights() { // }); // } +fn next_block_no_epoch(netuid: u16) -> u64 { + // high tempo to skip automatic epochs in on_initialize + let high_tempo: u16 = u16::MAX - 1; + let old_tempo: u16 = SubtensorModule::get_tempo(netuid); + + SubtensorModule::set_tempo(netuid, high_tempo); + let new_block = next_block(); + SubtensorModule::set_tempo(netuid, old_tempo); + + new_block +} + +fn run_to_block_no_epoch(netuid: u16, n: u64) { + // high tempo to skip automatic epochs in on_initialize + let high_tempo: u16 = u16::MAX - 1; + let old_tempo: u16 = SubtensorModule::get_tempo(netuid); + + SubtensorModule::set_tempo(netuid, high_tempo); + run_to_block(n); + SubtensorModule::set_tempo(netuid, old_tempo); +} + // Test bonds exponential moving average over a sequence of epochs. #[test] fn test_bonds() { @@ -989,7 +1011,7 @@ fn test_bonds() { let sparse: bool = true; let n: u16 = 8; let netuid: u16 = 1; - let tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead + let tempo: u16 = 1; let max_stake: u64 = 4; let stakes: Vec = vec![1, 2, 3, 4, 0, 0, 0, 0]; let block_number = System::block_number(); @@ -1018,7 +1040,7 @@ fn test_bonds() { 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 - next_block(); // run to next block to ensure weights are set on nodes after their registration block + next_block_no_epoch(netuid); // run to next block to ensure weights are set on nodes after their registration block // === Set weights [val->srv1: 0.1, val->srv2: 0.2, val->srv3: 0.3, val->srv4: 0.4] for uid in 0..(n/2) as u64 { @@ -1068,7 +1090,8 @@ fn test_bonds() { // === Set self-weight only on val1 let uid = 0; assert_ok!(SubtensorModule::set_weights(RuntimeOrigin::signed(U256::from(uid)), netuid, vec![uid], vec![u16::MAX], 0)); - next_block(); + next_block_no_epoch(netuid); + if sparse { SubtensorModule::epoch( netuid, 1_000_000_000 ); } else { SubtensorModule::epoch_dense( netuid, 1_000_000_000 ); } /* n: 8 @@ -1115,7 +1138,8 @@ fn test_bonds() { // === Set self-weight only on val2 let uid = 1; assert_ok!(SubtensorModule::set_weights(RuntimeOrigin::signed(U256::from(uid)), netuid, vec![uid], vec![u16::MAX], 0)); - next_block(); + next_block_no_epoch(netuid); + if sparse { SubtensorModule::epoch( netuid, 1_000_000_000 ); } else { SubtensorModule::epoch_dense( netuid, 1_000_000_000 ); } /* current_block: 3 @@ -1151,7 +1175,8 @@ fn test_bonds() { // === Set self-weight only on val3 let uid = 2; assert_ok!(SubtensorModule::set_weights(RuntimeOrigin::signed(U256::from(uid)), netuid, vec![uid], vec![u16::MAX], 0)); - next_block(); + next_block_no_epoch(netuid); + if sparse { SubtensorModule::epoch( netuid, 1_000_000_000 ); } else { SubtensorModule::epoch_dense( netuid, 1_000_000_000 ); } /* current_block: 4 @@ -1186,7 +1211,8 @@ fn test_bonds() { // === Set val3->srv4: 1 assert_ok!(SubtensorModule::set_weights(RuntimeOrigin::signed(U256::from(2)), netuid, vec![7], vec![u16::MAX], 0)); - next_block(); + next_block_no_epoch(netuid); + if sparse { SubtensorModule::epoch( netuid, 1_000_000_000 ); } else { SubtensorModule::epoch_dense( netuid, 1_000_000_000 ); } /* current_block: 5 @@ -1219,7 +1245,8 @@ fn test_bonds() { assert_eq!(bonds[2][7], 49150); assert_eq!(bonds[3][7], 65535); - next_block(); + next_block_no_epoch(netuid); + if sparse { SubtensorModule::epoch( netuid, 1_000_000_000 ); } else { SubtensorModule::epoch_dense( netuid, 1_000_000_000 ); } /* current_block: 6 @@ -1240,7 +1267,8 @@ fn test_bonds() { assert_eq!(bonds[2][7], 49150); assert_eq!(bonds[3][7], 65535); - next_block(); + next_block_no_epoch(netuid); + if sparse { SubtensorModule::epoch( netuid, 1_000_000_000 ); } else { SubtensorModule::epoch_dense( netuid, 1_000_000_000 ); } /* current_block: 7 @@ -1261,7 +1289,8 @@ fn test_bonds() { assert_eq!(bonds[2][7], 49150); assert_eq!(bonds[3][7], 65535); - next_block(); + next_block_no_epoch(netuid); + if sparse { SubtensorModule::epoch( netuid, 1_000_000_000 ); } else { SubtensorModule::epoch_dense( netuid, 1_000_000_000 ); } /* current_block: 8 @@ -1286,7 +1315,7 @@ fn test_bonds_with_liquid_alpha() { let sparse: bool = true; let n: u16 = 8; let netuid: u16 = 1; - let tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead + let tempo: u16 = 1; let max_stake: u64 = 4; let stakes: Vec = vec![1, 2, 3, 4, 0, 0, 0, 0]; let block_number = System::block_number(); @@ -1326,7 +1355,7 @@ fn test_bonds_with_liquid_alpha() { // Initilize with first epoch SubtensorModule::epoch(netuid, 1_000_000_000); - next_block(); + next_block_no_epoch(netuid); // Set weights for uid in 0..(n / 2) { @@ -1417,7 +1446,7 @@ fn test_bonds_with_liquid_alpha() { vec![u16::MAX], 0 )); - next_block(); + next_block_no_epoch(netuid); if sparse { SubtensorModule::epoch(netuid, 1_000_000_000); } else { @@ -1439,7 +1468,7 @@ fn test_bonds_with_liquid_alpha() { vec![u16::MAX], 0 )); - next_block(); + next_block_no_epoch(netuid); if sparse { SubtensorModule::epoch(netuid, 1_000_000_000); } else { @@ -1543,7 +1572,7 @@ fn test_active_stake() { let sparse: bool = true; let n: u16 = 4; let netuid: u16 = 1; - let tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead + let tempo: u16 = 1; let block_number: u64 = System::block_number(); let stake: u64 = 1; add_network(netuid, tempo, 0); @@ -1586,7 +1615,7 @@ fn test_active_stake() { 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 - next_block(); // run to next block to ensure weights are set on nodes after their registration block + next_block_no_epoch(netuid); // run to next block to ensure weights are set on nodes after their registration block // === Set weights [val1->srv1: 0.5, val1->srv2: 0.5, val2->srv1: 0.5, val2->srv2: 0.5] for uid in 0..(n / 2) as u64 { @@ -1627,7 +1656,7 @@ fn test_active_stake() { } } let activity_cutoff: u64 = SubtensorModule::get_activity_cutoff(netuid) as u64; - run_to_block(activity_cutoff + 2); // run to block where validator (uid 0, 1) weights become outdated + run_to_block_no_epoch(netuid, activity_cutoff + 2); // run to block where validator (uid 0, 1) weights become outdated // === Update uid 0 weights assert_ok!(SubtensorModule::set_weights( @@ -1697,7 +1726,7 @@ fn test_active_stake() { vec![u16::MAX / (n / 2); (n / 2) as usize], 0 )); - run_to_block(activity_cutoff + 3); // run to block where validator (uid 0, 1) weights become outdated + run_to_block_no_epoch(netuid, activity_cutoff + 3); // run to block where validator (uid 0, 1) weights become outdated if sparse { SubtensorModule::epoch(netuid, 1_000_000_000); } else { @@ -1750,7 +1779,7 @@ fn test_outdated_weights() { let sparse: bool = true; let n: u16 = 4; let netuid: u16 = 1; - let tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead + let tempo: u16 = 0; let mut block_number: u64 = System::block_number(); let stake: u64 = 1; add_network(netuid, tempo, 0); @@ -1796,7 +1825,7 @@ fn test_outdated_weights() { 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); - block_number = next_block(); // run to next block to ensure weights are set on nodes after their registration block + block_number = next_block_no_epoch(netuid); // 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, srv1->srv1: 1, srv2->srv2: 1] @@ -1877,7 +1906,7 @@ fn test_outdated_weights() { SubtensorModule::get_hotkey_for_net_and_uid(netuid, deregistered_uid) .expect("Not registered") ); - next_block(); // run to next block to outdate weights and bonds set on deregistered uid + next_block_no_epoch(netuid); // run to next block to outdate weights and bonds set on deregistered uid // === Update weights from only uid=0 assert_ok!(SubtensorModule::set_weights( @@ -2290,7 +2319,7 @@ fn test_deregistered_miner_bonds() { // 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: {}", bond_0_2_new, bond_0_2 From cbca1c39579a67d57b3e301b999399ad73622ee1 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Fri, 21 Mar 2025 01:30:26 -0400 Subject: [PATCH 5/5] chore: fmt --- pallets/subtensor/src/tests/epoch.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index c11271017..9c32c836c 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -983,25 +983,25 @@ fn test_512_graph_random_weights() { // } fn next_block_no_epoch(netuid: u16) -> u64 { - // high tempo to skip automatic epochs in on_initialize - let high_tempo: u16 = u16::MAX - 1; - let old_tempo: u16 = SubtensorModule::get_tempo(netuid); + // high tempo to skip automatic epochs in on_initialize + let high_tempo: u16 = u16::MAX - 1; + let old_tempo: u16 = SubtensorModule::get_tempo(netuid); - SubtensorModule::set_tempo(netuid, high_tempo); - let new_block = next_block(); - SubtensorModule::set_tempo(netuid, old_tempo); + SubtensorModule::set_tempo(netuid, high_tempo); + let new_block = next_block(); + SubtensorModule::set_tempo(netuid, old_tempo); - new_block + new_block } fn run_to_block_no_epoch(netuid: u16, n: u64) { - // high tempo to skip automatic epochs in on_initialize - let high_tempo: u16 = u16::MAX - 1; - let old_tempo: u16 = SubtensorModule::get_tempo(netuid); + // high tempo to skip automatic epochs in on_initialize + let high_tempo: u16 = u16::MAX - 1; + let old_tempo: u16 = SubtensorModule::get_tempo(netuid); - SubtensorModule::set_tempo(netuid, high_tempo); - run_to_block(n); - SubtensorModule::set_tempo(netuid, old_tempo); + SubtensorModule::set_tempo(netuid, high_tempo); + run_to_block(n); + SubtensorModule::set_tempo(netuid, old_tempo); } // Test bonds exponential moving average over a sequence of epochs.