diff --git a/.github/workflows/publish-tag.yml b/.github/workflows/publish-tag.yml deleted file mode 100644 index 53d7a4760..000000000 --- a/.github/workflows/publish-tag.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Rust build, benchmarks, and tests - -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: true - -## -on: - ## - # Run when a semantic version is tagged - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+' - -## -# Environment variables shared for all targets -env: - CARGO_TERM_COLOR: always - RELEASE: true - RELEASE_NAME: release - RUSTFLAGS: -A warnings - RUST_BACKTRACE: full - SKIP_WASM_BUILD: 1 - VERBOSE: ${{ github.events.input.verbose }} - -## -# Test and build and publish -jobs: - check: - name: Tests and publishes targeting ${{ matrix.rust-target }} for OS ${{ matrix.os }} - runs-on: ${{ matrix.os }} - - ## - # Define multiple targets for builds and tests - strategy: - matrix: - rust-branch: - - stable - - rust-target: - - x86_64-unknown-linux-gnu - - x86_64-apple-darwin - - os: - - ubuntu-latest - - macos-latest - - include: - - os: ubuntu-latest - - os: macos-latest - - ## - # Environment variables specific to each target - env: - RUSTV: ${{ matrix.rust-branch }} - RUST_BIN_DIR: target/${{ matrix.rust-target }}/release - TARGET: ${{ matrix.rust-target }} - - ## - steps: - - name: Check-out repository under $GITHUB_WORKSPACE - uses: actions/checkout@v2 - - - name: Install dependencies - run: | - sudo apt update && - sudo apt install -y git clang curl libssl-dev llvm libudev-dev protobuf-compiler - - - name: Install Rust ${{ matrix.rust-branch }} - uses: actions-rs/toolchain@v1.0.6 - with: - toolchain: ${{ matrix.rust-branch }} - profile: minimal - - - name: Utilize Rust shared cached - uses: Swatinem/rust-cache@v2.2.1 - with: - key: ${{ matrix.os }}-${{ env.RUST_BIN_DIR }} - - - name: Run tests - run: | - cargo test --tests - - - name: Build executable - run: | - cargo build --release - - ## TODO: double-check `artifacts` path(s) be correct - # :warning: values for the following must always match; - # - RUST_BIN_DIR - # - target/${{ matrix.rust-target }}/release/node-subtensor - - name: Create Release - uses: ncipollo/release-action@v1.12.0 - with: - allowUpdates: false - artifactErrorsFailBuild: true - bodyFile: CHANGELOG.md - makeLatest: true - tag: ${{ github.ref }}-${{ matrix.rust-target }} - name: Release ${{ github.ref }} for ${{ matrix.rust-target }} - artifacts: 'raw_spec.json,raw_spec_finney.json,target/${{ matrix.rust-target }}/release/node-subtensor' - diff --git a/pallets/admin-utils/tests/mock.rs b/pallets/admin-utils/tests/mock.rs index 0df2da99e..6579433b0 100644 --- a/pallets/admin-utils/tests/mock.rs +++ b/pallets/admin-utils/tests/mock.rs @@ -76,7 +76,7 @@ parameter_types! { pub const InitialStakePruningMin: u16 = 0; pub const InitialFoundationDistribution: u64 = 0; pub const InitialDefaultTake: u16 = 11_796; // 18% honest number. - pub const InitialMinTake: u16 = 0; + pub const InitialMinTake: u16 = 5_898; // 9%; pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing diff --git a/pallets/admin-utils/tests/tests.rs b/pallets/admin-utils/tests/tests.rs index 2b0015673..f491209bd 100644 --- a/pallets/admin-utils/tests/tests.rs +++ b/pallets/admin-utils/tests/tests.rs @@ -945,7 +945,7 @@ mod sudo_set_nominator_min_required_stake { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(cold1), hot1, - 0 + u16::MAX / 10 )); assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot1), cold1); @@ -954,7 +954,7 @@ mod sudo_set_nominator_min_required_stake { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(cold2), hot2, - 0 + u16::MAX / 10 )); assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot2), cold2); diff --git a/pallets/subtensor/src/block_step.rs b/pallets/subtensor/src/block_step.rs index e31c1998b..80733e6b7 100644 --- a/pallets/subtensor/src/block_step.rs +++ b/pallets/subtensor/src/block_step.rs @@ -109,9 +109,9 @@ impl Pallet { pub fn generate_emission(block_number: u64) { // --- 1. Iterate across each network and add pending emission into stash. for (netuid, tempo) in as IterableStorageMap>::iter() { - // Skip the root network. - if netuid == Self::get_root_netuid() { - // Root emission is burned. + // Skip the root network or subnets with registrations turned off + if netuid == Self::get_root_netuid() || !Self::is_registration_allowed(netuid) { + // Root emission or subnet emission is burned continue; } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 50390c789..8fc392885 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2226,7 +2226,7 @@ where Pallet::::get_registrations_this_interval(*netuid); let max_registrations_per_interval = Pallet::::get_target_registrations_per_interval(*netuid); - if registrations_this_interval >= max_registrations_per_interval { + if registrations_this_interval >= (max_registrations_per_interval * 3) { // If the registration limit for the interval is exceeded, reject the transaction return InvalidTransaction::ExhaustsResources.into(); } diff --git a/pallets/subtensor/src/root.rs b/pallets/subtensor/src/root.rs index 34cacb256..42c783c3b 100644 --- a/pallets/subtensor/src/root.rs +++ b/pallets/subtensor/src/root.rs @@ -288,6 +288,24 @@ impl Pallet { Self::deposit_event(Event::NetworkRateLimitSet(limit)); } + /// Checks if registrations are allowed for a given subnet. + /// + /// This function retrieves the subnet hyperparameters for the specified subnet and checks the `registration_allowed` flag. + /// If the subnet doesn't exist or doesn't have hyperparameters defined, it returns `false`. + /// + /// # Arguments + /// + /// * `netuid` - The unique identifier of the subnet. + /// + /// # Returns + /// + /// * `bool` - `true` if registrations are allowed for the subnet, `false` otherwise. + pub fn is_registration_allowed(netuid: u16) -> bool { + Self::get_subnet_hyperparams(netuid) + .map(|params| params.registration_allowed) + .unwrap_or(false) + } + /// Computes and sets emission values for the root network which determine the emission for all subnets. /// /// This function is responsible for calculating emission based on network weights, stake values, diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index bf7c3b103..08b65b8a7 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -432,26 +432,18 @@ impl Pallet { Error::::UnstakeRateLimitExceeded ); - // If this is a nomination stake, check if total stake after removing will be above - // the minimum required stake. - - // If coldkey is not owner of the hotkey, it's a nomination stake. - if !Self::coldkey_owns_hotkey(&coldkey, &hotkey) { - let total_stake_after_remove = - Stake::::get(&hotkey, &coldkey).saturating_sub(stake_to_be_removed); - - ensure!( - total_stake_after_remove >= NominatorMinRequiredStake::::get(), - Error::::NomStakeBelowMinimumThreshold - ); - } - // We remove the balance from the hotkey. Self::decrease_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake_to_be_removed); - // We add the balancer to the coldkey. If the above fails we will not credit this coldkey. + // We add the balance to the coldkey. If the above fails we will not credit this coldkey. Self::add_balance_to_coldkey_account(&coldkey, stake_to_be_removed); + // If the stake is below the minimum, we clear the nomination from storage. + // This only applies to nominator stakes. + // If the coldkey does not own the hotkey, it's a nominator stake. + let new_stake = Self::get_stake_for_coldkey_and_hotkey(&coldkey, &hotkey); + Self::clear_small_nomination_if_required(&hotkey, &coldkey, new_stake); + // Set last block for rate limiting let block: u64 = Self::get_current_block_as_u64(); Self::set_last_tx_block(&coldkey, block); @@ -675,17 +667,24 @@ impl Pallet { /// It also removes the stake entry for the hotkey-coldkey pairing and adjusts the TotalStake /// and TotalIssuance by subtracting the removed stake amount. /// + /// Returns the amount of stake that was removed. + /// /// # Arguments /// /// * `coldkey` - A reference to the AccountId of the coldkey involved in the staking. /// * `hotkey` - A reference to the AccountId of the hotkey associated with the coldkey. - pub fn empty_stake_on_coldkey_hotkey_account(coldkey: &T::AccountId, hotkey: &T::AccountId) { + pub fn empty_stake_on_coldkey_hotkey_account( + coldkey: &T::AccountId, + hotkey: &T::AccountId, + ) -> u64 { let current_stake: u64 = Stake::::get(hotkey, coldkey); TotalColdkeyStake::::mutate(coldkey, |old| *old = old.saturating_sub(current_stake)); TotalHotkeyStake::::mutate(hotkey, |stake| *stake = stake.saturating_sub(current_stake)); Stake::::remove(hotkey, coldkey); TotalStake::::mutate(|stake| *stake = stake.saturating_sub(current_stake)); TotalIssuance::::mutate(|issuance| *issuance = issuance.saturating_sub(current_stake)); + + current_stake } /// Clears the nomination for an account, if it is a nominator account and the stake is below the minimum required threshold. @@ -700,9 +699,9 @@ impl Pallet { if stake < Self::get_nominator_min_required_stake() { // Remove the stake from the nominator account. (this is a more forceful unstake operation which ) // Actually deletes the staking account. - Self::empty_stake_on_coldkey_hotkey_account(coldkey, hotkey); + let cleared_stake = Self::empty_stake_on_coldkey_hotkey_account(coldkey, hotkey); // Add the stake to the coldkey account. - Self::add_balance_to_coldkey_account(coldkey, stake); + Self::add_balance_to_coldkey_account(coldkey, cleared_stake); } } } diff --git a/pallets/subtensor/src/subnet_info.rs b/pallets/subtensor/src/subnet_info.rs index b9203da0e..df768b0cd 100644 --- a/pallets/subtensor/src/subnet_info.rs +++ b/pallets/subtensor/src/subnet_info.rs @@ -40,7 +40,7 @@ pub struct SubnetHyperparams { weights_rate_limit: Compact, adjustment_interval: Compact, activity_cutoff: Compact, - registration_allowed: bool, + pub registration_allowed: bool, target_regs_per_interval: Compact, min_burn: Compact, max_burn: Compact, diff --git a/pallets/subtensor/tests/block_step.rs b/pallets/subtensor/tests/block_step.rs index 5d9cbdd7b..e1b4fe1de 100644 --- a/pallets/subtensor/tests/block_step.rs +++ b/pallets/subtensor/tests/block_step.rs @@ -803,3 +803,70 @@ fn test_burn_adjustment_case_e_zero_registrations() { assert_eq!(adjusted_diff, 5_000); }); } + +#[test] +fn test_emission_based_on_registration_status() { + new_test_ext(1).execute_with(|| { + let n: u16 = 100; + let netuid_off: u16 = 1; + let netuid_on: u16 = 2; + let tempo: u16 = 1; + let netuids: Vec = vec![netuid_off, netuid_on]; + let emissions: Vec = vec![1000000000, 1000000000]; + + // Add subnets with registration turned off and on + add_network(netuid_off, tempo, 0); + add_network(netuid_on, tempo, 0); + SubtensorModule::set_max_allowed_uids(netuid_off, n); + SubtensorModule::set_max_allowed_uids(netuid_on, n); + SubtensorModule::set_emission_values(&netuids, emissions).unwrap(); + SubtensorModule::set_network_registration_allowed(netuid_off, false); + SubtensorModule::set_network_registration_allowed(netuid_on, true); + + // Populate the subnets with neurons + for i in 0..n { + SubtensorModule::append_neuron(netuid_off, &U256::from(i), 0); + SubtensorModule::append_neuron(netuid_on, &U256::from(i), 0); + } + + // Generate emission at block 0 + let block: u64 = 0; + SubtensorModule::generate_emission(block); + + // Verify that no emission tuples are loaded for the subnet with registration off + assert!(SubtensorModule::get_loaded_emission_tuples(netuid_off).is_none()); + + // Verify that emission tuples are loaded for the subnet with registration on + assert!(SubtensorModule::get_loaded_emission_tuples(netuid_on).is_some()); + assert_eq!( + SubtensorModule::get_loaded_emission_tuples(netuid_on) + .unwrap() + .len(), + n as usize + ); + + // Step to the next epoch block + let epoch_block: u16 = tempo; + step_block(epoch_block); + + // Verify that no emission tuples are loaded for the subnet with registration off + assert!(SubtensorModule::get_loaded_emission_tuples(netuid_off).is_none()); + log::info!( + "Emissions for netuid with registration off: {:?}", + SubtensorModule::get_loaded_emission_tuples(netuid_off) + ); + + // Verify that emission tuples are loaded for the subnet with registration on + assert!(SubtensorModule::get_loaded_emission_tuples(netuid_on).is_some()); + log::info!( + "Emissions for netuid with registration on: {:?}", + SubtensorModule::get_loaded_emission_tuples(netuid_on) + ); + assert_eq!( + SubtensorModule::get_loaded_emission_tuples(netuid_on) + .unwrap() + .len(), + n as usize + ); + }); +} diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index ea50055ff..9995acf84 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -126,7 +126,7 @@ parameter_types! { pub const InitialStakePruningMin: u16 = 0; pub const InitialFoundationDistribution: u64 = 0; pub const InitialDefaultTake: u16 = 11_796; // 18%, same as in production - pub const InitialMinTake: u16 = 0; + pub const InitialMinTake: u16 =5_898; // 9%; pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing diff --git a/pallets/subtensor/tests/registration.rs b/pallets/subtensor/tests/registration.rs index a228b44af..5ee941f26 100644 --- a/pallets/subtensor/tests/registration.rs +++ b/pallets/subtensor/tests/registration.rs @@ -249,9 +249,10 @@ fn test_registration_rate_limit_exceeded() { let coldkey_account_id = U256::from(667); let who: ::AccountId = hotkey_account_id; - let max_registrants = 1; - SubtensorModule::set_target_registrations_per_interval(netuid, max_registrants); - SubtensorModule::set_registrations_this_interval(netuid, 1); + let target_registrants = 1; + let max_registrants = target_registrants * 3; + SubtensorModule::set_target_registrations_per_interval(netuid, target_registrants); + SubtensorModule::set_registrations_this_interval(netuid, max_registrants); let (nonce, work) = SubtensorModule::create_work_for_block_number( netuid, @@ -290,18 +291,18 @@ fn test_burned_registration_under_limit() { let netuid: u16 = 1; let hotkey_account_id: U256 = U256::from(1); let coldkey_account_id = U256::from(667); - let who: ::AccountId = hotkey_account_id; - let block_number: u64 = 0; + let who: ::AccountId = coldkey_account_id; + let burn_cost = 1000; + // Set the burn cost + SubtensorModule::set_burn(netuid, burn_cost); - let (nonce, work) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 129123813, - &hotkey_account_id, - ); + add_network(netuid, 13, 0); // Add the network + // Give it some TAO to the coldkey balance; more than the burn cost + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, burn_cost + 10_000); - let max_registrants = 2; - SubtensorModule::set_target_registrations_per_interval(netuid, max_registrants); + let target_registrants = 2; + let max_registrants = target_registrants * 3; // Maximum is 3 times the target + SubtensorModule::set_target_registrations_per_interval(netuid, target_registrants); let call_burned_register: pallet_subtensor::Call = pallet_subtensor::Call::burned_register { @@ -317,21 +318,15 @@ fn test_burned_registration_under_limit() { extension.validate(&who, &call_burned_register.into(), &info, 10); assert_ok!(burned_register_result); - add_network(netuid, 13, 0); //actually call register - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), netuid, - block_number, - nonce, - work, hotkey_account_id, - coldkey_account_id )); let current_registrants = SubtensorModule::get_registrations_this_interval(netuid); - let target_registrants = SubtensorModule::get_target_registrations_per_interval(netuid); - assert!(current_registrants <= target_registrants); + assert!(current_registrants <= max_registrants); }); } @@ -340,11 +335,15 @@ fn test_burned_registration_rate_limit_exceeded() { new_test_ext(1).execute_with(|| { let netuid: u16 = 1; let hotkey_account_id: U256 = U256::from(1); - let who: ::AccountId = hotkey_account_id; - let max_registrants = 1; + let coldkey_account_id = U256::from(667); + let who: ::AccountId = coldkey_account_id; - SubtensorModule::set_target_registrations_per_interval(netuid, max_registrants); - SubtensorModule::set_registrations_this_interval(netuid, 1); + let target_registrants = 1; + let max_registrants = target_registrants * 3; // Maximum is 3 times the target + + SubtensorModule::set_target_registrations_per_interval(netuid, target_registrants); + // Set the current registrations to the maximum; should not be able to register more + SubtensorModule::set_registrations_this_interval(netuid, max_registrants); let call_burned_register: pallet_subtensor::Call = pallet_subtensor::Call::burned_register { @@ -369,6 +368,55 @@ fn test_burned_registration_rate_limit_exceeded() { }); } +#[test] +fn test_burned_registration_rate_allows_burn_adjustment() { + // We need to be able to register more than the *target* registrations per interval + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let hotkey_account_id: U256 = U256::from(1); + let coldkey_account_id = U256::from(667); + let who: ::AccountId = coldkey_account_id; + + let burn_cost = 1000; + // Set the burn cost + SubtensorModule::set_burn(netuid, burn_cost); + + add_network(netuid, 13, 0); // Add the network + // Give it some TAO to the coldkey balance; more than the burn cost + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, burn_cost + 10_000); + + let target_registrants = 1; // Target is 1, but we can register more than that, up to some maximum. + SubtensorModule::set_target_registrations_per_interval(netuid, target_registrants); + // Set the current registrations to above the target; we should be able to register at least 1 more + SubtensorModule::set_registrations_this_interval(netuid, target_registrants); + + // Register one more, so the current registrations are above the target + let call_burned_register: pallet_subtensor::Call = + pallet_subtensor::Call::burned_register { + netuid, + hotkey: hotkey_account_id, + }; + + let info: DispatchInfo = + DispatchInfoOf::<::RuntimeCall>::default(); + let extension = SubtensorSignedExtension::::new(); + //does not actually call register + let burned_register_result = + extension.validate(&who, &call_burned_register.into(), &info, 10); + assert_ok!(burned_register_result); + + //actually call register + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid, + hotkey_account_id + )); + + let current_registrants = SubtensorModule::get_registrations_this_interval(netuid); + assert!(current_registrants > target_registrants); // Should be able to register more than the target + }); +} + #[test] fn test_burned_registration_ok() { new_test_ext(1).execute_with(|| { diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 95a17a2e0..ffe9de27a 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -1429,12 +1429,12 @@ fn test_full_with_delegating() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - 10 + SubtensorModule::get_min_take() )); assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey1), hotkey1, - 10 + SubtensorModule::get_min_take() )); assert!(SubtensorModule::hotkey_is_delegate(&hotkey0)); assert!(SubtensorModule::hotkey_is_delegate(&hotkey1)); @@ -1444,7 +1444,7 @@ fn test_full_with_delegating() { SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - 1000 + SubtensorModule::get_min_take() ), Err(Error::::HotKeyAlreadyDelegate.into()) ); @@ -1452,7 +1452,7 @@ fn test_full_with_delegating() { SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey1), hotkey1, - 1000 + u16::MAX / 10 ), Err(Error::::HotKeyAlreadyDelegate.into()) ); @@ -1509,23 +1509,32 @@ fn test_full_with_delegating() { // Lets emit inflation through the hot and coldkeys. SubtensorModule::emit_inflation_through_hotkey_account(&hotkey0, 0, 1000); SubtensorModule::emit_inflation_through_hotkey_account(&hotkey1, 0, 1000); + + // validator_take = take * validator_emission = 10% * 1000 = 100 + // old_stake + (validator_emission - validator_take) * stake_for_coldkey_and_hotkey / total_stake_for_hotkey + validator_take + // = + // 200 + 900 * 200 / 500 + 100 = 660 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), - 601 - ); // 200 + 1000 x ( 200 / 500 ) = 200 + 400 = 600 ~= 601 + 654 + ); + // validator_take = take * validator_emission = 9% * 1000 = 90 + // old_stake + (validator_emission - validator_take) * stake_for_coldkey_and_hotkey / total_stake_for_hotkey + // = + // 200 + (1000 - 90) * 200 / 400 = 655 ~ 654 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey1), - 700 - ); // 200 + 1000 x ( 200 / 400 ) = 200 + 500 = 700 + 655 + ); assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), - 899 - ); // 300 + 1000 x ( 300 / 500 ) = 300 + 600 = 900 ~= 899 + 846 + ); // 300 + 910 x ( 300 / 500 ) = 300 + 546 = 846 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey1), - 700 - ); // 200 + 1000 x ( 200 / 400 ) = 300 + 600 = 700 - assert_eq!(SubtensorModule::get_total_stake(), 2900); // 600 + 700 + 900 + 700 = 2900 + 745 + ); // 200 + 1090 x ( 200 / 400 ) = 300 + 545 = 745 + assert_eq!(SubtensorModule::get_total_stake(), 2900); // 600 + 700 + 900 + 750 = 2900 // // Try unstaking too much. assert_eq!( @@ -1586,19 +1595,19 @@ fn test_full_with_delegating() { // All the amounts have been decreased. assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), - 501 + 554 ); assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey1), - 600 + 555 ); assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), - 799 + 746 ); assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey1), - 600 + 645 ); // Lets register and stake a new key. @@ -1648,7 +1657,7 @@ fn test_full_with_delegating() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey2), hotkey2, - u16::MAX / 10 + SubtensorModule::get_min_take() )); // Add nominate some stake. @@ -1686,15 +1695,15 @@ fn test_full_with_delegating() { SubtensorModule::emit_inflation_through_hotkey_account(&hotkey2, 0, 1000); assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey2, &hotkey2), - 1_400 - ); // 1000 + 100 + 900 * (1000/3000) = 1400 + 1_394 + ); // 1000 + 94 + 900 * (1000/3000) = 1400 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey2), - 1_300 + 1_303 ); // 1000 + 900 * (1000/3000) = 1300 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey2), - 1_300 + 1_303 ); // 1000 + 900 * (1000/3000) = 1300 assert_eq!(SubtensorModule::get_total_stake(), 6_500); // before + 1_000 = 5_500 + 1_000 = 6_500 @@ -1716,7 +1725,7 @@ fn test_full_with_delegating() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey3), hotkey3, - u16::MAX / 10 + SubtensorModule::get_min_take() )); // Full take. assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey0), @@ -1754,19 +1763,19 @@ fn test_full_with_delegating() { SubtensorModule::emit_inflation_through_hotkey_account(&hotkey3, 0, 1000); assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey3), - 1225 + 1227 ); // 1000 + 90% * 1000 * 1000/4000 = 1225 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey3), - 1225 + 1227 ); // 1000 + 90% * 1000 * 1000/4000 = 1225 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey2, &hotkey3), - 1225 + 1227 ); // 1000 + 90% * 1000 * 1000/4000 = 1225 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey3, &hotkey3), - 1325 + 1319 ); // 1000 + 25 * 3 + 1000 * 1000/4000 = 1325 assert_eq!(SubtensorModule::get_total_stake(), 11_500); // before + 1_000 = 10_500 + 1_000 = 11_500 }); @@ -1882,12 +1891,12 @@ fn test_full_with_delegating_some_servers() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - 10 + SubtensorModule::get_min_take() )); assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey1), hotkey1, - 10 + SubtensorModule::get_min_take() )); assert!(SubtensorModule::hotkey_is_delegate(&hotkey0)); assert!(SubtensorModule::hotkey_is_delegate(&hotkey1)); @@ -1945,24 +1954,24 @@ fn test_full_with_delegating_some_servers() { SubtensorModule::emit_inflation_through_hotkey_account(&hotkey1, 123, 2_000); // 2_123 total emission. assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), - 801 - ); // 200 + (200 + 1000 x ( 200 / 500 )) = 200 + (200 + 400) = 800 ~= 801 + 854 + ); // 200 + (200 + 910 x ( 200 / 500 )) = 200 + (200 + 400) + 60 = 854 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), - 899 - ); // 300 + 1000 x ( 300 / 500 ) = 300 + 600 = 900 ~= 899 + 846 + ); // 300 + 910 x ( 300 / 500 ) = 300 + 546 = 846 assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey0), 1_700); // initial + server emission + validator emission = 799 + 899 = 1_698 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey1), - 1_200 - ); // 200 + (0 + 2000 x ( 200 / 400 )) = 200 + (1000) = 1_200 + 1_110 + ); // 200 + (0 + 2000 x ( 200 / 400 )) - 100 = 200 + (1000) - 100= 1_110 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey1), - 1_323 - ); // 200 + (123 + 2000 x ( 200 / 400 )) = 200 + (1_200) = 1_323 + 1_413 + ); // 200 + (123 + 2000 x ( 200 / 400 )) + 100 = 200 + (1_200)+ 100 = 1_423 assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 2_523); // 400 + 2_123 - assert_eq!(SubtensorModule::get_total_stake(), 4_223); // 1_700 + 2_523 = 4_223 + assert_eq!(SubtensorModule::get_total_stake(), 4_223); // 2_100 + 2_123 = 4_223 // Lets emit MORE inflation through the hot and coldkeys. // This time only server emission. This should go to the owner of the hotkey. @@ -1970,20 +1979,20 @@ fn test_full_with_delegating_some_servers() { SubtensorModule::emit_inflation_through_hotkey_account(&hotkey1, 150, 0); assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), - 1_151 - ); // + 350 = 1_151 + 1_204 + ); // + 350 + 54 = 1_204 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey1), - 1_200 + 1_110 ); // No change. assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), - 899 + 846 ); // No change. assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey1), - 1_473 - ); // 1_323 + 150 = 1_473 + 1_563 + ); // 1_323 + 150 + 90 = 1_573 assert_eq!(SubtensorModule::get_total_stake(), 4_723); // 4_223 + 500 = 4_823 // Lets register and stake a new key. @@ -2024,11 +2033,11 @@ fn test_full_with_delegating_some_servers() { assert_eq!(SubtensorModule::get_total_stake(), 5_623); // 4_723 + 900 = 5_623 - // Lets make this new key a delegate with a 10% take. + // Lets make this new key a delegate with a 9% take. assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey2), hotkey2, - u16::MAX / 10 + SubtensorModule::get_min_take() )); // Add nominate some stake. @@ -2068,15 +2077,15 @@ fn test_full_with_delegating_some_servers() { SubtensorModule::emit_inflation_through_hotkey_account(&hotkey2, 100, 1000); assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey2, &hotkey2), - 1_500 - ); // 1000 + 100 + 100 + 900 * (1000/3000) = 1000 + 200 + 300 = 1500 + 1_494 + ); // 1000 + 100 + 94 + 900 * (1000/3000) = 1000 + 200 + 300 = 1_494 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey2), - 1_300 - ); // 1000 + 900 * (1000/3000) = 1000 + 300 = 1300 + 1_303 + ); // 1000 + 900 * (1000/3000) = 1000 + 300 = 1_303 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey2), - 1_300 + 1_303 ); // 1000 + 900 * (1000/3000) = 1000 + 300 = 1300 assert_eq!(SubtensorModule::get_total_stake(), 8_823); // 7_723 + 1_100 = 8_823 @@ -2087,15 +2096,15 @@ fn test_full_with_delegating_some_servers() { SubtensorModule::emit_inflation_through_hotkey_account(&hotkey2, 123, 0); assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey2, &hotkey2), - 1_623 - ); // 1_500 + 123 = 1_623 + 1_617 + ); // 1_500 + 117 = 1_617 assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey2), - 1_300 + 1_303 ); // No change. assert_eq!( SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey2), - 1_300 + 1_303 ); // No change. assert_eq!(SubtensorModule::get_total_stake(), 8_946); // 8_823 + 123 = 8_946 }); @@ -2210,12 +2219,12 @@ fn test_full_block_emission_occurs() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - 10 + SubtensorModule::get_min_take() )); assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey1), hotkey1, - 10 + SubtensorModule::get_min_take() )); assert!(SubtensorModule::hotkey_is_delegate(&hotkey0)); assert!(SubtensorModule::hotkey_is_delegate(&hotkey1)); @@ -2461,7 +2470,7 @@ fn test_clear_small_nominations() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(cold1), hot1, - 0 + SubtensorModule::get_min_take() )); assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot1), cold1); @@ -2470,7 +2479,7 @@ fn test_clear_small_nominations() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(cold2), hot2, - 0 + SubtensorModule::get_min_take() )); assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot2), cold2); @@ -2695,16 +2704,52 @@ fn test_remove_stake_below_minimum_threshold() { stake_amount_to_remove )); - // Nomination stake cannot stake below min threshold. - assert_noop!( - SubtensorModule::remove_stake( - <::RuntimeOrigin>::signed(coldkey2), - hotkey1, - stake_amount_to_remove - ), - Error::::NomStakeBelowMinimumThreshold + // Nomination stake cannot unstake below min threshold, + // without unstaking all and removing the nomination. + let total_hotkey_stake_before = SubtensorModule::get_total_stake_for_hotkey(&hotkey1); + let bal_before = Balances::free_balance(coldkey2); + let staked_before = SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey2, &hotkey1); + let total_network_stake_before = SubtensorModule::get_total_stake(); + let total_issuance_before = SubtensorModule::get_total_issuance(); + // check the premise of the test is correct + assert!(initial_stake - stake_amount_to_remove < minimum_threshold); + assert_ok!(SubtensorModule::remove_stake( + <::RuntimeOrigin>::signed(coldkey2), + hotkey1, + stake_amount_to_remove + )); + + // Has no stake now + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey2, &hotkey1), + 0 ); - }) + let stake_removed = staked_before; // All stake was removed + // Has the full balance + assert_eq!(Balances::free_balance(coldkey2), bal_before + stake_removed); + + // Stake map entry is removed + assert!(Stake::::try_get(hotkey1, coldkey2).is_err(),); + // Stake tracking is updated + assert_eq!( + TotalColdkeyStake::::try_get(coldkey2).unwrap(), + 0 // Did not have any stake before; Entry is NOT removed + ); + assert_eq!( + TotalHotkeyStake::::try_get(hotkey1).unwrap(), + total_hotkey_stake_before - stake_removed // Stake was removed from hotkey1 tracker + ); + assert_eq!( + TotalStake::::try_get().unwrap(), + total_network_stake_before - stake_removed + ); + + // Total issuance is the same + assert_eq!( + SubtensorModule::get_total_issuance(), + total_issuance_before // Nothing was issued + ); + }); } // Verify delegate take can be decreased @@ -2723,21 +2768,26 @@ fn test_delegate_take_can_be_decreased() { add_network(netuid, 0, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); - // Coldkey / hotkey 0 become delegates with 10% take + // Coldkey / hotkey 0 become delegates with 9% take assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - u16::MAX / 10 + SubtensorModule::get_min_take() )); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); - // Coldkey / hotkey 0 decreases take to 5% - assert_ok!(SubtensorModule::do_decrease_take( - <::RuntimeOrigin>::signed(coldkey0), - hotkey0, - u16::MAX / 20 - )); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + // Coldkey / hotkey 0 decreases take to 5%. This should fail as the minimum take is 9% + assert_err!( + SubtensorModule::do_decrease_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 20 + ), + Error::::DelegateTakeTooLow + ); }); } @@ -2770,7 +2820,10 @@ fn test_can_set_min_take_ok() { hotkey0, SubtensorModule::get_min_take() )); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), 0); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); }); } @@ -2790,24 +2843,30 @@ fn test_delegate_take_can_not_be_increased_with_decrease_take() { add_network(netuid, 0, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); - // Coldkey / hotkey 0 become delegates with 5% take + // Coldkey / hotkey 0 become delegates with 10% take assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - u16::MAX / 20 + SubtensorModule::get_min_take() )); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); - // Coldkey / hotkey 0 tries to increase take to 10% + // Coldkey / hotkey 0 tries to increase take to 12.5% assert_eq!( SubtensorModule::do_decrease_take( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - u16::MAX / 10 + u16::MAX / 8 ), Err(Error::::DelegateTakeTooLow.into()) ); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); }); } @@ -2827,23 +2886,26 @@ fn test_delegate_take_can_be_increased() { add_network(netuid, 0, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); - // Coldkey / hotkey 0 become delegates with 5% take + // Coldkey / hotkey 0 become delegates with 9% take assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - u16::MAX / 20 + SubtensorModule::get_min_take() )); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); - // Coldkey / hotkey 0 decreases take to 10% + // Coldkey / hotkey 0 decreases take to 12.5% assert_ok!(SubtensorModule::do_increase_take( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - u16::MAX / 10 + u16::MAX / 8 )); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 8); }); } @@ -2863,13 +2925,16 @@ fn test_delegate_take_can_not_be_decreased_with_increase_take() { add_network(netuid, 0, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); - // Coldkey / hotkey 0 become delegates with 10% take + // Coldkey / hotkey 0 become delegates with 9% take assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - u16::MAX / 10 + SubtensorModule::get_min_take() )); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); // Coldkey / hotkey 0 tries to decrease take to 5% assert_eq!( @@ -2880,7 +2945,10 @@ fn test_delegate_take_can_not_be_decreased_with_increase_take() { ), Err(Error::::DelegateTakeTooLow.into()) ); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); }); } @@ -2900,13 +2968,16 @@ fn test_delegate_take_can_be_increased_to_limit() { add_network(netuid, 0, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); - // Coldkey / hotkey 0 become delegates with 10% take + // Coldkey / hotkey 0 become delegates with 9% take assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - u16::MAX / 10 + SubtensorModule::get_min_take() )); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); @@ -2972,13 +3043,16 @@ fn test_delegate_take_can_not_be_increased_beyond_limit() { add_network(netuid, 0, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); - // Coldkey / hotkey 0 become delegates with 10% take + // Coldkey / hotkey 0 become delegates with 9% take assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - u16::MAX / 10 + SubtensorModule::get_min_take() )); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); // Coldkey / hotkey 0 tries to increase take to InitialDefaultTake+1 // (Disable this check if InitialDefaultTake is u16::MAX) @@ -2992,7 +3066,10 @@ fn test_delegate_take_can_not_be_increased_beyond_limit() { Err(Error::::DelegateTakeTooHigh.into()) ); } - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); }); } @@ -3012,24 +3089,30 @@ fn test_rate_limits_enforced_on_increase_take() { add_network(netuid, 0, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); - // Coldkey / hotkey 0 become delegates with 5% take + // Coldkey / hotkey 0 become delegates with 9% take assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - u16::MAX / 20 + SubtensorModule::get_min_take() )); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); - // Coldkey / hotkey 0 increases take to 10% + // Coldkey / hotkey 0 increases take to 12.5% assert_eq!( SubtensorModule::do_increase_take( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - u16::MAX / 10 + u16::MAX / 8 ), Err(Error::::DelegateTxRateLimitExceeded.into()) ); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); @@ -3037,8 +3120,8 @@ fn test_rate_limits_enforced_on_increase_take() { assert_ok!(SubtensorModule::do_increase_take( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - u16::MAX / 10 + u16::MAX / 8 )); - assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 8); }); } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 421bab9aa..e051ab57d 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -797,7 +797,7 @@ parameter_types! { pub const SubtensorInitialPruningScore : u16 = u16::MAX; pub const SubtensorInitialBondsMovingAverage: u64 = 900_000; pub const SubtensorInitialDefaultTake: u16 = 11_796; // 18% honest number. - pub const SubtensorInitialMinTake: u16 = 11_796; // 18%, no change is allowed initially + pub const SubtensorInitialMinTake: u16 = 5_898; // 9% pub const SubtensorInitialWeightsVersionKey: u64 = 0; pub const SubtensorInitialMinDifficulty: u64 = 10_000_000; pub const SubtensorInitialMaxDifficulty: u64 = u64::MAX / 4;