diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 2a13a18aa0..0e53facabc 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1619,6 +1619,7 @@ mod pallet_benchmarks { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 642a7f18ac..80965f0bbb 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -348,7 +348,6 @@ impl Pallet { RAORecycledForRegistration::::remove(netuid); MaxRegistrationsPerBlock::::remove(netuid); WeightsVersionKey::::remove(netuid); - PendingRootAlphaDivs::::remove(netuid); // --- 17. Subtoken / feature flags. LiquidAlphaOn::::remove(netuid); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 53ac3c6ac5..9b1ad984af 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -69,19 +69,21 @@ impl Pallet { if price_i < tao_in_ratio { tao_in_i = price_i.saturating_mul(U96F32::saturating_from_num(block_emission)); alpha_in_i = block_emission; - let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); + let difference_tao: TaoCurrency = + tou64!(default_tao_in_i.saturating_sub(tao_in_i)).into(); + TotalIssuance::::mutate(|total| { + *total = total.saturating_add(difference_tao); + }); // Difference becomes buy. let buy_swap_result = Self::swap_tao_for_alpha( *netuid_i, - tou64!(difference_tao).into(), + difference_tao, T::SwapInterface::max_price(), true, ); if let Ok(buy_swap_result_ok) = buy_swap_result { let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); - SubnetAlphaOut::::mutate(*netuid_i, |total| { - *total = total.saturating_sub(bought_alpha); - }); + Self::recycle_subnet_alpha(*netuid_i, bought_alpha); } is_subsidized.insert(*netuid_i, true); } else { @@ -106,6 +108,7 @@ impl Pallet { alpha_in.insert(*netuid_i, alpha_in_i); alpha_out.insert(*netuid_i, alpha_out_i); } + log::debug!("is_subsidized: {is_subsidized:?}"); log::debug!("tao_in: {tao_in:?}"); log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); @@ -189,21 +192,23 @@ impl Pallet { .unwrap_or(asfloat!(0.0)); log::debug!("root_proportion: {root_proportion:?}"); // Get root proportion of alpha_out dividends. - let root_alpha: U96F32 = root_proportion - .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. - .saturating_mul(asfloat!(0.5)); // 50% to validators. - // Remove root alpha from alpha_out. - log::debug!("root_alpha: {root_alpha:?}"); - // Get pending alpha as original alpha_out - root_alpha. - let pending_alpha: U96F32 = alpha_out_i.saturating_sub(root_alpha); - log::debug!("pending_alpha: {pending_alpha:?}"); - + let mut root_alpha: U96F32 = asfloat!(0.0); let subsidized: bool = *is_subsidized.get(netuid_i).unwrap_or(&false); if !subsidized { + // Only give root alpha if not being subsidized. + root_alpha = root_proportion + .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. + .saturating_mul(asfloat!(0.5)); // 50% to validators. PendingRootAlphaDivs::::mutate(*netuid_i, |total| { *total = total.saturating_add(tou64!(root_alpha).into()); }); } + // Remove root alpha from alpha_out. + log::debug!("root_alpha: {root_alpha:?}"); + + // Get pending alpha as original alpha_out - root_alpha. + let pending_alpha: U96F32 = alpha_out_i.saturating_sub(root_alpha); + log::debug!("pending_alpha: {pending_alpha:?}"); // Accumulate alpha emission in pending. PendingEmission::::mutate(*netuid_i, |total| { @@ -246,7 +251,13 @@ impl Pallet { PendingOwnerCut::::insert(netuid, AlphaCurrency::ZERO); // Drain pending root alpha divs, alpha emission, and owner cut. - Self::drain_pending_emission(netuid, pending_alpha, pending_root_alpha, owner_cut); + Self::drain_pending_emission( + netuid, + pending_alpha, + pending_root_alpha, + pending_alpha.saturating_add(pending_root_alpha), + owner_cut, + ); } else { // Increment BlocksSinceLastStep::::mutate(netuid, |total| *total = total.saturating_add(1)); @@ -482,6 +493,7 @@ impl Pallet { let destination = maybe_dest.clone().unwrap_or(hotkey.clone()); if let Some(dest) = maybe_dest { + log::debug!("incentives: auto staking {incentive:?} to {dest:?}"); Self::deposit_event(Event::::AutoStakeAdded { netuid, destination: dest, @@ -490,6 +502,9 @@ impl Pallet { incentive, }); } + log::debug!( + "incentives: increasing stake for {hotkey:?} to {incentive:?} with owner {owner:?}" + ); Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &destination, &owner, @@ -604,6 +619,7 @@ impl Pallet { netuid: NetUid, pending_alpha: AlphaCurrency, pending_root_alpha: AlphaCurrency, + total_alpha: AlphaCurrency, owner_cut: AlphaCurrency, ) { log::debug!( @@ -614,7 +630,7 @@ impl Pallet { // Run the epoch. let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = - Self::epoch_with_mechanisms(netuid, pending_alpha.saturating_add(pending_root_alpha)); + Self::epoch_with_mechanisms(netuid, total_alpha); log::debug!("hotkey_emission: {hotkey_emission:?}"); // Compute the pending validator alpha. @@ -630,8 +646,7 @@ impl Pallet { log::debug!("incentive_sum: {incentive_sum:?}"); let pending_validator_alpha = if !incentive_sum.is_zero() { - pending_alpha - .saturating_add(pending_root_alpha) + total_alpha .saturating_div(2.into()) .saturating_sub(pending_root_alpha) } else { diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index e73417a326..c75b4f5784 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -76,6 +76,7 @@ fn test_claim_root_with_drain_emissions() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -144,6 +145,7 @@ fn test_claim_root_with_drain_emissions() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -245,6 +247,7 @@ fn test_claim_root_adding_stake_proportionally_for_two_stakers() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -346,6 +349,7 @@ fn test_claim_root_adding_stake_disproportionally_for_two_stakers() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -437,6 +441,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -489,6 +494,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -542,6 +548,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -634,6 +641,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -678,6 +686,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -714,6 +723,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1100,6 +1110,7 @@ fn test_claim_root_with_swap_coldkey() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1190,6 +1201,7 @@ fn test_claim_root_with_swap_hotkey() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1306,6 +1318,7 @@ fn test_claim_root_on_network_deregistration() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1446,6 +1459,7 @@ fn test_claim_root_with_unrelated_subnets() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 60644b2a28..56f7d2171f 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -245,7 +245,7 @@ fn test_coinbase_tao_issuance_different_prices() { ); // Prices are low => we limit tao issued (buy alpha with it) - let tao_issued = TaoCurrency::from(((0.1 + 0.2) * emission as f64) as u64); + let tao_issued = TaoCurrency::from(((1.0) * emission as f64) as u64); assert_abs_diff_eq!( TotalIssuance::::get(), tao_issued, @@ -690,6 +690,7 @@ fn test_drain_base() { AlphaCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ) }); } @@ -705,6 +706,7 @@ fn test_drain_base_with_subnet() { AlphaCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ) }); } @@ -730,6 +732,7 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { pending_alpha.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ); let stake_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); @@ -758,6 +761,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after = @@ -802,6 +806,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { netuid, pending_alpha, pending_root_alpha, + pending_alpha.saturating_add(pending_root_alpha), AlphaCurrency::ZERO, ); let stake_after = @@ -849,6 +854,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -914,6 +920,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -989,6 +996,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, 0.into(), ); let stake_after1 = @@ -1069,6 +1077,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1130,6 +1139,7 @@ fn test_drain_alpha_childkey_parentkey() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); @@ -1355,6 +1365,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1379,6 +1390,7 @@ fn test_get_root_children_drain() { pending_alpha, // pending_root1, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1402,6 +1414,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1490,6 +1503,7 @@ fn test_get_root_children_drain_half_proportion() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1576,6 +1590,7 @@ fn test_get_root_children_drain_with_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1663,6 +1678,7 @@ fn test_get_root_children_drain_with_half_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -2378,6 +2394,7 @@ fn test_drain_pending_emission_no_miners_all_drained() { netuid, emission, AlphaCurrency::ZERO, + emission, AlphaCurrency::ZERO, ); @@ -2451,6 +2468,7 @@ fn test_drain_pending_emission_zero_emission() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ); // Get the new stake of the hotkey. @@ -2743,6 +2761,7 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); @@ -2909,3 +2928,353 @@ fn test_zero_shares_zero_emission() { assert_eq!(SubnetAlphaIn::::get(netuid2), initial.into()); }); } + +#[test] +fn test_mining_emission_distribution_with_subsidy() { + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let validator_miner_coldkey = U256::from(3); + let validator_miner_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO + + // Create root network + SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + SubtokenEnabled::::insert(NetUid::ROOT, true); + NetworksAdded::::insert(NetUid::ROOT, true); + + // Add network, register hotkeys, and setup network parameters + add_network(netuid, subnet_tempo, 0); + SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 + + // Setup large LPs to prevent slippage + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator_miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_miner_coldkey), + validator_miner_hotkey, + netuid, + stake.into() + )); + + // Setup YUMA so that it creates emissions + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); + + // Make subsidy happen + // set price very low, e.g. a lot of alpha in + //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(0.01), + ); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + log::info!("is_sub: Running epoch with subsidy"); + + let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid).into(), + ) + .unwrap_or(0); + + // step by one block + step_block(1); + // Verify that root alpha divs + let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // Check that we are indeed being subsidized, i.e. that root alpha divs are NOT increasing + assert_eq!( + new_root_alpha_divs, old_root_alpha_divs, + "Root alpha divs should not increase" + ); + // Check root divs are zero + assert_eq!( + new_root_alpha_divs, + AlphaCurrency::ZERO, + "Root alpha divs should be zero" + ); + let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ); + // Run again but with some root stake + step_block(subnet_tempo - 2); + assert_abs_diff_eq!( + PendingEmission::::get(netuid).to_u64(), + U96F32::saturating_from_num(per_block_emission) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo as u64)) + .saturating_mul(U96F32::saturating_from_num(0.90)) + .saturating_to_num::(), + epsilon = 100_000_u64.into() + ); + step_block(1); + assert!( + BlocksSinceLastStep::::get(netuid) == 0, + "Blocks since last step should be 0" + ); + + let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + log::info!("Miner uid: {miner_uid:?}"); + let miner_incentive: AlphaCurrency = + (*Incentive::::get(NetUidStorageIndex::from(netuid)) + .get(miner_uid as usize) + .expect("Miner uid should be present") as u64) + .into(); + log::info!("Miner incentive: {miner_incentive:?}"); + + // Miner emissions + let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ) + .to_u64() + - miner_stake_before_epoch.to_u64(); + + assert_abs_diff_eq!( + Incentive::::get(NetUidStorageIndex::from(netuid)) + .iter() + .sum::(), + u16::MAX, + epsilon = 10 + ); + + assert_abs_diff_eq!( + miner_emission_1, + U96F32::saturating_from_num(miner_incentive) + .saturating_div(u16::MAX.into()) + .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + .saturating_to_num::(), + epsilon = 1_000_000_u64 + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_mining_emission_distribution_with_no_subsidy --exact --show-output --nocapture +#[test] +fn test_mining_emission_distribution_with_no_subsidy() { + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let validator_miner_coldkey = U256::from(3); + let validator_miner_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO + + // Create root network + SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + SubtokenEnabled::::insert(NetUid::ROOT, true); + NetworksAdded::::insert(NetUid::ROOT, true); + + // Add network, register hotkeys, and setup network parameters + add_network(netuid, subnet_tempo, 0); + SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 + + // Setup large LPs to prevent slippage + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator_miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_miner_coldkey), + validator_miner_hotkey, + netuid, + stake.into() + )); + + // Setup YUMA so that it creates emissions + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); + + // Make subsidy not happen + // set price very high + // e.g. very little alpha in pool + //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(5)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(10.0), + ); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + log::info!("is_sub: Running epoch with no subsidy"); + + let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ); + + // step by one block + step_block(1); + // Verify that root alpha divs + let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // Check that we are NOT being subsidized, i.e. that root alpha divs are are changing + assert_ne!( + new_root_alpha_divs, old_root_alpha_divs, + "Root alpha divs should be changing" + ); + assert!( + new_root_alpha_divs > AlphaCurrency::ZERO, + "Root alpha divs should be greater than 0" + ); + + // Run again but with some root stake + step_block(subnet_tempo - 1); + + let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + let miner_incentive: AlphaCurrency = + (*Incentive::::get(NetUidStorageIndex::from(netuid)) + .get(miner_uid as usize) + .expect("Miner uid should be present") as u64) + .into(); + log::info!("Miner incentive: {miner_incentive:?}"); + + let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid).into(), + ) + .unwrap_or(0); + + // Miner emissions + let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ) + .to_u64() + - miner_stake_before_epoch.to_u64(); + + assert_abs_diff_eq!( + miner_emission_1, + U96F32::saturating_from_num(miner_incentive) + .saturating_div(u16::MAX.into()) + .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + .saturating_to_num::(), + epsilon = 1_000_000_u64 + ); + }); +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 266a755708..42f35cbc80 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,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: 338, + spec_version: 341, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,