Skip to content

Commit af50dd6

Browse files
author
unconst
committed
adds a doublt Tempo limit to setting childkeys
1 parent cd76142 commit af50dd6

File tree

6 files changed

+137
-33
lines changed

6 files changed

+137
-33
lines changed

pallets/subtensor/src/coinbase/run_coinbase.rs

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use super::*;
2-
use frame_support::storage::IterableStorageDoubleMap;
32
use substrate_fixed::types::I64F64;
43
use substrate_fixed::types::I96F32;
54

@@ -262,7 +261,7 @@ impl<T: Config> Pallet<T> {
262261
PendingdHotkeyEmission::<T>::insert(hotkey, 0);
263262

264263
// --- 2 Retrieve the last time this hotkey's emissions were drained.
265-
let last_hotkey_emission_drain: u64 = LastHotkeyEmissionDrain::<T>::get(hotkey);
264+
let last_emission_drain: u64 = LastHotkeyEmissionDrain::<T>::get(hotkey);
266265

267266
// --- 3 Update the block value to the current block number.
268267
LastHotkeyEmissionDrain::<T>::insert(hotkey, block_number);
@@ -282,43 +281,48 @@ impl<T: Config> Pallet<T> {
282281
// --- 7 Calculate the remaining emission after the hotkey's take.
283282
let mut remainder: u64 = emission_minus_take;
284283

285-
// --- 8 Iterate over each nominator.
286-
for (nominator, nominator_stake) in
287-
<Stake<T> as IterableStorageDoubleMap<T::AccountId, T::AccountId, u64>>::iter_prefix(
288-
hotkey,
289-
)
290-
{
291-
// --- 9 Check if the stake was manually increased by the user since the last emission drain for this hotkey.
292-
// If it was, skip this nominator as they will not receive their proportion of the emission.
293-
if LastAddStakeIncrease::<T>::get(hotkey, nominator.clone())
294-
> last_hotkey_emission_drain
295-
{
296-
continue;
284+
// --- 8 Iterate over each nominator and get all viable stake.
285+
let mut total_viable_nominator_stake: u64 = total_hotkey_stake;
286+
for (nominator, nominator_stake) in Stake::<T>::iter_prefix(hotkey) {
287+
if LastAddStakeIncrease::<T>::get(hotkey, nominator) > last_emission_drain {
288+
total_viable_nominator_stake =
289+
total_viable_nominator_stake.saturating_sub(nominator_stake);
297290
}
291+
}
298292

299-
// --- 10 Calculate this nominator's share of the emission.
300-
let nominator_emission: I64F64 = I64F64::from_num(emission_minus_take)
301-
.saturating_mul(I64F64::from_num(nominator_stake))
302-
.checked_div(I64F64::from_num(total_hotkey_stake))
303-
.unwrap_or(I64F64::from_num(0));
304-
305-
// --- 11 Increase the stake for the nominator.
306-
Self::increase_stake_on_coldkey_hotkey_account(
307-
&nominator,
308-
hotkey,
309-
nominator_emission.to_num::<u64>(),
310-
);
293+
// --- 9 Iterate over each nominator.
294+
if total_viable_nominator_stake != 0 {
295+
for (nominator, nominator_stake) in Stake::<T>::iter_prefix(hotkey) {
296+
// --- 10 Check if the stake was manually increased by the user since the last emission drain for this hotkey.
297+
// If it was, skip this nominator as they will not receive their proportion of the emission.
298+
if LastAddStakeIncrease::<T>::get(hotkey, nominator.clone()) > last_emission_drain {
299+
continue;
300+
}
311301

312-
// --- 11* Record event and Subtract the nominator's emission from the remainder.
313-
total_new_tao = total_new_tao.saturating_add(nominator_emission.to_num::<u64>());
314-
remainder = remainder.saturating_sub(nominator_emission.to_num::<u64>());
302+
// --- 11 Calculate this nominator's share of the emission.
303+
let nominator_emission: I64F64 = I64F64::from_num(emission_minus_take)
304+
.saturating_mul(I64F64::from_num(nominator_stake))
305+
.checked_div(I64F64::from_num(total_viable_nominator_stake))
306+
.unwrap_or(I64F64::from_num(0));
307+
308+
// --- 12 Increase the stake for the nominator.
309+
Self::increase_stake_on_coldkey_hotkey_account(
310+
&nominator,
311+
hotkey,
312+
nominator_emission.to_num::<u64>(),
313+
);
314+
315+
// --- 13* Record event and Subtract the nominator's emission from the remainder.
316+
total_new_tao = total_new_tao.saturating_add(nominator_emission.to_num::<u64>());
317+
remainder = remainder.saturating_sub(nominator_emission.to_num::<u64>());
318+
}
315319
}
316320

317-
// --- 13 Finally, add the stake to the hotkey itself, including its take and the remaining emission.
321+
// --- 14 Finally, add the stake to the hotkey itself, including its take and the remaining emission.
318322
let hotkey_new_tao: u64 = hotkey_take.saturating_add(remainder);
319323
Self::increase_stake_on_hotkey_account(hotkey, hotkey_new_tao);
320324

321-
// --- 14 Record new tao creation event and return the amount created.
325+
// --- 15 Record new tao creation event and return the amount created.
322326
total_new_tao = total_new_tao.saturating_add(hotkey_new_tao);
323327
total_new_tao
324328
}

pallets/subtensor/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub mod staking;
4444
pub mod subnets;
4545
pub mod swap;
4646
pub mod utils;
47+
use crate::utils::TransactionType;
4748
use macros::{config, dispatches, errors, events, genesis, hooks};
4849

4950
// apparently this is stabilized since rust 1.36
@@ -1051,6 +1052,17 @@ pub mod pallet {
10511052
/// =================================
10521053
/// ==== Axon / Promo Endpoints =====
10531054
/// =================================
1055+
#[pallet::storage] // --- NMAP ( hot, netuid, name ) --> last_block | Returns the last block of a transaction for a given key, netuid, and name.
1056+
pub type TransactionKeyLastBlock<T: Config> = StorageNMap<
1057+
_,
1058+
(
1059+
NMapKey<Blake2_128Concat, T::AccountId>, // hot
1060+
NMapKey<Identity, u16>, // netuid
1061+
NMapKey<Identity, u16>, // extrinsic enum.
1062+
),
1063+
u64,
1064+
ValueQuery,
1065+
>;
10541066
#[pallet::storage]
10551067
/// --- MAP ( key ) --> last_block
10561068
pub type LastTxBlock<T: Config> =

pallets/subtensor/src/macros/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,5 +166,7 @@ mod errors {
166166
ProportionOverflow,
167167
/// Too many children MAX 5.
168168
TooManyChildren,
169+
/// Default transaction rate limit exceeded.
170+
TxRateLimitExceeded,
169171
}
170172
}

pallets/subtensor/src/staking/add_stake.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ impl<T: Config> Pallet<T> {
7070
Error::<T>::StakeRateLimitExceeded
7171
);
7272

73-
// If this is a nomination stake, check if total stake after adding will be above
74-
// the minimum required stake.
73+
// Set the last time the stake increased for nominator drain protection.
74+
LastAddStakeIncrease::<T>::insert(&hotkey, &coldkey, Self::get_current_block_as_u64());
7575

7676
// If coldkey is not owner of the hotkey, it's a nomination stake.
7777
if !Self::coldkey_owns_hotkey(&coldkey, &hotkey) {

pallets/subtensor/src/staking/set_children.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ impl<T: Config> Pallet<T> {
5858
children
5959
);
6060

61+
// Ensure the hotkey passes the rate limit.
62+
ensure!(
63+
Self::passes_rate_limit_globally(
64+
&TransactionType::SetChildren, // Set children.
65+
&hotkey, // Specific to a hotkey.
66+
),
67+
Error::<T>::TxRateLimitExceeded
68+
);
69+
6170
// --- 2. Check that this delegation is not on the root network. Child hotkeys are not valid on root.
6271
ensure!(
6372
netuid != Self::get_root_netuid(),

pallets/subtensor/src/utils.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,33 @@ use sp_core::Get;
77
use sp_core::U256;
88
use substrate_fixed::types::I32F32;
99

10+
/// Enum representing different types of transactions
11+
#[derive(Copy, Clone)]
12+
pub enum TransactionType {
13+
SetChildren,
14+
Unknown,
15+
}
16+
17+
/// Implement conversion from TransactionType to u16
18+
impl From<TransactionType> for u16 {
19+
fn from(tx_type: TransactionType) -> Self {
20+
match tx_type {
21+
TransactionType::SetChildren => 0,
22+
TransactionType::Unknown => 1,
23+
}
24+
}
25+
}
26+
27+
/// Implement conversion from u16 to TransactionType
28+
impl From<u16> for TransactionType {
29+
fn from(value: u16) -> Self {
30+
match value {
31+
0 => TransactionType::SetChildren,
32+
_ => TransactionType::Unknown,
33+
}
34+
}
35+
}
36+
1037
impl<T: Config> Pallet<T> {
1138
pub fn ensure_subnet_owner_or_root(
1239
o: T::RuntimeOrigin,
@@ -278,6 +305,56 @@ impl<T: Config> Pallet<T> {
278305
// ========================
279306
// ==== Rate Limiting =====
280307
// ========================
308+
/// Get the rate limit for a specific transaction type
309+
pub fn get_rate_limit(tx_type: &TransactionType) -> u64 {
310+
match tx_type {
311+
TransactionType::SetChildren => (DefaultTempo::<T>::get().saturating_mul(2)).into(), // Cannot set children twice within the default tempo period.
312+
TransactionType::Unknown => 0, // Default to no limit for unknown types (no limit)
313+
}
314+
}
315+
316+
/// Check if a transaction should be rate limited on a specific subnet
317+
pub fn passes_rate_limit_on_subnet(
318+
tx_type: &TransactionType,
319+
hotkey: &T::AccountId,
320+
netuid: u16,
321+
) -> bool {
322+
let block: u64 = Self::get_current_block_as_u64();
323+
let limit: u64 = Self::get_rate_limit(tx_type);
324+
let last_block: u64 = Self::get_last_transaction_block(hotkey, netuid, tx_type);
325+
block.saturating_sub(last_block) < limit
326+
}
327+
328+
/// Check if a transaction should be rate limited globally
329+
pub fn passes_rate_limit_globally(tx_type: &TransactionType, hotkey: &T::AccountId) -> bool {
330+
let netuid: u16 = u16::MAX;
331+
let block: u64 = Self::get_current_block_as_u64();
332+
let limit: u64 = Self::get_rate_limit(tx_type);
333+
let last_block: u64 = Self::get_last_transaction_block(hotkey, netuid, tx_type);
334+
block.saturating_sub(last_block) >= limit
335+
}
336+
337+
/// Get the block number of the last transaction for a specific hotkey, network, and transaction type
338+
pub fn get_last_transaction_block(
339+
hotkey: &T::AccountId,
340+
netuid: u16,
341+
tx_type: &TransactionType,
342+
) -> u64 {
343+
let tx_as_u16: u16 = (*tx_type).into();
344+
TransactionKeyLastBlock::<T>::get((hotkey, netuid, tx_as_u16))
345+
}
346+
347+
/// Set the block number of the last transaction for a specific hotkey, network, and transaction type
348+
pub fn set_last_transaction_block(
349+
hotkey: &T::AccountId,
350+
netuid: u16,
351+
tx_type: &TransactionType,
352+
block: u64,
353+
) {
354+
let tx_as_u16: u16 = (*tx_type).into();
355+
TransactionKeyLastBlock::<T>::insert((hotkey, netuid, tx_as_u16), block);
356+
}
357+
281358
pub fn set_last_tx_block(key: &T::AccountId, block: u64) {
282359
LastTxBlock::<T>::insert(key, block)
283360
}

0 commit comments

Comments
 (0)