Skip to content

Commit fd70d5d

Browse files
authored
Merge pull request #1629 from opentensor/vpermit-respect-min-stake
Ensure stake threshold is respected when handing out vpermits
2 parents 82f50ec + 1bcbc22 commit fd70d5d

File tree

4 files changed

+356
-11
lines changed

4 files changed

+356
-11
lines changed

pallets/subtensor/src/epoch/math.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,22 @@ pub fn is_topk(vector: &[I32F32], k: usize) -> Vec<bool> {
246246
result
247247
}
248248

249+
// Returns a bool vector where an item is true if the vector item is in topk values and is non-zero.
250+
#[allow(dead_code, clippy::indexing_slicing)]
251+
pub fn is_topk_nonzero(vector: &[I32F32], k: usize) -> Vec<bool> {
252+
let n: usize = vector.len();
253+
let mut result: Vec<bool> = vector.iter().map(|&elem| elem != I32F32::from(0)).collect();
254+
if n < k {
255+
return result;
256+
}
257+
let mut idxs: Vec<usize> = (0..n).collect();
258+
idxs.sort_by_key(|&idx| &vector[idx]); // ascending stable sort
259+
for &idx in idxs.iter().take(n.saturating_sub(k)) {
260+
result[idx] = false;
261+
}
262+
result
263+
}
264+
249265
// Returns a normalized (sum to 1 except 0) copy of the input vector.
250266
#[allow(dead_code)]
251267
pub fn normalize(x: &[I32F32]) -> Vec<I32F32> {

pallets/subtensor/src/epoch/run_epoch.rs

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,26 @@ impl<T: Config> Pallet<T> {
8080
log::trace!("hotkeys: {:?}", &hotkeys);
8181

8282
// Access network stake as normalized vector.
83-
let (mut total_stake, _alpha_stake, _tao_stake): (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) =
83+
let (total_stake, _alpha_stake, _tao_stake): (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) =
8484
Self::get_stake_weights_for_network(netuid);
85-
inplace_normalize_64(&mut total_stake);
86-
let stake: Vec<I32F32> = vec_fixed64_to_fixed32(total_stake);
85+
86+
// Get the minimum stake required.
87+
let min_stake = Self::get_stake_threshold();
88+
89+
// Set stake of validators that doesn't meet the staking threshold to 0 as filter.
90+
let mut filtered_stake: Vec<I64F64> = total_stake
91+
.iter()
92+
.map(|&s| {
93+
if fixed64_to_u64(s) < min_stake {
94+
return I64F64::from(0);
95+
}
96+
s
97+
})
98+
.collect();
99+
log::debug!("Filtered stake: {:?}", &filtered_stake);
100+
101+
inplace_normalize_64(&mut filtered_stake);
102+
let stake: Vec<I32F32> = vec_fixed64_to_fixed32(filtered_stake);
87103
log::trace!("S: {:?}", &stake);
88104

89105
// =======================
@@ -102,7 +118,8 @@ impl<T: Config> Pallet<T> {
102118
log::trace!("max_allowed_validators: {:?}", max_allowed_validators);
103119

104120
// Get new validator permits.
105-
let new_validator_permits: Vec<bool> = is_topk(&stake, max_allowed_validators as usize);
121+
let new_validator_permits: Vec<bool> =
122+
is_topk_nonzero(&stake, max_allowed_validators as usize);
106123
log::trace!("new_validator_permits: {:?}", new_validator_permits);
107124

108125
// ==================
@@ -470,10 +487,26 @@ impl<T: Config> Pallet<T> {
470487
log::debug!("hotkeys: {:?}", &hotkeys);
471488

472489
// Access network stake as normalized vector.
473-
let (mut total_stake, _alpha_stake, _tao_stake): (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) =
490+
let (total_stake, _alpha_stake, _tao_stake): (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) =
474491
Self::get_stake_weights_for_network(netuid);
475-
inplace_normalize_64(&mut total_stake);
476-
let stake: Vec<I32F32> = vec_fixed64_to_fixed32(total_stake);
492+
493+
// Get the minimum stake required.
494+
let min_stake = Self::get_stake_threshold();
495+
496+
// Set stake of validators that doesn't meet the staking threshold to 0 as filter.
497+
let mut filtered_stake: Vec<I64F64> = total_stake
498+
.iter()
499+
.map(|&s| {
500+
if fixed64_to_u64(s) < min_stake {
501+
return I64F64::from(0);
502+
}
503+
s
504+
})
505+
.collect();
506+
log::debug!("Filtered stake: {:?}", &filtered_stake);
507+
508+
inplace_normalize_64(&mut filtered_stake);
509+
let stake: Vec<I32F32> = vec_fixed64_to_fixed32(filtered_stake);
477510
log::debug!("Normalised Stake: {:?}", &stake);
478511

479512
// =======================
@@ -492,7 +525,8 @@ impl<T: Config> Pallet<T> {
492525
log::trace!("max_allowed_validators: {:?}", max_allowed_validators);
493526

494527
// Get new validator permits.
495-
let new_validator_permits: Vec<bool> = is_topk(&stake, max_allowed_validators as usize);
528+
let new_validator_permits: Vec<bool> =
529+
is_topk_nonzero(&stake, max_allowed_validators as usize);
496530
log::trace!("new_validator_permits: {:?}", new_validator_permits);
497531

498532
// ==================

pallets/subtensor/src/tests/epoch.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2095,13 +2095,14 @@ fn test_deregistered_miner_bonds() {
20952095
});
20962096
}
20972097

2098-
// Test that epoch assigns validator permits to highest stake uids, varies uid interleaving and stake values.
2098+
// Test that epoch assigns validator permits to highest stake uids that are over the stake threshold, varies uid interleaving and stake values.
20992099
#[test]
21002100
fn test_validator_permits() {
21012101
let netuid: u16 = 1;
21022102
let tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead
21032103
for interleave in 0..3 {
21042104
for (network_n, validators_n) in [(2, 1), (4, 2), (8, 4)] {
2105+
let min_stake = validators_n as u64;
21052106
for assignment in 0..=1 {
21062107
let (validators, servers) =
21072108
distribute_nodes(validators_n as usize, network_n, interleave as usize);
@@ -2132,6 +2133,7 @@ fn test_validator_permits() {
21322133
netuid,
21332134
network_n as u16,
21342135
);
2136+
SubtensorModule::set_stake_threshold(min_stake);
21352137

21362138
// === Register [validator1, validator2, server1, server2]
21372139
for key in 0..network_n as u64 {
@@ -2173,7 +2175,7 @@ fn test_validator_permits() {
21732175
SubtensorModule::epoch(netuid, 1_000_000_000); // run first epoch to set allowed validators
21742176
for validator in &validators {
21752177
assert_eq!(
2176-
correct,
2178+
stake[*validator as usize] >= min_stake,
21772179
SubtensorModule::get_validator_permit_for_uid(netuid, *validator)
21782180
);
21792181
}
@@ -2211,7 +2213,7 @@ fn test_validator_permits() {
22112213
}
22122214
for server in &servers {
22132215
assert_eq!(
2214-
correct,
2216+
(stake[*server as usize] + (2 * network_n as u64)) >= min_stake,
22152217
SubtensorModule::get_validator_permit_for_uid(netuid, *server)
22162218
);
22172219
}

0 commit comments

Comments
 (0)