Skip to content
Merged
2 changes: 1 addition & 1 deletion pallets/admin-utils/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ parameter_types! {
pub const InitialImmunityPeriod: u16 = 2;
pub const InitialMaxAllowedUids: u16 = 2;
pub const InitialBondsMovingAverage: u64 = 900_000;
pub const InitialBondsPenalty: u16 = 0;
pub const InitialBondsPenalty: u16 = u16::MAX;
pub const InitialStakePruningMin: u16 = 0;
pub const InitialFoundationDistribution: u64 = 0;
pub const InitialDefaultDelegateTake: u16 = 11_796; // 18% honest number.
Expand Down
26 changes: 18 additions & 8 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,24 @@ impl<T: Config> Pallet<T> {
let current_block: u64 = Self::get_current_block_as_u64();
log::debug!("Current block: {:?}", current_block);

// --- 1. Get all netuids (filter out root and new subnet without first emission block)
// --- 1. Get all netuids (filter out root)
let subnets: Vec<u16> = Self::get_all_subnet_netuids()
.into_iter()
.filter(|netuid| *netuid != 0)
.filter(|netuid| FirstEmissionBlockNumber::<T>::get(*netuid).is_some())
.collect();
log::debug!("All subnet netuids: {:?}", subnets);
// Filter out subnets with no first emission block number.
let subnets_to_emit_to: Vec<u16> = subnets
.clone()
.into_iter()
.filter(|netuid| FirstEmissionBlockNumber::<T>::get(*netuid).is_some())
.collect();
log::debug!("Subnets to emit to: {:?}", subnets_to_emit_to);

// --- 2. Get sum of tao reserves ( in a later version we will switch to prices. )
let mut total_moving_prices: I96F32 = I96F32::saturating_from_num(0.0);
for netuid_i in subnets.iter() {
// Only get price EMA for subnets that we emit to.
for netuid_i in subnets_to_emit_to.iter() {
// Get and update the moving price of each subnet adding the total together.
total_moving_prices =
total_moving_prices.saturating_add(Self::get_moving_alpha_price(*netuid_i));
Expand All @@ -59,7 +66,8 @@ impl<T: Config> Pallet<T> {
let mut tao_in: BTreeMap<u16, I96F32> = BTreeMap::new();
let mut alpha_in: BTreeMap<u16, I96F32> = BTreeMap::new();
let mut alpha_out: BTreeMap<u16, I96F32> = BTreeMap::new();
for netuid_i in subnets.iter() {
// Only calculate for subnets that we are emitting to.
for netuid_i in subnets_to_emit_to.iter() {
// Get subnet price.
let price_i: I96F32 = Self::get_alpha_price(*netuid_i);
log::debug!("price_i: {:?}", price_i);
Expand Down Expand Up @@ -104,7 +112,7 @@ impl<T: Config> Pallet<T> {
// --- 4. Injection.
// Actually perform the injection of alpha_in, alpha_out and tao_in into the subnet pool.
// This operation changes the pool liquidity each block.
for netuid_i in subnets.iter() {
for netuid_i in subnets_to_emit_to.iter() {
// Inject Alpha in.
let alpha_in_i: u64 = tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)));
SubnetAlphaInEmission::<T>::insert(*netuid_i, alpha_in_i);
Expand Down Expand Up @@ -136,7 +144,7 @@ impl<T: Config> Pallet<T> {
// Owner cuts are accumulated and then fed to the drain at the end of this func.
let cut_percent: I96F32 = Self::get_float_subnet_owner_cut();
let mut owner_cuts: BTreeMap<u16, I96F32> = BTreeMap::new();
for netuid_i in subnets.iter() {
for netuid_i in subnets_to_emit_to.iter() {
// Get alpha out.
let alpha_out_i: I96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0));
log::debug!("alpha_out_i: {:?}", alpha_out_i);
Expand All @@ -155,7 +163,7 @@ impl<T: Config> Pallet<T> {

// --- 6. Seperate out root dividends in alpha and sell them into tao.
// Then accumulate those dividends for later.
for netuid_i in subnets.iter() {
for netuid_i in subnets_to_emit_to.iter() {
// Get remaining alpha out.
let alpha_out_i: I96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0.0));
log::debug!("alpha_out_i: {:?}", alpha_out_i);
Expand Down Expand Up @@ -200,12 +208,14 @@ impl<T: Config> Pallet<T> {
}

// --- 7 Update moving prices after using them in the emission calculation.
for netuid_i in subnets.iter() {
// Only update price EMA for subnets that we emit to.
for netuid_i in subnets_to_emit_to.iter() {
// Update moving prices after using them above.
Self::update_moving_price(*netuid_i);
}

// --- 7. Drain pending emission through the subnet based on tempo.
// Run the epoch for *all* subnets, even if we don't emit anything.
for &netuid in subnets.iter() {
// Pass on subnets that have not reached their tempo.
if Self::should_run_epoch(netuid, current_block) {
Expand Down
242 changes: 242 additions & 0 deletions pallets/subtensor/src/tests/coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1889,3 +1889,245 @@ fn test_drain_pending_emission_no_miners_all_drained() {
assert_abs_diff_eq!(new_stake, emission.saturating_add(init_stake), epsilon = 1);
});
}

#[test]
fn test_drain_pending_emission_zero_emission() {
new_test_ext(1).execute_with(|| {
let netuid = add_dynamic_network(&U256::from(1), &U256::from(2));
let hotkey = U256::from(3);
let coldkey = U256::from(4);
let miner_hk = U256::from(5);
let miner_ck = U256::from(6);
let init_stake: u64 = 100_000_000_000_000;
let tempo = 2;
SubtensorModule::set_tempo(netuid, tempo);
// Set weight-set limit to 0.
SubtensorModule::set_weights_set_rate_limit(netuid, 0);

register_ok_neuron(netuid, hotkey, coldkey, 0);
register_ok_neuron(netuid, miner_hk, miner_ck, 0);
// Give non-zero stake
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey, &coldkey, netuid, init_stake,
);
assert_eq!(
SubtensorModule::get_total_stake_for_hotkey(&hotkey),
init_stake
);

// Set the weight of root TAO to be 0%, so only alpha is effective.
SubtensorModule::set_tao_weight(0);

run_to_block_no_epoch(netuid, 50);

// Run epoch for initial setup.
SubtensorModule::epoch(netuid, 0);

// Set weights on miner
assert_ok!(SubtensorModule::set_weights(
RuntimeOrigin::signed(hotkey),
netuid,
vec![0, 1, 2],
vec![0, 0, 1],
0,
));

run_to_block_no_epoch(netuid, 50);

// Clear incentive and dividends.
Incentive::<Test>::remove(netuid);
Dividends::<Test>::remove(netuid);

// Set the emission to be ZERO.
SubtensorModule::drain_pending_emission(netuid, 0, 0, 0, 0);

// Get the new stake of the hotkey.
let new_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey);
// We expect the stake to remain unchanged.
assert_eq!(new_stake, init_stake);

// Check that the incentive and dividends are set by epoch.
assert!(Incentive::<Test>::get(netuid).iter().sum::<u16>() > 0);
assert!(Dividends::<Test>::get(netuid).iter().sum::<u16>() > 0);
});
}

#[test]
fn test_run_coinbase_not_started() {
new_test_ext(1).execute_with(|| {
let netuid = 1;
let tempo = 2;

let sn_owner_hk = U256::from(7);
let sn_owner_ck = U256::from(8);

add_network_without_emission_block(netuid, tempo, 0);
assert_eq!(FirstEmissionBlockNumber::<Test>::get(netuid), None);

SubnetOwner::<Test>::insert(netuid, sn_owner_ck);
SubnetOwnerHotkey::<Test>::insert(netuid, sn_owner_hk);

let hotkey = U256::from(3);
let coldkey = U256::from(4);
let miner_hk = U256::from(5);
let miner_ck = U256::from(6);
let init_stake: u64 = 100_000_000_000_000;
let tempo = 2;
SubtensorModule::set_tempo(netuid, tempo);
// Set weight-set limit to 0.
SubtensorModule::set_weights_set_rate_limit(netuid, 0);

register_ok_neuron(netuid, hotkey, coldkey, 0);
register_ok_neuron(netuid, miner_hk, miner_ck, 0);
register_ok_neuron(netuid, sn_owner_hk, sn_owner_ck, 0);
// Give non-zero stake
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey, &coldkey, netuid, init_stake,
);
assert_eq!(
SubtensorModule::get_total_stake_for_hotkey(&hotkey),
init_stake
);

// Set the weight of root TAO to be 0%, so only alpha is effective.
SubtensorModule::set_tao_weight(0);

run_to_block_no_epoch(netuid, 30);

// Run epoch for initial setup.
SubtensorModule::epoch(netuid, 0);

// Set weights on miner
assert_ok!(SubtensorModule::set_weights(
RuntimeOrigin::signed(hotkey),
netuid,
vec![0, 1, 2],
vec![0, 0, 1],
0,
));

// Clear incentive and dividends.
Incentive::<Test>::remove(netuid);
Dividends::<Test>::remove(netuid);

// Step so tempo should run.
next_block_no_epoch(netuid);
next_block_no_epoch(netuid);
next_block_no_epoch(netuid);
let current_block = System::block_number();
assert!(SubtensorModule::should_run_epoch(netuid, current_block));

// Run coinbase with emission.
SubtensorModule::run_coinbase(I96F32::saturating_from_num(100_000_000));

// We expect that the epoch ran.
assert_eq!(BlocksSinceLastStep::<Test>::get(netuid), 0);

// Get the new stake of the hotkey. We expect no emissions.
let new_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey);
// We expect the stake to remain unchanged.
assert_eq!(new_stake, init_stake);

// Check that the incentive and dividends are set.
assert!(Incentive::<Test>::get(netuid).iter().sum::<u16>() > 0);
assert!(Dividends::<Test>::get(netuid).iter().sum::<u16>() > 0);
});
}

#[test]
fn test_run_coinbase_not_started_start_after() {
new_test_ext(1).execute_with(|| {
let netuid = 1;
let tempo = 2;

let sn_owner_hk = U256::from(7);
let sn_owner_ck = U256::from(8);

add_network_without_emission_block(netuid, tempo, 0);
assert_eq!(FirstEmissionBlockNumber::<Test>::get(netuid), None);

SubnetOwner::<Test>::insert(netuid, sn_owner_ck);
SubnetOwnerHotkey::<Test>::insert(netuid, sn_owner_hk);

let hotkey = U256::from(3);
let coldkey = U256::from(4);
let miner_hk = U256::from(5);
let miner_ck = U256::from(6);
let init_stake: u64 = 100_000_000_000_000;
let tempo = 2;
SubtensorModule::set_tempo(netuid, tempo);
// Set weight-set limit to 0.
SubtensorModule::set_weights_set_rate_limit(netuid, 0);

register_ok_neuron(netuid, hotkey, coldkey, 0);
register_ok_neuron(netuid, miner_hk, miner_ck, 0);
register_ok_neuron(netuid, sn_owner_hk, sn_owner_ck, 0);
// Give non-zero stake
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey, &coldkey, netuid, init_stake,
);
assert_eq!(
SubtensorModule::get_total_stake_for_hotkey(&hotkey),
init_stake
);

// Set the weight of root TAO to be 0%, so only alpha is effective.
SubtensorModule::set_tao_weight(0);

run_to_block_no_epoch(netuid, 30);

// Run epoch for initial setup.
SubtensorModule::epoch(netuid, 0);

// Set weights on miner
assert_ok!(SubtensorModule::set_weights(
RuntimeOrigin::signed(hotkey),
netuid,
vec![0, 1, 2],
vec![0, 0, 1],
0,
));

// Clear incentive and dividends.
Incentive::<Test>::remove(netuid);
Dividends::<Test>::remove(netuid);

// Step so tempo should run.
next_block_no_epoch(netuid);
next_block_no_epoch(netuid);
next_block_no_epoch(netuid);
let current_block = System::block_number();
assert!(SubtensorModule::should_run_epoch(netuid, current_block));

// Run coinbase with emission.
SubtensorModule::run_coinbase(I96F32::saturating_from_num(100_000_000));
// We expect that the epoch ran.
assert_eq!(BlocksSinceLastStep::<Test>::get(netuid), 0);

let block_number = DurationOfStartCall::get();
run_to_block_no_epoch(netuid, block_number);

let current_block = System::block_number();

// Run start call.
assert_ok!(SubtensorModule::start_call(
RuntimeOrigin::signed(sn_owner_ck),
netuid
));
assert_eq!(
FirstEmissionBlockNumber::<Test>::get(netuid),
Some(current_block + 1)
);

// Run coinbase with emission.
SubtensorModule::run_coinbase(I96F32::saturating_from_num(100_000_000));
// We expect that the epoch ran.
assert_eq!(BlocksSinceLastStep::<Test>::get(netuid), 0);

// Get the new stake of the hotkey. We expect no emissions.
let new_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey);
// We expect the stake to remain unchanged.
assert!(new_stake > init_stake);
log::info!("new_stake: {}", new_stake);
});
}
22 changes: 0 additions & 22 deletions pallets/subtensor/src/tests/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -983,28 +983,6 @@ fn test_512_graph_random_weights() {
// });
// }

fn next_block_no_epoch(netuid: u16) -> u64 {
// high tempo to skip automatic epochs in on_initialize
let high_tempo: u16 = u16::MAX - 1;
let old_tempo: u16 = SubtensorModule::get_tempo(netuid);

SubtensorModule::set_tempo(netuid, high_tempo);
let new_block = next_block();
SubtensorModule::set_tempo(netuid, old_tempo);

new_block
}

fn run_to_block_no_epoch(netuid: u16, n: u64) {
// high tempo to skip automatic epochs in on_initialize
let high_tempo: u16 = u16::MAX - 1;
let old_tempo: u16 = SubtensorModule::get_tempo(netuid);

SubtensorModule::set_tempo(netuid, high_tempo);
run_to_block(n);
SubtensorModule::set_tempo(netuid, old_tempo);
}

// Test bonds exponential moving average over a sequence of epochs.
#[test]
fn test_bonds() {
Expand Down
Loading
Loading