Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5f312e9
not check rate limit for reduce child take
open-junius May 12, 2025
cc31e65
cargo clippy
open-junius May 12, 2025
45f66a1
cargo fmt
open-junius May 12, 2025
5e43402
update benchmark
open-junius May 12, 2025
3d99afc
Add UidLookupPrecompile address to used_address
keithtensor May 13, 2025
2ecebb1
Ensure stake threshold is respected when handing out vpermits
keithtensor May 13, 2025
4e3f374
Set stake threshold to 0 in commit reveal test
keithtensor May 13, 2025
17a81ad
Set validator stake to 1 during testing
keithtensor May 13, 2025
1bcbc22
Merge remote-tracking branch 'origin/devnet-ready' into vpermit-respe…
keithtensor May 15, 2025
3d3444f
Merge remote-tracking branch 'origin/devnet-ready' into uid-lookup-pr…
keithtensor May 15, 2025
dc92bad
Fix up a comment to reflect what the code is doing
keithtensor May 15, 2025
dc8d26c
Bump spec version
keithtensor May 15, 2025
06146ed
Merge pull request #1643 from opentensor/fix-comment
sam0x17 May 15, 2025
5d96ce2
Merge branch 'devnet-ready' into remove-rate-limit-reduce-take
open-junius May 17, 2025
e5653ee
Merge pull request #1624 from opentensor/remove-rate-limit-reduce-take
open-junius May 19, 2025
82f50ec
Merge pull request #1628 from opentensor/uid-lookup-precompile-fix
keithtensor May 19, 2025
fd70d5d
Merge pull request #1629 from opentensor/vpermit-respect-min-stake
sam0x17 May 19, 2025
299f9f5
remove ensure, fix tests, add test
camfairchild May 19, 2025
7c447a9
bump spec
camfairchild May 19, 2025
599ac71
add registration to positive test and negative test
camfairchild May 19, 2025
4ccb847
decrement reads
JohnReedV May 19, 2025
3ee10a9
Merge pull request #1652 from opentensor/fix/burn-reg-w-sn-token-disa…
sam0x17 May 19, 2025
70a77f5
Merge pull request #1653 from opentensor/devnet-ready
sam0x17 May 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions pallets/subtensor/src/epoch/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,22 @@ pub fn is_topk(vector: &[I32F32], k: usize) -> Vec<bool> {
result
}

// Returns a bool vector where an item is true if the vector item is in topk values and is non-zero.
#[allow(dead_code, clippy::indexing_slicing)]
pub fn is_topk_nonzero(vector: &[I32F32], k: usize) -> Vec<bool> {
let n: usize = vector.len();
let mut result: Vec<bool> = vector.iter().map(|&elem| elem != I32F32::from(0)).collect();
if n < k {
return result;
}
let mut idxs: Vec<usize> = (0..n).collect();
idxs.sort_by_key(|&idx| &vector[idx]); // ascending stable sort
for &idx in idxs.iter().take(n.saturating_sub(k)) {
result[idx] = false;
}
result
}

// Returns a normalized (sum to 1 except 0) copy of the input vector.
#[allow(dead_code)]
pub fn normalize(x: &[I32F32]) -> Vec<I32F32> {
Expand Down
50 changes: 42 additions & 8 deletions pallets/subtensor/src/epoch/run_epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,26 @@ impl<T: Config> Pallet<T> {
log::trace!("hotkeys: {:?}", &hotkeys);

// Access network stake as normalized vector.
let (mut total_stake, _alpha_stake, _tao_stake): (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) =
let (total_stake, _alpha_stake, _tao_stake): (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) =
Self::get_stake_weights_for_network(netuid);
inplace_normalize_64(&mut total_stake);
let stake: Vec<I32F32> = vec_fixed64_to_fixed32(total_stake);

// Get the minimum stake required.
let min_stake = Self::get_stake_threshold();

// Set stake of validators that doesn't meet the staking threshold to 0 as filter.
let mut filtered_stake: Vec<I64F64> = total_stake
.iter()
.map(|&s| {
if fixed64_to_u64(s) < min_stake {
return I64F64::from(0);
}
s
})
.collect();
log::debug!("Filtered stake: {:?}", &filtered_stake);

inplace_normalize_64(&mut filtered_stake);
let stake: Vec<I32F32> = vec_fixed64_to_fixed32(filtered_stake);
log::trace!("S: {:?}", &stake);

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

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

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

// Access network stake as normalized vector.
let (mut total_stake, _alpha_stake, _tao_stake): (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) =
let (total_stake, _alpha_stake, _tao_stake): (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) =
Self::get_stake_weights_for_network(netuid);
inplace_normalize_64(&mut total_stake);
let stake: Vec<I32F32> = vec_fixed64_to_fixed32(total_stake);

// Get the minimum stake required.
let min_stake = Self::get_stake_threshold();

// Set stake of validators that doesn't meet the staking threshold to 0 as filter.
let mut filtered_stake: Vec<I64F64> = total_stake
.iter()
.map(|&s| {
if fixed64_to_u64(s) < min_stake {
return I64F64::from(0);
}
s
})
.collect();
log::debug!("Filtered stake: {:?}", &filtered_stake);

inplace_normalize_64(&mut filtered_stake);
let stake: Vec<I32F32> = vec_fixed64_to_fixed32(filtered_stake);
log::debug!("Normalised Stake: {:?}", &stake);

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

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

// ==================
Expand Down
4 changes: 2 additions & 2 deletions pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ mod dispatches {
/// User register a new subnetwork via burning token
#[pallet::call_index(7)]
#[pallet::weight((Weight::from_parts(219_400_000, 0)
.saturating_add(T::DbWeight::get().reads(34))
.saturating_add(T::DbWeight::get().reads(33))
.saturating_add(T::DbWeight::get().writes(29)), DispatchClass::Normal, Pays::No))]
pub fn burned_register(
origin: OriginFor<T>,
Expand Down Expand Up @@ -1012,7 +1012,7 @@ mod dispatches {
#[pallet::call_index(75)]
#[pallet::weight((
Weight::from_parts(49_470_000, 0)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2)),
DispatchClass::Normal,
Pays::Yes
Expand Down
2 changes: 1 addition & 1 deletion pallets/subtensor/src/staking/remove_stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ impl<T: Config> Pallet<T> {
pub fn get_max_amount_remove(netuid: u16, limit_price: u64) -> Result<u64, Error<T>> {
// Corner case: root and stao
// There's no slippage for root or stable subnets, so if limit price is 1e9 rao or
// higher, then max_amount equals u64::MAX, otherwise it is 0.
// lower, then max_amount equals u64::MAX, otherwise it is 0.
if (netuid == Self::get_root_netuid()) || (SubnetMechanism::<T>::get(netuid)) == 0 {
if limit_price <= 1_000_000_000 {
return Ok(u64::MAX);
Expand Down
22 changes: 13 additions & 9 deletions pallets/subtensor/src/staking/set_children.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,15 +317,19 @@ impl<T: Config> Pallet<T> {
Error::<T>::InvalidChildkeyTake
);

// Ensure the hotkey passes the rate limit.
ensure!(
Self::passes_rate_limit_on_subnet(
&TransactionType::SetChildkeyTake, // Set childkey take.
&hotkey, // Specific to a hotkey.
netuid, // Specific to a subnet.
),
Error::<T>::TxChildkeyTakeRateLimitExceeded
);
let current_take = Self::get_childkey_take(&hotkey, netuid);
// Check the rate limit for increasing childkey take case
if take > current_take {
// Ensure the hotkey passes the rate limit.
ensure!(
Self::passes_rate_limit_on_subnet(
&TransactionType::SetChildkeyTake, // Set childkey take.
&hotkey, // Specific to a hotkey.
netuid, // Specific to a subnet.
),
Error::<T>::TxChildkeyTakeRateLimitExceeded
);
}

// Set last transaction block
let current_block = Self::get_current_block_as_u64();
Expand Down
2 changes: 0 additions & 2 deletions pallets/subtensor/src/subnets/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,6 @@ impl<T: Config> Pallet<T> {
Error::<T>::SubNetworkDoesNotExist
);

Self::ensure_subtoken_enabled(netuid)?;

// --- 3. Ensure the passed network allows registrations.
ensure!(
Self::get_network_registration_allowed(netuid),
Expand Down
106 changes: 106 additions & 0 deletions pallets/subtensor/src/tests/children.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3988,3 +3988,109 @@ fn test_pending_cooldown_one_day() {
assert_eq!(pending_children.1, curr_block + expected_cooldown);
});
}

#[test]
fn test_do_set_childkey_take_success() {
new_test_ext(1).execute_with(|| {
// Setup
let coldkey = U256::from(1);
let hotkey = U256::from(2);
let netuid: u16 = 1;
let take = 5000;

// Add network and register hotkey
add_network(netuid, 13, 0);
register_ok_neuron(netuid, hotkey, coldkey, 0);

// Set childkey take
assert_ok!(SubtensorModule::do_set_childkey_take(
coldkey, hotkey, netuid, take
));

// Verify the take was set correctly
assert_eq!(SubtensorModule::get_childkey_take(&hotkey, netuid), take);
let tx_type: u16 = TransactionType::SetChildkeyTake.into();
assert_eq!(
TransactionKeyLastBlock::<Test>::get((hotkey, netuid, tx_type,)),
System::block_number()
);
});
}

#[test]
fn test_do_set_childkey_take_non_associated_coldkey() {
new_test_ext(1).execute_with(|| {
// Setup
let coldkey = U256::from(1);
let hotkey = U256::from(2);
let hotkey2 = U256::from(3);
let netuid: u16 = 1;
let take = 5000;

// Add network and register hotkey
add_network(netuid, 13, 0);
register_ok_neuron(netuid, hotkey, coldkey, 0);

// Set childkey take
assert_noop!(
SubtensorModule::do_set_childkey_take(coldkey, hotkey2, netuid, take),
Error::<Test>::NonAssociatedColdKey
);
});
}

#[test]
fn test_do_set_childkey_take_invalid_take_value() {
new_test_ext(1).execute_with(|| {
// Setup
let coldkey = U256::from(1);
let hotkey = U256::from(2);
let netuid: u16 = 1;
let take = SubtensorModule::get_max_childkey_take() + 1;

// Add network and register hotkey
add_network(netuid, 13, 0);
register_ok_neuron(netuid, hotkey, coldkey, 0);

// Set childkey take
assert_noop!(
SubtensorModule::do_set_childkey_take(coldkey, hotkey, netuid, take),
Error::<Test>::InvalidChildkeyTake
);
});
}

#[test]
fn test_do_set_childkey_take_rate_limit_exceeded() {
new_test_ext(1).execute_with(|| {
// Setup
let coldkey = U256::from(1);
let hotkey = U256::from(2);
let netuid: u16 = 1;
let initial_take = 3000;
let higher_take = 5000;
let lower_take = 1000;

add_network(netuid, 13, 0);
register_ok_neuron(netuid, hotkey, coldkey, 0);

// Set initial childkey take
assert_ok!(SubtensorModule::do_set_childkey_take(
coldkey,
hotkey,
netuid,
initial_take
));

// Try to increase the take value, should hit rate limit
assert_noop!(
SubtensorModule::do_set_childkey_take(coldkey, hotkey, netuid, higher_take),
Error::<Test>::TxChildkeyTakeRateLimitExceeded
);

// lower take value should be ok
assert_ok!(SubtensorModule::do_set_childkey_take(
coldkey, hotkey, netuid, lower_take
));
});
}
8 changes: 5 additions & 3 deletions pallets/subtensor/src/tests/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2095,13 +2095,14 @@ fn test_deregistered_miner_bonds() {
});
}

// Test that epoch assigns validator permits to highest stake uids, varies uid interleaving and stake values.
// Test that epoch assigns validator permits to highest stake uids that are over the stake threshold, varies uid interleaving and stake values.
#[test]
fn test_validator_permits() {
let netuid: u16 = 1;
let tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead
for interleave in 0..3 {
for (network_n, validators_n) in [(2, 1), (4, 2), (8, 4)] {
let min_stake = validators_n as u64;
for assignment in 0..=1 {
let (validators, servers) =
distribute_nodes(validators_n as usize, network_n, interleave as usize);
Expand Down Expand Up @@ -2132,6 +2133,7 @@ fn test_validator_permits() {
netuid,
network_n as u16,
);
SubtensorModule::set_stake_threshold(min_stake);

// === Register [validator1, validator2, server1, server2]
for key in 0..network_n as u64 {
Expand Down Expand Up @@ -2173,7 +2175,7 @@ fn test_validator_permits() {
SubtensorModule::epoch(netuid, 1_000_000_000); // run first epoch to set allowed validators
for validator in &validators {
assert_eq!(
correct,
stake[*validator as usize] >= min_stake,
SubtensorModule::get_validator_permit_for_uid(netuid, *validator)
);
}
Expand Down Expand Up @@ -2211,7 +2213,7 @@ fn test_validator_permits() {
}
for server in &servers {
assert_eq!(
correct,
(stake[*server as usize] + (2 * network_n as u64)) >= min_stake,
SubtensorModule::get_validator_permit_for_uid(netuid, *server)
);
}
Expand Down
Loading
Loading