Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make all stake viable #803

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 13 additions & 27 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ impl<T: Config> Pallet<T> {
PendingdHotkeyEmission::<T>::insert(hotkey, 0);

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

// --- 3 Update the block value to the current block number.
LastHotkeyEmissionDrain::<T>::insert(hotkey, block_number);
Expand All @@ -289,48 +289,34 @@ impl<T: Config> Pallet<T> {
// --- 7 Calculate the remaining emission after the hotkey's take.
let mut remainder: u64 = emission_minus_take;

// --- 8 Iterate over each nominator and get all viable stake.
let mut total_viable_nominator_stake: u64 = total_hotkey_stake;
for (nominator, nominator_stake) in Stake::<T>::iter_prefix(hotkey) {
if LastAddStakeIncrease::<T>::get(hotkey, nominator) > last_emission_drain {
total_viable_nominator_stake =
total_viable_nominator_stake.saturating_sub(nominator_stake);
}
}

// --- 9 Iterate over each nominator.
if total_viable_nominator_stake != 0 {
// --- 8. Iterate over each nominator and distribute emissions.
if total_hotkey_stake != 0 {
for (nominator, nominator_stake) in Stake::<T>::iter_prefix(hotkey) {
// --- 10 Check if the stake was manually increased by the user since the last emission drain for this hotkey.
// If it was, skip this nominator as they will not receive their proportion of the emission.
if LastAddStakeIncrease::<T>::get(hotkey, nominator.clone()) > last_emission_drain {
continue;
}

// --- 11 Calculate this nominator's share of the emission.
// --- 9. Calculate this nominator's share of the emission.
let nominator_emission: I64F64 = I64F64::from_num(emission_minus_take)
.saturating_mul(I64F64::from_num(nominator_stake))
.checked_div(I64F64::from_num(total_viable_nominator_stake))
.checked_div(I64F64::from_num(total_hotkey_stake))
.unwrap_or(I64F64::from_num(0));

// --- 12 Increase the stake for the nominator.
// --- 10. Increase the stake for the nominator.
let nominator_emission_u64: u64 = nominator_emission.to_num::<u64>();
Self::increase_stake_on_coldkey_hotkey_account(
&nominator,
hotkey,
nominator_emission.to_num::<u64>(),
nominator_emission_u64,
);

// --- 13* Record event and Subtract the nominator's emission from the remainder.
total_new_tao = total_new_tao.saturating_add(nominator_emission.to_num::<u64>());
remainder = remainder.saturating_sub(nominator_emission.to_num::<u64>());
// --- 11. Record event and subtract the nominator's emission from the remainder.
total_new_tao = total_new_tao.saturating_add(nominator_emission_u64);
remainder = remainder.saturating_sub(nominator_emission_u64);
}
}

// --- 14 Finally, add the stake to the hotkey itself, including its take and the remaining emission.
// --- 12. Finally, add the stake to the hotkey itself, including its take and the remaining emission.
let hotkey_new_tao: u64 = hotkey_take.saturating_add(remainder);
Self::increase_stake_on_hotkey_account(hotkey, hotkey_new_tao);

// --- 15 Record new tao creation event and return the amount created.
// --- 13 Record new tao creation event and return the amount created.
total_new_tao = total_new_tao.saturating_add(hotkey_new_tao);
total_new_tao
}
Expand Down
144 changes: 143 additions & 1 deletion pallets/subtensor/tests/coinbase.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#![allow(unused, clippy::indexing_slicing, clippy::panic, clippy::unwrap_used)]
use crate::mock::*;
mod mock;
// use frame_support::{assert_err, assert_ok};
use frame_support::{assert_err, assert_ok};
use frame_system::Config;
use sp_core::U256;

// Test the ability to hash all sorts of hotkeys.
Expand Down Expand Up @@ -154,3 +155,144 @@ fn test_set_and_get_hotkey_emission_tempo() {
assert_eq!(updated_tempo, new_tempo);
});
}

#[test]
fn test_coinbase_nominator_drainage() {
new_test_ext(1).execute_with(|| {
// 1. Set up the network and accounts
let netuid: u16 = 1;
let hotkey = U256::from(0);
let coldkey = U256::from(3);
let nominator1 = U256::from(1);
let nominator2 = U256::from(2);

log::debug!("Setting up network with netuid: {}", netuid);
log::debug!("Hotkey: {:?}, Coldkey: {:?}", hotkey, coldkey);
log::debug!("Nominators: {:?}, {:?}", nominator1, nominator2);

// 2. Create network and register neuron
add_network(netuid, 1, 0);
register_ok_neuron(netuid, hotkey, coldkey, 100000);
SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey);

log::debug!("Network created and neuron registered");

// 3. Set up balances and stakes
SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1000);
SubtensorModule::add_balance_to_coldkey_account(&nominator1, 1500);
SubtensorModule::add_balance_to_coldkey_account(&nominator2, 1500);

log::debug!("Balances added to accounts");

// 4. Make the hotkey a delegate
assert_ok!(SubtensorModule::do_become_delegate(
<<Test as Config>::RuntimeOrigin>::signed(coldkey),
hotkey,
(u16::MAX as u64 / 10) as u16
));

log::debug!("Hotkey became a delegate with minimum take");

// Add stakes for nominators
SubtensorModule::add_stake(
<<Test as Config>::RuntimeOrigin>::signed(nominator1),
hotkey,
100,
);

SubtensorModule::add_stake(
<<Test as Config>::RuntimeOrigin>::signed(nominator2),
hotkey,
100,
);

// Log the stakes for hotkey, nominator1, and nominator2
log::debug!(
"Initial stakes - Hotkey: {}, Nominator1: {}, Nominator2: {}",
SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey, &hotkey),
SubtensorModule::get_stake_for_coldkey_and_hotkey(&nominator1, &hotkey),
SubtensorModule::get_stake_for_coldkey_and_hotkey(&nominator2, &hotkey)
);
log::debug!("Stakes added for nominators");

// 5. Set emission and verify initial states
SubtensorModule::set_emission_values(&[netuid], vec![10]).unwrap();
assert_eq!(SubtensorModule::get_subnet_emission_value(netuid), 10);
assert_eq!(SubtensorModule::get_pending_hotkey_emission(&hotkey), 0);
assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey), 200);
assert_eq!(SubtensorModule::get_pending_emission(netuid), 0);

log::debug!("Emission set and initial states verified");

// 6. Set hotkey emission tempo
SubtensorModule::set_hotkey_emission_tempo(1);
log::debug!("Hotkey emission tempo set to 1");

// 7. Simulate blocks and check emissions
next_block();
assert_eq!(SubtensorModule::get_pending_emission(netuid), 10);
log::debug!(
"After first block, pending emission: {}",
SubtensorModule::get_pending_emission(netuid)
);

next_block();
assert_eq!(SubtensorModule::get_pending_emission(netuid), 0);
assert_eq!(SubtensorModule::get_pending_hotkey_emission(&hotkey), 0);
log::debug!("After second block, pending emission drained");

// 8. Check final stakes
let hotkey_stake = SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey, &hotkey);
let nominator1_stake =
SubtensorModule::get_stake_for_coldkey_and_hotkey(&nominator1, &hotkey);
let nominator2_stake =
SubtensorModule::get_stake_for_coldkey_and_hotkey(&nominator2, &hotkey);

log::debug!(
"Final stakes - Hotkey: {}, Nominator1: {}, Nominator2: {}",
hotkey_stake,
nominator1_stake,
nominator2_stake
);

// 9. Verify distribution
let min_take = SubtensorModule::get_min_delegate_take() as u64;
let total_emission = 20; // 10 per block for 2 blocks
let hotkey_emission = total_emission * min_take / u16::MAX as u64;
let remaining_emission = total_emission - hotkey_emission;
let nominator_emission = remaining_emission / 2;

log::debug!(
"Calculated emissions - Hotkey: {}, Each Nominator: {}",
hotkey_emission,
nominator_emission
);

// Debug: Print the actual stakes
log::debug!("Actual hotkey stake: {}", hotkey_stake);
log::debug!("Actual nominator1 stake: {}", nominator1_stake);
log::debug!("Actual nominator2 stake: {}", nominator2_stake);

// Debug: Check the total stake for the hotkey
let total_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey);
log::debug!("Total stake for hotkey: {}", total_stake);

// Assertions
assert_eq!(hotkey_stake, 2, "Hotkey stake mismatch");
assert_eq!(
nominator1_stake,
100 + nominator_emission,
"Nominator1 stake mismatch"
);
assert_eq!(
nominator2_stake,
100 + nominator_emission,
"Nominator2 stake mismatch"
);

// 10. Check total stake
assert_eq!(total_stake, 200 + total_emission, "Total stake mismatch");

log::debug!("Test completed");
});
}
2 changes: 1 addition & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// `spec_version`, and `authoring_version` are the same between Wasm and native.
// This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
// the compatible custom types.
spec_version: 196,
spec_version: 199,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down
Loading