Skip to content

Commit b07fdbb

Browse files
committed
Hotfix/only exclude add stake from emission no packing (#807)
* add impl * add tests * add impl * chore: clippy * oops fix test * bump spec * add test from PR #805 * Revert "bump spec" This reverts commit 00ad4bf. * chore: fmt * remove lastaddstakeincrease logic * fix comment command * coinbase test with no deltas * add tests * renumber and remove * fix for childkey set weights * fix test for last commit * clippy: allow manual inspect * chore: fmt * add shebang * chore: clippy * move assert up * move clippy allow to toml * add fuzzy assert * fix neg delta test * Fix tests with correct take percent * fix typo
1 parent 87a5e3c commit b07fdbb

File tree

15 files changed

+1423
-132
lines changed

15 files changed

+1423
-132
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ indexing-slicing = "deny"
4141
arithmetic-side-effects = "deny"
4242
type_complexity = "allow"
4343
unwrap-used = "deny"
44+
manual_inspect = "allow"
4445

4546
[workspace.dependencies]
4647
cargo-husky = { version = "1", default-features = false }

pallets/subtensor/src/coinbase/run_coinbase.rs

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,20 @@ impl<T: Config> Pallet<T> {
249249
});
250250
}
251251

252+
/// Calculates the nonviable stake for a nominator.
253+
/// The nonviable stake is the stake that was added by the nominator since the last emission drain.
254+
/// This stake will not receive emission until the next emission drain.
255+
/// Note: if the stake delta is below zero, we return zero. We don't allow more stake than the nominator has.
256+
pub fn get_nonviable_stake(hotkey: &T::AccountId, nominator: &T::AccountId) -> u64 {
257+
let stake_delta = StakeDeltaSinceLastEmissionDrain::<T>::get(hotkey, nominator);
258+
if stake_delta.is_negative() {
259+
0
260+
} else {
261+
// Should never fail the into, but we handle it anyway.
262+
stake_delta.try_into().unwrap_or(u64::MAX)
263+
}
264+
}
265+
252266
//. --- 4. Drains the accumulated hotkey emission through to the nominators. The hotkey takes a proportion of the emission.
253267
/// The remainder is drained through to the nominators keeping track of the last stake increase event to ensure that the hotkey does not
254268
/// gain more emission than it's stake since the last drain.
@@ -268,71 +282,67 @@ impl<T: Config> Pallet<T> {
268282
// --- 1.0 Drain the hotkey emission.
269283
PendingdHotkeyEmission::<T>::insert(hotkey, 0);
270284

271-
// --- 2 Retrieve the last time this hotkey's emissions were drained.
272-
let last_emission_drain: u64 = LastHotkeyEmissionDrain::<T>::get(hotkey);
273-
274-
// --- 3 Update the block value to the current block number.
285+
// --- 2 Update the block value to the current block number.
275286
LastHotkeyEmissionDrain::<T>::insert(hotkey, block_number);
276287

277-
// --- 4 Retrieve the total stake for the hotkey from all nominations.
288+
// --- 3 Retrieve the total stake for the hotkey from all nominations.
278289
let total_hotkey_stake: u64 = Self::get_total_stake_for_hotkey(hotkey);
279290

280-
// --- 5 Calculate the emission take for the hotkey.
291+
// --- 4 Calculate the emission take for the hotkey.
281292
let take_proportion: I64F64 = I64F64::from_num(Delegates::<T>::get(hotkey))
282293
.saturating_div(I64F64::from_num(u16::MAX));
283294
let hotkey_take: u64 =
284295
(take_proportion.saturating_mul(I64F64::from_num(emission))).to_num::<u64>();
285296

286-
// --- 6 Compute the remaining emission after deducting the hotkey's take.
297+
// --- 5 Compute the remaining emission after deducting the hotkey's take.
287298
let emission_minus_take: u64 = emission.saturating_sub(hotkey_take);
288299

289-
// --- 7 Calculate the remaining emission after the hotkey's take.
300+
// --- 6 Calculate the remaining emission after the hotkey's take.
290301
let mut remainder: u64 = emission_minus_take;
291302

292-
// --- 8 Iterate over each nominator and get all viable stake.
303+
// --- 7 Iterate over each nominator and get all viable stake.
293304
let mut total_viable_nominator_stake: u64 = total_hotkey_stake;
294-
for (nominator, nominator_stake) in Stake::<T>::iter_prefix(hotkey) {
295-
if false && LastAddStakeIncrease::<T>::get(hotkey, nominator) > last_emission_drain {
296-
total_viable_nominator_stake =
297-
total_viable_nominator_stake.saturating_sub(nominator_stake);
298-
}
305+
for (nominator, _) in Stake::<T>::iter_prefix(hotkey) {
306+
let nonviable_nomintaor_stake = Self::get_nonviable_stake(hotkey, &nominator);
307+
308+
total_viable_nominator_stake =
309+
total_viable_nominator_stake.saturating_sub(nonviable_nomintaor_stake);
299310
}
300311

301-
// --- 9 Iterate over each nominator.
312+
// --- 8 Iterate over each nominator.
302313
if total_viable_nominator_stake != 0 {
303314
for (nominator, nominator_stake) in Stake::<T>::iter_prefix(hotkey) {
304-
// --- 10 Check if the stake was manually increased by the user since the last emission drain for this hotkey.
305-
// If it was, skip this nominator as they will not receive their proportion of the emission.
306-
if false
307-
&& LastAddStakeIncrease::<T>::get(hotkey, nominator.clone())
308-
> last_emission_drain
309-
{
310-
continue;
311-
}
315+
// --- 9 Skip emission for any stake the was added by the nominator since the last emission drain.
316+
// This means the nominator will get emission on existing stake, but not on new stake, until the next emission drain.
317+
let viable_nominator_stake =
318+
nominator_stake.saturating_sub(Self::get_nonviable_stake(hotkey, &nominator));
312319

313-
// --- 11 Calculate this nominator's share of the emission.
314-
let nominator_emission: I64F64 = I64F64::from_num(nominator_stake)
320+
// --- 10 Calculate this nominator's share of the emission.
321+
let nominator_emission: I64F64 = I64F64::from_num(viable_nominator_stake)
315322
.checked_div(I64F64::from_num(total_viable_nominator_stake))
316323
.unwrap_or(I64F64::from_num(0))
317324
.saturating_mul(I64F64::from_num(emission_minus_take));
318325

319-
// --- 12 Increase the stake for the nominator.
326+
// --- 11 Increase the stake for the nominator.
320327
Self::increase_stake_on_coldkey_hotkey_account(
321328
&nominator,
322329
hotkey,
323330
nominator_emission.to_num::<u64>(),
324331
);
325332

326-
// --- 13* Record event and Subtract the nominator's emission from the remainder.
333+
// --- 12* Record event and Subtract the nominator's emission from the remainder.
327334
total_new_tao = total_new_tao.saturating_add(nominator_emission.to_num::<u64>());
328335
remainder = remainder.saturating_sub(nominator_emission.to_num::<u64>());
329336
}
330337
}
331338

332-
// --- 14 Finally, add the stake to the hotkey itself, including its take and the remaining emission.
339+
// --- 13 Finally, add the stake to the hotkey itself, including its take and the remaining emission.
333340
let hotkey_new_tao: u64 = hotkey_take.saturating_add(remainder);
334341
Self::increase_stake_on_hotkey_account(hotkey, hotkey_new_tao);
335342

343+
// --- 14 Reset the stake delta for the hotkey.
344+
let _ = StakeDeltaSinceLastEmissionDrain::<T>::clear_prefix(hotkey, u32::MAX, None);
345+
336346
// --- 15 Record new tao creation event and return the amount created.
337347
total_new_tao = total_new_tao.saturating_add(hotkey_new_tao);
338348
total_new_tao

pallets/subtensor/src/lib.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@ pub mod pallet {
226226
0
227227
}
228228
#[pallet::type_value]
229+
/// Default stake delta.
230+
pub fn DefaultStakeDelta<T: Config>() -> i128 {
231+
0
232+
}
233+
#[pallet::type_value]
229234
/// Default stakes per interval.
230235
pub fn DefaultStakesPerInterval<T: Config>() -> (u64, u64) {
231236
(0, 0)
@@ -770,6 +775,18 @@ pub mod pallet {
770775
DefaultAccountTake<T>,
771776
>;
772777
#[pallet::storage]
778+
/// Map ( hot, cold ) --> stake: i128 | Stake added/removed since last emission drain.
779+
pub type StakeDeltaSinceLastEmissionDrain<T: Config> = StorageDoubleMap<
780+
_,
781+
Blake2_128Concat,
782+
T::AccountId,
783+
Identity,
784+
T::AccountId,
785+
i128,
786+
ValueQuery,
787+
DefaultStakeDelta<T>,
788+
>;
789+
#[pallet::storage]
773790
/// DMAP ( parent, netuid ) --> Vec<(proportion,child)>
774791
pub type ChildKeys<T: Config> = StorageDoubleMap<
775792
_,
@@ -1250,7 +1267,7 @@ pub mod pallet {
12501267
/// Returns the transaction priority for setting weights.
12511268
pub fn get_priority_set_weights(hotkey: &T::AccountId, netuid: u16) -> u64 {
12521269
if let Ok(uid) = Self::get_uid_for_net_and_hotkey(netuid, hotkey) {
1253-
let _stake = Self::get_total_stake_for_hotkey(hotkey);
1270+
let _stake = Self::get_stake_for_hotkey_on_subnet(hotkey, netuid);
12541271
let current_block_number: u64 = Self::get_current_block_as_u64();
12551272
let default_priority: u64 =
12561273
current_block_number.saturating_sub(Self::get_last_update_for_uid(netuid, uid));
@@ -1260,9 +1277,9 @@ pub mod pallet {
12601277
}
12611278

12621279
/// Is the caller allowed to set weights
1263-
pub fn check_weights_min_stake(hotkey: &T::AccountId) -> bool {
1280+
pub fn check_weights_min_stake(hotkey: &T::AccountId, netuid: u16) -> bool {
12641281
// Blacklist weights transactions for low stake peers.
1265-
Self::get_total_stake_for_hotkey(hotkey) >= Self::get_weights_min_stake()
1282+
Self::get_stake_for_hotkey_on_subnet(hotkey, netuid) >= Self::get_weights_min_stake()
12661283
}
12671284

12681285
/// Helper function to check if register is allowed
@@ -1355,8 +1372,8 @@ where
13551372
Pallet::<T>::get_priority_set_weights(who, netuid)
13561373
}
13571374

1358-
pub fn check_weights_min_stake(who: &T::AccountId) -> bool {
1359-
Pallet::<T>::check_weights_min_stake(who)
1375+
pub fn check_weights_min_stake(who: &T::AccountId, netuid: u16) -> bool {
1376+
Pallet::<T>::check_weights_min_stake(who, netuid)
13601377
}
13611378
}
13621379

@@ -1394,7 +1411,7 @@ where
13941411
) -> TransactionValidity {
13951412
match call.is_sub_type() {
13961413
Some(Call::commit_weights { netuid, .. }) => {
1397-
if Self::check_weights_min_stake(who) {
1414+
if Self::check_weights_min_stake(who, *netuid) {
13981415
let priority: u64 = Self::get_priority_set_weights(who, *netuid);
13991416
Ok(ValidTransaction {
14001417
priority,
@@ -1406,7 +1423,7 @@ where
14061423
}
14071424
}
14081425
Some(Call::reveal_weights { netuid, .. }) => {
1409-
if Self::check_weights_min_stake(who) {
1426+
if Self::check_weights_min_stake(who, *netuid) {
14101427
let priority: u64 = Self::get_priority_set_weights(who, *netuid);
14111428
Ok(ValidTransaction {
14121429
priority,
@@ -1418,7 +1435,7 @@ where
14181435
}
14191436
}
14201437
Some(Call::set_weights { netuid, .. }) => {
1421-
if Self::check_weights_min_stake(who) {
1438+
if Self::check_weights_min_stake(who, *netuid) {
14221439
let priority: u64 = Self::get_priority_set_weights(who, *netuid);
14231440
Ok(ValidTransaction {
14241441
priority,
@@ -1430,7 +1447,7 @@ where
14301447
}
14311448
}
14321449
Some(Call::set_root_weights { netuid, hotkey, .. }) => {
1433-
if Self::check_weights_min_stake(hotkey) {
1450+
if Self::check_weights_min_stake(hotkey, *netuid) {
14341451
let priority: u64 = Self::get_priority_set_weights(hotkey, *netuid);
14351452
Ok(ValidTransaction {
14361453
priority,

pallets/subtensor/src/staking/add_stake.rs

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

73-
// Set the last time the stake increased for nominator drain protection.
74-
LastAddStakeIncrease::<T>::insert(&hotkey, &coldkey, Self::get_current_block_as_u64());
73+
// Track this addition in the stake delta.
74+
StakeDeltaSinceLastEmissionDrain::<T>::mutate(&hotkey, &coldkey, |stake_delta| {
75+
*stake_delta = stake_delta.saturating_add_unsigned(stake_to_be_added as u128);
76+
});
7577

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

pallets/subtensor/src/staking/helpers.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ impl<T: Config> Pallet<T> {
297297
staking_hotkeys.retain(|h| h != hotkey);
298298
StakingHotkeys::<T>::insert(coldkey, staking_hotkeys);
299299

300+
// Update stake delta
301+
StakeDeltaSinceLastEmissionDrain::<T>::remove(hotkey, coldkey);
302+
300303
current_stake
301304
}
302305

@@ -431,6 +434,9 @@ impl<T: Config> Pallet<T> {
431434

432435
// Add the balance to the coldkey account.
433436
Self::add_balance_to_coldkey_account(&delegate_coldkey_i, stake_i);
437+
438+
// Remove stake delta
439+
StakeDeltaSinceLastEmissionDrain::<T>::remove(hotkey, &delegate_coldkey_i);
434440
}
435441
}
436442
}

pallets/subtensor/src/staking/remove_stake.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ impl<T: Config> Pallet<T> {
7676
// We remove the balance from the hotkey.
7777
Self::decrease_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake_to_be_removed);
7878

79+
// Track this removal in the stake delta.
80+
StakeDeltaSinceLastEmissionDrain::<T>::mutate(&hotkey, &coldkey, |stake_delta| {
81+
*stake_delta = stake_delta.saturating_sub_unsigned(stake_to_be_removed as u128);
82+
});
83+
7984
// We add the balance to the coldkey. If the above fails we will not credit this coldkey.
8085
Self::add_balance_to_coldkey_account(&coldkey, stake_to_be_removed);
8186

pallets/subtensor/src/subnets/uids.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ impl<T: Config> Pallet<T> {
117117
///
118118
pub fn get_stake_for_uid_and_subnetwork(netuid: u16, neuron_uid: u16) -> u64 {
119119
if let Ok(hotkey) = Self::get_hotkey_for_net_and_uid(netuid, neuron_uid) {
120-
Self::get_total_stake_for_hotkey(&hotkey)
120+
Self::get_stake_for_hotkey_on_subnet(&hotkey, netuid)
121121
} else {
122122
0
123123
}

pallets/subtensor/src/swap/swap_coldkey.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,17 @@ impl<T: Config> Pallet<T> {
169169
weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2));
170170
}
171171

172-
// 4. Swap LastAddStakeIncrease.
172+
// 4. Swap StakeDeltaSinceLastEmissionDrain
173173
for hotkey in StakingHotkeys::<T>::get(old_coldkey) {
174-
let last_add_stake_increase = LastAddStakeIncrease::<T>::get(&hotkey, old_coldkey);
175-
LastAddStakeIncrease::<T>::remove(&hotkey, old_coldkey);
176-
LastAddStakeIncrease::<T>::insert(&hotkey, new_coldkey, last_add_stake_increase);
177-
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
174+
let old_stake_delta = StakeDeltaSinceLastEmissionDrain::<T>::get(&hotkey, old_coldkey);
175+
let new_stake_delta = StakeDeltaSinceLastEmissionDrain::<T>::get(&hotkey, new_coldkey);
176+
StakeDeltaSinceLastEmissionDrain::<T>::insert(
177+
&hotkey,
178+
new_coldkey,
179+
new_stake_delta.saturating_add(old_stake_delta),
180+
);
181+
StakeDeltaSinceLastEmissionDrain::<T>::remove(&hotkey, old_coldkey);
182+
weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2));
178183
}
179184

180185
// 5. Swap total coldkey stake.

pallets/subtensor/src/swap/swap_hotkey.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -207,24 +207,13 @@ impl<T: Config> Pallet<T> {
207207
weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2));
208208
}
209209

210-
// 9.1. swap PendingdHotkeyEmission
210+
// 9. swap PendingdHotkeyEmission
211211
if PendingdHotkeyEmission::<T>::contains_key(old_hotkey) {
212212
let old_pending_hotkey_emission = PendingdHotkeyEmission::<T>::get(old_hotkey);
213213
PendingdHotkeyEmission::<T>::remove(old_hotkey);
214214
PendingdHotkeyEmission::<T>::insert(new_hotkey, old_pending_hotkey_emission);
215215
weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2));
216216
}
217-
// 9.2. swap LastAddStakeIncrease
218-
// Swap double map prefix
219-
let old_coldkey_entries: Vec<(T::AccountId, u64)> =
220-
LastAddStakeIncrease::<T>::iter_prefix(old_hotkey).collect();
221-
if !old_coldkey_entries.is_empty() {
222-
for (coldkey, last_add_stake_increase) in old_coldkey_entries {
223-
LastAddStakeIncrease::<T>::remove(old_hotkey, &coldkey);
224-
LastAddStakeIncrease::<T>::insert(new_hotkey, &coldkey, last_add_stake_increase);
225-
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
226-
}
227-
}
228217

229218
// 10. Swap all subnet specific info.
230219
let all_netuids: Vec<u16> = Self::get_all_subnet_netuids();
@@ -363,6 +352,19 @@ impl<T: Config> Pallet<T> {
363352
}
364353
}
365354

355+
// 13. Swap Stake Delta for all coldkeys.
356+
for (coldkey, stake_delta) in StakeDeltaSinceLastEmissionDrain::<T>::iter_prefix(old_hotkey)
357+
{
358+
let new_stake_delta = StakeDeltaSinceLastEmissionDrain::<T>::get(new_hotkey, &coldkey);
359+
StakeDeltaSinceLastEmissionDrain::<T>::insert(
360+
new_hotkey,
361+
&coldkey,
362+
new_stake_delta.saturating_add(stake_delta),
363+
);
364+
StakeDeltaSinceLastEmissionDrain::<T>::remove(old_hotkey, &coldkey);
365+
weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2));
366+
}
367+
366368
// Return successful after swapping all the relevant terms.
367369
Ok(())
368370
}

0 commit comments

Comments
 (0)