diff --git a/.github/workflows/check-docker-localnet.yml b/.github/workflows/check-docker-localnet.yml new file mode 100644 index 0000000000..126b718d8c --- /dev/null +++ b/.github/workflows/check-docker-localnet.yml @@ -0,0 +1,21 @@ +name: Build Localnet Docker Image + +on: + pull_request: + +jobs: + build: + runs-on: SubtensorCI + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build Docker Image + run: docker build -f Dockerfile-localnet -t localnet . diff --git a/.github/workflows/docker-localnet.yml b/.github/workflows/docker-localnet.yml new file mode 100644 index 0000000000..cd6460bfe4 --- /dev/null +++ b/.github/workflows/docker-localnet.yml @@ -0,0 +1,69 @@ +name: Publish Localnet Docker Image + +on: + release: + types: [published] + workflow_dispatch: + inputs: + branch-or-tag: + description: "Branch or tag to use for the Docker image tag and ref to checkout (optional)" + required: false + default: "" + push: + branches: + - devnet-ready + +permissions: + contents: read + packages: write + actions: read + security-events: write + +jobs: + publish: + runs-on: SubtensorCI + + steps: + - name: Determine Docker tag and ref + id: tag + run: | + branch_or_tag="${{ github.event.inputs.branch-or-tag || github.ref_name }}" + echo "Determined branch or tag: $branch_or_tag" + echo "tag=$branch_or_tag" >> $GITHUB_ENV + echo "ref=$branch_or_tag" >> $GITHUB_ENV + + # Check if this is a tagged release (not devnet-ready/devnet/testnet) + if [[ "$branch_or_tag" != "devnet-ready" ]]; then + echo "latest_tag=true" >> $GITHUB_ENV + else + echo "latest_tag=false" >> $GITHUB_ENV + fi + + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ env.ref }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + file: Dockerfile-localnet + push: true + platforms: linux/amd64,linux/arm64 + tags: | + ghcr.io/${{ github.repository }}-localnet:${{ env.tag }} + ${{ env.latest_tag == 'true' && format('ghcr.io/{0}-localnet:latest', github.repository) || '' }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 904027bde2..2b36e37282 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -22,7 +22,7 @@ permissions: security-events: write jobs: - publish: + publish-x86: runs-on: SubtensorCI steps: @@ -64,7 +64,53 @@ jobs: with: context: . push: true - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64 + tags: | + ghcr.io/${{ github.repository }}:${{ env.tag }} + ${{ env.latest_tag == 'true' && format('ghcr.io/{0}:latest', github.repository) || '' }} + publish-arm: + runs-on: SubtensorCI + + steps: + - name: Determine Docker tag and ref + id: tag + run: | + branch_or_tag="${{ github.event.inputs.branch-or-tag || github.ref_name }}" + echo "Determined branch or tag: $branch_or_tag" + echo "tag=$branch_or_tag" >> $GITHUB_ENV + echo "ref=$branch_or_tag" >> $GITHUB_ENV + + # Check if this is a tagged release (not devnet-ready/devnet/testnet) + if [[ "${{ github.event_name }}" == "release" && "$branch_or_tag" != "devnet-ready" && "$branch_or_tag" != "devnet" && "$branch_or_tag" != "testnet" ]]; then + echo "latest_tag=true" >> $GITHUB_ENV + else + echo "latest_tag=false" >> $GITHUB_ENV + fi + + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ env.ref }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + push: true + platforms: linux/arm64 tags: | ghcr.io/${{ github.repository }}:${{ env.tag }} ${{ env.latest_tag == 'true' && format('ghcr.io/{0}:latest', github.repository) || '' }} diff --git a/Dockerfile-localnet b/Dockerfile-localnet new file mode 100644 index 0000000000..0efaf5b47a --- /dev/null +++ b/Dockerfile-localnet @@ -0,0 +1,65 @@ +ARG BASE_IMAGE=ubuntu:latest + +FROM $BASE_IMAGE AS builder +SHELL ["/bin/bash", "-c"] + +# Set noninteractive mode for apt-get +ARG DEBIAN_FRONTEND=noninteractive + +LABEL ai.opentensor.image.authors="operations@opentensor.ai" \ + ai.opentensor.image.vendor="Opentensor Foundation" \ + ai.opentensor.image.title="opentensor/subtensor-localnet" \ + ai.opentensor.image.description="Opentensor Subtensor Blockchain" \ + ai.opentensor.image.documentation="https://docs.bittensor.com" + +# Set up Rust environment +ENV RUST_BACKTRACE=1 + +RUN apt-get update +RUN apt-get install -y curl build-essential protobuf-compiler clang git pkg-config libssl-dev llvm libudev-dev + +# Copy entire repository +COPY . /build +WORKDIR /build + +# Install Rust +RUN set -o pipefail && curl https://sh.rustup.rs -sSf | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" +RUN rustup toolchain install +RUN rustup target add wasm32-unknown-unknown + +## Build fast-blocks node +RUN ./scripts/localnet.sh --build-only +# Build non-fast-blocks +RUN ./scripts/localnet.sh False --build-only + +# Verify the binaries was produced +RUN test -e /build/target/fast-blocks/release/node-subtensor +RUN test -e /build/target/non-fast-blocks/release/node-subtensor + +FROM $BASE_IMAGE AS subtensor-localnet + +# Copy binaries +COPY --from=builder /build/target/fast-blocks/release/node-subtensor target/fast-blocks/release/node-subtensor +RUN chmod +x target/fast-blocks/release/node-subtensor + +COPY --from=builder /build/target/non-fast-blocks/release/node-subtensor target/non-fast-blocks/release/node-subtensor +RUN chmod +x target/non-fast-blocks/release/node-subtensor + +COPY --from=builder /build/snapshot.json /snapshot.json + +COPY --from=builder /build/scripts/localnet.sh scripts/localnet.sh +RUN chmod +x /scripts/localnet.sh + +## Ubdate certificates +RUN apt-get update && apt-get install -y ca-certificates + +# Do not build (just run) +ENV BUILD_BINARY=0 +# Switch to local run with IP 0.0.0.0 within docker image +ENV RUN_IN_DOCKER=1 +# Expose ports +EXPOSE 30334 30335 9944 9945 + +ENTRYPOINT ["/scripts/localnet.sh"] +CMD ["True"] diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index c6665bcd97..8f90197bb5 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -46,6 +46,7 @@ sp_api::decl_runtime_apis! { fn get_stake_info_for_coldkey( coldkey_account: AccountId32 ) -> Vec>; fn get_stake_info_for_coldkeys( coldkey_accounts: Vec ) -> Vec<(AccountId32, Vec>)>; fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account: AccountId32, coldkey_account: AccountId32, netuid: u16 ) -> Option>; + fn get_stake_fee( origin: Option<(AccountId32, u16)>, origin_coldkey_account: AccountId32, destination: Option<(AccountId32, u16)>, destination_coldkey_account: AccountId32, amount: u64 ) -> u64; } pub trait SubnetRegistrationRuntimeApi { diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index bbd52e00d3..bcd2bb33f5 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -966,7 +966,7 @@ mod dispatches { ) -> DispatchResultWithPostInfo { // Ensure it's called with root privileges (scheduler has root privileges) ensure_root(origin)?; - log::info!("swap_coldkey: {:?} -> {:?}", old_coldkey, new_coldkey); + log::debug!("swap_coldkey: {:?} -> {:?}", old_coldkey, new_coldkey); Self::do_swap_coldkey(&old_coldkey, &new_coldkey, swap_cost) } @@ -1389,40 +1389,42 @@ mod dispatches { .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(31)), DispatchClass::Operational, Pays::Yes))] pub fn schedule_dissolve_network( - origin: OriginFor, - netuid: u16, + _origin: OriginFor, + _netuid: u16, ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - let current_block: BlockNumberFor = >::block_number(); - let duration: BlockNumberFor = DissolveNetworkScheduleDuration::::get(); - let when: BlockNumberFor = current_block.saturating_add(duration); - - let call = Call::::dissolve_network { - coldkey: who.clone(), - netuid, - }; - - let bound_call = T::Preimages::bound(LocalCallOf::::from(call.clone())) - .map_err(|_| Error::::FailedToSchedule)?; - - T::Scheduler::schedule( - DispatchTime::At(when), - None, - 63, - frame_system::RawOrigin::Root.into(), - bound_call, - ) - .map_err(|_| Error::::FailedToSchedule)?; - - // Emit the SwapScheduled event - Self::deposit_event(Event::DissolveNetworkScheduled { - account: who.clone(), - netuid, - execution_block: when, - }); - - Ok(().into()) + Err(Error::::CallDisabled.into()) + + // let who = ensure_signed(origin)?; + + // let current_block: BlockNumberFor = >::block_number(); + // let duration: BlockNumberFor = DissolveNetworkScheduleDuration::::get(); + // let when: BlockNumberFor = current_block.saturating_add(duration); + + // let call = Call::::dissolve_network { + // coldkey: who.clone(), + // netuid, + // }; + + // let bound_call = T::Preimages::bound(LocalCallOf::::from(call.clone())) + // .map_err(|_| Error::::FailedToSchedule)?; + + // T::Scheduler::schedule( + // DispatchTime::At(when), + // None, + // 63, + // frame_system::RawOrigin::Root.into(), + // bound_call, + // ) + // .map_err(|_| Error::::FailedToSchedule)?; + + // // Emit the SwapScheduled event + // Self::deposit_event(Event::DissolveNetworkScheduled { + // account: who.clone(), + // netuid, + // execution_block: when, + // }); + + // Ok(().into()) } /// ---- Set prometheus information for the neuron. diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 5bc0089ba1..1f189cd2f6 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -193,5 +193,7 @@ mod errors { TransferDisallowed, /// Activity cutoff is being set too low. ActivityCutoffTooLow, + /// Call is disabled + CallDisabled, } } diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index df9dffabca..dda43af10b 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -81,7 +81,9 @@ mod hooks { // Remove Stake map entries .saturating_add(migrations::migrate_remove_stake_map::migrate_remove_stake_map::()) // Remove unused maps entries - .saturating_add(migrations::migrate_remove_unused_maps_and_values::migrate_remove_unused_maps_and_values::()); + .saturating_add(migrations::migrate_remove_unused_maps_and_values::migrate_remove_unused_maps_and_values::()) + // Migrate dissolve sn73 + .saturating_add(migrations::migrate_dissolve_sn73::migrate_dissolve_sn73::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_dissolve_sn73.rs b/pallets/subtensor/src/migrations/migrate_dissolve_sn73.rs new file mode 100644 index 0000000000..9839d2d93d --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_dissolve_sn73.rs @@ -0,0 +1,219 @@ +use alloc::string::String; + +use frame_support::{traits::Get, weights::Weight}; +use substrate_fixed::types::I96F32; + +use super::*; + +pub fn migrate_dissolve_sn73() -> Weight { + let migration_name = b"migrate_dissolve_sn73".to_vec(); + let this_netuid = 73; + + // Initialize the weight with one read operation. + let mut weight = T::DbWeight::get().reads(1); + + // Check if the migration has already run + if HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + migration_name + ); + return weight; + } + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + if NetworksAdded::::get(this_netuid) { + // Subnet exists, skip + log::info!("Subnet was already added, skipping"); + } else { + // ======== Migration Logic ======== + + // Get the subnet TAO + let subnet_tao = I96F32::from_num(SubnetTAO::::get(this_netuid)); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + log::debug!("Subnet TAO: {}", subnet_tao); + + let mut total_alpha: I96F32 = I96F32::from_num(0); + // Iterate over every hotkey and sum up the total alpha + let mut hotkeys_to_remove: Vec = Vec::new(); + for (hotkey, netuid_i, total_hotkey_alpha) in TotalHotkeyAlpha::::iter() { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if netuid_i != this_netuid { + continue; + } + + hotkeys_to_remove.push(hotkey); + total_alpha = total_alpha.saturating_add(I96F32::from_num(total_hotkey_alpha)); + } + log::debug!("Total alpha: {}", total_alpha); + + // Iterate over every hotkey and distribute the TAO from the pool + // using previous total alpha as the denominator + for hotkey in hotkeys_to_remove.iter() { + log::debug!("Hotkey: {:?}", hotkey.clone()); + + let total_hotkey_alpha_i = TotalHotkeyAlpha::::get(hotkey.clone(), this_netuid); + let total_hotkey_alpha = I96F32::from_num(total_hotkey_alpha_i); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + + // Get the total hotkey shares + let total_hotkey_shares = + I96F32::from_num(TotalHotkeyShares::::get(hotkey.clone(), this_netuid)); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + log::debug!("Total hotkey shares: {}", total_hotkey_shares); + + // Get the equivalent amount of TAO + let hotkey_tao: I96F32 = total_hotkey_alpha + .saturating_div(total_alpha) + .saturating_mul(subnet_tao); + log::debug!("Total hotkey alpha: {}", total_hotkey_alpha); + log::debug!("Hotkey TAO: {}", hotkey_tao); + + let mut coldkeys_to_remove: Vec = Vec::new(); + // Distribute the TAO to each of the stakers to the hotkey + for ((coldkey, netuid_i), alpha_i) in Alpha::::iter_prefix((&hotkey,)) { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if netuid_i != this_netuid { + continue; + } + + coldkeys_to_remove.push(coldkey.clone()); + + let alpha_shares = I96F32::from_num(alpha_i); + let coldkey_share: I96F32 = alpha_shares.saturating_div(total_hotkey_shares); + let coldkey_tao = coldkey_share.saturating_mul(hotkey_tao); + let coldkey_alpha = coldkey_share.saturating_mul(total_hotkey_alpha); + log::debug!("Alpha shares: {}", alpha_shares); + log::debug!("Coldkey share: {}", coldkey_share); + log::debug!("Coldkey TAO: {}", coldkey_tao); + + // Distribute the TAO to the coldkey + let as_tao: u64 = coldkey_tao.saturating_to_num::(); + let as_alpha: u64 = coldkey_alpha.saturating_to_num::(); + + if as_tao > 0 { + Pallet::::add_balance_to_coldkey_account(&coldkey, as_tao); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + + // Emit event + Pallet::::deposit_event(Event::StakeRemoved( + coldkey.clone(), + hotkey.clone(), + as_tao, + as_alpha, + this_netuid, + )); + } + } + // Clear coldkeys + for coldkey in coldkeys_to_remove { + Alpha::::remove((&hotkey, coldkey, this_netuid)); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + } + } + + // === Clear storage entries === + // Clear subnet owner and hotkey + SubnetOwner::::remove(this_netuid); + SubnetOwnerHotkey::::remove(this_netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(2)); + + // Clear hotkeys + for hotkey in hotkeys_to_remove { + TotalHotkeyAlpha::::remove(hotkey.clone(), this_netuid); + TotalHotkeyShares::::remove(hotkey.clone(), this_netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(2)); + } + + // Clear pool + SubnetTAO::::remove(this_netuid); + SubnetAlphaIn::::remove(this_netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(2)); + + // Clear AlphaOut + SubnetAlphaOut::::remove(this_netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + // Clear pending emissions + SubnetTaoInEmission::::remove(this_netuid); + SubnetAlphaInEmission::::remove(this_netuid); + SubnetAlphaOutEmission::::remove(this_netuid); + PendingEmission::::remove(this_netuid); + PendingRootDivs::::remove(this_netuid); + PendingAlphaSwapped::::remove(this_netuid); + PendingOwnerCut::::remove(this_netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(7)); + + // Clear trackers + let clear_results_0 = + AlphaDividendsPerSubnet::::clear_prefix(this_netuid, u32::MAX, None); + weight = weight.saturating_add(T::DbWeight::get().writes(clear_results_0.unique.into())); + let clear_results_1 = TaoDividendsPerSubnet::::clear_prefix(this_netuid, u32::MAX, None); + weight = weight.saturating_add(T::DbWeight::get().writes(clear_results_1.unique.into())); + + // Adjust total stake + TotalStake::::mutate(|total| { + *total = total.saturating_sub(subnet_tao.saturating_to_num::()); + }); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + + // Clear subnet volume + SubnetVolume::::remove(this_netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + // Clear child keys + let clear_results_2 = PendingChildKeys::::clear_prefix(this_netuid, u32::MAX, None); + weight = weight.saturating_add(T::DbWeight::get().writes(clear_results_2.unique.into())); + + let mut childkeys_to_remove: Vec = Vec::new(); + for (childkey, netuid_i, _parents) in ParentKeys::::iter() { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if netuid_i != this_netuid { + continue; + } + + childkeys_to_remove.push(childkey); + } + + let mut parent_keys_to_remove: Vec = Vec::new(); + for (parent_key, netuid_i, _children) in ChildKeys::::iter() { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if netuid_i != this_netuid { + continue; + } + + parent_keys_to_remove.push(parent_key); + } + + for child_key in childkeys_to_remove { + ParentKeys::::remove(child_key, this_netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + } + + for parent_key in parent_keys_to_remove { + ChildKeys::::remove(parent_key, this_netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + } + + // Clear reg allowed maps + NetworkRegistrationAllowed::::remove(this_netuid); + NetworkPowRegistrationAllowed::::remove(this_netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(2)); + // ======== End Migration Logic ======== + } + + // Mark the migration as completed + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed.", + String::from_utf8_lossy(&migration_name) + ); + + // Return the migration weight. + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 6af6ad2a56..181ce31830 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -4,6 +4,7 @@ pub mod migrate_commit_reveal_v2; pub mod migrate_create_root_network; pub mod migrate_delete_subnet_21; pub mod migrate_delete_subnet_3; +pub mod migrate_dissolve_sn73; pub mod migrate_fix_is_network_member; pub mod migrate_identities_v2; pub mod migrate_init_total_issuance; diff --git a/pallets/subtensor/src/rpc_info/stake_info.rs b/pallets/subtensor/src/rpc_info/stake_info.rs index bda619596a..7ac5b6b0c6 100644 --- a/pallets/subtensor/src/rpc_info/stake_info.rs +++ b/pallets/subtensor/src/rpc_info/stake_info.rs @@ -2,6 +2,7 @@ use super::*; use frame_support::pallet_prelude::{Decode, Encode}; extern crate alloc; use codec::Compact; +use substrate_fixed::types::I96F32; #[freeze_struct("5cfb3c84c3af3116")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] @@ -112,4 +113,33 @@ impl Pallet { is_registered, }) } + + pub fn get_stake_fee( + origin: Option<(T::AccountId, u16)>, + origin_coldkey_account: T::AccountId, + destination: Option<(T::AccountId, u16)>, + destination_coldkey_account: T::AccountId, + amount: u64, + ) -> u64 { + let origin_: Option<(&T::AccountId, u16)> = + if let Some((ref origin_hotkey, origin_netuid)) = origin { + Some((origin_hotkey, origin_netuid)) + } else { + None + }; + + let destination_ = if let Some((ref destination_hotkey, destination_netuid)) = destination { + Some((destination_hotkey, destination_netuid)) + } else { + None + }; + + Self::calculate_staking_fee( + origin_, + &origin_coldkey_account, + destination_, + &destination_coldkey_account, + I96F32::saturating_from_num(amount), + ) + } } diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index cfe3b8cde1..1ccc932157 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -51,7 +51,7 @@ impl Pallet { )?; // Log the event. - log::info!( + log::debug!( "StakeMoved( coldkey:{:?}, origin_hotkey:{:?}, origin_netuid:{:?}, destination_hotkey:{:?}, destination_netuid:{:?} )", coldkey.clone(), origin_hotkey.clone(), @@ -133,7 +133,7 @@ impl Pallet { )?; // 9. Emit an event for logging/monitoring. - log::info!( + log::debug!( "StakeTransferred(origin_coldkey: {:?}, destination_coldkey: {:?}, hotkey: {:?}, origin_netuid: {:?}, destination_netuid: {:?}, amount: {:?})", coldkey, destination_coldkey, @@ -203,7 +203,7 @@ impl Pallet { )?; // Emit an event for logging. - log::info!( + log::debug!( "StakeSwapped(coldkey: {:?}, hotkey: {:?}, origin_netuid: {:?}, destination_netuid: {:?}, amount: {:?})", coldkey, hotkey, @@ -275,7 +275,7 @@ impl Pallet { )?; // Emit an event for logging. - log::info!( + log::debug!( "StakeSwapped(coldkey: {:?}, hotkey: {:?}, origin_netuid: {:?}, destination_netuid: {:?}, amount: {:?})", coldkey, hotkey, @@ -339,8 +339,10 @@ impl Pallet { // Unstake from the origin subnet, returning TAO (or a 1:1 equivalent). let fee = Self::calculate_staking_fee( - origin_netuid, - origin_hotkey, + Some((origin_hotkey, origin_netuid)), + origin_coldkey, + Some((destination_hotkey, destination_netuid)), + destination_coldkey, I96F32::saturating_from_num(alpha_amount), ) .safe_div(2); diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 235d075e6f..96c04c9456 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -39,7 +39,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; - log::info!( + log::debug!( "do_remove_stake( origin:{:?} hotkey:{:?}, netuid: {:?}, alpha_unstaked:{:?} )", coldkey, hotkey, @@ -59,8 +59,10 @@ impl Pallet { // 3. Swap the alpba to tao and update counters for this subnet. let fee = Self::calculate_staking_fee( - netuid, - &hotkey, + Some((&hotkey, netuid)), + &coldkey, + None, + &coldkey, I96F32::saturating_from_num(alpha_unstaked), ); let tao_unstaked: u64 = @@ -115,7 +117,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; - log::info!("do_unstake_all( origin:{:?} hotkey:{:?} )", coldkey, hotkey); + log::debug!("do_unstake_all( origin:{:?} hotkey:{:?} )", coldkey, hotkey); // 2. Ensure that the hotkey account exists this is only possible through registration. ensure!( @@ -133,8 +135,10 @@ impl Pallet { let alpha_unstaked = Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); let fee = Self::calculate_staking_fee( - netuid, - &hotkey, + Some((&hotkey, netuid)), + &coldkey, + None, + &coldkey, I96F32::saturating_from_num(alpha_unstaked), ); @@ -187,7 +191,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; - log::info!("do_unstake_all( origin:{:?} hotkey:{:?} )", coldkey, hotkey); + log::debug!("do_unstake_all( origin:{:?} hotkey:{:?} )", coldkey, hotkey); // 2. Ensure that the hotkey account exists this is only possible through registration. ensure!( @@ -208,8 +212,10 @@ impl Pallet { let alpha_unstaked = Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); let fee = Self::calculate_staking_fee( - netuid, - &hotkey, + Some((&hotkey, netuid)), + &coldkey, + None, + &coldkey, I96F32::saturating_from_num(alpha_unstaked), ); @@ -288,7 +294,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; - log::info!( + log::debug!( "do_remove_stake( origin:{:?} hotkey:{:?}, netuid: {:?}, alpha_unstaked:{:?} )", coldkey, hotkey, @@ -315,8 +321,10 @@ impl Pallet { // 4. Swap the alpha to tao and update counters for this subnet. let fee = Self::calculate_staking_fee( - netuid, - &hotkey, + Some((&hotkey, netuid)), + &coldkey, + None, + &coldkey, I96F32::saturating_from_num(alpha_unstaked), ); let tao_unstaked = diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 8be22c32f9..894a5a9132 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -795,7 +795,7 @@ impl Pallet { actual_alpha_decrease, netuid, )); - log::info!( + log::debug!( "StakeRemoved( coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?} )", coldkey.clone(), hotkey.clone(), @@ -858,7 +858,7 @@ impl Pallet { actual_alpha, netuid, )); - log::info!( + log::debug!( "StakeAdded( coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?} )", coldkey.clone(), hotkey.clone(), @@ -1073,24 +1073,48 @@ impl Pallet { } pub(crate) fn calculate_staking_fee( - netuid: u16, - hotkey: &T::AccountId, + origin: Option<(&T::AccountId, u16)>, + _origin_coldkey: &T::AccountId, + destination: Option<(&T::AccountId, u16)>, + _destination_coldkey: &T::AccountId, alpha_estimate: I96F32, ) -> u64 { - if (netuid == Self::get_root_netuid()) || (SubnetMechanism::::get(netuid)) == 0 { - DefaultStakingFee::::get() - } else { - let fee = alpha_estimate - .saturating_mul( - I96F32::saturating_from_num(AlphaDividendsPerSubnet::::get(netuid, &hotkey)) - .safe_div(I96F32::saturating_from_num(TotalHotkeyAlpha::::get( - &hotkey, netuid, - ))), - ) - .saturating_mul(Self::get_alpha_price(netuid)) // fee needs to be in TAO - .saturating_to_num::(); + match origin { + // If origin is defined, we are removing/moving stake + Some((origin_hotkey, origin_netuid)) => { + if let Some((_destination_hotkey, destination_netuid)) = destination { + // This is a stake move/swap/transfer + if destination_netuid == origin_netuid { + // If destination is on the same subnet, use the default fee + return DefaultStakingFee::::get(); + } + } - fee.max(DefaultStakingFee::::get()) + if origin_netuid == Self::get_root_netuid() + || SubnetMechanism::::get(origin_netuid) == 0 + { + // If the origin netuid is root, or the subnet mechanism is 0, use the default fee + DefaultStakingFee::::get() + } else { + // Otherwise, calculate the fee based on the alpha estimate + let fee = alpha_estimate + .saturating_mul( + I96F32::saturating_from_num(AlphaDividendsPerSubnet::::get( + origin_netuid, + &origin_hotkey, + )) + .safe_div(I96F32::saturating_from_num( + TotalHotkeyAlpha::::get(&origin_hotkey, origin_netuid), + )), + ) + .saturating_mul(Self::get_alpha_price(origin_netuid)) // fee needs to be in TAO + .saturating_to_num::(); + + fee.max(DefaultStakingFee::::get()) + } + } + // If origin is not defined, we are adding stake; use default fee + None => DefaultStakingFee::::get(), } } } diff --git a/pallets/subtensor/src/subnets/registration.rs b/pallets/subtensor/src/subnets/registration.rs index 121f38b338..5c698c1b33 100644 --- a/pallets/subtensor/src/subnets/registration.rs +++ b/pallets/subtensor/src/subnets/registration.rs @@ -20,7 +20,7 @@ impl Pallet { // Expand subnetwork with new account. Self::append_neuron(netuid, hotkey, block_number); - log::info!("add new neuron account"); + log::debug!("add new neuron account"); } else { // Replacement required. // We take the neuron with the lowest pruning score here. @@ -28,7 +28,7 @@ impl Pallet { // Replace the neuron account with the new info. Self::replace_neuron(netuid, neuron_uid, hotkey, block_number); - log::info!("prune neuron"); + log::debug!("prune neuron"); } // Return the UID of the neuron. diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 2b2feae4b8..be0d563576 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2,6 +2,8 @@ use super::mock::*; use crate::*; +use alloc::collections::BTreeMap; +use approx::assert_abs_diff_eq; use codec::{Decode, Encode}; use frame_support::{ StorageHasher, Twox64Concat, assert_ok, @@ -413,3 +415,414 @@ fn test_migrate_subnet_volume() { assert_eq!(new_value, Some(old_value as u128)); }); } + +#[test] +fn test_migrate_dissolve_sn73_removes_entries() { + new_test_ext(1).execute_with(|| { + let this_netuid: u16 = 73; + let sn_owner_hk: U256 = U256::from(1); + let sn_owner_ck: U256 = U256::from(2); + + let subnet_tao: u64 = 678_900_000_000; + + let staker_0_hk: U256 = U256::from(3); + let staker_0_ck: U256 = U256::from(4); + + let staker_1_hk: U256 = U256::from(5); + let staker_1_ck: U256 = U256::from(6); + + let staker_2_hk: U256 = U256::from(7); + let staker_2_ck: U256 = U256::from(8); + + let delegate_0_ck: U256 = U256::from(9); + let delegate_1_ck: U256 = U256::from(10); + + let stakes = vec![ + (staker_0_hk, staker_0_ck, 100_000_000_000), + (staker_1_hk, staker_1_ck, 200_000_000_000), + (staker_2_hk, staker_2_ck, 123_456_789_000), + (staker_2_hk, delegate_0_ck, 100_000_000_000), // delegates to hk 2 + (staker_2_hk, delegate_1_ck, 200_000_000_000), // delegates to hk 2 + ]; + let total_alpha = stakes.iter().map(|(_, _, stake)| stake).sum::(); + + let mut created_netuid = 0; + while created_netuid < this_netuid { + created_netuid = add_dynamic_network(&sn_owner_hk, &sn_owner_ck); + } + assert_eq!(created_netuid, this_netuid); + + for (hk, ck, stake) in stakes.iter() { + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + hk, + ck, + this_netuid, + *stake, + ); + } + // Set subnetTAO + SubnetTAO::::insert(this_netuid, subnet_tao); + + // ==== Make sure all the maps are set non-default + + // Set some child keys + ParentKeys::::insert(U256::from(1), this_netuid, vec![(1, U256::from(2))]); + ChildKeys::::insert(U256::from(2), this_netuid, vec![(1, U256::from(1))]); + PendingChildKeys::::insert( + this_netuid, + U256::from(1), + (vec![(1, U256::from(2))], 123), + ); + + // Set some alpha dividends + AlphaDividendsPerSubnet::::insert(this_netuid, U256::from(1), 100_000_000_000); + TaoDividendsPerSubnet::::insert(this_netuid, U256::from(2), 200_000_000_000); + + // Set pending emissions + PendingEmission::::insert(this_netuid, 123); + PendingAlphaSwapped::::insert(this_netuid, 456); + PendingOwnerCut::::insert(this_netuid, 789); + SubnetAlphaInEmission::::insert(this_netuid, 789); + SubnetTaoInEmission::::insert(this_netuid, 101); + SubnetAlphaOutEmission::::insert(this_netuid, 102); + + // Set sn volume + SubnetVolume::::insert(this_netuid, 123); + + // Set alpha out + SubnetAlphaOut::::insert(this_netuid, 100_000_000_000); + // Set alpha in + SubnetAlphaIn::::insert(this_netuid, 100_000_000_000); + + // Set reg allowed maps + NetworkRegistrationAllowed::::insert(this_netuid, true); + NetworkPowRegistrationAllowed::::insert(this_netuid, true); + + // === All maps are non-default === + + // Run existing remove network dissolve + SubtensorModule::remove_network(this_netuid); + + // Run new dissolve migration code + crate::migrations::migrate_dissolve_sn73::migrate_dissolve_sn73::(); + + // Verify sn owner is removed + assert!(SubnetOwner::::try_get(this_netuid).is_err()); + assert!(SubnetOwnerHotkey::::try_get(this_netuid).is_err()); + + // Verify all the maps are now empty + assert_eq!(SubnetTAO::::get(this_netuid), 0); + assert_eq!(SubnetVolume::::get(this_netuid), 0); + + for (childkey, netuid_i) in ParentKeys::::iter_keys() { + assert_ne!( + netuid_i, this_netuid, + "Child key {} should be removed", + childkey + ); + } + + for (parent_key, netuid_i) in ChildKeys::::iter_keys() { + assert_ne!( + netuid_i, this_netuid, + "Parent key {} should be removed", + parent_key + ); + } + + // Verify all the stake entries are removed + for (hk, ck, netuid_i) in Alpha::::iter_keys() { + assert_ne!( + netuid_i, this_netuid, + "Stake entry for {} {} {} should be removed", + hk, ck, netuid_i + ); + } + + for (hk, netuid_i) in TotalHotkeyAlpha::::iter_keys() { + assert_ne!( + netuid_i, this_netuid, + "Total alpha entry for {} {} should be removed", + hk, netuid_i + ); + } + + for (ck, netuid_i) in TotalHotkeyShares::::iter_keys() { + assert_ne!( + netuid_i, this_netuid, + "Total shares entry for {} {} should be removed", + ck, netuid_i + ); + } + + // Verify div maps + assert!( + AlphaDividendsPerSubnet::::iter_prefix(this_netuid) + .collect::>() + .is_empty() + ); + assert!( + TaoDividendsPerSubnet::::iter_prefix(this_netuid) + .collect::>() + .is_empty() + ); + + // Verify all the pending maps are removed + assert!(PendingEmission::::try_get(this_netuid).is_err()); + assert!(PendingAlphaSwapped::::try_get(this_netuid).is_err()); + assert!(PendingOwnerCut::::try_get(this_netuid).is_err()); + assert!(SubnetAlphaInEmission::::try_get(this_netuid).is_err()); + assert!(SubnetTaoInEmission::::try_get(this_netuid).is_err()); + assert!(SubnetAlphaOutEmission::::try_get(this_netuid).is_err()); + + // verify pool is removed + assert!(SubnetAlphaIn::::try_get(this_netuid).is_err()); + assert!(SubnetAlphaOut::::try_get(this_netuid).is_err()); + assert!(SubnetTAO::::try_get(this_netuid).is_err()); + + // Verify sn volume is removed + assert!(SubnetVolume::::try_get(this_netuid).is_err()); + + // verify reg allowed maps are removed + assert!(NetworkRegistrationAllowed::::try_get(this_netuid).is_err()); + assert!(NetworkPowRegistrationAllowed::::try_get(this_netuid).is_err()); + }); +} + +#[test] +fn test_migrate_dissolve_sn73_pays_out_subnet_tao() { + new_test_ext(1).execute_with(|| { + let this_netuid: u16 = 73; + let sn_owner_hk: U256 = U256::from(1); + let sn_owner_ck: U256 = U256::from(2); + + let subnet_tao: u64 = 678_900_000_000; + + let staker_0_hk: U256 = U256::from(3); + let staker_0_ck: U256 = U256::from(4); + + let staker_1_hk: U256 = U256::from(5); + let staker_1_ck: U256 = U256::from(6); + + let staker_2_hk: U256 = U256::from(7); + let staker_2_ck: U256 = U256::from(8); + + let delegate_0_ck: U256 = U256::from(9); + let delegate_1_ck: U256 = U256::from(10); + + let stakes = vec![ + (staker_0_hk, staker_0_ck, 100_000_000_000), + (staker_1_hk, staker_1_ck, 200_000_000_000), + (staker_2_hk, staker_2_ck, 123_456_789_000), + (staker_2_hk, delegate_0_ck, 400_000_000_000), // delegates to hk 2 + (staker_2_hk, delegate_1_ck, 500_000_000_000), // delegates to hk 2 + (staker_1_hk, delegate_0_ck, 456_789_000_000), // delegate 0 also stakes to hk 1 + ]; + let total_alpha = stakes.iter().map(|(_, _, stake)| stake).sum::(); + + let mut created_netuid = 0; + while created_netuid < this_netuid { + created_netuid = add_dynamic_network(&sn_owner_hk, &sn_owner_ck); + } + assert_eq!(created_netuid, this_netuid); + + for (hk, ck, stake) in stakes.iter() { + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + hk, + ck, + this_netuid, + *stake, + ); + } + // Set subnetTAO + SubnetTAO::::insert(this_netuid, subnet_tao); + + // Run existing remove network dissolve + SubtensorModule::remove_network(this_netuid); + + // Run new dissolve migration code + crate::migrations::migrate_dissolve_sn73::migrate_dissolve_sn73::(); + + // Calculate expected balances + let denom = I96F32::from_num(total_alpha); + let subnet_tao_float = I96F32::from_num(subnet_tao); + + log::debug!("Subnet TAO: {}", subnet_tao); + log::debug!("Denom: {}", denom); + let mut expected_balances: BTreeMap = BTreeMap::new(); + for (hk, ck, stake) in stakes.iter() { + // Calculate share of the subnetTAO expected for the coldkey + let hotkey_alpha = I96F32::from_num(*stake); + let hotkey_share = hotkey_alpha.saturating_div(denom); + let hotkey_tao = hotkey_share.saturating_mul(subnet_tao_float); + + log::debug!( + "Expected: hk {}, ck {}, stake {}, hotkey_tao {}", + hk, + ck, + stake, + hotkey_tao + ); + expected_balances + .entry(*ck) + .and_modify(|e| *e = e.saturating_add(hotkey_tao.saturating_to_num::())) + .or_insert(hotkey_tao.saturating_to_num::()); + } + + // Verify that each staker has received their share of the subnetTAO + for (ck, expected_balance) in expected_balances { + assert_abs_diff_eq!( + SubtensorModule::get_coldkey_balance(&ck), + expected_balance, + epsilon = 100 + ); + } + }); +} + +#[test] +fn test_migrate_dissolve_sn73_doesnt_affect_other_subnets() { + new_test_ext(1).execute_with(|| { + let this_netuid: u16 = 73; + let other_netuid: u16 = 72; // Also created + let sn_owner_hk: U256 = U256::from(1); + let sn_owner_ck: U256 = U256::from(2); + + let subnet_tao: u64 = 678_900_000_000; + + let staker_0_hk: U256 = U256::from(3); + let staker_0_ck: U256 = U256::from(4); + + let staker_1_hk: U256 = U256::from(5); + let staker_1_ck: U256 = U256::from(6); + + let staker_2_hk: U256 = U256::from(7); + let staker_2_ck: U256 = U256::from(8); + + let delegate_0_ck: U256 = U256::from(9); + let delegate_1_ck: U256 = U256::from(10); + + let stakes = vec![ + (staker_0_hk, staker_0_ck, 100_000_000_000), + (staker_1_hk, staker_1_ck, 200_000_000_000), + (staker_2_hk, staker_2_ck, 123_456_789_000), + (staker_2_hk, delegate_0_ck, 400_000_000_000), // delegates to hk 2 + (staker_2_hk, delegate_1_ck, 500_000_000_000), // delegates to hk 2 + (staker_1_hk, delegate_0_ck, 456_789_000_000), // delegate 0 also stakes to hk 1 + ]; + let total_alpha = stakes.iter().map(|(_, _, stake)| stake).sum::(); + + let mut created_netuid = 0; + while created_netuid < this_netuid { + created_netuid = add_dynamic_network(&sn_owner_hk, &sn_owner_ck); + } + assert_eq!(created_netuid, this_netuid); + + for netuid in [this_netuid, other_netuid] { + // Stake to both subnets same amounts + for (hk, ck, stake) in stakes.iter() { + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + hk, ck, netuid, *stake, + ); + } + } + + // Set subnetTAO + SubnetTAO::::insert(this_netuid, subnet_tao); + + // ===== Set some storage maps ==== + + // Set some child keys + ParentKeys::::insert(U256::from(1), other_netuid, vec![(1, U256::from(2))]); + ChildKeys::::insert(U256::from(2), other_netuid, vec![(1, U256::from(1))]); + PendingChildKeys::::insert( + other_netuid, + U256::from(1), + (vec![(1, U256::from(2))], 123), + ); + + // Set some alpha dividends + AlphaDividendsPerSubnet::::insert(other_netuid, U256::from(1), 100_000_000_000); + TaoDividendsPerSubnet::::insert(other_netuid, U256::from(2), 200_000_000_000); + + // Set pending emissions + PendingEmission::::insert(other_netuid, 123); + PendingAlphaSwapped::::insert(other_netuid, 456); + PendingOwnerCut::::insert(other_netuid, 789); + SubnetAlphaInEmission::::insert(other_netuid, 789); + SubnetTaoInEmission::::insert(other_netuid, 101); + SubnetAlphaOutEmission::::insert(other_netuid, 102); + + // Set sn volume + SubnetVolume::::insert(other_netuid, 123); + + // Set alpha out + SubnetAlphaOut::::insert(other_netuid, 100_000_000_000); + // Set alpha in + SubnetAlphaIn::::insert(other_netuid, 100_000_000_000); + + // Set reg allowed maps + NetworkRegistrationAllowed::::insert(other_netuid, true); + NetworkPowRegistrationAllowed::::insert(other_netuid, true); + + // ===== End of setting storage maps ==== + + // Run existing remove network dissolve + SubtensorModule::remove_network(this_netuid); + + // Run new dissolve migration code + crate::migrations::migrate_dissolve_sn73::migrate_dissolve_sn73::(); + + // Verify that the other netuid is unaffected + for (hk, ck, stake) in stakes.iter() { + let stake_on_subnet = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(hk, ck, other_netuid); + + assert_eq!(stake_on_subnet, *stake); + } + + // Check other storages + assert!(SubnetOwner::::try_get(other_netuid).is_ok()); + assert!(SubnetOwnerHotkey::::try_get(other_netuid).is_ok()); + + // Verify all the maps are not touched + assert!(SubnetTAO::::try_get(other_netuid).is_ok()); + assert!(SubnetVolume::::try_get(other_netuid).is_ok()); + + assert!(ParentKeys::::try_get(U256::from(1), other_netuid).is_ok()); + assert!(ChildKeys::::try_get(U256::from(2), other_netuid).is_ok()); + assert!(PendingChildKeys::::try_get(other_netuid, U256::from(1)).is_ok()); + + // Verify div maps + assert!( + !AlphaDividendsPerSubnet::::iter_prefix(other_netuid) + .collect::>() + .is_empty() + ); + assert!( + !TaoDividendsPerSubnet::::iter_prefix(other_netuid) + .collect::>() + .is_empty() + ); + + // Verify all the pending maps are not touched + assert!(PendingEmission::::try_get(other_netuid).is_ok()); + assert!(PendingAlphaSwapped::::try_get(other_netuid).is_ok()); + assert!(PendingOwnerCut::::try_get(other_netuid).is_ok()); + assert!(SubnetAlphaInEmission::::try_get(other_netuid).is_ok()); + assert!(SubnetTaoInEmission::::try_get(other_netuid).is_ok()); + assert!(SubnetAlphaOutEmission::::try_get(other_netuid).is_ok()); + + // verify pool is present + assert!(SubnetAlphaIn::::try_get(other_netuid).is_ok()); + assert!(SubnetAlphaOut::::try_get(other_netuid).is_ok()); + assert!(SubnetTAO::::try_get(other_netuid).is_ok()); + + // Verify sn volume is present + assert!(SubnetVolume::::try_get(other_netuid).is_ok()); + + // verify reg allowed maps are present + assert!(NetworkRegistrationAllowed::::try_get(other_netuid).is_ok()); + assert!(NetworkPowRegistrationAllowed::::try_get(other_netuid).is_ok()); + }); +} diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index f367a01d02..7dda0502c1 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -1,6 +1,5 @@ use super::mock::*; use crate::*; -use crate::{ColdkeySwapScheduleDuration, DissolveNetworkScheduleDuration, Event}; use frame_support::assert_ok; use frame_system::Config; use sp_core::U256; @@ -42,245 +41,245 @@ fn test_registration_ok() { }) } -#[test] -fn test_schedule_dissolve_network_execution() { - new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; - let netuid: u16 = 2; - let tempo: u16 = 13; - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id = U256::from(0); // Neighbour of the beast, har har - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 129123813, - &hotkey_account_id, - ); - - //add network - add_network(netuid, tempo, 0); - - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), - netuid, - block_number, - nonce, - work.clone(), - hotkey_account_id, - coldkey_account_id - )); - - assert!(SubtensorModule::if_subnet_exist(netuid)); - - assert_ok!(SubtensorModule::schedule_dissolve_network( - <::RuntimeOrigin>::signed(coldkey_account_id), - netuid - )); - - let current_block = System::block_number(); - let execution_block = current_block + DissolveNetworkScheduleDuration::::get(); - - System::assert_last_event( - Event::DissolveNetworkScheduled { - account: coldkey_account_id, - netuid, - execution_block, - } - .into(), - ); - - run_to_block(execution_block); - assert!(!SubtensorModule::if_subnet_exist(netuid)); - }) -} - -#[test] -fn test_non_owner_schedule_dissolve_network_execution() { - new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; - let netuid: u16 = 2; - let tempo: u16 = 13; - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id = U256::from(0); // Neighbour of the beast, har har - let non_network_owner_account_id = U256::from(2); // - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 129123813, - &hotkey_account_id, - ); - - //add network - add_network(netuid, tempo, 0); - - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), - netuid, - block_number, - nonce, - work.clone(), - hotkey_account_id, - coldkey_account_id - )); - - assert!(SubtensorModule::if_subnet_exist(netuid)); - - assert_ok!(SubtensorModule::schedule_dissolve_network( - <::RuntimeOrigin>::signed(non_network_owner_account_id), - netuid - )); - - let current_block = System::block_number(); - let execution_block = current_block + DissolveNetworkScheduleDuration::::get(); - - System::assert_last_event( - Event::DissolveNetworkScheduled { - account: non_network_owner_account_id, - netuid, - execution_block, - } - .into(), - ); - - run_to_block(execution_block); - // network exists since the caller is no the network owner - assert!(SubtensorModule::if_subnet_exist(netuid)); - }) -} - -#[test] -fn test_new_owner_schedule_dissolve_network_execution() { - new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; - let netuid: u16 = 2; - let tempo: u16 = 13; - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id = U256::from(0); // Neighbour of the beast, har har - let new_network_owner_account_id = U256::from(2); // - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 129123813, - &hotkey_account_id, - ); - - //add network - add_network(netuid, tempo, 0); - - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), - netuid, - block_number, - nonce, - work.clone(), - hotkey_account_id, - coldkey_account_id - )); - - assert!(SubtensorModule::if_subnet_exist(netuid)); - - // the account is not network owner when schedule the call - assert_ok!(SubtensorModule::schedule_dissolve_network( - <::RuntimeOrigin>::signed(new_network_owner_account_id), - netuid - )); - - let current_block = System::block_number(); - let execution_block = current_block + DissolveNetworkScheduleDuration::::get(); - - System::assert_last_event( - Event::DissolveNetworkScheduled { - account: new_network_owner_account_id, - netuid, - execution_block, - } - .into(), - ); - run_to_block(current_block + 1); - // become network owner after call scheduled - crate::SubnetOwner::::insert(netuid, new_network_owner_account_id); - - run_to_block(execution_block); - // network exists since the caller is no the network owner - assert!(!SubtensorModule::if_subnet_exist(netuid)); - }) -} - -#[test] -fn test_schedule_dissolve_network_execution_with_coldkey_swap() { - new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; - let netuid: u16 = 2; - let tempo: u16 = 13; - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id = U256::from(0); // Neighbour of the beast, har har - let new_network_owner_account_id = U256::from(2); // - - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 1000000000000000); - - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 129123813, - &hotkey_account_id, - ); - - //add network - add_network(netuid, tempo, 0); - - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), - netuid, - block_number, - nonce, - work.clone(), - hotkey_account_id, - coldkey_account_id - )); - - assert!(SubtensorModule::if_subnet_exist(netuid)); - - // the account is not network owner when schedule the call - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(coldkey_account_id), - new_network_owner_account_id - )); - - let current_block = System::block_number(); - let execution_block = current_block + ColdkeySwapScheduleDuration::::get(); - - run_to_block(execution_block - 1); - - // the account is not network owner when schedule the call - assert_ok!(SubtensorModule::schedule_dissolve_network( - <::RuntimeOrigin>::signed(new_network_owner_account_id), - netuid - )); - - System::assert_last_event( - Event::DissolveNetworkScheduled { - account: new_network_owner_account_id, - netuid, - execution_block: DissolveNetworkScheduleDuration::::get() + execution_block - - 1, - } - .into(), - ); - - run_to_block(execution_block); - assert_eq!( - crate::SubnetOwner::::get(netuid), - new_network_owner_account_id - ); - - let current_block = System::block_number(); - let execution_block = current_block + DissolveNetworkScheduleDuration::::get(); - - run_to_block(execution_block); - // network exists since the caller is no the network owner - assert!(!SubtensorModule::if_subnet_exist(netuid)); - }) -} +// #[test] +// fn test_schedule_dissolve_network_execution() { +// new_test_ext(1).execute_with(|| { +// let block_number: u64 = 0; +// let netuid: u16 = 2; +// let tempo: u16 = 13; +// let hotkey_account_id: U256 = U256::from(1); +// let coldkey_account_id = U256::from(0); // Neighbour of the beast, har har +// let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( +// netuid, +// block_number, +// 129123813, +// &hotkey_account_id, +// ); + +// //add network +// add_network(netuid, tempo, 0); + +// assert_ok!(SubtensorModule::register( +// <::RuntimeOrigin>::signed(hotkey_account_id), +// netuid, +// block_number, +// nonce, +// work.clone(), +// hotkey_account_id, +// coldkey_account_id +// )); + +// assert!(SubtensorModule::if_subnet_exist(netuid)); + +// assert_ok!(SubtensorModule::schedule_dissolve_network( +// <::RuntimeOrigin>::signed(coldkey_account_id), +// netuid +// )); + +// let current_block = System::block_number(); +// let execution_block = current_block + DissolveNetworkScheduleDuration::::get(); + +// System::assert_last_event( +// Event::DissolveNetworkScheduled { +// account: coldkey_account_id, +// netuid, +// execution_block, +// } +// .into(), +// ); + +// run_to_block(execution_block); +// assert!(!SubtensorModule::if_subnet_exist(netuid)); +// }) +// } + +// #[test] +// fn test_non_owner_schedule_dissolve_network_execution() { +// new_test_ext(1).execute_with(|| { +// let block_number: u64 = 0; +// let netuid: u16 = 2; +// let tempo: u16 = 13; +// let hotkey_account_id: U256 = U256::from(1); +// let coldkey_account_id = U256::from(0); // Neighbour of the beast, har har +// let non_network_owner_account_id = U256::from(2); // +// let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( +// netuid, +// block_number, +// 129123813, +// &hotkey_account_id, +// ); + +// //add network +// add_network(netuid, tempo, 0); + +// assert_ok!(SubtensorModule::register( +// <::RuntimeOrigin>::signed(hotkey_account_id), +// netuid, +// block_number, +// nonce, +// work.clone(), +// hotkey_account_id, +// coldkey_account_id +// )); + +// assert!(SubtensorModule::if_subnet_exist(netuid)); + +// assert_ok!(SubtensorModule::schedule_dissolve_network( +// <::RuntimeOrigin>::signed(non_network_owner_account_id), +// netuid +// )); + +// let current_block = System::block_number(); +// let execution_block = current_block + DissolveNetworkScheduleDuration::::get(); + +// System::assert_last_event( +// Event::DissolveNetworkScheduled { +// account: non_network_owner_account_id, +// netuid, +// execution_block, +// } +// .into(), +// ); + +// run_to_block(execution_block); +// // network exists since the caller is no the network owner +// assert!(SubtensorModule::if_subnet_exist(netuid)); +// }) +// } + +// #[test] +// fn test_new_owner_schedule_dissolve_network_execution() { +// new_test_ext(1).execute_with(|| { +// let block_number: u64 = 0; +// let netuid: u16 = 2; +// let tempo: u16 = 13; +// let hotkey_account_id: U256 = U256::from(1); +// let coldkey_account_id = U256::from(0); // Neighbour of the beast, har har +// let new_network_owner_account_id = U256::from(2); // +// let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( +// netuid, +// block_number, +// 129123813, +// &hotkey_account_id, +// ); + +// //add network +// add_network(netuid, tempo, 0); + +// assert_ok!(SubtensorModule::register( +// <::RuntimeOrigin>::signed(hotkey_account_id), +// netuid, +// block_number, +// nonce, +// work.clone(), +// hotkey_account_id, +// coldkey_account_id +// )); + +// assert!(SubtensorModule::if_subnet_exist(netuid)); + +// // the account is not network owner when schedule the call +// assert_ok!(SubtensorModule::schedule_dissolve_network( +// <::RuntimeOrigin>::signed(new_network_owner_account_id), +// netuid +// )); + +// let current_block = System::block_number(); +// let execution_block = current_block + DissolveNetworkScheduleDuration::::get(); + +// System::assert_last_event( +// Event::DissolveNetworkScheduled { +// account: new_network_owner_account_id, +// netuid, +// execution_block, +// } +// .into(), +// ); +// run_to_block(current_block + 1); +// // become network owner after call scheduled +// crate::SubnetOwner::::insert(netuid, new_network_owner_account_id); + +// run_to_block(execution_block); +// // network exists since the caller is no the network owner +// assert!(!SubtensorModule::if_subnet_exist(netuid)); +// }) +// } + +// #[test] +// fn test_schedule_dissolve_network_execution_with_coldkey_swap() { +// new_test_ext(1).execute_with(|| { +// let block_number: u64 = 0; +// let netuid: u16 = 2; +// let tempo: u16 = 13; +// let hotkey_account_id: U256 = U256::from(1); +// let coldkey_account_id = U256::from(0); // Neighbour of the beast, har har +// let new_network_owner_account_id = U256::from(2); // + +// SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 1000000000000000); + +// let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( +// netuid, +// block_number, +// 129123813, +// &hotkey_account_id, +// ); + +// //add network +// add_network(netuid, tempo, 0); + +// assert_ok!(SubtensorModule::register( +// <::RuntimeOrigin>::signed(hotkey_account_id), +// netuid, +// block_number, +// nonce, +// work.clone(), +// hotkey_account_id, +// coldkey_account_id +// )); + +// assert!(SubtensorModule::if_subnet_exist(netuid)); + +// // the account is not network owner when schedule the call +// assert_ok!(SubtensorModule::schedule_swap_coldkey( +// <::RuntimeOrigin>::signed(coldkey_account_id), +// new_network_owner_account_id +// )); + +// let current_block = System::block_number(); +// let execution_block = current_block + ColdkeySwapScheduleDuration::::get(); + +// run_to_block(execution_block - 1); + +// // the account is not network owner when schedule the call +// assert_ok!(SubtensorModule::schedule_dissolve_network( +// <::RuntimeOrigin>::signed(new_network_owner_account_id), +// netuid +// )); + +// System::assert_last_event( +// Event::DissolveNetworkScheduled { +// account: new_network_owner_account_id, +// netuid, +// execution_block: DissolveNetworkScheduleDuration::::get() + execution_block +// - 1, +// } +// .into(), +// ); + +// run_to_block(execution_block); +// assert_eq!( +// crate::SubnetOwner::::get(netuid), +// new_network_owner_account_id +// ); + +// let current_block = System::block_number(); +// let execution_block = current_block + DissolveNetworkScheduleDuration::::get(); + +// run_to_block(execution_block); +// // network exists since the caller is no the network owner +// assert!(!SubtensorModule::if_subnet_exist(netuid)); +// }) +// } // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::networks::test_register_subnet_low_lock_cost --exact --show-output --nocapture #[test] diff --git a/pallets/subtensor/src/tests/staking2.rs b/pallets/subtensor/src/tests/staking2.rs index d4c1145435..57f880ffba 100644 --- a/pallets/subtensor/src/tests/staking2.rs +++ b/pallets/subtensor/src/tests/staking2.rs @@ -623,3 +623,326 @@ fn test_try_associate_hotkey() { assert_eq!(SubtensorModule::get_owned_hotkeys(&coldkey2).len(), 0); }); } + +#[test] +fn test_stake_fee_api() { + // The API should match the calculation + new_test_ext(1).execute_with(|| { + let hotkey1 = U256::from(1); + let coldkey1 = U256::from(2); + let hotkey2 = U256::from(3); + let coldkey2 = U256::from(4); + + let netuid0 = 1; + let netuid1 = 2; + let root_netuid = SubtensorModule::get_root_netuid(); + + let alpha_divs = 100_000_000_000; + let total_hotkey_alpha = 100_000_000_000; + let tao_in = 100_000_000_000; // 100 TAO + let reciprocal_price = 2; // 1 / price + let stake_amount = 100_000_000_000; + + // Setup alpha out + SubnetAlphaOut::::insert(netuid0, 100_000_000_000); + SubnetAlphaOut::::insert(netuid1, 100_000_000_000); + // Set pools using price + SubnetAlphaIn::::insert(netuid0, tao_in * reciprocal_price); + SubnetTAO::::insert(netuid0, tao_in); + SubnetAlphaIn::::insert(netuid1, tao_in * reciprocal_price); + SubnetTAO::::insert(netuid1, tao_in); + + // Setup alpha divs for hotkey1 + AlphaDividendsPerSubnet::::insert(netuid0, hotkey1, alpha_divs); + AlphaDividendsPerSubnet::::insert(netuid1, hotkey1, alpha_divs); + + // Setup total hotkey alpha for hotkey1 + TotalHotkeyAlpha::::insert(hotkey1, netuid0, total_hotkey_alpha); + TotalHotkeyAlpha::::insert(hotkey1, netuid1, total_hotkey_alpha); + + // Test stake fee for add_stake + let stake_fee_0 = SubtensorModule::get_stake_fee( + None, + coldkey1, + Some((hotkey1, netuid0)), + coldkey1, + stake_amount, + ); + let dynamic_fee_0 = SubtensorModule::calculate_staking_fee( + None, + &coldkey1, + Some((&hotkey1, netuid0)), + &coldkey1, + I96F32::saturating_from_num(stake_amount), + ); + assert_eq!(stake_fee_0, dynamic_fee_0); + + // Test stake fee for remove on root + let stake_fee_1 = SubtensorModule::get_stake_fee( + Some((hotkey1, root_netuid)), + coldkey1, + None, + coldkey1, + stake_amount, + ); + let dynamic_fee_1 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, root_netuid)), + &coldkey1, + None, + &coldkey1, + I96F32::saturating_from_num(stake_amount), + ); + assert_eq!(stake_fee_1, dynamic_fee_1); + + // Test stake fee for move from root to non-root + let stake_fee_2 = SubtensorModule::get_stake_fee( + Some((hotkey1, root_netuid)), + coldkey1, + Some((hotkey1, netuid0)), + coldkey1, + stake_amount, + ); + let dynamic_fee_2 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, root_netuid)), + &coldkey1, + Some((&hotkey1, netuid0)), + &coldkey1, + I96F32::saturating_from_num(stake_amount), + ); + assert_eq!(stake_fee_2, dynamic_fee_2); + + // Test stake fee for move between hotkeys on root + let stake_fee_3 = SubtensorModule::get_stake_fee( + Some((hotkey1, root_netuid)), + coldkey1, + Some((hotkey2, root_netuid)), + coldkey1, + stake_amount, + ); + let dynamic_fee_3 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, root_netuid)), + &coldkey1, + Some((&hotkey2, root_netuid)), + &coldkey1, + I96F32::saturating_from_num(stake_amount), + ); + assert_eq!(stake_fee_3, dynamic_fee_3); + + // Test stake fee for move between coldkeys on root + let stake_fee_4 = SubtensorModule::get_stake_fee( + Some((hotkey1, root_netuid)), + coldkey1, + Some((hotkey1, root_netuid)), + coldkey2, + stake_amount, + ); + let dynamic_fee_4 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, root_netuid)), + &coldkey1, + Some((&hotkey1, root_netuid)), + &coldkey2, + I96F32::saturating_from_num(stake_amount), + ); + assert_eq!(stake_fee_4, dynamic_fee_4); + + // Test stake fee for *swap* from non-root to root + let stake_fee_5 = SubtensorModule::get_stake_fee( + Some((hotkey1, netuid0)), + coldkey1, + Some((hotkey1, root_netuid)), + coldkey1, + stake_amount, + ); + let dynamic_fee_5 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, netuid0)), + &coldkey1, + Some((&hotkey1, root_netuid)), + &coldkey1, + I96F32::saturating_from_num(stake_amount), + ); + assert_eq!(stake_fee_5, dynamic_fee_5); + + // Test stake fee for move between hotkeys on non-root + let stake_fee_6 = SubtensorModule::get_stake_fee( + Some((hotkey1, netuid0)), + coldkey1, + Some((hotkey2, netuid0)), + coldkey1, + stake_amount, + ); + let dynamic_fee_6 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, netuid0)), + &coldkey1, + Some((&hotkey2, netuid0)), + &coldkey1, + I96F32::saturating_from_num(stake_amount), + ); + assert_eq!(stake_fee_6, dynamic_fee_6); + + // Test stake fee for move between coldkeys on non-root + let stake_fee_7 = SubtensorModule::get_stake_fee( + Some((hotkey1, netuid0)), + coldkey1, + Some((hotkey1, netuid0)), + coldkey2, + stake_amount, + ); + let dynamic_fee_7 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, netuid0)), + &coldkey1, + Some((&hotkey1, netuid0)), + &coldkey2, + I96F32::saturating_from_num(stake_amount), + ); + assert_eq!(stake_fee_7, dynamic_fee_7); + + // Test stake fee for *swap* from non-root to non-root + let stake_fee_8 = SubtensorModule::get_stake_fee( + Some((hotkey1, netuid0)), + coldkey1, + Some((hotkey1, netuid1)), + coldkey1, + stake_amount, + ); + let dynamic_fee_8 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, netuid0)), + &coldkey1, + Some((&hotkey1, netuid1)), + &coldkey1, + I96F32::saturating_from_num(stake_amount), + ); + assert_eq!(stake_fee_8, dynamic_fee_8); + }); +} + +#[test] +fn test_stake_fee_calculation() { + new_test_ext(1).execute_with(|| { + let hotkey1 = U256::from(1); + let coldkey1 = U256::from(2); + let hotkey2 = U256::from(3); + let coldkey2 = U256::from(4); + + let netuid0 = 1; + let netuid1 = 2; + let root_netuid = SubtensorModule::get_root_netuid(); + // Set SubnetMechanism to 1 (Dynamic) + SubnetMechanism::::insert(netuid0, 1); + SubnetMechanism::::insert(netuid1, 1); + + let alpha_divs = 100_000_000_000; + let total_hotkey_alpha = 100_000_000_000; + let tao_in = 100_000_000_000; // 100 TAO + let reciprocal_price = 2; // 1 / price + let stake_amount = 100_000_000_000_u64; + + let default_fee = DefaultStakingFee::::get(); + + // Setup alpha out + SubnetAlphaOut::::insert(netuid0, 100_000_000_000); + SubnetAlphaOut::::insert(netuid1, 100_000_000_000); + // Set pools using price + SubnetAlphaIn::::insert(netuid0, tao_in * reciprocal_price); + SubnetTAO::::insert(netuid0, tao_in); + SubnetAlphaIn::::insert(netuid1, tao_in * reciprocal_price); + SubnetTAO::::insert(netuid1, tao_in); + + // Setup alpha divs for hotkey1 + AlphaDividendsPerSubnet::::insert(netuid0, hotkey1, alpha_divs); + AlphaDividendsPerSubnet::::insert(netuid1, hotkey1, alpha_divs); + + // Setup total hotkey alpha for hotkey1 + TotalHotkeyAlpha::::insert(hotkey1, netuid0, total_hotkey_alpha); + TotalHotkeyAlpha::::insert(hotkey1, netuid1, total_hotkey_alpha); + + // Test stake fee for add_stake + let stake_fee_0 = SubtensorModule::calculate_staking_fee( + None, + &coldkey1, + Some((&hotkey1, netuid0)), + &coldkey1, + I96F32::from_num(stake_amount), + ); // Default for adding stake + assert_eq!(stake_fee_0, default_fee); + + // Test stake fee for remove on root + let stake_fee_1 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, root_netuid)), + &coldkey1, + None, + &coldkey1, + I96F32::from_num(stake_amount), + ); // Default for removing stake from root + assert_eq!(stake_fee_1, default_fee); + + // Test stake fee for move from root to non-root + let stake_fee_2 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, root_netuid)), + &coldkey1, + Some((&hotkey1, netuid0)), + &coldkey1, + I96F32::from_num(stake_amount), + ); // Default for moving stake from root to non-root + assert_eq!(stake_fee_2, default_fee); + + // Test stake fee for move between hotkeys on root + let stake_fee_3 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, root_netuid)), + &coldkey1, + Some((&hotkey2, root_netuid)), + &coldkey1, + I96F32::from_num(stake_amount), + ); // Default for moving stake between hotkeys on root + assert_eq!(stake_fee_3, default_fee); + + // Test stake fee for move between coldkeys on root + let stake_fee_4 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, root_netuid)), + &coldkey1, + Some((&hotkey1, root_netuid)), + &coldkey2, + I96F32::from_num(stake_amount), + ); // Default for moving stake between coldkeys on root + assert_eq!(stake_fee_4, default_fee); + + // Test stake fee for *swap* from non-root to root + let stake_fee_5 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, netuid0)), + &coldkey1, + Some((&hotkey1, root_netuid)), + &coldkey1, + I96F32::from_num(stake_amount), + ); // Charged a dynamic fee + assert_ne!(stake_fee_5, default_fee); + + // Test stake fee for move between hotkeys on non-root + let stake_fee_6 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, netuid0)), + &coldkey1, + Some((&hotkey2, netuid0)), + &coldkey1, + I96F32::from_num(stake_amount), + ); // Charge the default fee + assert_eq!(stake_fee_6, default_fee); + + // Test stake fee for move between coldkeys on non-root + let stake_fee_7 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, netuid0)), + &coldkey1, + Some((&hotkey1, netuid0)), + &coldkey2, + I96F32::from_num(stake_amount), + ); // Charge the default fee; stake did not leave the subnet. + assert_eq!(stake_fee_7, default_fee); + + // Test stake fee for *swap* from non-root to non-root + let stake_fee_8 = SubtensorModule::calculate_staking_fee( + Some((&hotkey1, netuid0)), + &coldkey1, + Some((&hotkey1, netuid1)), + &coldkey1, + I96F32::from_num(stake_amount), + ); // Charged a dynamic fee + assert_ne!(stake_fee_8, default_fee); + }); +} diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index ad1fd68526..96394743a4 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -540,7 +540,13 @@ fn test_swap_concurrent_modifications() { let additional_stake = 500_000_000_000; let initial_stake_alpha = I96F32::from(initial_stake).saturating_mul(SubtensorModule::get_alpha_price(netuid)); - let fee = SubtensorModule::calculate_staking_fee(netuid, &hotkey, initial_stake_alpha); + let fee = SubtensorModule::calculate_staking_fee( + None, + &new_coldkey, + Some((&hotkey, netuid)), + &new_coldkey, + initial_stake_alpha, + ); // Setup initial state add_network(netuid, 1, 1); diff --git a/pallets/subtensor/src/utils/identity.rs b/pallets/subtensor/src/utils/identity.rs index 0e83205cc0..c4b9fdc821 100644 --- a/pallets/subtensor/src/utils/identity.rs +++ b/pallets/subtensor/src/utils/identity.rs @@ -135,7 +135,7 @@ impl Pallet { SubnetIdentitiesV2::::insert(netuid, identity.clone()); // Log the identity set event - log::info!("SubnetIdentitySet( netuid:{:?} ) ", netuid); + log::debug!("SubnetIdentitySet( netuid:{:?} ) ", netuid); // Emit an event to notify that an identity has been set Self::deposit_event(Event::SubnetIdentitySet(netuid)); diff --git a/precompiles/src/extensions.rs b/precompiles/src/extensions.rs index 373bf0a60d..2d3d65a41c 100644 --- a/precompiles/src/extensions.rs +++ b/precompiles/src/extensions.rs @@ -91,7 +91,7 @@ pub(crate) trait PrecompileHandleExt: PrecompileHandle { ); } - log::info!("Dispatch succeeded. Post info: {:?}", post_info); + log::debug!("Dispatch succeeded. Post info: {:?}", post_info); Ok(()) } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 69e7cb9636..2bd96a1e1a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -205,7 +205,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: 248, + spec_version: 249, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -2066,6 +2066,10 @@ impl_runtime_apis! { fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account: AccountId32, coldkey_account: AccountId32, netuid: u16 ) -> Option> { SubtensorModule::get_stake_info_for_hotkey_coldkey_netuid( hotkey_account, coldkey_account, netuid ) } + + fn get_stake_fee( origin: Option<(AccountId32, u16)>, origin_coldkey_account: AccountId32, destination: Option<(AccountId32, u16)>, destination_coldkey_account: AccountId32, amount: u64 ) -> u64 { + SubtensorModule::get_stake_fee( origin, origin_coldkey_account, destination, destination_coldkey_account, amount ) + } } impl subtensor_custom_rpc_runtime_api::SubnetRegistrationRuntimeApi for Runtime { diff --git a/scripts/localnet.sh b/scripts/localnet.sh index cecce2d814..4de17f1521 100755 --- a/scripts/localnet.sh +++ b/scripts/localnet.sh @@ -61,7 +61,7 @@ if [[ $BUILD_BINARY == "1" ]]; then fi echo "*** Building chainspec..." -"$BUILD_DIR/release/node-subtensor" build-spec --disable-default-bootnode --raw --chain $CHAIN >$FULL_PATH +"$BUILD_DIR/release/node-subtensor" build-spec --disable-default-bootnode --raw --chain "$CHAIN" >"$FULL_PATH" echo "*** Chainspec built and output to file" # Generate node keys @@ -79,6 +79,7 @@ fi if [ $BUILD_ONLY -eq 0 ]; then echo "*** Starting localnet nodes..." + alice_start=( "$BUILD_DIR/release/node-subtensor" --base-path /tmp/alice @@ -107,6 +108,12 @@ if [ $BUILD_ONLY -eq 0 ]; then --unsafe-force-node-key-generation ) + # Provide RUN_IN_DOCKER local environment variable if run script in the docker image + if [ "${RUN_IN_DOCKER}" == "1" ]; then + alice_start+=(--unsafe-rpc-external) + bob_start+=(--unsafe-rpc-external) + fi + trap 'pkill -P $$' EXIT SIGINT SIGTERM ( @@ -114,4 +121,4 @@ if [ $BUILD_ONLY -eq 0 ]; then ("${bob_start[@]}" 2>&1) wait ) -fi \ No newline at end of file +fi