From 1938dc90742c06ec80462a2175806ef3a9a3f129 Mon Sep 17 00:00:00 2001 From: markojovanovic_elite Date: Wed, 22 Apr 2026 13:09:10 -0500 Subject: [PATCH] fix: clear ValidatorTrust/ValidatorPermit on neuron replace clear_neuron zeroed the per-UID slot of Emission, Consensus, Incentive, Dividends, StakeWeight, Bonds and Weights, but missed ValidatorTrust and ValidatorPermit. Both are Vec<_>-per-netuid storage that append_neuron initialises on a fresh UID, so when replace_neuron reuses a UID it currently left the old occupant's validator_permit=true flag and validator_trust score in place until the next epoch recomputed the vectors. Between replacement and the next epoch, RPC surfaces (delegate_info, show_subnet) and on-chain readers (trim_to_max_allowed_uids and the per-mechanism validator aggregation in mechanism.rs) observed stale validator state for the freshly registered neuron. Same class of stale-inheritance bug PR #755 was introduced to fix; these two fields were missed there. - clear_neuron now also zeroes ValidatorTrust[uid] and resets ValidatorPermit[uid] = false, using the same set_element_at idiom already applied to the other vec-per-netuid fields two lines above. - Add test_replace_neuron_clears_validator_trust_and_permit mirroring the existing test_replace_neuron_resets_last_update style. --- pallets/subtensor/src/subnets/uids.rs | 2 ++ pallets/subtensor/src/tests/uids.rs | 46 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index d21509f83d..3665b139ff 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -46,6 +46,8 @@ impl Pallet { } Dividends::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); StakeWeight::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + ValidatorTrust::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + ValidatorPermit::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, false)); } /// Replace the neuron under this uid. diff --git a/pallets/subtensor/src/tests/uids.rs b/pallets/subtensor/src/tests/uids.rs index 37d9733991..7679f2b727 100644 --- a/pallets/subtensor/src/tests/uids.rs +++ b/pallets/subtensor/src/tests/uids.rs @@ -225,6 +225,52 @@ fn test_replace_neuron_resets_last_update() { }); } +#[test] +fn test_replace_neuron_clears_validator_trust_and_permit() { + new_test_ext(1).execute_with(|| { + let registration_block: u64 = 0; + let replacement_block: u64 = 123; + let netuid = NetUid::from(1); + let tempo: u16 = 13; + let hotkey_account_id = U256::from(1); + let coldkey_account_id = U256::from(1234); + let new_hotkey_account_id = U256::from(2); + + System::set_block_number(registration_block); + add_network(netuid, tempo, 0); + register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 0); + + let neuron_uid = + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).unwrap(); + let idx = neuron_uid as usize; + + // Simulate the previous occupant having earned a validator_permit and trust score. + ValidatorTrust::::mutate(netuid, |v| { + if v.len() <= idx { + v.resize(idx + 1, 0); + } + v[idx] = 42; + }); + ValidatorPermit::::mutate(netuid, |v| { + if v.len() <= idx { + v.resize(idx + 1, false); + } + v[idx] = true; + }); + + SubtensorModule::replace_neuron( + netuid, + neuron_uid, + &new_hotkey_account_id, + replacement_block, + ); + + // The replaced neuron must not inherit the previous occupant's validator state. + assert_eq!(ValidatorTrust::::get(netuid)[idx], 0); + assert!(!ValidatorPermit::::get(netuid)[idx]); + }); +} + #[test] fn test_replace_neuron_multiple_subnets() { new_test_ext(1).execute_with(|| {