Skip to content
Open
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
30 changes: 30 additions & 0 deletions pallets/admin-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2141,6 +2141,36 @@ pub mod pallet {

Ok(())
}

/// Set whether the subnet owner cut is enabled for a subnet.
/// It is only callable by root and subnet owner.
#[pallet::call_index(92)]
#[pallet::weight((
Weight::from_parts(25_000_000, 0)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(1)),
DispatchClass::Operational,
Pays::Yes,
))]
pub fn sudo_set_owner_cut_enabled(
origin: OriginFor<T>,
netuid: NetUid,
enabled: bool,
) -> DispatchResult {
pallet_subtensor::Pallet::<T>::ensure_subnet_owner_or_root(origin, netuid)?;
pallet_subtensor::Pallet::<T>::ensure_admin_window_open(netuid)?;

ensure!(
pallet_subtensor::Pallet::<T>::if_subnet_exist(netuid),
Error::<T>::SubnetDoesNotExist
);
ensure!(!netuid.is_root(), Error::<T>::NotPermittedOnRootSubnet);

pallet_subtensor::Pallet::<T>::set_owner_cut_enabled_flag(netuid, enabled);
log::debug!("OwnerCutEnabledSet( netuid: {netuid:?}, enabled: {enabled:?} ) ");

Ok(())
}
}
}

Expand Down
38 changes: 38 additions & 0 deletions pallets/admin-utils/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2358,6 +2358,44 @@ fn test_sudo_set_mechanism_count() {
});
}

#[test]
fn test_sudo_set_owner_cut_enabled() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(11);
let owner = U256::from(1234);
let call = RuntimeCall::AdminUtils(crate::Call::sudo_set_owner_cut_enabled {
netuid,
enabled: false,
});

add_network(netuid, 10);
SubnetOwner::<Test>::insert(netuid, owner);

assert_ok!(AdminUtils::sudo_set_admin_freeze_window(
<<Test as Config>::RuntimeOrigin>::root(),
0
));

let dispatch_info = call.get_dispatch_info();
assert_eq!(dispatch_info.pays_fee, Pays::Yes);

assert!(SubtensorModule::get_owner_cut_enabled(netuid));
assert_ok!(AdminUtils::sudo_set_owner_cut_enabled(
<<Test as Config>::RuntimeOrigin>::signed(owner),
netuid,
false
));
assert!(!SubtensorModule::get_owner_cut_enabled(netuid));

assert_ok!(AdminUtils::sudo_set_owner_cut_enabled(
<<Test as Config>::RuntimeOrigin>::root(),
netuid,
true
));
assert!(SubtensorModule::get_owner_cut_enabled(netuid));
});
}

// cargo test --package pallet-admin-utils --lib -- tests::test_sudo_set_mechanism_count_and_emissions --exact --show-output
#[test]
fn test_sudo_set_mechanism_count_and_emissions() {
Expand Down
18 changes: 10 additions & 8 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,14 +258,16 @@ impl<T: Config> Pallet<T> {
Self::resolve_to_alpha_out(Self::mint_alpha(*netuid_i, alpha_created));

// Calculate the owner cut.
let owner_cut_i: U96F32 = alpha_out_i.saturating_mul(cut_percent);
log::debug!("owner_cut_i: {owner_cut_i:?}");
// Deduct owner cut from alpha_out.
alpha_out_i = alpha_out_i.saturating_sub(owner_cut_i);
// Accumulate the owner cut in pending.
PendingOwnerCut::<T>::mutate(*netuid_i, |total| {
*total = total.saturating_add(tou64!(owner_cut_i).into());
});
if Self::get_owner_cut_enabled(*netuid_i) {
let owner_cut_i: U96F32 = alpha_out_i.saturating_mul(cut_percent);
log::debug!("owner_cut_i: {owner_cut_i:?}");
// Deduct owner cut from alpha_out.
alpha_out_i = alpha_out_i.saturating_sub(owner_cut_i);
// Accumulate the owner cut in pending.
PendingOwnerCut::<T>::mutate(*netuid_i, |total| {
*total = total.saturating_add(tou64!(owner_cut_i).into());
});
}

// Get root proportional dividends.
let root_proportion = Self::root_proportion(*netuid_i);
Expand Down
11 changes: 11 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,17 @@ pub mod pallet {
#[pallet::storage]
pub type SubnetOwnerCut<T> = StorageValue<_, u16, ValueQuery, DefaultSubnetOwnerCut<T>>;

/// Default value for subnet owner cut enabled flag.
#[pallet::type_value]
pub fn DefaultOwnerCutEnabled<T: Config>() -> bool {
true
}

/// --- MAP ( netuid ) --> owner_cut_enabled
#[pallet::storage]
pub type OwnerCutEnabled<T> =
StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultOwnerCutEnabled<T>>;

/// ITEM( network_rate_limit )
#[pallet::storage]
pub type NetworkRateLimit<T> = StorageValue<_, u64, ValueQuery, DefaultNetworkRateLimit<T>>;
Expand Down
16 changes: 16 additions & 0 deletions pallets/subtensor/src/subnets/subnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,4 +462,20 @@ impl<T: Config> Pallet<T> {
_ => None,
}
}

/// Returns whether the owner cut is enabled for the given subnet.
///
/// Returns `true` if the owner cut is enabled for the subnet, otherwise `false`.
pub fn get_owner_cut_enabled(netuid: NetUid) -> bool {
OwnerCutEnabled::<T>::get(netuid)
}

/// Sets whether the owner cut is enabled for the given subnet.
///
/// # Parameters
/// - `netuid`: The identifier of the subnet to update.
/// - `value`: `true` to enable the owner cut for the subnet, `false` to disable it.
pub fn set_owner_cut_enabled_flag(netuid: NetUid, value: bool) {
OwnerCutEnabled::<T>::insert(netuid, value);
}
}
117 changes: 117 additions & 0 deletions pallets/subtensor/src/tests/coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3834,6 +3834,123 @@ fn test_coinbase_emit_to_subnets_with_root_sell() {
});
}

// cargo test --package pallet-subtensor --lib -- tests::coinbase::test_disabling_owner_cut_sends_subnet_emission_to_miners_and_validators --exact --nocapture
#[test]
fn test_disabling_owner_cut_sends_subnet_emission_to_miners_and_validators() {
new_test_ext(1).execute_with(|| {
let subnet_owner_coldkey = U256::from(1001);
let subnet_owner_hotkey = U256::from(1002);
let validator_coldkey = U256::from(1);
let validator_hotkey = U256::from(2);
let miner_coldkey = U256::from(5);
let miner_hotkey = U256::from(6);
let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey);
let subnet_tempo = 10;
let stake = 100_000_000_000u64;

SubtensorModule::set_tempo(netuid, subnet_tempo);
setup_reserves(netuid, (stake * 10_000).into(), (stake * 10_000).into());

register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0);
register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 1);

add_balance_to_coldkey_account(
&validator_coldkey,
TaoBalance::from(stake) + ExistentialDeposit::get(),
);

assert_ok!(SubtensorModule::add_stake(
RuntimeOrigin::signed(validator_coldkey),
validator_hotkey,
netuid,
stake.into()
));

SubtensorModule::set_weights_set_rate_limit(netuid, 0);
SubtensorModule::set_max_allowed_validators(netuid, 1);
step_block(subnet_tempo);

SubnetOwnerCut::<Test>::set(u16::MAX / 10);
SubtensorModule::set_owner_cut_enabled_flag(netuid, false);

let owner_uid =
SubtensorModule::get_uid_for_net_and_hotkey(netuid, &subnet_owner_hotkey).unwrap();
let validator_uid =
SubtensorModule::get_uid_for_net_and_hotkey(netuid, &validator_hotkey).unwrap();
let miner_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &miner_hotkey).unwrap();
let uid_count = [
owner_uid as usize,
validator_uid as usize,
miner_uid as usize,
]
.into_iter()
.max()
.unwrap()
+ 1;

Weights::<Test>::insert(
NetUidStorageIndex::from(netuid),
validator_uid,
vec![(miner_uid, 0xFFFF)],
);
BlockAtRegistration::<Test>::set(netuid, owner_uid, 1);
BlockAtRegistration::<Test>::set(netuid, validator_uid, 1);
BlockAtRegistration::<Test>::set(netuid, miner_uid, 1);
LastUpdate::<Test>::set(NetUidStorageIndex::from(netuid), vec![2; uid_count]);
Kappa::<Test>::set(netuid, u16::MAX / 5);
ActivityCutoff::<Test>::set(netuid, u16::MAX);
let mut validator_permit = vec![false; uid_count];
validator_permit[validator_uid as usize] = true;
ValidatorPermit::<Test>::insert(netuid, validator_permit);

let owner_stake_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
&subnet_owner_hotkey,
&subnet_owner_coldkey,
netuid,
);
let validator_stake_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
&validator_hotkey,
&validator_coldkey,
netuid,
);
let miner_stake_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
&miner_hotkey,
&miner_coldkey,
netuid,
);

// Disabling owner cut removes the subnet owner from emission distribution, so the
// subnet emission is fully distributed across the validator and miner paths instead.
step_block(subnet_tempo);

let owner_stake_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
&subnet_owner_hotkey,
&subnet_owner_coldkey,
netuid,
);
let validator_stake_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
&validator_hotkey,
&validator_coldkey,
netuid,
);
let miner_stake_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
&miner_hotkey,
&miner_coldkey,
netuid,
);

assert_eq!(owner_stake_after, owner_stake_before);
assert!(validator_stake_after > validator_stake_before);
assert!(miner_stake_after > miner_stake_before);
assert_eq!(PendingOwnerCut::<Test>::get(netuid), AlphaBalance::ZERO);
assert!(
Lock::<Test>::iter_prefix((subnet_owner_coldkey, netuid))
.next()
.is_none()
);
});
}

// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_pending_emission_start_call_not_done --exact --show-output --nocapture
#[test]
fn test_pending_emission_start_call_not_done() {
Expand Down
Loading