Skip to content
3 changes: 3 additions & 0 deletions pallets/admin-utils/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ parameter_types! {
pub const InitialNetworkLockReductionInterval: u64 = 2; // 2 blocks.
pub const InitialSubnetLimit: u16 = 10; // Max 10 subnets.
pub const InitialNetworkRateLimit: u64 = 0;
pub const InitialTargetStakesPerInterval: u16 = 1;

}

impl pallet_subtensor::Config for Test {
Expand Down Expand Up @@ -158,6 +160,7 @@ impl pallet_subtensor::Config for Test {
type InitialNetworkLockReductionInterval = InitialNetworkLockReductionInterval;
type InitialSubnetLimit = InitialSubnetLimit;
type InitialNetworkRateLimit = InitialNetworkRateLimit;
type InitialTargetStakesPerInterval = InitialTargetStakesPerInterval;
}

impl system::Config for Test {
Expand Down
62 changes: 53 additions & 9 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ pub mod pallet {
type InitialSubnetLimit: Get<u16>;
#[pallet::constant] // Initial network creation rate limit
type InitialNetworkRateLimit: Get<u64>;
#[pallet::constant] // Initial target stakes per interval issuance.
type InitialTargetStakesPerInterval: Get<u64>;
}

pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
Expand Down Expand Up @@ -212,6 +214,10 @@ pub mod pallet {
0
}
#[pallet::type_value]
pub fn DefaultStakesPerInterval<T: Config>() -> (u64, u64) {
(0, 0)
}
#[pallet::type_value]
pub fn DefaultBlockEmission<T: Config>() -> u64 {
1_000_000_000
}
Expand All @@ -227,6 +233,14 @@ pub mod pallet {
pub fn DefaultAccount<T: Config>() -> T::AccountId {
T::AccountId::decode(&mut TrailingZeroInput::zeroes()).unwrap()
}
#[pallet::type_value]
pub fn DefaultTargetStakesPerInterval<T: Config>() -> u64 {
T::InitialTargetStakesPerInterval::get()
}
#[pallet::type_value]
pub fn DefaultStakeInterval<T: Config>() -> u64 {
360
}

#[pallet::storage] // --- ITEM ( total_stake )
pub type TotalStake<T> = StorageValue<_, u64, ValueQuery>;
Expand All @@ -236,12 +250,22 @@ pub mod pallet {
pub type BlockEmission<T> = StorageValue<_, u64, ValueQuery, DefaultBlockEmission<T>>;
#[pallet::storage] // --- ITEM ( total_issuance )
pub type TotalIssuance<T> = StorageValue<_, u64, ValueQuery, DefaultTotalIssuance<T>>;
#[pallet::storage] // --- ITEM (target_stakes_per_interval)
pub type TargetStakesPerInterval<T> =
StorageValue<_, u64, ValueQuery, DefaultTargetStakesPerInterval<T>>;
#[pallet::storage] // --- ITEM (default_stake_interval)
pub type StakeInterval<T> = StorageValue<_, u64, ValueQuery, DefaultStakeInterval<T>>;
#[pallet::storage] // --- MAP ( hot ) --> stake | Returns the total amount of stake under a hotkey.
pub type TotalHotkeyStake<T: Config> =
StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultAccountTake<T>>;
#[pallet::storage] // --- MAP ( cold ) --> stake | Returns the total amount of stake under a coldkey.
pub type TotalColdkeyStake<T: Config> =
StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultAccountTake<T>>;
#[pallet::storage]
// --- MAP (hot) --> stake | Returns a tuple (u64: stakes, u64: block_number)
pub type TotalHotkeyStakesThisInterval<T: Config> =
StorageMap<_, Identity, T::AccountId, (u64, u64), ValueQuery, DefaultStakesPerInterval<T>>;

#[pallet::storage] // --- MAP ( hot ) --> cold | Returns the controlling coldkey for a hotkey.
pub type Owner<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId, ValueQuery, DefaultAccount<T>>;
Expand Down Expand Up @@ -924,7 +948,9 @@ pub mod pallet {
MaxAllowedUidsExceeded, // --- Thrown when number of accounts going to be registered exceeds MaxAllowedUids for the network.
TooManyUids, // ---- Thrown when the caller attempts to set weights with more uids than allowed.
TxRateLimitExceeded, // --- Thrown when a transactor exceeds the rate limit for transactions.
RegistrationDisabled, // --- Thrown when registration is disabled
StakeRateLimitExceeded, // --- Thrown when a transactor exceeds the rate limit for stakes.
UnstakeRateLimitExceeded, // --- Thrown when a transactor exceeds the rate limit for unstakes.
RegistrationDisabled, // --- Thrown when registration is disabled
TooManyRegistrationsThisInterval, // --- Thrown when registration attempt exceeds allowed in interval
BenchmarkingOnly, // --- Thrown when a function is only available for benchmarking
HotkeyOriginMismatch, // --- Thrown when the hotkey passed is not the origin, but it should be
Expand Down Expand Up @@ -1822,14 +1848,32 @@ where
return Err(InvalidTransaction::Call.into());
}
}
Some(Call::add_stake { .. }) => Ok(ValidTransaction {
priority: Self::get_priority_vanilla(),
..Default::default()
}),
Some(Call::remove_stake { .. }) => Ok(ValidTransaction {
priority: Self::get_priority_vanilla(),
..Default::default()
}),
Some(Call::add_stake { hotkey, .. }) => {
let stakes_this_interval = Pallet::<T>::get_stakes_this_interval_for_hotkey(hotkey);
let max_stakes_per_interval = Pallet::<T>::get_target_stakes_per_interval();

if stakes_this_interval >= max_stakes_per_interval {
return InvalidTransaction::ExhaustsResources.into();
}

Ok(ValidTransaction {
priority: Self::get_priority_vanilla(),
..Default::default()
})
}
Some(Call::remove_stake { hotkey, .. }) => {
let stakes_this_interval = Pallet::<T>::get_stakes_this_interval_for_hotkey(hotkey);
let max_stakes_per_interval = Pallet::<T>::get_target_stakes_per_interval();

if stakes_this_interval >= max_stakes_per_interval {
return InvalidTransaction::ExhaustsResources.into();
}

Ok(ValidTransaction {
priority: Self::get_priority_vanilla(),
..Default::default()
})
}
Some(Call::register { netuid, .. } | Call::burned_register { netuid, .. }) => {
let registrations_this_interval =
Pallet::<T>::get_registrations_this_interval(*netuid);
Expand Down
71 changes: 63 additions & 8 deletions pallets/subtensor/src/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,27 +164,39 @@ impl<T: Config> Pallet<T> {
Error::<T>::TxRateLimitExceeded
);

// --- 7. Ensure the remove operation from the coldkey is a success.
// --- 7. Ensure we don't exceed stake rate limit
let stakes_this_interval = Self::get_stakes_this_interval_for_hotkey(&hotkey);
ensure!(
stakes_this_interval < Self::get_target_stakes_per_interval(),
Error::<T>::StakeRateLimitExceeded
);

// --- 8. Ensure the remove operation from the coldkey is a success.
ensure!(
Self::remove_balance_from_coldkey_account(&coldkey, stake_as_balance.unwrap()) == true,
Error::<T>::BalanceWithdrawalError
);

// --- 8. If we reach here, add the balance to the hotkey.
// --- 9. If we reach here, add the balance to the hotkey.
Self::increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake_to_be_added);

// Set last block for rate limiting
Self::set_last_tx_block(&coldkey, block);

// --- 9. Emit the staking event.
// --- 10. Emit the staking event.
Self::set_stakes_this_interval_for_hotkey(
&hotkey,
stakes_this_interval + 1,
block,
);
log::info!(
"StakeAdded( hotkey:{:?}, stake_to_be_added:{:?} )",
hotkey,
stake_to_be_added
);
Self::deposit_event(Event::StakeAdded(hotkey, stake_to_be_added));

// --- 10. Ok and return.
// --- 11. Ok and return.
Ok(())
}

Expand Down Expand Up @@ -273,24 +285,36 @@ impl<T: Config> Pallet<T> {
Error::<T>::TxRateLimitExceeded
);

// --- 7. We remove the balance from the hotkey.
// --- 7. Ensure we don't exceed stake rate limit
let unstakes_this_interval = Self::get_stakes_this_interval_for_hotkey(&hotkey);
ensure!(
unstakes_this_interval < Self::get_target_stakes_per_interval(),
Error::<T>::UnstakeRateLimitExceeded
);

// --- 8. We remove the balance from the hotkey.
Self::decrease_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake_to_be_removed);

// --- 8. We add the balancer to the coldkey. If the above fails we will not credit this coldkey.
// --- 9. We add the balancer to the coldkey. If the above fails we will not credit this coldkey.
Self::add_balance_to_coldkey_account(&coldkey, stake_to_be_added_as_currency.unwrap());

// Set last block for rate limiting
Self::set_last_tx_block(&coldkey, block);

// --- 9. Emit the unstaking event.
// --- 10. Emit the unstaking event.
Self::set_stakes_this_interval_for_hotkey(
&hotkey,
unstakes_this_interval + 1,
block,
);
log::info!(
"StakeRemoved( hotkey:{:?}, stake_to_be_removed:{:?} )",
hotkey,
stake_to_be_removed
);
Self::deposit_event(Event::StakeRemoved(hotkey, stake_to_be_removed));

// --- 10. Done and ok.
// --- 11. Done and ok.
Ok(())
}

Expand Down Expand Up @@ -342,6 +366,37 @@ impl<T: Config> Pallet<T> {
return Stake::<T>::get(hotkey, coldkey);
}

// Retrieves the total stakes for a given hotkey (account ID) for the current staking interval.
pub fn get_stakes_this_interval_for_hotkey(hotkey: &T::AccountId) -> u64 {
// Retrieve the configured stake interval duration from storage.
let stake_interval = StakeInterval::<T>::get();

// Obtain the current block number as an unsigned 64-bit integer.
let current_block = Self::get_current_block_as_u64();

// Fetch the total stakes and the last block number when stakes were made for the hotkey.
let (stakes, block_last_staked_at) = TotalHotkeyStakesThisInterval::<T>::get(hotkey);

// Calculate the block number after which the stakes for the hotkey should be reset.
let block_to_reset_after = block_last_staked_at + stake_interval;

// If the current block number is beyond the reset point,
// it indicates the end of the staking interval for the hotkey.
if block_to_reset_after <= current_block {
// Reset the stakes for this hotkey for the current interval.
Self::set_stakes_this_interval_for_hotkey(hotkey, 0, block_last_staked_at);
// Return 0 as the stake amount since we've just reset the stakes.
return 0;
}

// If the staking interval has not yet ended, return the current stake amount.
stakes
}

pub fn get_target_stakes_per_interval() -> u64 {
return TargetStakesPerInterval::<T>::get();
}

// Creates a cold - hot pairing account if the hotkey is not already an active account.
//
pub fn create_account_if_non_existent(coldkey: &T::AccountId, hotkey: &T::AccountId) {
Expand Down
10 changes: 9 additions & 1 deletion pallets/subtensor/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,15 @@ impl<T: Config> Pallet<T> {
WeightsMinStake::<T>::put(min_stake);
Self::deposit_event(Event::WeightsMinStake(min_stake));
}

pub fn set_target_stakes_per_interval(target_stakes_per_interval: u64) {
TargetStakesPerInterval::<T>::set(target_stakes_per_interval)
}
pub fn set_stakes_this_interval_for_hotkey(hotkey: &T::AccountId, stakes_this_interval: u64, last_staked_block_number: u64) {
TotalHotkeyStakesThisInterval::<T>::insert(hotkey, (stakes_this_interval, last_staked_block_number));
}
pub fn set_stake_interval(block: u64) {
StakeInterval::<T>::set(block);
}
pub fn get_rank_for_uid(netuid: u16, uid: u16) -> u16 {
let vec = Rank::<T>::get(netuid);
if (uid as usize) < vec.len() {
Expand Down
2 changes: 2 additions & 0 deletions pallets/subtensor/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ parameter_types! {
pub const InitialNetworkLockReductionInterval: u64 = 2; // 2 blocks.
pub const InitialSubnetLimit: u16 = 10; // Max 10 subnets.
pub const InitialNetworkRateLimit: u64 = 0;
pub const InitialTargetStakesPerInterval: u16 = 1;
}

// Configure collective pallet for council
Expand Down Expand Up @@ -357,6 +358,7 @@ impl pallet_subtensor::Config for Test {
type InitialNetworkLockReductionInterval = InitialNetworkLockReductionInterval;
type InitialSubnetLimit = InitialSubnetLimit;
type InitialNetworkRateLimit = InitialNetworkRateLimit;
type InitialTargetStakesPerInterval = InitialTargetStakesPerInterval;
}

impl pallet_utility::Config for Test {
Expand Down
2 changes: 2 additions & 0 deletions pallets/subtensor/tests/senate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,8 @@ fn test_senate_not_leave_when_stake_removed() {
let burn_cost = 1000;
let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har

SubtensorModule::set_target_stakes_per_interval(2);

//add network
SubtensorModule::set_burn(netuid, burn_cost);
add_network(netuid, tempo, 0);
Expand Down
Loading